1. 程式人生 > 其它 >【Kubernetes系列四】pod探測及資源限制

【Kubernetes系列四】pod探測及資源限制

1.pod存活性探測

pod spec為容器列表中的相應容器定義其專用的探針即可啟用存活性探測,目前,k8s的容器支援存活性探測的方法包含:ExecAction、TCPSocketActon和HTTPGetAction。

(1) 設定exec探針

exec型別的探針通過在目標容器中執行由使用者自定義的命令來判定容器的健康狀態,若命令狀態返回值為0則表示成功通過探測。spec.containers.livenessProbe.exec欄位用於定義此類檢測,它只有一個屬性“command”,用於定義要執行的命令。
示列:

~]# cat liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
    name: lifecycle-exec
spec:
  containers:
  - name: lifecycle-echo-demo
    image: busybox
    args: ["/bin/sh","-c","touch /tmp/healthy; sleep 60; rm -rf /tmp/healthy; sleep 600"]
    livenessProbe:
      exec:
        command: ["test", "-e", "/tmp/healthy"]   #檔案存在則返回狀態碼0,表示成功通過測試
~]# kubectl apply -f liveness-exec.yaml
~]# kubectl describe pods/liveness-exec  #檢視觀察,超過60s,存活性探測失敗,進行重啟pod

exec指定的命令運行於容器中,會消耗容器的可用資源配額,另外,考慮到探測操作的效率本身等因素,探測操作的命令應該儘可能簡單和輕量。

(2) 設定HTTP探針

基於HTTP的探測向目標容器發起一個HTTP請求,根據其響應碼進行結果判定,響應碼形如2或3時表示檢測通過。spec.containers.livenessProbe.httpGet欄位用於定義此類檢測,它的可用配置欄位包括如下:
host:請求的主機地址,預設為podIP;也可以在httpHeaders中使用host來定義
httpHeaders:定義的請求報文首部
path:請求的HTTP資源路徑,即URL path
scheme:建立連線使用的協議,僅可為HTTP或HTTPS,預設為HTTP

示列:通過lifecycle中的postStart hook建立一個用於httpGet測試的頁面檔案healthz

~]# cat liveness-http.yaml
apiVersion: v1
kind: Pod
metadata:
    labels:
      test: liveness
    name: lifecycle-http
spec:
  containers:
  - name: lifecycle-http-demo
    image: nginx:1.12-alpine
    ports:
    - name: http
      containersPort: 80
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh","-c","echo 'Healthy' > /usr/share/nginx/html/healthz"]
    livenessProbe:
      httpGet:
        path: /healthz
        port: http
        scheme: HTTP

上面httpGet測試,請求資源路徑為"/healthz",地址預設為Pod IP。

~]# kubectl apply -f liveness-http.yaml
~]# kubectl describe pods liveness-http

接下來手動刪除常見的測試路徑的檔案

~]# kubectl exec liveness-http rm /usr/share/nginx/html/healthz
~]# kubectl describe pods liveness-http  #再次檢視,探測失敗後,容器被殺掉重建

這種檢測方式僅對分層架構中的當前一層有效,例如,它能檢測應用程式工作正常與否的狀態,但重啟操作卻無法解決其後端服務(如資料庫或快取服務)導致的故障。此時,容器可能會被一次次的重啟,直到後端服務恢復正常為止。其他兩種方式也存在類似的問題。

(3) 設定TCP探針

基於TCP的存活性探測用於向容器的特定埠發起TCP請求並嘗試建立連線進行結果判定,連線建立成功即為通過檢測。它比基於HTTP的探測要更高效更節省資源,但精確度略低,畢竟建立連線成功未必意味著頁面資源可用。spec.containers.livenessProbe.Socket欄位用於定義此類檢測,主要包含以下兩個可用的屬性:
host:請求連線的目標IP地址,預設為podIP
port:請求連線的目標埠,必選欄位。

示列:

~]# cat liveness-tcp.yaml
apiVersion: v1
kind: Pod
metadata:
    labels:
      test: liveness
    name: lifecycle-tcp
spec:
  containers:
  - name: lifecycle-tcp-demo
    image: nginx:1.12-alpine
    ports:
    - name: http
      containersPort: 80
    livenessProbe:
      tcpSocket:
        port: http

(4) 存活性探測行為屬性

使用kubectl describe命令可檢視配置了存活性探測的pod物件的相關資訊,相關內容類似如下:
Liveness: exec [test -e /tmp/healthy] delay=0s timeout=1s period=10s #success=1 #failure=3

它給出了探測方式及額外的配置屬性delay、timeout、period、success和failure及其各自的相關屬性值。使用者沒有明確定義這些屬性欄位時,它們會使用各自的預設值。這些屬性資訊可通過spec.containers.livenessProbe的如下屬性欄位來給出:

initialDelaySeconds:存活性探測延遲時長,即容器啟動多久之後再開始第一次探測操作,顯示為delay屬性;預設為0s,即容器啟動後立刻便開始進行探測。
timeoutSeconds:存活性探測的超時時長,顯示為timeout屬性,預設為1s,最小值也是1s。
periodSeconds:存活性探測的頻度,顯示為period屬性,預設為10s,最小值為1s;過高的頻率會對pod物件帶來較大的額外開銷,而過低的頻率又會使得對錯誤的反應不及時。
successThreshold:處於失敗狀態時,探測操作至少連續多少次的成功才被認為是通過檢測,顯示為success屬性,預設值為1,最小值也為1。
failureThreshold:處於成功狀態時,探測操作至少連續多少次的失敗才被視為檢測不通過,顯示為failure屬性,預設值為3,最小值為1。

2.pod就緒性檢測

pod物件啟動後,容器應用通常需要一段時間才能完成其初始化過程,避免pod物件啟動後立即讓其處理客戶端請求,而等待容器初始化工作執行完成並轉為就緒狀態,尤其是存在其他提供相同服務的pod物件的場景更是如此。

與存活性探測機制相同,就緒性探測也支援Exec、HTTP GET和TCPSocket三種探測方式,且各自的定義機制也都相同。但與存活性探測觸發的操作不同的是,探測失敗時,就緒性探測不會殺死或重啟容器以保證其健康性,而是通知其尚未就緒,並觸發依賴於其就緒狀態的操作(例如,從service物件中移除此pod物件)以確保不會有客戶端請求接入此pod物件。未定義就緒性探測的pod物件在pod進入running狀態後將立即就緒,在容器需要時間進行初始化的場景中,在應用真正就緒之前必然無法正常相應客戶端請求,因此,生成時間中,必須為關鍵性pod資源中的容器定義就緒性探測機制。

將容器定義中的livenessProbe欄位替換為readinessProbe即可定義就緒性探測的配置。

3.資源需求及資源限制

在k8s上,可由容器或pod請求或消費的計算資源時指cpu和記憶體,這也是目前僅有的受支援的兩種型別。相比較來說,cpu屬於可壓縮資源,即資源額度可按需收縮,而記憶體則是不可壓縮型資源,對其執行收縮操作可能會導致某種程度的問題。

目前來說,資源隔離尚且屬於容器級別,cpu和記憶體資源的配置需要在pod中的容器上執行,每種資源均可由request屬性定義其請求的確保可用值,即容器執行可能用不到這些額度的資源,但用到的時候必須要確保有如此多的資源可用,而limits屬性則用於吸納子資源可用的最大值,即硬限制。通常把資源配置稱作pod資源的請求和限制,只不過它是pod內所有容器上某種型別資源的請求和限制的總和。

在k8s系統上,1個單位的cpu相當於虛擬機器上的1顆cpu(vcpu)或物理機上的一個超執行緒或邏輯cpu,它支援分數計量方式,一個核心相當於1000個微核心,因此500m相當於是0.5個核心,記憶體的計量方式與日常使用方式相同,預設單位是位元組,也可以使用E、P、T、G、M和K作為單位字尾。

(1) 資源需求
自主式pod要求為stress容器確保128M的記憶體及五分之一個cpu核心資源可用,它執行stress-ng映象啟動一個程序進行記憶體效能壓力測試,滿載測試時它也會盡可能多地佔用cpu資源,另外再啟動一個專用的cpu壓力測試程序。stress-ng是一個多功能系統壓力測試工具,master/worker模型,master為主程序,負責生成和控制子程序,worker是負責執行各類特定測試的子程序。
示例:

~]# cat pod-resources-test.yaml
apiVersion: v1
kind: Pod
metadata:
    name: stress-pod
spec:
  containers:
  - name: stress
    image: ikubernetes/stress-ng
    command: ["/usr/bin/stress-ng", "-m 1", "-c 1", "-metrics-brief"]
    resources:
      requests:
        memory: "128Mi"
        cpu: "200m"
~] kubectl create -f pod-resources-test.yaml

叢集中的每個節點都擁有定量的cpu和記憶體資源,排程pod時,僅那些被請求資源的餘量可容納當前排程的pod的請求量的節點才可作為目標節點。也就是說,k8s的排程器會根據容器的requests屬性中定義的資源需求量來判定僅哪些節點可接受執行相關的pod資源,而對於一個節點的資源來說,每執行一個pod物件,其requestes中定義的請求量都要被預留,直到被所有pod物件瓜分完畢為止。
對於pod物件內的cpu資源,可以被壓縮,記憶體為非壓縮資源,所以pod在記憶體資源緊張時會因OOM被殺死

(2) 資源限制
容器的資源需求僅能達到為其保證可用的最少資源量的目的,它並不會限制容器的可用資源上限,因此對因應用程式自身存在bug等多種原因而導致的系統資源被長期佔用的情況則無計可施,這就需要通過limits屬性定義資源的最大可用量。
資源分配時,可壓縮型資源cpu的控制閾可自由調節,容器程序無法獲得超出其cpu配額的可用時間。不過,如果程序申請分配超出其limits屬性定義的硬限制的記憶體資源時,它將被OOM killer殺死。不過,隨後可能會被其控制程序所重啟。例如,容器程序的pod物件會被殺死並重啟(重啟策略為always或onfailure時),或者是容器程序的子程序被其父程序所重啟。
示例:

~]# cat memleak-pod.yaml
apiVersion: v1
kind: Pod
metadata:
    name: memleak-pod
    labels:
      app: memleak
spec:
  containers:
  - name: simmemleak
    image: saadali/simmemleak
    resources:
      requests:
        memory: "64Mi"
        cpu: "1"
      limits:
        memory: "64Mi"
        cpu: "1"
~] kubectl apply -f pod-resources-test.yaml

與requests不同的是,limits並不會影響pod的排程結果,也就是說,一個節點上的說有pod物件的limits數量之和可以大於節點所擁有的資源量,即支援資源的過載使用。不過,這麼一來一旦資源耗盡,尤其是記憶體資源耗盡,則必然會有容器因OOMKilled而終止。另外,k8s僅會確保pod能夠獲得他們請求的cpu時間額度,他們能否獲得額外的cpu時間,則取決於其他正在執行的作業對cpu資源的佔用情況。例如,對於總數為1000m的cpu來說,容器a請求使用200m,容器b請求使用500m,在不超出它們各自的最大限額的前提下,餘下的300m在雙方都需要時會以2:5的方式進行配置。

(3) 容器的可見資源
於容器中執行top等命令觀察資源可用量資訊時,即便定義了requests和limits屬性,雖然其可用資源受限於此兩個屬性的定義,但容器中可見的資源量依然是節點級別的可用總量。

(4) pod的服務質量類別
k8s允許節點資源對limits的過載使用,這意味著節點無法同時滿足其上的所有pod物件以資源滿載的方式執行。於是,在記憶體資源緊缺時,應該以何種次序先後終止哪些pod物件?k8s無法自行對此做出決策,它需要藉助於pod物件的優先順序完成判定。根據pod物件的requests和limits屬性,k8s將pod物件歸類到BestEffort、Burstable和Guaranteed三個服務質量類別下,具體如下:

Guaranteed:每個容器都為cpu資源設定了具有相同值的requests和limits屬性,以及每個容器都為記憶體資源設定了具有相同值的requests和limits屬性的pod資源會自動歸屬於此類別,這類pod資源具有最高優先順序.
Burstable:至少有一個容器設定了cpu或記憶體資源的requests屬性,但不滿足Guaranteed類別要求的pod資源將自動歸屬此類別,它們具有中等優先順序。
BestEffort:未為任何一個容器設定requests和limits屬性的pod資源將自動歸屬於此類別,它們的優先順序為最低級別。

記憶體資源緊缺時,BestEfford類別的容器將首當其衝地終止,因為系統不為其提供任何級別的資源保證,但換來的好處是,它們能夠在可用時做到儘可能多地佔用資源。若已然不存在BestEfford類別的容器,則接下來是有著中等優先順序的Burstable類別的pod被終止。Guaranteed類別的容器擁有最高優先順序,它們不會被殺死,除非其記憶體資源需求超限,或者OOM時沒有其他更低優先順序的pod資源存在。

每個執行狀態的容器都有其OOM得分,得分越高越會被優先殺死。OOM得分主要根據兩個維度進行計算:由QoS類別繼承而來的預設分值和容器的可用記憶體資源比例。
同等類別的pod資源的預設分值相同。同等級別優先順序的pod資源在OOM時,與自身requests屬性相比,其記憶體佔用比例最大的pod物件將被首先殺死。需要特別說明的是,OOM是記憶體耗盡時的處理機制,它們與可壓縮型資源cpu無關,因此cpu資源的需求無法得到保證時,pod僅僅是暫時獲取不到相應的資源而已。