Kubernetes之(八)Pod的生命周期
理解Pod
Pod是kubernetes中你可以創建和部署的最⼩也是最簡的單位。 ⼀個Pod代表着集群中運⾏的⼀個進程。
Pod中封裝着應⽤的容器(有的情況下是好⼏個容器) , 存儲、 獨⽴的⽹絡IP, 管理容器如何運⾏的策略選項。 Pod代
表着部署的⼀個單位: kubernetes中應⽤的⼀個實例, 可能由⼀個或者多個容器組合在⼀起共享資源。
在Kubrenetes集群中Pod有如下兩種使⽤⽅式:
- ⼀個Pod中運⾏⼀個容器。 “每個Pod中⼀個容器”的模式是最常⻅的⽤法; 在這種使⽤⽅式中, 你可以把Pod想象成是單個容器的封裝, kuberentes管理的是Pod⽽不是直接管理容器。
- 在⼀個Pod中同時運⾏多個容器。 ⼀個Pod中也可以同時封裝⼏個需要緊密耦合互相協作的容器, 它們之間共享資源。 這些在同⼀個Pod中的容器可以互相協作成為⼀個service單位——⼀個容器共享⽂件, 另⼀個“sidecar”容器來更新這些⽂件。 Pod將這些容器的存儲資源作為⼀個實體來管理。
每個Pod都是應⽤的⼀個實例。 如果你想平⾏擴展應⽤的話(運⾏多個實例) , 你應該運⾏多個Pod, 每個Pod都是⼀個應⽤實例。 在Kubernetes中, 這通常被稱為replication。
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也可以⽤於垂直應⽤棧(例如LAMP) , 這樣使⽤的主要動機是為了⽀持共同調度和協調管理應⽤程序, 例如:
- 內容管理系統、 ⽂件和數據加載器、 本地換群管理器等。
- ⽇志和檢查點備份、 壓縮、 旋轉、 快照等。
- 數據變更觀察者、 ⽇志和監控適配器、 活動發布者等。
- 代理、 橋接和適配器等。
- 控制器、 管理器、 配置器、 更新器等。
通常單個pod中不會同時運⾏⼀個應⽤的多個實例。
其他替代選擇
為什么不直接在⼀個容器中運⾏多個應⽤程序呢?
1、透明,讓Pod中的容器對基礎設施可⻅, 以便基礎設施能夠為這些容器提供服務, 例如進程管理和資源監控。 這可以為⽤戶帶來極⼤的便利。
2、解耦軟件依賴。 每個容器都可以進⾏版本管理, 獨⽴的編譯和發布。 未來kubernetes甚⾄可能⽀持單個容器的在線升級。
3、使⽤⽅便。 ⽤戶不必運⾏⾃⼰的進程管理器, 還要擔⼼錯誤信號傳播等。
4、效率。 因為由基礎架構提供更多的職責, 所以容器可以變得更加輕量級。
Pod的持久性
Pod在設計⽀持就不是作為持久化實體的。 在調度失敗、 節點故障、 缺少資源或者節點維護的狀態下都會死掉會被驅逐。
通常, ⽤戶不需要⼿動直接創建Pod, ⽽是應該使⽤controller(例如Deployments) , 即使是在創建單個Pod的情況下。 Controller可以提供集群級別的⾃愈功能、 復制和升級管理。
Pod原語有利於:
- 調度程序和控制器可插拔性
- 支持pod級操作,無需通過控屍氣API代理他們
- 將pod生命周期與控制器生命周期分離
- 控制器與服務的分離,端點控制器只是監視pod
- 將集群集功能與kubelet及共鞥的清晰組合
- 高可用性應用程序,他們可以在終止前及在刪除前更換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進程:
- 如果在pod中定義了preStop hook,在停止pod前會被調用。如果寬限期后 preStop hook仍然在運行,第二部會增加2秒寬限期;
- 向Pod中的進程發送TERM信號
- 跟第三部同時,該Pod將從該service的端點列表中刪除,不再是replication controller中的一部分,關閉的慢的pod將繼續處理load balancer轉發的流量
- 過了寬限期后,將向Pod中易安存在的進程發SIGKILL信號殺掉進程。
- kubelet會在APIserver中完成Pod的刪除,通過將優雅周期設置為0(立即刪除)Pod在API中小時,並且客戶端也不可見。
刪除寬限期默認是30秒。 kubectl delete 命令⽀持 —grace-period=
Init容器
Pod能夠具有多個容器,應用運行在容器里面,但是它也可能有一個或多個咸魚應用容器啟動的Init容器。
Init容器與普通容器很像,除了一下亮點:
- Init容器總是u以女性到成功完成為止。
- 每個Init容器都必要在下一個Init容器啟動之前完成。
如果 Pod 的 Init 容器失敗, Kubernetes 會不斷地重啟該 Pod, 直到 Init 容器成功為⽌。 然⽽, 如果 Pod 對應的restartPolicy 為 Never, 它不會重新啟動。
Pause容器
Pause容器,又叫Infra容器。我們檢查node節點的時候會發現每個node上都運行了很多的pause容器,例如如下。
[root@node02 ~]# docker ps |grep pause
db1f4da98626 registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 24 hours ago Up 24 hours k8s_POD_myapp-9b4987d5-djdr9_default_995067e0-5124-11e9-80a7-000c295ec349_0
e344da31cee8 registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 24 hours ago Up 24 hours k8s_POD_client-f5cdb799f-pklmc_default_25f4bf9b-5121-11e9-80a7-000c295ec349_0
de5e811fa5fa registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 28 hours ago Up 28 hours k8s_POD_nginx-deploy-84cbfc56b6-tcssz_default_10003a4a-5104-11e9-80a7-000c295ec349_0
3f7d179b79b9 registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 30 hours ago Up 30 hours k8s_POD_kube-flannel-ds-amd64-gwbql_kube-system_0f2de1c5-506c-11e9-80a7-000c295ec349_1
cc07e6411d32 registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 30 hours ago Up 30 hours k8s_POD_kube-proxy-cz2rf_kube-system_1f58e488-5068-11e9-80a7-000c295ec349_1
kubernetes中的pause容器主要為每個業務容器提供以下功能:
- 在pod中擔任Linux命名空間共享的基礎;
- 啟用pid命名空間,開啟init進程。
pause容器的作⽤可以從這個例⼦中看出, ⾸先⻅下
圖:
(圖片來源於網絡)
我們⾸先在節點上運⾏⼀個pause容器。
[root@node02 ~]# docker run --name pause -d -p 8880:80 xiaobai20201/pause:3.1
Unable to find image 'xiaobai20201/pause:3.1' locally
3.1: Pulling from xiaobai20201/pause
Digest: sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610
Status: Downloaded newer image for xiaobai20201/pause:3.1
d4078dc99bec81167c8d288c2b44485d407780913eee88458986d5bb3750c509
然后再運⾏⼀個nginx容器, nginx將為 localhost:2368 創建⼀個代理。
[root@node02 ~]# vim nginx.conf
error_log stderr;
events { worker_connections 1024; }
http {
access_log /dev/stdout combined;
server {
listen 80 default_server;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:2368;
}
}
}
[root@node02 ~]# docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause nginx
然后為ghost創建一個容器應用,這是一款博客軟件。
[root@node02 ~]# docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause ghost
現在訪問http://10.0.0.12:8880/ 就可以看到ghost博客的界面了
解析
pause容器將內部的80端⼝映射到宿主機的8880端⼝, pause容器在宿主機上設置好了⽹絡namespace后, nginx容器加⼊到該⽹絡namespace中, 我們看到nginx容器啟動的時候指定了 --net=container:pause , ghost容器同樣加⼊到了該⽹絡namespace中, 這樣三個容器就共享了⽹絡, 互相之間就可以使⽤ localhost 直接通信, --ipc=contianer:pause --pid=container:pause 就是三個容器處於同⼀個namespace中, init進程為 pause , 這時我們進⼊到ghost容器中查看進程情況。
Pod的生命周期
Pod的phase
Pod的status信息保存在PodStatus中定義,有一個phase字段
Pod的相位(phase)是Pod在其生命周期中的簡單宏觀概述,該階段並不是對容器或Pod的總和匯總,也不是為了作為綜合狀態機。
Pod香味的數量和含義是嚴格指定的。除了文檔中列舉的狀態外,不應該再假定Pod有其他phase值。
下面是phase可能的值:
- 掛起 Pending:Pod已經被Kubernetes系統接受,但是有一個或多個容器鏡像尚未創建,等待時間包括調度Pod的時間和通過網絡下載鏡像的時間。
- 運行中 Running:該Pod已經綁定到一個節點上,Pod中所有的容器都已經被創建,至少有一個容器正在運行或者處於啟動或者重啟狀態。
- 成功 Succeeded:Pod中的所有容器都被成功終止,並且不會在重啟。
- 失敗 Failed:Pod中的所有容器已經終止,並且至少有一個容器是因為失敗終止,也就是說,容器以非0狀態退出或者被系統終止。
- 位置 Unknown:因為某些議無法獲取Pod的狀態,哦那個廠是因為與Pod所在的節點通信失敗。
Pod的狀態
Pod 有一個 PodStatus 對象,其中包含一個 PodCondition 數組。 PodCondition 數組的每個元素都有一個 type 字段和一個 status 字段。type 字段是字符串,可能的值有 PodScheduled、Ready、Initialized 和 Unschedulable。status 字段是一個字符串,可能的值有 True、False 和 Unknown。
容器探針
在pod生命周期中可以做的一些事情。主容器啟動前可以完成初始化容器,初始化容器可以有多個,他們是串行執行的,執行完成后就推出了,在主程序剛剛啟動的時候可以指定一個post start 主程序啟動開始后執行一些操作,在主程序結束前可以指定一個 pre stop 表示主程序結束前執行的一些操作。在程序啟動后可以做兩類檢測 liveness probe(存活性探測) 和 readness probe(就緒性探測)
探針是由 kubelet 對容器執⾏的定期診斷。 要執⾏診斷, kubelet 調⽤由容器實現的 Handler。 有三種類型的處理程序:
- ExecAction: 在容器內執⾏指定命令。 如果命令退出時返回碼為 0 則認為診斷成功。
- TCPSocketAction: 對指定端⼝上的容器的 IP 地址進⾏ TCP 檢查。 如果端⼝打開, 則診斷被認為是成功的
- HTTPGetAction: 對指定的端⼝和路徑上的容器的 IP 地址執⾏ HTTP Get 請求。 如果響應的狀態碼⼤於等於200且⼩於 400, 則診斷被認為是成功的。
Kubelet 可以選擇是否執行在容器上運行的兩種探針執行和做出反應:
- livenessProbe:指示容器是否正在運行。如果存活探測失敗,則 kubelet 會殺死容器,並且容器將受到其 重啟策略 的影響。如果容器不提供存活探針,則默認狀態為 Success。
- readinessProbe:指示容器是否准備好服務請求。如果就緒探測失敗,端點控制器將從與 Pod 匹配的所有 Service 的端點中刪除該 Pod 的 IP 地址。初始延遲之前的就緒狀態默認為 Failure。如果容器不提供就緒探針,則默認狀態為 Success。
存活性探測 livenessProbe
解析
[root@master ~]# kubectl explain pods.spec.containers.livenessProbe.
KIND: Pod
VERSION: v1
exec <Object> #命令式探針
failureThreshold <integer> #探測失敗次數,超過該次數才算失敗,默認是3
periodSeconds <integer> #探測間隔時間,默認10s
successThreshold <integer> #探測成功的最少次數,默認是1,即探測成功1次就算是成功
tcpSocket <Object> #檢測端口的探測
initialDelaySeconds #初始化延遲探測,第一次探測的時候,因為主程序未必啟動完成
timeoutSeconds <integer> # 探測超時的秒數 默認1s
httpGet <Object> #http請求探測
exec探針舉例
[root@master manifests]# vim liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","touch /tmp/healthy;sleep 30;rm -f /tmp/healthy;sleep 600"]
livenessProbe:
exec:
command: ["test","-e","/tmp/healthy"]
initialDelaySecond: 1
periodSecond: 3
#運行並查看pod
[root@master manifests]# kubectl apply -f liveness-exec.yaml
pod/liveness-exec-pod created
[root@master manifests]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 25h
liveness-exec-pod 1/1 Running 0 5s
myapp-9b4987d5-47sjj 1/1 Running 0 25h
myapp-9b4987d5-684q9 1/1 Running 0 25h
myapp-9b4987d5-djdr9 1/1 Running 0 25h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 29h
pod-demo 2/2 Running 4 4h33m
liveness-exec-pod 1/1 Running 1 87s
#重啟次數變成1
上面的資源清單中定義了一個Pod對象,基於busybox鏡像啟動一個運行“touch /tmp/healthy;sleep 30;rm -f /tmp/healthy;sleep 600”命令的容器,此命令在容器啟動時創建/tmp/healthy文件,並於60秒之后將其刪除。存活性探針運行“test -e /tmp/healthy”命令檢查/tmp/healthy文件的存在性,若文件存在則返回狀態碼0,表示成功通過測試。
httpGet探測舉例
[root@master manifests]# vim liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-httpget-container
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
httpGet:
port: http
path: /index.html
#創建Pod並查看
[root@master manifests]# kubectl create -f liveness-httpget.yaml
pod/liveness-httpget-pod created
[root@master manifests]# kubectl get pods
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 26h
liveness-exec-pod 1/1 Running 8 20m
liveness-httpget-pod 1/1 Running 0 8s
myapp-9b4987d5-47sjj 1/1 Running 0 25h
myapp-9b4987d5-684q9 1/1 Running 0 25h
myapp-9b4987d5-djdr9 1/1 Running 1 25h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 29h
pod-demo 2/2 Running 4 4h53m
#手動進入容器刪除index.html文件,再監控pod狀態
[root@master manifests]# kubectl exec -it liveness-httpget-pod -- /bin/sh
/ # rm -f /usr/share/nginx/html/index.html
#查看pod
^C[root@master manifests]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 26h
liveness-exec-pod 0/1 CrashLoopBackOff 9 24m
liveness-httpget-pod 1/1 Running 1 3m41s
myapp-9b4987d5-47sjj 1/1 Running 0 25h
myapp-9b4987d5-684q9 1/1 Running 0 25h
myapp-9b4987d5-djdr9 1/1 Running 1 25h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 29h
pod-demo 2/2 Running 4 4h57m
#發現liveness-httpget-pod已經重啟了一次
TCP探測舉例
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness-tcp
name: liveness-tcp
spec:
containers:
- name: liveness-tcp-demo
image: nginx:1.12-alpine
ports:
- name: http
containerPort: 80
livenessProbe:
tcpSocket:
port: http
就緒性探測 readnessProbe
[root@master manifests]# vim readness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
port: http
path: /index.html
initialDelaySecond: 1
periodSeconds: 3
此時pod運行正常,進入容器刪除首頁文件后觀察pod狀態
[root@master manifests]# kubectl exec -it readiness-httpget-pod -- /bin/sh
/ # rm -f /usr/share/nginx/html/index.html
[root@master manifests]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 26h
myapp-9b4987d5-47sjj 1/1 Running 0 26h
myapp-9b4987d5-684q9 1/1 Running 0 26h
myapp-9b4987d5-djdr9 1/1 Running 1 26h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 30h
pod-demo 2/2 Running 5 5h27m
readiness-httpget-pod 0/1 Running 0 115s
重寫寫入index.html文件后繼續觀察pod
/ # echo ad >/usr/share/nginx/html/index.html
^C[root@master manifests]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 26h
myapp-9b4987d5-47sjj 1/1 Running 0 26h
myapp-9b4987d5-684q9 1/1 Running 0 26h
myapp-9b4987d5-djdr9 1/1 Running 1 26h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 30h
pod-demo 2/2 Running 5 5h29m
readiness-httpget-pod 1/1 Running 0 3m54s
livenessProbe和readinessProbe使用場景
如果容器中的進程能夠在遇到問題或不健康的情況下自行崩潰,則不一定需要存活探針; kubelet 將根據 Pod 的restartPolicy 自動執行正確的操作。
如果希望容器在探測失敗時被殺死並重新啟動,那么請指定一個存活探針,並指定restartPolicy 為 Always 或 OnFailure。
如果要僅在探測成功時才開始向 Pod 發送流量,請指定就緒探針。在這種情況下,就緒探針可能與存活探針相同,但是 spec 中的就緒探針的存在意味着 Pod 將在沒有接收到任何流量的情況下啟動,並且只有在探針探測成功后才開始接收流量。
如果您希望容器能夠自行維護,您可以指定一個就緒探針,該探針檢查與存活探針不同的端點。
請注意,如果您只想在 Pod 被刪除時能夠排除請求,則不一定需要使用就緒探針;在刪除 Pod 時,Pod 會自動將自身置於未完成狀態,無論就緒探針是否存在。當等待 Pod 中的容器停止時,Pod 仍處於未完成狀態。
lifecycle
定義容器啟動后和終止前立即執行的動作
解析
[root@master ~]# kubectl explain pods.spec.containers.lifecycle.
postStart <Object> #啟動后
- exec
- httpGet
- tcpSocket
preStop <Object> #終止前
- exec
- httpGet
- tcpSocket
postStart舉例
[root@master manifests]# vim poststart-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: poststart-pod
namespace: default
spec:
containers:
- name: busybox-httpd
image: busybox
imagePullPolicy: IfNotPresent
lifecycle:
postStart:
exec:
command: ["mkdir","-p","/tmp/share"]
command: ["/bin/sh","-c","sleep 3600"]
#進入容器查看是否有/tmp/share目錄
[root@master manifests]# kubectl exec -it poststart-pod -- /bin/sh
/ # ls -l /tmp
total 0
drwxr-xr-x 2 root root 6 Mar 29 09:19 share
參考資料
https://www.cnblogs.com/linuxk
馬永亮. Kubernetes進階實戰 (雲計算與虛擬化技術叢書)
Kubernetes-handbook-jimmysong-20181218