什么是Pod
Pod是可以創建和管理Kubernetes計算的最小可部署單元。一個Pod代表着集群中運行的一個進程。
Pod就像是豌豆莢一樣,它由一個或者多個容器組成(例如Docker容器),它們共享容器存儲、網絡和容器運行配置項。Pod中的容器總是被同時調度,有共同的運行環境。你可以把單個Pod想象成是運行獨立應用的“邏輯主機”——其中運行着一個或者多個緊密耦合的應用容器——在有容器之前,這些應用都是運行在幾個相同的物理機或者虛擬機上。
盡管kubernetes支持多種容器運行時,但是Docker依然是最常用的運行時環境,我們可以使用Docker的術語和規則來定義Pod。
Pod中共享的環境包括Linux的namespace,cgroup和其他可能的隔絕環境,這一點跟Docker容器一致。在Pod的環境中,每個容器中可能還有更小的子隔離環境。
Pod中的容器共享IP地址和端口號,它們之間可以通過localhost互相發現。它們之間可以通過進程間通信,例如SystemV信號或者POSIX共享內存。不同Pod之間的容器具有不同的IP地址,不能直接通過IPC通信。
Pod中的容器也有訪問共享volume的權限,這些volume會被定義成pod的一部分並掛載到應用容器的文件系統中。
就像每個應用容器,pod被認為是臨時實體。在Pod的生命周期中,pod被創建后,被分配一個唯一的ID(UID),調度到節點上,並一致維持期望的狀態直到被終結(根據重啟策略)或者被刪除。如果node死掉了,分配到了這個node上的pod,在經過一個超時時間后會被重新調度到其他node節點上。一個給定的pod(如UID定義的)不會被“重新調度”到新的節點上,而是被一個同樣的pod取代,如果期望的話甚至可以是相同的名字,但是會有一個新的UID(查看replication controller獲取詳情)。
Pod中如何管理多個容器
Pod中可以同時運行多個進程(作為容器運行)協同工作。同一個Pod中的容器會自動的分配到同一個 node 上。同一個Pod中的容器共享資源、網絡環境和依賴,它們總是被同時調度。
注意在一個Pod中同時運行多個容器是一種比較高級的用法。只有當你的容器需要緊密配合協作的時候才考慮用這種模式。例如,你有一個容器作為web服務器運行,需要用到共享的volume,有另一個“sidecar”容器來從遠端獲取資源更新這些文件。如圖

Pod中可以共享兩種資源
- 網絡 每個Pod都會被分配一個唯一的IP地址。Pod中的所有容器共享網絡空間,包括IP地址和端口。Pod內部的容器可以使用
localhost互相通信。Pod中的容器與外界通信時,必須分配共享網絡資源(例如使用宿主機的端口映射)。 - 存儲 可以Pod指定多個共享的Volume。Pod中的所有容器都可以訪問共享的volume。Volume也可以用來持久化Pod中的存儲資源,以防容器重啟后文件丟失。
使用Pod
我通常把pod分為兩類:
- 自主式Pod 這種Pod本身是不能自我修復的,當Pod被創建后(不論是由你直接創建還是被其他Controller),都會被Kuberentes調度到集群的Node上。直到Pod的進程終止、被刪掉、因為缺少資源而被驅逐、或者Node故障之前這個Pod都會一直保持在那個Node上。Pod不會自愈。如果Pod運行的Node故障,或者是調度器本身故障,這個Pod就會被刪除。同樣的,如果Pod所在Node缺少資源或者Pod處於維護狀態,Pod也會被驅逐。
- 控制器管理的Pod Kubernetes使用更高級的稱為Controller的抽象層,來管理Pod實例。Controller可以創建和管理多個Pod,提供副本管理、滾動升級和集群級別的自愈能力。例如,如果一個Node故障,Controller就能自動將該節點上的Pod調度到其他健康的Node上。雖然可以直接使用Pod,但是在Kubernetes中通常是使用Controller來管理Pod的。

上圖所示是Pod的組成示意圖,我們看到每個Pod都有一個特殊的被稱為“根容器”的Pause 容器。 Pause容器對應的鏡像屬於Kubernetes平台的一部分,除了Pause容器,每個Pod還包含一個或者多個緊密相關的用戶業務容器。
Pod的終止
因為Pod作為在集群的節點上運行的進程,所以在不再需要的時候能夠優雅的終止掉是十分必要的(比起使用發送KILL信號這種暴力的方式)。用戶需要能夠放松刪除請求,並且知道它們何時會被終止,是否被正確的刪除。用戶想終止程序時發送刪除pod的請求,在pod可以被強制刪除前會有一個寬限期,會發送一個TERM請求到每個容器的主進程。一旦超時,將向主進程發送KILL信號並從API server中刪除。如果kubelet或者container manager在等待進程終止的過程中重啟,在重啟后仍然會重試完整的寬限期。
示例流程如下:
- 用戶發送刪除pod的命令,默認寬限期是30秒;
- 在Pod超過該寬限期后API server就會更新Pod的狀態為“dead”;
- 在客戶端命令行上顯示的Pod狀態為“terminating”;
- 跟第三步同時,當kubelet發現pod被標記為“terminating”狀態時,開始停止pod進程:1 如果在pod中定義了preStop hook,在停止pod前會被調用。如果在寬限期過后,preStop hook依然在運行,第二步會再增加2秒的寬限期;2 向Pod中的進程發送TERM信號;
- 跟第三步同時,該Pod將從該service的端點列表中刪除,不再是replication controller的一部分。關閉的慢的pod將繼續處理load balancer轉發的流量;
- 過了寬限期后,將向Pod中依然運行的進程發送SIGKILL信號而殺掉進程。
- Kublete會在API server中完成Pod的的刪除,通過將優雅周期設置為0(立即刪除)。Pod在API中消失,並且在客戶端也不可見。
刪除寬限期默認是30秒。 kubectl delete命令支持 --grace-period=<seconds> 選項,允許用戶設置自己的寬限期。如果設置為0將強制刪除pod。在kubectl>=1.5版本的命令中,你必須同時使用 --force 和 --grace-period=0 來強制刪除pod。
Pause 容器
在接觸Kubernetes的初期,便知道集群搭建需要下載一個gcr.io/google_containers/pause-amd64:3.0鏡像,然后每次啟動一個容器,都會伴隨一個pause容器的啟動。
但這個pause容器的功能是什么,它是如何做出來的,以及為何都伴隨容器啟動等等。這些問題一直在我心里,如今有緣學習相關內容。
189fbd12e903 rancher/rancher-agent:v2.0.6 "run.sh -- share-r..." 10 days ago Exited (0) 10 days ago share-mnt [root@k8s-master ~]# docker ps -a | grep pause-amd64 f30cc4df0eff rancher/pause-amd64:3.1 "/pause" 4 days ago Up 4 days k8s_POD_confserver-bdf79c8cb-xxf82_confserver_f92c3ecc-a11b-11e8-a1c4-005056936694_0 af651c01f1e4 rancher/pause-amd64:3.1 "/pause" 5 days ago Up 5 days k8s_POD_jenkins-5cf89c84f6-h4hs6_jenkins_954201e0-a057-11e8-a1c4-005056936694_0 7ab1920551ca rancher/pause-amd64:3.1 "/pause" 10 days ago Up 10 days k8s_POD_nfs-provisioner-2cjpp_nfs-provisioner_a24395b7-9c42-11e8-a1c4-005056936694_0 4f89f1c2e83b rancher/pause-amd64:3.1 "/pause" 10 days ago Up 10 days k8s_POD_cattle-node-agent-s8s75_cattle-system_a2443a27-9c42-11e8-a1c4-005056936694_0 74ff9a7eb776 rancher/pause-amd64:3.1 "/pause" 10 days ago Up 10 days k8s_POD_nginx-ingress-controller-gl7k6_ingress-nginx_a239056d-9c42-11e8-a1c4-005056936694_0 76a3177f05ec rancher/pause-amd64:3.1 "/pause" 10 days ago Up 10 days k8s_POD_calico-node-7vzlj_kube-system_a2391472-9c42-11e8-a1c4-005056936694_0
kubernetes中的pause容器主要為每個業務容器提供以下功能:
- 在pod中擔任Linux命名空間共享的基礎;
- 啟用pid命名空間,開啟init進程。
Pod 的生命周期
Pod phase
Pod 的 status 在信息保存在 Podstatus 中定義,其中有一個 phase 字段。
Pod 的相位(phase)是 Pod 在其生命周期中的簡單宏觀概述。該階段並不是對容器或 Pod 的綜合匯總,也不是為了做為綜合狀態機。
Pod 相位的數量和含義是嚴格指定的。除了本文檔中列舉的狀態外,不應該再假定 Pod 有其他的 phase 值。
下面是 phase 可能的值:
| 值 | 描述 |
| Pending | Pod已被Kubernetes系統接受,但尚未創建一個或多個Container圖像。這包括計划之前的時間以及通過網絡下載圖像所花費的時間,這可能需要一段時間 |
| Running | Pod已綁定到節點,並且已創建所有Container。至少有一個Container仍在運行,或者正在啟動或重新啟動 |
| Succeeded | Pod中的所有容器都已成功終止,並且不會重新啟動 |
| Failed | Pod中的所有容器都已終止,並且至少有一個Container已終止失敗。也就是說,Container要么退出非零狀態,要么被系統終止 |
| Unknown | 由於某種原因,無法獲得Pod的狀態,這通常是由於與Pod的主機通信時出錯 |
下圖是Pod的生命周期示意圖,從圖中可以看到Pod狀態的變化。

在pod生命周期中可以做的一些事情。主容器啟動前可以完成初始化容器,初始化容器可以有多個,他們是串行執行的,執行完成后就推出了,在主程序剛剛啟動的時候可以指定一個post start 主程序啟動開始后執行一些操作,在主程序結束前可以指定一個 pre stop 表示主程序結束前執行的一些操作。在程序啟動后可以做兩類檢測 liveness probe 和 readness probe。如下圖:

POD的重啟策略
restartPolicy:
- Always 默認策略
- OnFailure 只有狀態錯誤的時候重啟(正常退出不會重啟)
- Never 重來不重啟
Pod條件
Pod有一個PodStatus,它有一個PodConditions 數組, Pod已經或沒有通過它。PodCondition數組的每個元素都有六個可能的字段:
-
該
lastProbeTime字段提供上次探測Pod條件的時間戳。 - 該
lastTransitionTime字段提供Pod最后從一個狀態轉換到另一個狀態的時間戳 - 該
message字段是人類可讀的消息,指示有關轉換的詳細信息 - 該
reason字段是該條件最后一次轉換的唯一,單字,CamelCase原因 - 該
status字段是一個字符串,可能的值為“True”,“False”和“Unknown” - 該
type字段是一個包含以下可能值的字符串:PodScheduled:Pod已被安排到一個節點;- Read: Pod能夠提供請求,應該添加到所有匹配服務的負載平衡池中;
Initialized:所有init容器都已成功啟動;- U
nschedulable:調度程序現在無法調度Pod,例如由於缺少資源或其他限制 - C
ontainersReady:Pod中的所有容器都已准備就緒
容器探針
探針是由 Kubelet 對容器執行的定期診斷。要執行診斷,kubelet 調用由容器實現的 Handler。有三種類型的處理程序:
- ExecAction:在Container內執行指定的命令。如果命令以狀態代碼0退出,則認為診斷成功
- TCPSocketAction:對指定端口上的Container的IP地址執行TCP檢查。如果端口打開,則診斷被認為是成功的
- HTTPGetAction:對指定端口和路徑上的Container的IP地址執行HTTP Get請求。如果響應的狀態代碼大於或等於200且小於400,則認為診斷成功。
每個探針都有三個結果之一:
- 成功:Container通過了診斷。
- 失敗:容器未通過診斷。
- 未知:診斷失敗,因此不應采取任何措施。
kubelet可以選擇在運行容器上執行兩種探測並對其做出反應:
livenessProbe:指示Container是否正在運行。如果存活探測失敗,則kubelet會殺死Container,並且Container將受其重啟策略的約束。如果Container未提供存活探測,則默認狀態為Success。readinessProbe:指示Container是否已准備好為服務請求。如果就緒探測失敗,則端點控制器會從與Pod匹配的所有服務的端點中刪除Pod的IP地址。初始延遲之前的默認就緒狀態是Failure。如果Container未提供就緒狀態探測,則默認狀態為Success。
什么時候應該使用livenessProbe或readinessProbe探針?
如果您的Container中的進程在遇到問題或變得不健康時自行崩潰,則不一定需要存活探測器; kubelet將根據Pod的restartPolicy自動執行正確的操作。
如果您希望在容器探測失敗時殺死並重新啟動,則指定存活探測,並指定restartPolicy為Always或OnFailure。
如果您只想在探測成功時開始向Pod發送流量,請指定就緒探測。在這種情況下,就緒探針可能與存活探測相同,但spec中就緒探針的存在意味着Pod將在不接收任何流量的情況下啟動,並且僅在探針探測成功后才開始接收流量。
如果Container需要在啟動期間處理大型數據,配置文件或遷移,請指定就緒探針。
如果您希望Container能夠自行維護,您可以指定一個就緒探針,該探針檢查特定於就緒的端點,該端點與活動探針不同。
請注意,如果您只想在Pod被刪除時排除請求,則不一定需要就緒探測; 在刪除Pod時,無論是否存在就緒探針,Pod都會自動將其置於未完成狀態。當等待 Pod 中的容器停止時,Pod 仍處於未完成狀態。
livenessProbe詳解
kubectl explain pod.spec.containers.livenessProbe
- exec command 的方式探測 例如 ps 一個進程
- failureThreshold 探測幾次失敗 才算失敗 默認是連續三次
- periodSeconds 每次的多長時間探測一次 默認10s
- timeoutSeconds 探測超市的秒數 默認1s
- initialDelaySeconds 初始化延遲探測,第一次探測的時候,因為主程序未必啟動完成
- tcpSocket 檢測端口的探測
- httpGet http請求探測
案例
apiVersion: v1 kind: Pod metadata: name: liveness-exec-pod namespace: default labels: name: myapp spec: containers: - name: livess-exec image: busybox:latest imagePullPolicy: IfNotPresent command: ["/bin/sh","-c","touch /tmp/test; sleep 30; rm -f /tmp/test; sleep 3600"] livenessProbe: exec: command: ["test","-e","/tmp/test"] initialDelaySeconds: 1 periodSeconds: 3
看一下描述信息
$ kubectl describe pods liveness-exec-pod ...... Command: /bin/sh -c touch /tmp/test; sleep 30; rm -f /tmp/test; sleep 3600 State: Running Started: Thu, 23 Aug 2018 11:09:28 -0400 Last State: Terminated Reason: Error Exit Code: 137 Started: Thu, 23 Aug 2018 11:08:22 -0400 Finished: Thu, 23 Aug 2018 11:09:28 -0400 Ready: True Restart Count: 1 重啟次數一次(會疊加) Liveness: exec [test -e /tmp/test] delay=1s timeout=1s period=3s #success=1 #failure=3 失敗3次 Environment: <none> ..... $ kubectl get pods NAME READY STATUS RESTARTS AGE jdk-94bcfc779-fs2cl 1/1 Running 0 9d liveness-exec-pod 1/1 Running 3 (重啟三次了) 4m
httpGet 案例
apiVersion: v1 kind: Pod metadata: name: liveness-httpget-pod namespace: default labels: name: myapp spec: containers: - name: livess-httpget image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80 imagePullPolicy: IfNotPresent livenessProbe: httpGet: port: http path: /index.html initialDelaySeconds: 1 periodSeconds: 3
readinessProbe詳解
kubectl explain pod.spec.containers.readinessProbe
- exec command 的方式探測 例如 ps 一個進程
- failureThreshold 探測幾次失敗 才算失敗 默認是連續三次
- periodSeconds 每次的多長時間探測一次 默認10s
- timeoutSeconds 探測超市的秒數 默認1s
- initialDelaySeconds 初始化延遲探測,第一次探測的時候,因為主程序未必啟動完成
- tcpSocket 檢測端口的探測
- httpGet http請求探測
案例
apiVersion: v1 kind: Pod metadata: name: readiness-httpget-pod namespace: default labels: name: myapp spec: containers: - name: readiness-httpget image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80 imagePullPolicy: IfNotPresent readinessProbe: httpGet: port: http path: /index.html initialDelaySeconds: 1 periodSeconds: 3
詳情
Name: readiness-httpget-pod Namespace: default Node: rancher-node/172.16.138.170 Start Time: Sun, 26 Aug 2018 10:13:03 -0400 Labels: name=myapp Annotations: cni.projectcalico.org/podIP=10.42.0.194/32 kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"labels":{"name":"myapp"},"name":"readiness-httpget-pod","namece":"default"},"spec":{"c... Status: Running IP: 10.42.0.194 Controllers: <none> Containers: readiness-httpget: Container ID: docker://7eacd3604aa53352e9d744a33b527a35b6a4e21e5a47b26c5d1e6ead7f045c3f Image: ikubernetes/myapp:v1 Image ID: docker-pullable://ikubernetes/myapp@sha256:9c3dc30b5219788b2b8a4b065f548b922a34479577befb54b03330999d30d513 Port: 80/TCP State: Running Started: Sun, 26 Aug 2018 10:13:09 -0400 Ready: True Restart Count: 0 Readiness: http-get http://:http/index.html delay=1s timeout=1s period=3s #success=1 #failure=3 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-ws5gd (ro) Conditions: Type Status Initialized True Ready True PodScheduled True Volumes:
.....
lifecycle詳解
極少數會用到
kubectl explain pod.spec.containers.lifecycle
- postStart 啟動后執行
- preStop 終止前執行
同樣是有三種類型
- exec command 的方式 例如 初始化環境
- httpGet http方式
- tcpSocket TCP port 的方式
案例
apiVersion: v1 kind: Pod metadata: name: lifecycle-poststart namespace: default labels: name: myapp tier: appfront spec: containers: - name: lifecycle-poststart-pod image: nginx imagePullPolicy: IfNotPresent lifecycle: postStart: exec: command: ["/bin/sh","-c","echo Home+Page >> /usr/share/message"] preStop: exec: command: ["/usr/sbin/nginx","-s","quit"]
