pod 生命周期
我們一般將 pod 對象從創建至終的這段時間范圍稱為 pod 的生命周期,它主要包含下面的過程
- pod 創建過程
- 運行初始化容器(init container)過程
- 運行主容器(main container)
- 容器啟動后鈎子(post start)、容器終止前鈎子(pre stop)
- 容器的存活性探測(liveness probe)、就緒性探測(readiness probe)
- pod 終止過程
在整個生命周期中,pod 會出現 5 種狀態(相位),分別如下
- 掛起(Pending):apiserver 已經創建了 pod 資源對象,但它尚未被調度完成或者仍處於下載鏡像的過程中
- 運行中(Running):pod 已經被調度至某節點,並且所有容器都已經被 kubelet 創建完成
- 成功(Succeeded):pod 中的所有容器都已經成功終止並且不會被重啟
- 失敗(Failed):所有容器都已經終止,但至少有一個容器終止失敗,即容器返回了非 0 值的退出狀態
- 未知(Unknown):apiserver 無法正常獲取到 pod 對象的狀態信息,通常由網絡通信失敗所導致
創建和終止
pod 的創建過程
-
用戶通過 kubectl 或其他 api 客戶端提交需要創建的 pod 信息給 apiServer
-
apiServer 開始生成 pod 對象的信息,並將信息存入 etcd,然后返回確認信息至客戶端
-
apiServer 開始反映 etcd 中的 pod 對象的變化,其它組件使用 watch 機制來跟蹤檢查 apiServer 上的變動
-
scheduler 發現有新的 pod 對象要創建,開始為 Pod 分配主機並將結果信息更新至 apiServer
-
node 節點上的 kubelet 發現有 pod 調度過來,嘗試調用 docker 啟動容器,並將結果回送至 apiServer
-
apiServer 將接收到的 pod 狀態信息存入 etcd 中
pod 的終止過程
- 用戶向 apiServer 發送刪除 pod 對象的命令
- apiServcer 中的 pod 對象信息會隨着時間的推移而更新,在寬限期內(默認30s),pod 被視為 dead
- 將 pod 標記為 terminating 狀態
- kubelet 在監控到 pod 對象轉為 terminating 狀態的同時啟動 pod 關閉過程
- 端點控制器監控到 pod 對象的關閉行為時將其從所有匹配到此端點的 service 資源的端點列表中移除
- 如果當前 pod 對象定義了 preStop 鈎子處理器,則在其標記為 terminating 后即會以同步的方式啟動執行
- pod 對象中的容器進程收到停止信號
- 寬限期結束后,若 pod 還存在仍在運行的進程,那么 pod 對象會收到立即終止的信號
- kubelet 請求 apiServer 將此 pod 資源的寬限期設置為 0 從而完成刪除操作,此時 pod 對於用戶已不可見
初始化容器
初始化容器是在 pod 的主容器啟動之前要運行的容器,主要是做一些主容器的前置工作,它具有兩大特征:
- 初始化容器必須運行完成直至結束,若某初始化容器運行失敗,那么 kubernetes 需要重啟它直到成功完成
- 初始化容器必須按照定義的順序執行,當且僅當前一個成功之后,后面的一個才能運行
初始化容器有很多的應用場景,下面列出的是最常見的幾個
- 提供主容器鏡像中不具備的工具程序或自定義代碼
- 初始化容器要先於應用容器啟動
接下來做一個案例,模擬下面這個需求:
假設要以主容器來運行 nginx,但是要求在運行 nginx 之前先要能夠連接上 mysql 和 redis 所在服務器
為了簡化測試,事先規定好mysql(192.168.90.14)
和redis(192.168.90.15)
服務器的地址
創建 pod-initcontainer.yaml,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: zouzou
spec:
containers:
- name: main-container
image: nginx:1.14
ports:
- name: nginx-port
containerPort: 80
initContainers: # 初始化容器,有兩個,分別 ping mysql 和 redis,mysql 可以 ping 通才會 ping redis
- name: test-mysql
image: busybox:1.30
command: ['sh', '-c', 'until ping 192.168.90.14 -c 1 ; do echo waiting for mysql...; sleep 2; done;']
- name: test-redis
image: busybox:1.30
command: ['sh', '-c', 'until ping 192.168.90.15 -c 1 ; do echo waiting for reids...; sleep 2; done;']
執行下面命令
# 創建pod
[root@dce-10-6-215-215 ~]# kubectl create -f pod-initcontainer.yaml
pod/pod-initcontainer created
# 查看pod狀態
# 發現pod卡在啟動第一個初始化容器過程中,后面的容器不會運行
[root@dce-10-6-215-215 ~]# kubectl describe pod pod-initcontainer -n zouzou
........
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 49s default-scheduler Successfully assigned dev/pod-initcontainer to node1
Normal Pulled 48s kubelet, node1 Container image "busybox:1.30" already present on machine
Normal Created 48s kubelet, node1 Created container test-mysql
Normal Started 48s kubelet, node1 Started container test-mysql
# 動態查看pod
[root@dce-10-6-215-215 ~]# kubectl get pods pod-initcontainer -n zouzou -w
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Init:0/2 0 15s
pod-initcontainer 0/1 Init:1/2 0 52s
pod-initcontainer 0/1 Init:1/2 0 53s
pod-initcontainer 0/1 PodInitializing 0 89s
pod-initcontainer 1/1 Running 0 90s
# 接下來新開一個shell,為當前服務器新增兩個ip,觀察pod的變化
[root@dce-10-6-215-215 ~]# ifconfig ens33:1 192.168.90.14 netmask 255.255.255.0 up
[root@dce-10-6-215-215 ~]# ifconfig ens33:2 192.168.90.15 netmask 255.255.255.0 up
鈎子函數
鈎子函數能夠感知自身生命周期中的事件,並在相應的時刻到來時運行用戶指定的程序代碼
k8s 在主容器的啟動之后和停止之前提供了兩個鈎子函數
- post start:容器創建之后執行,如果失敗了會重啟容器
- pre stop:容器終止之前執行,執行完成之后容器將成功終止,在其完成之前會阻塞刪除容器的操作
鈎子處理器支持使用下面三種方式定義動作
- Exec 命令:在容器內執行一次命令
……
lifecycle:
postStart:
exec:
command:
- cat
- /tmp/healthy
……
- TCPSocket:在當前容器嘗試訪問指定的 socket
……
lifecycle:
postStart:
tcpSocket:
port: 8080
……
- HTTPGet:在當前容器中向某 url 發起 http 請求
……
lifecycle:
postStart:
httpGet:
path: / #URI地址
port: 80 #端口號
host: 192.168.5.3 #主機地址
scheme: HTTP #支持的協議,http或者https
……
接下來,以 exec 方式為例,演示下鈎子函數的使用,創建 pod-hook-exec.yaml 文件,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-hook-exec
namespace: zouzou
spec:
containers:
- name: main-container
image: nginx:1.14
ports:
- name: nginx-port
containerPort: 80
lifecycle:
postStart:
exec: # 在容器啟動的時候執行一個命令,修改掉 nginx 的默認首頁內容
command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
preStop:
exec: # 在容器停止之前停止nginx服務,這個效果在這里看不到
command: ["/usr/sbin/nginx","-s","quit"]
啟動 pod,查看內容
# 創建 pod
[root@dce-10-6-215-215 tmp]# kubectl apply -f pod-hook-exec.yaml
pod/pod-hook-exec created
# 查看 pod 的詳細信息
[root@dce-10-6-215-215 tmp]# kubectl get pod pod-hook-exec -n zouzou -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-hook-exec 1/1 Running 0 48s 172.29.34.229 dce-10-6-215-200 <none> <none>
# 查看內容,可以看到返回的是更改后的
[root@dce-10-6-215-215 tmp]# curl 172.29.34.229:80
postStart...