Kubernetes很酷,讓我們的機器的資源使用率和運維效率都得到了提升。然而,要想用好Kubernetes,還是有些東西要注意的,否則可能會給自己帶來一些小麻煩。在生産環境裡,如何保證我們的應用能穩定可靠的運作在Kubernetes裡呢?這篇文章将分享在阿裡雲容器服務上使用Kubernetes的一些有用的tips。
Master節點規格
通過容器服務建立出來的Kubernetes叢集,Master節點上運作着etcd、kube-apiserver、kube-controller等核心元件,對于Kubernetes叢集的穩定性有着至關重要的影響,對于生産環境的叢集,必須慎重選擇Master規格。Master規格跟叢集規模有關,叢集規模越大,所需要的Master規格也越大。當然,這裡的“叢集規模”是個很抽象的詞,我們可以從多個次元衡量Kubernetes叢集規模:節點數量/Pod數量/部署頻率/通路量……這裡簡單的認為叢集規模就是叢集裡的節點數量。對于常見的叢集規模,可以參考這種如下的方式選擇Master節點的規格(對于測試環境,規格可以小一些。下面的選擇能盡量保證Master負載維持在一個較低的水準上):
- 1-5個節點,Master規格:4C8G(不建議2C4G)
- 6-20個節點,Master規格:4C16G
- 21-100個節點,Master規格:8C32G
- 100-200個節點,Master規格:16C64G
選擇合理的磁盤大小
Kubernetes節點需要的磁盤空間也不小,docker鏡像、系統日志、應用日志都儲存在磁盤上。建立叢集的時候,要考慮每個節點上要部署的Pod數量,每個Pod的日志大小、鏡像大小、臨時資料,再加上一些系統預留的值。
建立出來的ECS,OS占了大約3G多的空間,我們可以給它多留點,算8G。剩下的空間都可以用在Pod上。
使用多可用區
阿裡雲支援很多Region,每個Region下又有不同的可用區。可用區是指在同一地域内,電力和網絡互相獨立的實體區域。多可用區能夠實作跨地域的容災能力。當然,響應的會帶來額外的網絡延時。建立Kubernetes叢集時,也可以建立一個包含多個可用區的叢集。在容器服務叢集建立頁面,點選“建立Kubernetes”按鈕右邊的小三角可以看到。
聲明每個Pod的resource
我最經常遇到的Kubernetes問題,就是一個節點上排程了太多的Pod,導緻節點負載太高,完全沒法對外提供服務。怎麼避免這種情況出現呢?
在Kubernetes中部署Pod時,你可以指定這個Pod需要的資源,Kubernetes在部署這個Pod的時候,就會根據Pod的需求找一個具有充足空閑資源的節點部署這個Pod。下面的例子中,聲明tomcat這個Pod需要0.25核CPU,64M的記憶體,運作中實際使用不能超過0.5核CPU和128M記憶體。
apiVersion: v1
kind: Pod
metadata:
name: tomcat
spec:
containers:
- name: tomcat
image: tomcat
resources: # 資源聲明
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Kubernetes采用靜态資源排程方式,對于每個節點上的剩餘資源,它是這樣計算的:
節點剩餘資源=節點總資源-已經配置設定出去的資源
,并不是實際使用的資源。如果你自己偷偷跑到節點上手工運作一個很耗資源的程式,Kubernetes并不能感覺到。
另外所有Pod上都要聲明resources。對于沒有聲明resources的Pod,它被排程到某個節點後,Kubernetes也不會在對應節點上扣掉這個Pod使用的資源。可能會導緻節點上排程過去太多的Pod
配置監控
在Pod上配置了resource很大程度了避免了節點堆積太多Pod的問題,然而還不夠。我們還可以再加一道保險:配置節點監控。通過添加監控告警規則,節點上的資源使用使用量很高的時候,我們可以知道出問題了。
通過容器服務建立Kubernetes叢集時,會自動在雲監控建立兩個應用分組,一個對應Master節點,一個對應Worker節點。我們可以在這兩個組下面添加一些報警規則,對組裡所有的機器生效。後續加入的節點,也會自動出現在組裡,不用單獨再去配置報警規則。
主要配置ECS資源的報警規則就可以了。
啟動時等待下遊服務,不要直接退出
應用或多或少都有一些外部依賴,比如需要從db讀取資料或者依賴另外一個服務的接口。應用啟動的時候,未必外部依賴都能滿足,過去手工運維的時候,通常采用依賴不滿足立即退出的方式,也就是所謂的failfast,但是在Kubernetes中,這種政策就未必合适了。原因何在?Kubernetes中多數運維操作都是自動的,不需要人工介入,比如部署應用,你不用自己選擇節點,再到節點上啟動應用,應用挂了,也不用自己跑過去重新開機,Kubernetes會自動把應用拉起來,負載高了,還可以通過HPA自動擴容。
針對啟動時依賴不滿足這個場景,假設有兩個應用A和B,A依賴B,剛好運作在同一個節點上。這個節點因為某些原因重新開機了,重新開機之後,A先被拉起來了,這個時候B還沒啟動,對A來說就是依賴不滿足。如果A還是按照傳統的方式直接退出了A,當B啟動之後,A也不會再被拉起了,必須人工介入處理才行。
Kubernetes的最好的做法是啟動時檢查依賴,如果不滿足,輪詢等待,而不是直接退出。可以通過
Init Container完成這個功能。
配置restart policy
Pod運作過程中程序退出是個很常見的問題,無論是代碼裡的一個bug,還是占用記憶體太多被OOM killer幹掉,都會導緻應用程序退出,Pod挂掉。Pod退出了怎麼辦?既然用了Kubernetes,就不要再用手工重新開機這種很low的方式了,隻要在Pod上配置restartPolicy,就能實作Pod挂掉之後自動拉起。
apiVersion: v1
kind: Pod
metadata:
name: tomcat
spec:
containers:
- name: tomcat
image: tomcat
restartPolicy: OnFailure #
restartPolicy有三個可選值
- Always: 總是自動重新開機
- OnFailure:異常退出才自動重新開機 (程序退出狀态非0)
- Never:永遠不重新開機
配置Liveness Probe和Readiness Probe
Pod處于Running狀态和Pod能正常提供服務是完全不同的概念,一個Running狀态的Pod,裡面的程序可能發生了死鎖而無法提供服務。但是因為Pod還是Running的,Kubernetes也不會自動重新開機這個Pod。是以我們要在所有Pod上配置Liveness Probe,探測Pod是否真的存活,是否還能提供服務。如果Liveness Probe發現了問題,Kubernetes會重新開機Pod。
Readiness Probe用于探測Pod是不是可以對外提供服務了。應用啟動過程中需要一些時間完成初始化,在這個過程中是沒法對外提供服務的,通過Readiness Probe,我們可以告訴Ingress或者Service能不能把流量轉發給這個Pod上。當Pod出現問題的時候,Readiness Probe能避免新流量繼續轉發給這個Pod。
apiVersion: v1
kind: Pod
metadata:
name: tomcat
spec:
containers:
- name: tomcat
image: tomcat
livenessProbe:
httpGet:
path: /index.jsp
port: 8080
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
httpGet:
path: /index.jsp
port: 8080
每個程序一個容器
很多剛剛接觸容器的人喜歡按照老習慣把容器當虛拟機用,在一個容器裡塞入多個程序,監控程序、日志程序、sshd程序、甚至整個Systemd。這種方式有什麼問題呢?首先,判斷Pod整體的資源占用會變複雜,不友善實施前面提到resource limit。其次,容器内隻有一個程序的情況,程序挂了,外面的容器引擎可以清楚的感覺到,然後重新開機容器,如果容器内有多個程序,某個程序挂了,容器未必受影響,外部的容器引擎感覺不到容器内程序挂了,也不會對容器做任何操作,但是容器實際上已經不能正常工作了。
如果确實有幾個程序需要協同工作,在Kubernetes裡也很容易實作,舉個例子,nginx和php-fpm,通過unix domain socket通信,我們可以用一個包含兩個容器的Pod,unix socket放在兩個容器的共享volume中。
確定不存在SPOF
如果應用隻有一個執行個體,當執行個體挂掉的時候,雖然Kubernetes能夠将執行個體重新拉起,但是中間不可避免的存在一段時間的不可用。甚至更新應用,釋出一個新版本的時候,也會出現這種情況。在Kubernetes裡,盡量避免直接使用Pod,盡可能使用Deployment/StatefulSet,并且讓應用的scale在兩個以上。