1. 程式人生 > 其它 >Kubernetes之Pod詳解

Kubernetes之Pod詳解

Pod型別

普通Pod

普通Pod一旦被建立,就會被放入etcd中儲存,隨後會被Kubernetes Master排程到某個具體的Node上並進行繫結(Binding),且該Pod會被對應的Node上的kubelet程序例項化成一組相關的Docker容器並啟動。

在預設情況下,當Pod裡的某個容器停止時,Kubernetes會自動檢測到這個問題並且重新啟動這個Pod(重啟Pod裡的所有容器),如果Pod所在的Node宕機,就會將這個Node上的所有Pod重新排程到其他節點上。

在 Kubernetes 中,所有物件都使用 manifest(yaml或json)來定義,下面定義一個簡單的Nginx Pod:

apiVersion: v1
kind: Pod
metadata:  
  name: nginx  
  labels:    
    app: nginx
spec:  
  containers:  
  - name: nginx    
    image: nginx    
    ports:    
    - containerPort: 80

釋出

kubectl create -f nginx.yaml

靜態Pod

靜態Pod是由kubelet進行管理的僅存在於特定Node上的Pod。它們不能通過API Server進行管理,並且不會像普通Pod一樣被存放在Kubernetes的etcd儲存裡,而是被存放在某個具體的Node上的一個具體檔案中,只在此Node上啟動、執行。

無法與ReplicationController、Deployment或者DaemonSet進行關聯,並且kubelet無法對它們進行健康檢查。靜態Pod總是由kubelet建立的,並且總在kubelet所在的Node上執行。

靜態 Pod 永遠都會繫結到一個指定節點上的 Kubelet

建立靜態Pod有兩種方式:配置檔案方式和HTTP方式。

一、配置檔案方式

設定kubelet的啟動引數“--config”,指定kubelet需要監控的配置檔案所在的目錄,kubelet會定期掃描該目錄,並根據該目錄下的.yaml.json檔案進行建立操作。假設配置目錄為/etc/kubelet.d/,配置啟動引數為--config=/etc/kubelet.d/

,然後重啟kubelet服務。

上述方式未成功,下次再試;

將yaml檔案放在/etc/kubernetes/manifests目錄下,kubelet會定期掃描該目錄,並根據該目錄下
的.yaml或.json檔案進行建立操作。如果沒反應重啟kubelet

cat > static-web.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    name: static-web
spec:
  containers:
  - name: static-web
    image: nginx
    ports:
    - name: web
      containerPort: 80
EOF

等待一會兒,檢視本機中已經啟動的容器,到Master上檢視Pod列表,可以看到這個static pod。

由於靜態Pod無法通過API Server直接管理,所以在Master上嘗試刪除這個Pod時,會使其變成Pending狀態,且不會被刪除。

刪除該Pod的操作只能是到其所在Node上將其定義檔案*.yaml檔案刪除

二、HTTP方式

通過設定kubelet的啟動引數“--manifest-url”,kubelet將會定期從該URL地址下載Pod的定義檔案,並以.yaml或.json檔案的格式進行解析,然後建立Pod。其實現方式與配置檔案方式是一致的。

Pod容器共享Volume

同一個Pod中的多個容器能夠共享Pod級別的儲存卷Volume。Volume可以被定義為各種型別,多個容器各自進行掛載操作,將一個Volume掛載為容器內部需要的目錄,如圖所示。

在下面的例子中,在Pod內包含兩個容器:tomcat和busybox,在Pod級別設定Volume“app-logs”,用於tomcat向其中寫日誌檔案,busybox讀日誌檔案。

apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: tomcat
    image: tomcat
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: app-logs
      mountPath: /usr/local/tomcat/logs
  - name: busybox
    image: busybox
    command: ["sh", "-c", "tail -f /logs/catalina*.log"]
    volumeMounts:
    - name: app-logs
      mountPath: /logs
  volumes:
  - name: app-logs
    emptyDir: {}

這裡設定的Volume名為app-logs,型別為emptyDir,掛載到tomcat容器內的/usr/local/tomcat/logs目錄,同時掛載到logreader容器內的/logs目錄。
tomcat容器在啟動後會向/usr/local/tomcat/logs目錄寫檔案,logreader容器就可以讀取其中的檔案了。

logreader容器的啟動命令為tail -f /logs/catalina*.log,通過kubectl logs命令檢視logreader容器的輸出內容:

kubectl logs volume-pod -c busybox

Pod的配置管理

ConfigMap

Docker通過將程式、依賴庫、資料及配置檔案“打包固化”到一個不變的映象檔案中的做法,解決了應用的部署的難題,但這同時帶來了配置檔案中的引數在執行期如何修改的問題。 不可能在啟動Docker容器後再修改容器裡的配置檔案,然後用新的配置檔案重啟容器裡的使用者主程序。

為了解決這個問題,Docker提供了兩種方式:

  • 在執行時通過容器的環境變數來傳遞引數;
  • 通過Docker Volume將容器外的配置檔案對映到容器內。

這兩種方式都有其優勢和缺點,在大多數情況下,後一種方式更合適 的系統,因為大多數應用通常從一個或多個配置檔案中讀取引數。但這種方式也有明顯的缺陷: 必須在目標主機上先建立好對應的配置檔案,然後才能對映到容器裡。上述缺陷在分散式情況下變得更為嚴重,因為無論採用哪種方式,寫入(修改)多臺伺服器上的某個指定檔案,並確保這些檔案保持一致,都是一個很難完成的目標。

此外,在大多數情況下, 都希望能集中管理系統的配置引數,而不是管理一堆配置檔案。針對上述問題,Kubernetes給出了一個很巧妙的設計實現,把所有的配置項都當作key-value字串,當然value可以來自某個文字檔案,比如配置項password=123456user=roothost=192.168.8.4用於表示連線FTP伺服器的配置引數。

這些配置項可以作為Map表中的一個項,整個Map的資料可以被持久化儲存在Kubernetes的Etcd資料庫中,然後提供API以方便Kubernetes相關元件或客戶應用CRUD操作這些資料,上述專門用來儲存配置引數的Map就是Kubernetes ConfigMap資源物件。接下來,Kubernetes提供了一種內建機制,將儲存在etcd中的ConfigMap通過Volume對映的方式變成目標Pod內的配置檔案,不管目標Pod被排程到哪臺伺服器上,都會完成自動對映。

進一步地,如果ConfigMap中的key-value資料被修改,則對映到Pod中的“配置檔案”也會隨之自動更新。於是,Kubernetes ConfigMap就成了分散式系統中最為簡單(使用方法簡單,但背後實現比較複雜)且對應用無侵入的配置中心。ConfigMap配置集中化的一種簡單方案如圖所示

建立Configap資源物件

ConfigMap供容器使用的典型用法如下。

  1. 生成為容器內的環境變數。
  2. 設定容器啟動命令的啟動引數(需設定為環境變數)。
  3. 以Volume的形式掛載為容器內部的檔案或目錄。

ConfigMap以一個或多個key:value的形式儲存在Kubernetes系統中供應用使用,既可以用於表示一個變數的值(例如apploglevel=info),也可以用於表示一個完整配置檔案的內容(例如server.xml=<?xml...>...)

一、通過YAML配置檔案方式建立

將幾個應用所需的變數定義為ConfigMap

cat > cm-appvars.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appvars
data:
  apploglevel: info
  appdatadir: /var/data
EOF

檢視建立好的ConfigMap

kubectl get configmap
kubectl describe configmap cm-appvars

二、通過kubectl命令列方式建立

(1)通過--from-file引數從檔案中進行建立,可以指定key的名稱,也可以在一個命令列中建立包含多個key的ConfigMap

kubectl create configmap NAME --from-file=[key=]source

(2)通過--from-file引數從目錄中進行建立,該目錄下的每個配置檔名都被設定為key,檔案的內容被設定為value

kubectl create configmap NAME --from-file=config-files-dir

(3)使用--from-literal時會從文字中進行建立,直接將指定的key#=value#建立為ConfigMap的內容

kubectl create configmap NAME --from-literal=key1=value1 --from-literal=key2=value2

使用ConfigMap的限制條件

  • ConfigMap必須在Pod之前建立。
  • ConfigMap受Namespace限制,只有處於相同Namespace中的Pod才可以引用它。
  • ConfigMap中的配額管理還未能實現。
  • kubelet只支援可以被API Server管理的Pod使用ConfigMap。kubelet在本Node上通過 --manifest-url--config自動建立的靜態Pod將無法引用ConfigMap。
  • 在Pod對ConfigMap進行掛載(volumeMount)操作時,在容器內部只能掛載為“目錄”,無法掛載為“檔案”。在掛載到容器內部後,在目錄下將包含ConfigMap定義的每個item,如果在該目錄下原來還有其他檔案,容器內的該目錄將被掛載的ConfigMap覆蓋。

Pod使用ConfigMap

通過環境變數方式使用ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appvars
data:
  apploglevel: info
  appdatadir: /var/data

在上面的定義中,將ConfigMap“cm-appvars”中的內容以環境變數(APPLOGLEVEL和APPDATADIR)方式設定為容器內部的環境變數,容器的啟動命令將顯示這兩個環境變數的值("env | grep APP"):

apiVersion: v1
kind: Pod
metadata:
  name: cm-test-pod
spec:
  containers:
  - name: cm-test
    image: busybox
    command: [ "/bin/sh", "-c", "env | grep APP" ]
    env:
    - name: APPLOGLEVEL
      valueFrom:
        configMapKeyRef:
          name: cm-appvars
          key: apploglevel
    - name: APPDATADIR
      valueFrom:
        configMapKeyRef:
          name: cm-appvars
          key: appdatadir
  restartPolicy: Never

建立Pod,該Pod在執行完啟動命令後將會退出(Completed),並且不會被系統自動重啟 restartPolicy=Never

檢視該Pod的日誌,可以看到啟動命令“env | grep APP”的執行結果

[root@master k8s]# kubectl logs cm-test-pod

APPDATADIR=/var/data
APPLOGLEVEL=info

說明容器內部的環境變數使用ConfigMap cm-appvars中的值進行了正確設定。

Kubernetes從1.6版本開始,引入了一個新的欄位envFrom,實現了在Pod環境中將ConfigMap(也可用於Secret資源物件)中所有定義的key=value自動生成為環境變數:

apiVersion: v1
kind: Pod
metadata:
  name: cm-test-pod
spec:
  containers:
  - name: cm-test
  image: busybox
  command: [ "/bin/sh","-c","env"]
  envFrom:
  - configMapRef
    name: cm-appvars 	#根據cmappvars中的key=value自動生成環境變數
    restartPolicy: Never

通過這個定義,在容器內部將會生成如下環境變數:

APPDATADIR=/var/data
APPLOGLEVEL=info

需要說明的是,變數名不能以數字開頭。如果包含非法字元,則系統將跳過該條環境變數的建立,並記錄一個Event來提示環境變數無法生成,但並不阻止Pod的啟動。

Pod 的生命週期

Pod 遵循一個預定義的生命週期,起始於 Pending 階段,如果至少 其中有一個主要容器正常啟動,則進入 Running,之後取決於 Pod 中是否有容器以 失敗狀態結束而進入 Succeeded 或者 Failed 階段。

在 Pod 執行期間,kubelet 能夠重啟容器以處理一些失效場景。 在 Pod 內部,Kubernetes 跟蹤不同容器的狀態 並確定使 Pod 重新變得健康所需要採取的動作。

在 Kubernetes API 中,Pod 包含規約部分和實際狀態部分。 Pod 物件的狀態包含了一組 Pod 狀況(Conditions)。 如果應用需要的話,也可以向其中注入自定義的就緒性資訊

Pod 在其生命週期中只會被排程一次。 一旦 Pod 被排程(分派)到某個節點,Pod 會一直在該節點執行,直到 Pod 停止或者 被終止

Pod 狀況

Pod 有一個 PodStatus 物件,其中包含一個 PodConditions 陣列。Pod 可能通過也可能未通過其中的一些狀況測試。

  • PodScheduled:Pod 已經被排程到某節點;
  • ContainersReady:Pod 中所有容器都已就緒;
  • Initialized:所有的 Init 容器 都已成功啟動;
  • Ready:Pod 可以為請求提供服務,並且應該被新增到對應服務的負載均衡池中。
欄位名稱 描述
type Pod 狀況的名稱
status 表明該狀況是否適用,可能的取值有 "True", "False" 或 "Unknown"
lastProbeTime 上次探測 Pod 狀況時的時間戳
lastTransitionTime Pod 上次從一種狀態轉換到另一種狀態時的時間戳
reason 機器可讀的、駝峰編碼(UpperCamelCase)的文字,表述上次狀況變化的原因
message 可讀的訊息,給出上次狀態轉換的詳細資訊

Pod重啟策略

當某個容器異常退出或者健康檢查失敗, kubelet將根據RestartPolicy的設定來進行相應的操作,預設為Always。重啟策略有:

  • Always: 當容器失效時, 由kubelet自動重啟該容器
  • OnFailure: 當容器終止執行且退出碼不為0時, 由kubelet自動重啟該容器
  • Never: 不論容器執行狀態如何, kubelet都不會重啟該容器

Pod的重啟策略與控制方式息息相關,當前可用於管理Pod的控制器包括ReplicationController、Job、DaemonSet及直接通過kubelet管理(靜態Pod)。每種控制器對Pod的重啟策略要求如下。

  • RC和DaemonSet:必須設定為Always,需要保證該容器持續執行。
  • Job:OnFailure或Never,確保容器執行完成後不再重啟。
  • kubelet:在Pod失效時自動重啟它,不論將RestartPolicy設定為什麼值,也不會對Pod進行健康檢查。

Pod 生命期

和一個個獨立的應用容器一樣,Pod 也被認為是相對臨時性(而不是長期存在)的實體。 Pod 會被建立、賦予一個唯一的 ID(UID), 並被排程到節點,並在終止(根據重啟策略)或刪除之前一直執行在該節點。

如果一個節點死掉了,排程到該節點 的 Pod 也被計劃在給定超時期限結束後刪除

Pod 自身不具有自愈能力。如果 Pod 被排程到某節點 而該節點之後失效,或者排程操作本身失效,Pod 會被刪除;與此類似,Pod 無法在節點資源 耗盡或者節點維護期間繼續存活。Kubernetes 使用一種高階抽象,稱作 控制器,來管理這些相對而言 可隨時丟棄的 Pod 例項。

任何給定的 Pod (由 UID 定義)從不會被“重新排程(rescheduled)”到不同的節點; 相反,這一 Pod 可以被一個新的、幾乎完全相同的 Pod 替換掉。 如果需要,新 Pod 的名字可以不變,但是其 UID 會不同。

如果某例項宣告其生命期與某 Pod 相同,例如儲存, 這就意味著該物件在此 Pod (UID 亦相同)存在期間也一直存在。 如果 Pod 因為任何原因被刪除,甚至某完全相同的替代 Pod 被建立時, 這個相關的物件(例如這裡的卷)也會被刪除並重建。

Pod 階段

Pod 的 status 欄位是一個 PodStatus 物件,其中包含一個 phase 欄位。

取值 描述
Pending(等待) Pod 已被 Kubernetes 系統接受,但有一個或者多個容器尚未建立亦未執行。此階段包括等待 Pod 被排程的時間和通過網路下載映象的時間,
Running(執行中) Pod 已經繫結到了某個節點,Pod 中所有的容器都已被建立。至少有一個容器仍在執行,或者正處於啟動或重啟狀態。
Succeeded(成功) Pod 中的所有容器都已成功終止,並且不會再重啟。
Failed(失敗) Pod 中的所有容器都已終止,並且至少有一個容器是因為失敗終止。也就是說,容器以非 0 狀態退出或者被系統終止。
Unknown(未知) 因為某些原因無法取得 Pod 的狀態。這種情況通常是因為與 Pod 所在主機通訊失敗。

如果某節點死掉或者與叢集中其他節點失聯,Kubernetes 會實施一種策略,將失去的節點上執行的所有 Pod 的 phase 設定為 Failed

容器狀態

Kubernetes 會跟蹤 Pod 中每個容器的狀態,就像它跟蹤 Pod 總體上的階段一樣。 可以使用容器生命週期回撥 來在容器生命週期中的特定時間點觸發事件。

一旦排程器將 Pod 分派給某個節點,kubelet 就通過 Docker Runtime 開始為 Pod 建立容器。 容器的狀態有三種:Waiting(等待)、Running(執行中)和 Terminated(已終止)。

要檢查 Pod 中容器的狀態,可以使用 kubectl describe pod <pod 名稱>。 其輸出中包含 Pod 中每個容器的狀態。

每種狀態都有特定的含義:

Waiting (等待)

如果容器並不處在 RunningTerminated 狀態之一,它就處在 Waiting 狀態。 處於 Waiting 狀態的容器仍在執行它完成啟動所需要的操作:例如,從某個容器映象 倉庫拉取容器映象,或者向容器應用 Secret 資料等等。 當你使用 kubectl 來查詢包含 Waiting 狀態的容器的 Pod 時,你也會看到一個 Reason 欄位,其中給出了容器處於等待狀態的原因。

Running(執行中)

Running 狀態表明容器正在執行狀態並且沒有問題發生。 如果配置了 postStart 回撥,那麼該回調已經執行且已完成。 如果你使用 kubectl 來查詢包含 Running 狀態的容器的 Pod 時,你也會看到 關於容器進入 Running 狀態的資訊。

Terminated(已終止)

處於 Terminated 狀態的容器已經開始執行並且或者正常結束或者因為某些原因失敗。 如果使用 kubectl 來查詢包含 Terminated 狀態的容器的 Pod 時,會看到 容器進入此狀態的原因、退出程式碼以及容器執行期間的起止時間。

如果容器配置了 preStop 回撥,則該回調會在容器進入 Terminated 狀態之前執行。

容器重啟策略

Pod 的 spec 中包含一個 restartPolicy 欄位,其可能取值包括 Always、OnFailure 和 Never。預設值是 Always。

restartPolicy 適用於 Pod 中的所有容器。restartPolicy 僅針對同一節點上 kubelet 的容器重啟動作。當 Pod 中的容器退出時,kubelet 會按指數回退 方式計算重啟的延遲(10s、20s、40s、...),其最長延遲為 5 分鐘。 一旦某容器執行了 10 分鐘並且沒有出現問題,kubelet 對該容器的重啟回退計時器執行 重置操作。

容器探針

Probe 是由 kubelet 對容器執行的定期診斷。 要執行診斷,kubelet 呼叫由容器實現的 Handler (處理程式)。有三種類型的處理程式:

  • ExecAction: 在容器內執行指定命令。如果命令退出時返回碼為 0 則認為診斷成功。
  • TCPSocketAction: 對容器的 IP 地址上的指定埠執行 TCP 檢查。如果埠開啟,則診斷被認為是成功的。
  • HTTPGetAction: 對容器的 IP 地址上指定埠和路徑執行 HTTP Get 請求。如果響應的狀態碼大於等於 200 且小於 400,則診斷被認為是成功的。

每次探測都將獲得以下三種結果之一:

  • Success(成功):容器通過了診斷。
  • Failure(失敗):容器未通過診斷。
  • Unknown(未知):診斷失敗,因此不會採取任何行動。

針對執行中的容器,kubelet 可以選擇是否執行以下三種探針,以及如何針對探測結果作出反應:

  • livenessProbe:指示容器是否正在執行。如果存活態探測失敗,則 kubelet 會殺死容器, 並且容器將根據其重啟策略決定未來。如果容器不提供存活探針, 則預設狀態為 Success
  • readinessProbe:指示容器是否準備好為請求提供服務。如果就緒態探測失敗, 端點控制器將從與 Pod 匹配的所有服務的端點列表中刪除該 Pod 的 IP 地址。 初始延遲之前的就緒態的狀態值預設為 Failure。 如果容器不提供就緒態探針,則預設狀態為 Success
  • startupProbe: 指示容器中的應用是否已經啟動。如果提供了啟動探針,則所有其他探針都會被 禁用,直到此探針成功為止。如果啟動探測失敗,kubelet 將殺死容器,而容器依其 重啟策略進行重啟。 如果容器沒有提供啟動探測,則預設狀態為 Success

如欲瞭解如何設定存活態、就緒態和啟動探針的進一步細節,可以參閱 配置存活態、就緒態和啟動探針

何時使用存活態探針

FEATURE STATE: Kubernetes v1.0 [stable]

如果容器中的程序能夠在遇到問題或不健康的情況下自行崩潰,則不一定需要存活態探針; kubelet 將根據 Pod 的restartPolicy 自動執行修復操作。

如果希望容器在探測失敗時被殺死並重新啟動,那麼請指定一個存活態探針, 並指定restartPolicy 為 "Always" 或 "OnFailure"。

何時使用就緒態探針

FEATURE STATE: Kubernetes v1.0 [stable]

如果要僅在探測成功時才開始向 Pod 傳送請求流量,請指定就緒態探針。 在這種情況下,就緒態探針可能與存活態探針相同,但是規約中的就緒態探針的存在意味著 Pod 將在啟動階段不接收任何資料,並且只有在探針探測成功後才開始接收資料。

如果容器需要載入大規模的資料、配置檔案或者在啟動期間執行遷移操作,可以新增一個 就緒態探針。

如果希望容器能夠自行進入維護狀態,也可以指定一個就緒態探針,檢查某個特定於 就緒態的因此不同於存活態探測的端點。

說明: 注意,如果只是想在 Pod 被刪除時能夠排空請求,則不一定需要使用就緒態探針; 在刪除 Pod 時,Pod 會自動將自身置於未就緒狀態,無論就緒態探針是否存在。 等待 Pod 中的容器停止期間,Pod 會一直處於未就緒狀態。

何時使用啟動探針

FEATURE STATE: Kubernetes v1.18 [beta]

對於所包含的容器需要較長時間才能啟動就緒的 Pod 而言,啟動探針是有用的。 你不再需要配置一個較長的存活態探測時間間隔,只需要設定另一個獨立的配置選定, 對啟動期間的容器執行探測,從而允許使用遠遠超出存活態時間間隔所允許的時長。

如果你的容器啟動時間通常超出 initialDelaySeconds + failureThreshold × periodSeconds 總值,你應該設定一個啟動探測,對存活態探針所使用的同一端點執行檢查。 periodSeconds 的預設值是 10 秒。你應該將其 failureThreshold 設定得足夠高, 以便容器有充足的時間完成啟動,並且避免更改存活態探針所使用的預設值。 這一設定有助於減少死鎖狀況的發生。

Pod 的終止

由於 Pod 所代表的是在叢集中節點上執行的程序,當不再需要這些程序時正常終止pod是很重要的。一般不應武斷地使用 KILL 訊號終止它們,導致這些程序沒有機會 完成清理操作。

設計的目標是令你能夠請求刪除程序,並且知道程序何時被終止,同時也能夠確保刪除 操作終將完成。當你請求刪除某個 Pod 時,叢集會記錄並跟蹤 Pod 的體面終止週期, 而不是直接強制地殺死 Pod。在存在強制關閉設施的前提下, kubelet 會嘗試終止 Pod。

通常情況下,容器執行時會發送一個 TERM 訊號到每個容器中的主程序。 很多容器執行時都能夠注意到容器映象中 STOPSIGNAL 的值,併發送該訊號而不是 TERM。 一旦超出了正常終止限期,容器執行時會向所有剩餘程序傳送 KILL 訊號,之後 Pod 就會被從 API 伺服器 上移除。如果 kubelet 或者容器執行時的管理服務在等待程序終止期間被重啟, 叢集會從頭開始重試,賦予 Pod 完整的正常終止限期。

強制終止 Pod

注意: 對於某些工作負載及其 Pod 而言,強制刪除很可能會帶來某種破壞。

預設情況下,所有的刪除操作都會附有 30 秒鐘的寬限期限。 kubectl delete 命令支援 --grace-period=<seconds> 選項,允許你過載預設值, 設定自己希望的期限值。

將寬限期限強制設定為 0 意味著立即從 API 伺服器刪除 Pod。 如果 Pod 仍然運行於某節點上,強制刪除操作會觸發 kubelet 立即執行清理操作。

說明: 必須在設定 --grace-period=0 的同時額外設定 --force 引數才能發起強制刪除請求。

執行強制刪除操作時,API 伺服器不再等待來自 kubelet 的、關於 Pod 已經在原來執行的節點上終止執行的確認訊息。 API 伺服器直接刪除 Pod 物件,這樣新的與之同名的 Pod 即可以被建立。 在節點側,被設定為立即終止的 Pod 仍然會在被強行殺死之前獲得一點點的寬限時間。

如果需要強制刪除 StatefulSet 的 Pod,請參閱 從 StatefulSet 中刪除 Pod 的任務文件。

失效 Pod 的垃圾收集

對於已失敗的 Pod ,對應的 API 物件仍然會保留在叢集的 API 伺服器上,直到 使用者或者控制器程序顯式地 將其刪除。

Pod排程

kube-scheduler

kube-scheduler 負責分配排程 Pod 到叢集內的節點上,它監聽 kube-apiserver,查詢還未分配 Node 的 Pod,然後根據排程策略為這些 Pod 分配節點(更新 Pod 的 NodeName 欄位)。

NodeSelector:定向排程

Kubernetes Master上的Scheduler服務(kube-scheduler程序)負責實現Pod的排程,整個排程過程通過執行一系列複雜的演算法,最終為每個Pod都計算出一個最佳的目標節點,這一過程是自動完成的,通常 無法知道Pod最終會被排程到哪個節點上。

在實際情況下,也可能需要將Pod排程到指定的一些Node上,可以通過Node的標籤(Label)和Pod的nodeSelector屬性相匹配,來達到上述目的。
(1)首先通過kubectl label命令為node-1節點打上一個zone=north標籤

kubectl label nodes node-1 zone=north

上述命令列操作也可以通過修改資源定義檔案的方式,並執行kubectl replace -f xxx.yaml命令來完成。

(2)然後,在Pod的定義中加上nodeSelector的設定,以redismaster-controller.yaml為例:執行create命令建立Pod,scheduler就會將該Pod排程到擁有zone=north標籤的Node上。

apiVersion: v1
kind: ReplicationController 
metadata:
  name: redis-master
  labels:
    name: redis-master 
spec:
  replicas: 1
  selector:
    name: redis-master
  template:
    metadata:
      labels:
        name: redis-master
    spec:
      containers:
      - name: master
        image: kubeguide/redis-master
        ports:
        - containerPort: 6379
      nodeSelector:
        zone: north

如果給多個Node都定義了相同的標籤,則scheduler會根據排程演算法從這組Node中挑選一個可用的Node進行Pod排程。通過基於Node標籤的排程方式,可以把叢集中具有不同特點的Node都貼上不同的標籤,例如role=frontend, role=backend, role=database 等標籤,在部署應用時就可以根據應用的需求設定NodeSelector來進行指定Node範圍的排程。

需要注意的是,如果指定了Pod的nodeSelector條件,且在叢集中不存在包含相應標籤的Node,則即使在叢集中還有其他可供使用的Node,這個Pod也無法被成功排程。

NodeAffinity:Node親和性排程

NodeAffinity意為Node親和性的排程策略,是用於替換NodeSelector的全新排程策略。

目前有兩種節點親和性表達。

  • RequiredDuringSchedulingIgnoredDuringExecution:必須滿足指定的規則才可以排程Pod到Node上(功能與nodeSelector很像,但是使用的是不同的語法),相當於硬限制。

  • PreferredDuringSchedulingIgnoredDuringExecution:強調優先滿足指定規則,排程器會嘗試排程Pod到Node上,但並不強求,相當於軟限制。多個優先順序規則還可以設定權重(weight)值,以定義執行的先後順序。

IgnoredDuringExecution的意思是:如果一個Pod所在的節點在Pod執行期間標籤發生了變更,不再符合該Pod的節點親和性需求,則系統將忽略Node上Label的變化,該Pod能繼續在該節點執行。

下面的例子設定了NodeAffinity排程的如下規則。

  • requiredDuringSchedulingIgnoredDuringExecution要求只執行在amd64的節點上。
  • preferredDuringSchedulingIgnoredDuringExecution的要求是儘量執行在磁碟型別為ssd(disk-type In ssd)的節點上。
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: beta.kubernetes.io/arch
            operator: In
            values:
            - amd64
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: disk-type
            operator: In
            values:
            - ssd
  containers:
  - name: with-node-affinity
    image: gcr.io/google_containers/pause:2.0

NodeAffinity規則設定的注意事項如下。

  • 如果同時定義了nodeSelector和nodeAffinity,那麼必須兩個條件都得到滿足,Pod才能最終執行在指定的Node上。
  • 如果nodeAffinity指定了多個nodeSelectorTerms,那麼其中一個能夠匹配成功即可。
  • 如果在nodeSelectorTerms中有多個matchExpressions,則一個節點必須滿足所有matchExpressions才能執行該Pod。

PodAffinity:Pod親和與互斥排程策略

Pod間的親和與互斥可以根據在節點上正在執行的Pod的標籤而不是節點的標籤進行判斷和排程,要求對節點和Pod兩個條件進行匹配。這種規則可以描述為:

如果在具有標籤X的Node上運行了一個或者多個符合條件Y的Pod,那麼Pod應該(如果是互斥的情況,那麼就變成拒絕)執行在這個Node上。

這裡X指的是一個叢集中的節點、機架、區域等概念,通過Kubernetes內建節點標籤中的key來進行宣告。這個key的名字為topologyKey,意為表達節點所屬的topology範圍。

與節點不同的是,Pod是屬於某個名稱空間的,所以條件Y表達的是一個或者全部名稱空間中的一個Label Selector。和節點親和相同,Pod親和與互斥的條件設定也是
requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution。Pod的親和性被定義於PodSpec的affinity欄位下的podAffinity子欄位中。Pod間的互斥性則被定義於同一層次的podAntiAffinity子欄位中。

Taints和Tolerations(汙點和容忍)

前面介紹的NodeAffinity節點親和性,是在Pod上定義的一種屬性,使得Pod能夠被排程到某些Node上執行(優先選擇或強制要求)。Taint則正好相反,它讓Node拒絕Pod的執行。Taint需要和Toleration配合使用,讓Pod避開那些不合適的Node。

在Node上設定一個或多個Taint之後,除非Pod明確宣告能夠容忍這些汙點,否則無法在這些Node上執行。Toleration是Pod的屬性,讓Pod能夠(注意,只是能夠,而非必須)執行在標註了Taint的Node上。

可以用kubectl taint命令為Node設定Taint資訊:

kubectl taint nodes node-1 key=value:NoSchedule

這個設定為node1加上了一個Taint。該Taint的鍵為key,值為value,Taint的效果是NoSchedule。這意味著除非Pod明確宣告可以容忍這個Taint,否則就不會被排程到node1上。

然後,需要在Pod上宣告Toleration。下面的兩個Toleration都被設定為可以容忍(Tolerate)具有該Taint的Node,使得Pod能夠被排程到node1上:

tolerations:
- key: "key"
operator: "Equal"
effect: "NoSchedule"

# 或

tolerations:
- key: "key"
  operator: "Exists"

Pod的Toleration宣告中的key和effect需要與Taint的設定保持一致,並且滿足以下條件之一。

  • operator的值是Exists(無須指定value)。
  • operator的值是Equal並且value相等。如果不指定operator,則預設值為Equal。

Pod Priority Preemption:Pod優先順序排程

對於執行各種負載(如Service、Job)的中等規模或者大規模的叢集來說,出於各種原因,需要儘可能提高叢集的資源利用率。而提高資源利用率的常規做法是採用優先順序方案,即不同型別的負載對應不同的優先順序,同時允許叢集中的所有負載所需的資源總量超過叢集可提供的資源,在這種情況下,當發生資源不足的情況時,系統可以選擇釋放一些不重要的負載(優先順序最低的),保障最重要的負載能夠獲取足夠的資源穩定執行。

Kubernetes 1.8版本引入了基於Pod優先順序搶佔(Pod Priority Preemption)的排程策略,此時Kubernetes會嘗試釋放目標節點上低優先順序的Pod,以騰出空間(資源)安置高優先順序的Pod,這種排程方式被稱為“搶佔式排程”。

優先順序搶佔排程策略的核心行為分別是驅逐(Eviction)與搶佔(Preemption),這兩種行為的使用場景不同,效果相同。

  • Eviction是 kubelet程序的行為,即當一個Node發生資源不足(under resource pressure)的情況時,該節點上的kubelet程序會執行驅逐動作,此時Kubelet會綜合考慮Pod的優先順序、資源申請量與實際使用量等資訊來計算哪些Pod需要被驅逐;當同樣優先順序的Pod需要被驅逐時,實際使用的資源量超過申請量最大倍數的高耗能Pod會被首先驅逐。

  • Preemption則是Scheduler執行的行為,當一個新的Pod因為資源無法滿足而不能被排程時,Scheduler可能(有權決定)選擇驅逐部分低優先順序的Pod例項來滿足此Pod的排程目標,這就是Preemption機制。

Pod優先順序排程示例如下
首先,由叢集管理員建立PriorityClasses,PriorityClass不屬於任何名稱空間:

apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
  name: high-priority
value: 100000  	#
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

上述YAML檔案定義了一個名為high-priority的優先順序類別,優先順序為100000,數字越大,優先順序越高,超過一億的數字被系統保留,用於指派給系統元件。

可以在任意Pod中引用上述Pod優先順序類別:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

DaemonSet:在每個Node上都排程一個Pod

DaemonSet用於管理在叢集中每個Node上僅執行一份Pod的副本例項

這種用法適合有這種需求的應用。

  • 在每個Node上都執行一個GlusterFS儲存或者Ceph儲存的Daemon程序。
  • 在每個Node上都執行一個日誌採集程式,例如Fluentd或者Logstach。
  • 在每個Node上都執行一個性能監控程式,採集該Node的執行效能資料,例如Prometheus Node Exporter、collectd、New Relic agent或者Ganglia gmond等。

DaemonSet的Pod排程策略與RC類似,除了使用系統內建的演算法在每個Node上進行排程,也可以在Pod的定義中使用NodeSelector或NodeAffinity來指定滿足條件的Node範圍進行排程。

下面的例子定義為在每個Node上都啟動一個fluentd容器,配置檔案fluentd-ds.yaml的內容如下,其中掛載了物理機的兩個目錄/var/log/var/lib/docker/containers

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: fluentd-cloud-logging
  namespace: kube-system
  labels:
    k8s-app: fluentd-cloud-logging
spec:
  template:
    metadata:
      namespace: kube-system
      labels:
        k8s-app: fluentd-cloud-logging
    spec:
      containers:
      - name: fluentd-cloud-logging
        image: gcr.io/google_containers/fluentd-elasticsearch:1.17
        resources:
          limits:
            cpu: 100m
            memory: 200Mi
        env:
        - name: FLUENTD_ARGS
          value: -q
        volumeMounts:
        - name: varlog
          mountPath: /var/log
          readOnly: false
        - name: containers
          mountPath: /var/lib/docker/containers
          readOnly: false
      volumes:
      - name: containers
        hostPath:
          path: /var/lib/docker/containers
      - name: varlog
        hostPath:
          path: /var/log

在Kubernetes 1.6以後的版本中,DaemonSet也可以執行滾動升級,即在更新一個DaemonSet模板的時候,舊的Pod副本會被自動刪除,同時新的Pod副本會被自動建立,此時DaemonSet的更新策略(updateStrategy)為RollingUpdate

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: goldpinger
spec:
  updateStrategy:
    type: RollingUpdate

updateStrategy的另外一個值是OnDelete,即只有手工刪除了DaemonSet建立的Pod副本,新的Pod副本才會被創建出來。如果不設定updateStrategy的值,則在Kubernetes 1.6之後的版本中會被預設設定為RollingUpdate。

Pod的升級和回滾

Deployment

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

已執行的Pod副本數量有3個

現在Pod映象需要被更新為Nginx:1.9.1,可以通過kubectl setimage命令為Deployment設定新的映象名稱:

kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

另一種更新的方法是使用kubectl edit命令修改Deployment的配置,將spec.template.spec.containers[0].image從Nginx:1.7.9更改為Nginx:1.9.1:

kubectl edit deployment/nginx-deployment

一旦映象名(或Pod定義)發生了修改,則將觸發系統完成Deployment所有執行Pod的滾動升級操作。可以使用kubectl rollout status命令檢視Deployment的更新過程:

kubectl rollout status deployment/nginx-deployment

Deployment更新過程

使用kubectl describe deployments/nginx-deployment命令仔細觀察Deployment的更新過程。

初始建立Deployment時,系統建立了一個ReplicaSet(nginx-deployment-4087004473),並按使用者的需求建立了3個Pod副本。當更新Deployment時,系統建立了一個新的ReplicaSet(nginx-deployment-3599678771),並將其副本數量擴充套件到1,然後將舊的ReplicaSet縮減為2。之後,系統繼續按照相同的更新策略對新舊兩個ReplicaSet進行逐個調整。最後,新的ReplicaSet運行了3個新版本Pod副本,舊的ReplicaSet副本數量則縮減為0。如圖所示。

在整個升級的過程中,系統會保證至少有兩個Pod可用,並且最多同時執行4個Pod,這是Deployment通過複雜的演算法完成的。Deployment需要確保在整個更新過程中只有一定數量的Pod可能處於不可用狀態。在預設情況下,Deployment確保可用的Pod總數至少為所需的副本數量(DESIRED)減1,也就是最多1個不可用(maxUnavailable=1)。

Deployment還需要確保在整個更新過程中Pod的總數量不會超過所需的副本數量太多。在預設情況 Deployment確保Pod的總數最多比所需的Pod數多1個,也就是最多1個浪湧值(maxSurge=1)。Kubernetes從1.6版本開始,maxUnavailable和maxSurge的預設值將從1更新為所需副本數量的25%、25%。

這樣,在升級過程中,Deployment就能夠保證服務不中斷,並且副本數量始終維持為使用者指定的數量(DESIRED)。

對更新策略的說明如下。

在Deployment的定義中,可以通過spec.strategy指定Pod更新的策略,目前支援兩種策略:Recreate(重建)和RollingUpdate(滾動更新),預設值為RollingUpdate。在前面的例子中使用的就是RollingUpdate策略。

  • Recreate:設定spec.strategy.type=Recreate,表示Deployment在更新Pod時,會先殺掉所有正在執行的Pod,然後建立新的Pod。
  • RollingUpdate:設定spec.strategy.type=RollingUpdate,表示Deployment會以滾動更新的方式來逐個更新Pod。同時,可以通過設定spec.strategy.rollingUpdate下的兩個引數(maxUnavailable和maxSurge)來控制滾動更新的過程。

Deployment回滾

有時(例如新的Deployment不穩定時)可能需要將Deployment回滾到舊版本。在預設情況下,所有Deployment的釋出歷史記錄都被保留在系統中,以便於 隨時進行回滾(可以配置歷史記錄數量)。

假設在更新Deployment映象時,將容器映象名誤設定成Nginx:1.91(一個不存在的映象):

kubectl set image deployment/nginx-deployment nginx=nginx:1.91

則這時Deployment的部署過程會卡住:

kubectl rollout status deployments nginx-deployment

檢視建立的Pod,會發現新的ReplicaSet建立的1個Pod被卡在映象拉取過程中。

kubectl get pods

為了解決上面這個問題,需要回滾到之前穩定版本的Deployment。
首先,用kubectl rollout history命令檢查這個Deployment部署的歷史記錄:

#如果需要檢視特定版本的詳細資訊,可以加上`--revision=N`引數
kubectl rollout history deployment/nginx-deployment

注意,在建立Deployment時使用--record引數,就可以在CHANGECAUSE列看到每個版本使用的命令了。

撤銷本次釋出並回滾到上一個部署版本:

#也可以使用--to-revision引數指定回滾到的部署版本號
kubectl rollout undo deployment/nginx-deployment

暫停和恢復Deployment的部署操作

對於一次複雜的Deployment配置修改,為了避免頻繁觸發Deployment的更新操作,可以先暫停Deployment的更新操作,然後進行配置修改,再恢復Deployment,一次性觸發完整的更新操作,就可以避免不必要的Deployment更新操作了。

通過kubectl rollout pause命令暫停Deployment的更新操作:

kubectl rollout pause deployment/nginx-deployment

在暫停Deployment部署之後,可以根據需要進行任意次數的配置更新。例如,再次更新容器的資源限制:

kubectl set resources deployment nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi

最後,恢復這個Deployment的部署操作:

kubectl rollout resume deployment nginx-deployment

RC

RC的滾動升級

對於RC的滾動升級,Kubernetes還提供了一個kubectl rolling-update命令進行實現。該命令建立了一個新的RC,然後自動控制舊的RC中的Pod副本數量逐漸減少到0,同時新的RC中的Pod副本數量從0逐步增加到目標值,來完成Pod的升級。需要注意的是,系統要求新的RC與舊的RC都在相同的名稱空間內。

以redis-master為例,假設當前執行的redis-master Pod是1.0版本,現在需要升級到2.0版本。建立redis-master-controller-v2.yaml的配置檔案如下:

apiVersion: v1
kind: ReplicationController
metadata:
  name: redis-master-v2
  labels:
    name: redis-master
    version: v2
spec:
  replicas: 1
  selector:
    name: redis-master
    version: v2
  template:
    metadata:
      labels:
        name: redis-master
        version: v2
    spec:
      containers:
      - name: master
        image: kubeguide/redis-master:2.0
        ports:
        - containerPort: 6379

在配置檔案中需要注意以下兩點。

  • RC的名字(name)不能與舊RC的名字相同。
  • 在selector中應至少有一個Label與舊RC的Label不同,以標識其為新RC。在本例中新增了一個名為version Label,以與舊RC進行區分。

執行kubectl rolling-update命令完成Pod的滾動升級:

kubectl rolling-update reids-master -f reids-master-controller-v2.yaml

等所有新的Pod都啟動完成後,舊的Pod也被全部銷燬,這樣就完成了容器叢集的更新工作。

另一種方法是不使用配置檔案,直接用kubectl rolling-update命令,加上--image引數指定新版映象名稱來完成Pod的滾動升級:

kubectl rolling-update reids-master --image=reids-master:2.0

與使用配置檔案的方式不同,執行的結果是舊RC被刪除,新RC仍將使用舊RC的名稱。

如果在更新過程中發現配置有誤,則使用者可以中斷更新操作,並通
過執行kubectl rolling- update --rollback完成Pod版本的回滾:

kubectl rolling-update reids-master --image=kubeguide/redis-master:2.0 --rollback

其他管理物件的更新策略

Kubernetes從1.6版本開始,對DaemonSet和StatefulSet的更新策略也引入類似於Deployment的滾動升級,通過不同的策略自動完成應用的版
本升級。

DaemonSet的更新策略

目前DaemonSet的升級策略包括兩種:OnDelete和RollingUpdate。

一、OnDelete

DaemonSet的預設升級策略,與1.5及以前版本的Kubernetes保持一致。當使用OnDelete作為升級策略時,在建立好新的DaemonSet配置之後,新的Pod並不會被自動建立,直到使用者手動刪除舊版本的Pod,才觸發新建操作。

二、RollingUpdate

從Kubernetes 1.6版本開始引入。當使用RollingUpdate作為升級策略對DaemonSet進行更新時,舊版本的Pod將被自動殺掉,然後自動建立新版本的DaemonSet Pod。整個過程與普通Deployment的滾動升級一樣是可控的。

不過有兩點不同於普通Pod的滾動升級:

一是目前Kubernetes還不支援檢視和管理DaemonSet的更新歷史記錄;

二是DaemonSet的回滾(Rollback)並不能如同Deployment一樣直接通過kubectl rollback命令來實現,必須通過再次提交舊版本配置的方式實現。

二、StatefulSet的更新策略
Kubernetes從1.6版本開始,針對StatefulSet的更新策略逐漸向Deployment和DaemonSet的更新策略看齊,也將實現RollingUpdate、Paritioned和OnDelete這幾種策略,以保證StatefulSet中各Pod有序地、逐個地更新,並且能夠保留更新歷史,也能回滾到某個歷史版本。

Pod的擴縮容

在實際生產系統中, 經常會遇到某個服務需要擴容的場景,也可能會遇到由於資源緊張或者工作負載降低而需要減少服務例項數量的場景。此時可以利用Deployment/RC的Scale機制來完成這些工作。

Kubernetes對Pod的擴縮容操作提供了手動和自動兩種模式,手動模式通過執行kubectl scale命令或通過RESTful API對一個Deployment/RC進行Pod副本數量的設定,即可一鍵完成。自動模式則需要使用者根據某個效能指標或者自定義業務指標,並指定Pod副本數量的範圍,系統將自動在這個範圍內根據效能指標的變化進行調整。

手動擴縮容機制

以Deployment nginx為例,已執行的Pod副本數量為3個

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

通過kubectl scale命令可以將Pod副本數量從初始的3個更新為5個:

kubectl scale deployment nginx-deployment --replicas 5

--replicas設定為比當前Pod副本數量更小的數字,系統將會“殺掉”一些執行中的Pod,以實現應用叢集縮容

自動擴縮容機制

Kubernetes從1.1版本開始,新增了名為Horizontal PodAutoscaler(HPA)的控制器,用於實現基於CPU使用率進行自動Pod縮容的功能。1.6版本,引入了基於應用自定義效能指標的HPA機制。HPA控制器基於Master的kube-controller-manager服務啟動引數--horizontal-pod-autoscaler-sync-period定義的探測週期(預設值為15s),週期性地監測目標Pod的資源效能指標,並與HPA資源物件中的擴縮容條件進行對比,在滿足條件時對Pod副本數量進行調整。

HPA的工作原理

Kubernetes中的某個Metrics Server(Heapster或自定義Metrics Server)持續採集所有Pod副本的指標資料。HPA控制器通過Metrics Server的API(Heapster的API或聚合API)獲取這些資料,基於使用者定義的擴縮容規則進行計算,得到目標Pod副本數量。當目標Pod副本數量與當前副本數量不同時,HPA控制器就向Pod的副本控制器(Deployment、RC或ReplicaSet)發起scale操作,調整Pod的副本數量,完成擴縮容操作。

指標的型別

Master的kube-controller-manager服務持續監測目標Pod的某種效能指標,以計算是否需要調整副本數量。目前Kubernetes支援的指標型別如下。

  • Pod資源使用率:Pod級別的效能指標,通常是一個比率值,例如CPU使用率。
  • Pod自定義指標:Pod級別的效能指標,通常是一個數值,例如接收的請求數量。
  • Object自定義指標或外部自定義指標:通常是一個數值,需要容器應用以某種方式提供,例如通過HTTP URL“/metrics”提供,或者使用外部服務提供的指標採集URL。

擴縮容演算法詳解

Autoscaler控制器從聚合API獲取到Pod效能指標資料之後,基於下面的演算法計算出目標Pod副本數量,與當前執行的Pod副本數量進行對比,決定是否需要進行擴縮容操作:

desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desirdMetricValue )]
# 即當前副本數×(當前指標值/期望的指標值),將結果向上取整。

以CPU請求數量為例,如果使用者設定的期望指標值為100m,當前實際使用的指標值為200m,則計算得到期望的Pod副本數量應為兩個(200/100=2)。如果設定的期望指標值為50m,計算結果為0.5,則向上取整值為1,得到目標Pod副本數量應為1個。當計算結果與1非常接近時,可以設定一個容忍度讓系統不做擴縮容操作。

容忍度通過kube-controller-manager服務的啟動引數--horizontalpod-autoscaler-tolerance進行設定,預設值為0.1(即10%),表示基於上述演算法得到的結果在[-10%-+10%]區間內,即[0.9-1.1],控制器都不會進行擴縮容操作。也可以將期望指標值(desiredMetricValue)設定為指標的平均值型別,例如targetAverageValue或targetAverageUtilization,此時當前指標值(currentMetricValue)的演算法為所有Pod副本當前指標值的總和除以Pod副本數量得到的平均值。

此外,存在幾種Pod異常的情況,如下所述。

  • Pod正在被刪除(設定了刪除時間戳):將不會計入目標Pod副本數量。
  • Pod的當前指標值無法獲得:本次探測不會將這個Pod納入目標Pod副本數量,後續的探測會被重新納入計算範圍。
  • 如果指標型別是CPU使用率,則對於正在啟動但是還未達到Ready狀態的Pod,也暫時不會納入目標副本數量範圍。可以通過kubecontroller-manager服務的啟動引數--horizontal-pod-autoscaler-initialreadiness-delay設定首次探測Pod是否Ready的延時時間,預設值為30s。另一個啟動引數--horizontal-pod-autoscaler-cpuinitialization-period設定首次採集Pod的CPU使用率的延時時間。

HorizontalPodAutoscaler配置詳解

Kubernetes將HorizontalPodAutoscaler資源物件提供給使用者來定義擴縮容的規則。

HorizontalPodAutoscaler資源物件處於Kubernetes的API組“autoscaling”中,目前包括v1和v2兩個版本。其中autoscaling/v1僅支援基於CPU使用率的自動擴縮容,autoscaling/v2則用於支援基於任意指標的自動擴縮容配置,包括基於資源使用率、Pod指標、其他指標等型別的指標資料,當前版本autoscaling/v2beta2。

下面對HorizontalPodAutoscaler的配置和用法進行說明。

  1. 基於autoscaling/v1版本的HorizontalPodAutoscaler配置,僅可以設定CPU使用率:
apiVersion: autoscaling/v1
kind: HorzontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion:  apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

主要引數如下。

  • scaleTargetRef:目標作用物件,可以是Deployment、ReplicationController或ReplicaSet。
  • targetCPUUtilizationPercentage:期望每個Pod的CPU使用率都為50%,該使用率基於Pod設定的CPU Request值進行計算,例如該值為200m,那麼系統將維持Pod的實際CPU使用值為100m。
  • minReplicas和maxReplicas:Pod副本數量的最小值和最大值,系統將在這個範圍內進行自動擴縮容操作,並維持每個Pod的CPU使用率為50%。

為了使用autoscaling/v1版本的HorizontalPodAutoscaler,需要預先安裝Heapster元件或Metrics Server,用於採集Pod的CPU使用率。

  1. 基於autoscaling/v2beta2的HorizontalPodAutoscaler配置:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target: 
        type: Utilization
        averageUtilization: 50    #定義目標平均CPU使用率 

主要引數如下:

  • scaleTargetRef:目標作用物件,可以是Deployment、ReplicationController或ReplicaSet。

  • minReplicasmaxReplicas:Pod副本數量的最小值和最大值,系統將在這個範圍內進行自動擴縮容操作,並維持每個Pod的CPU使用率為50%。

  • metrics:目標指標值。在metrics中通過引數type定義指標的型別;通過引數target定義相應的指標目標值,系統將在指標資料達到目標值時(考慮容忍度的區間,見前面演算法部分的說明)觸發擴縮容操作。

可以將metrics中的type(指標型別)設定為以下三種,可以設定一個或多個組合

  1. Resource:基於資源的指標值,可以設定的資源為CPU和記憶體。

  2. Pods:基於Pod的指標,系統將對全部Pod副本的指標值進行平均值計算。

  3. Object:基於某種資源物件(如Ingress)的指標或應用系統的任意自定義指標。