Kubernetes運維之微服務生產環境採坑分享

生產環境經驗java

一、限制了容器資源,還常常被殺死?
二、滾動更新之健康檢查重要性
三、滾動更新之流量的丟失

先說一下第一個問題,限制容器資源,還常常去殺死的緣由?
就是說部署的java應用,不一會就重啓了,其實重啓就是在重建了,這就意味着你的pod是不健康的,而後k8s從新再幫你去拉取了,這樣的話就要去找問題去排查了,說白了其實就是被殺死了,能夠經過describe去查看一下事件,通常都會能看到,因爲健康狀態檢查沒經過,而後再去拉取的,由於是java應用,因爲堆內存溢出,而後被kill掉,在日誌最後一行會出現一個kill的字段,看一下它重啓的緣由,以前我遇到的是它的堆內存沒限制住,它自己有jvm,jvm主要有內存作交換數據的,它堆內存主要是性能設計的一個體現,因此他的堆內存很容易就會超出,超出以後呢,極可能會被k8s殺死掉,爲何k8s會kill它,由於它超出了限制,默認容器會使用宿主機全部的資源,若是不作資源限制,會影響整個宿主機,而後整個宿主機資源不夠會實現飄移,會轉移到其餘主機上,而後再異常,可能會起到一種雪崩的效應,因此通常咱們都是要作資源限制的,難道這個限制,限制不了java應用嗎?其餘它是限制不住的,雖然k8s部署應用仍是很方便的,可是部署java應用仍是不兼容的,
好比它不能識別當前java容器的限制,就是不能識別咱們指定的限制,也就是你在yaml去限制的,容器的堆內存沒有限制,它就會超出這個限制,若是超出這個limits的限制,k8s就會把它殺掉,k8s自身它有這個策略,若是超出這個容量的限制,就會幫你殺掉,再幫你拉起。docker

面對java這樣的堆內存稍微有一點流量突發,就可能資料利用率就上來了,因此這個幅度仍是比較大的,這樣就會致使k8s殺掉,而後再拉起,這樣循環,可能一天幾百次的這樣效果。shell

說到這個問題,怎麼來解決docker的資源限制,其實這個資源限制仍是用的這個docker來作的,只不過k8s把他轉換成了,只不過k8s去調用接口去作一些限制,其實就是怎麼讓docker去識別java堆內存的限制,來解決這個問題。
來解決這個問題有兩種方案,也就是爲這個java再配置堆內存的使用
配置java堆內存的使用
Java -Xmx最大的堆內存使用,一個是-Xms是初始的堆內存使用
通常都設置一個最大的堆內存使用,若是不設置超出這個設置會不斷使用宿主機的一個內存,而致使物理內存不夠用,而出現堆內存溢出的現象,這個很廣泛,咱們就用這個java -Xmx就是當快用滿時,它會有一個垃圾回收,再進行循環的使用,這樣能保證這個java應用穩定的運行,全部咱們在yaml去配置資源限制確定是不夠的,咱們必須爲這個java去設置這個堆內存,咱們不可能手動的在Dockerfile去寫這個吧,通常在dockerfile去傳入這個值,在yaml文件裏設置一個變量數據庫

env:
  - name: JAVA_OPTS
   value: 「-Xmx1g 「

下面就是咱們以前配置的容器資源的一些限制這個變量就會就會傳入pod裏面,也就是這個構建鏡像的容器裏,也就是起到容器CMD 命令下去傳入$JAVA_OPTS的變量,它會調用咱們系統的系統變量,這個系統變量已經賦予它值了,因此它能直接飲用這個變量了,去起到這個應用,從而設置這個堆內存大小,這個值建議要比limlts要小一點,小個10%吧,由於超過這個limits限制就會殺死,再拉取了。
通常設置好,重建一下鏡像,進入容器中去查看一下進程就能夠看到,其餘的也是這麼設置的後端

第二個問題,滾動更新之健康檢查的重要性
滾動更新是k8s的默認策略,通常我們部署到k8s以後第一個會使用到的,當你配置了健康檢查時,滾動更新會根據probe的狀態來判斷是否是繼續的更新容許接入流量,也就是你當前的應用是否是提供服務,這樣你滾動更新的過程當中,纔會確保你是否是有可用的節點,保證一個平滑的升級,因此這是滾動更新設置的一個之初,那健康狀態檢查是很重要的
健康狀態檢查在滾動更新啓動什麼做用呢?api

列入一個副本,啓動以後提供業務須要一分鐘才能提供服務,好比java啓動比較慢,若是沒有健康狀態檢查,來確保他是否是準備好,就直接認爲它準備好了,這期間啓動起來,一分鐘以內它是沒法提供服務的,因此新來的流量確定是沒法處理的,這是第一種狀況,第二種狀況,因爲人爲的配置錯誤,好比鏈接不上數據庫了,或者鏈接不上其餘地方了,或者配置文件哪裏寫錯了,那觸發一個滾動更新,那這個pod呢所有滾動更新完成了,結果都是由問題的,這樣的話,新副本都把舊副本替換掉了,這樣的話,生產環境中後果很嚴重的,不少服務都會沒法提供了,因此在配置滾動更新的時候,健康狀態檢查必定要配上,那配上了健康狀態檢查以後,新的副本檢查完成以後纔會轉發新的流量,若是它沒有經過它不會所有替換的,也就是它不會繼續更新了,由於它是有一個可用節點的限制,若是可用節點達不到這個數,就不會繼續更新,健康狀態檢查有兩種類型,readinessprobe是就緒的檢查,這兩種檢查方式也有不一樣的方式,好比http,探測一個url,還有tcp socket,端口的探測,還有一個執行shell命令,執行一個shell命令,判斷一個返回值,因此提供者三種健康狀態檢查的方法,readinessprobe就緒檢查也就是你的Pod檢查失敗,若是是http,能夠經過一個頁面去探測,判斷這個返回狀態碼,若是探測這個本地的端口不通的話,它不會讓它加入service後面,由於service做爲你整個的統一訪問入口嘛,若是它不經過的話,新的流量也不會轉發給它,這就是就緒檢查,若是健康狀態不經過不會給你轉發新的流量,另外就是initialDelaySeconds:60,就是檢查60秒,由於通常java應用啓動也就是一分鐘左右,還有一個periodSeconds:10,也就是沒經過10秒鐘在作一次,而後就是livenessProbe:存活檢查。jvm

也就是檢查失敗它會殺死容器,根據你重啓策略,通常是重建,再給你新拉取一個容器,再判斷有沒有準備好,判斷的方法也是根據端口的探測這些的,或者特能夠用其餘兩種方法,http,exec,因此通常這兩個都要配置上,就緒檢查呢就是不爲你分配新的流量,存活檢查就是去幫你從新拉取。socket

最後一個問題滾動更新之丟失的流量
通常就是鏈接拒絕,響應錯誤,調用不到
通常滾動更新是關閉現有的pod,再起一新的pod,關閉現有的實際上是就是刪除了一個pod,而後apiserver會通知給kubelet,而後kubelet會關閉這個容器,而後從service後端摘掉,就不分發新的流量了,而後移除掉,而後再告訴kube-proxy,你能夠處理新的轉發規則了,而後調度到節點上,其實這也是一個pod的下線週期
另外再轉發的過程當中,轉發新的pod時間段裏,它是有間隔的,關閉pod以後會有一個等待時間,在這個時間呢,可能還會接入一些新的流量,可是它的服務已經再也不處理新的請求了,因此會致使鏈接拒絕了,怎麼去解決這個問題呢,實際上readiness探針在整個過程當中並起到關鍵的做用,一旦endpoint收到pod 的刪除事件後,這已經就與readiness探測結果不相關了tcp

怎麼保證它優雅的去處理呢?
其實之須要在關閉這個pod 時加個休眠的時間,其實就能夠解決這個問題了,在關閉和啓動都是有一個鉤子存在的,全部能夠在關閉容器前,執行這個鉤子,鉤子這個定義一個shell,y也能夠定義一個http請求,也就是支持者兩種類型,也就是在container同級,env這裏
休眠5秒也就是你關閉的容器不會立刻退出,而後休眠5秒鐘,再去關閉着應用,這5秒可以足夠讓kube-proxy刷新這個規則,這樣的話,就不會將新加入的流量轉發到這個剛關閉的pod上,增長這個鉤子就能暫緩你關閉pod的時間,從而讓kube-proxy增長刷新規則的時間,
添加ide

lifecycle :
preStop :
     exec :
           command :
             - sh
             - -c
             - 「sleep 5」

這樣,你不須要修改你應用的代碼,這樣的話,滾動更新就不會轉發即將關閉的pod上了,因此也能解決這個相關的問題了。

相關文章
相關標籤/搜索