概述
我們一般將Pod對象從創(chuàng)建至終的這段時間范圍稱為Pod的生命周期,它主要包含下面的過程:
- Pod 創(chuàng)建過程
- 運(yùn)行初始化容器(init container)過程
- 運(yùn)行主容器(main container)過程
- 容器啟動后鉤子(post start)搂抒、容器終止前鉤子(pre stop)
-容器的存活性探測(liveness probe)、就緒性探測(readiness probe)
- 容器啟動后鉤子(post start)搂抒、容器終止前鉤子(pre stop)
- pod 終止過程
在整個生命周期中,Pod會出現(xiàn)5中狀態(tài),分別如下:
- 掛起(Pending):apiserver已經(jīng)創(chuàng)建了pod資源對象肆捕,但它尚未被調(diào)度完成或者仍處于下載鏡像的過程中
- 運(yùn)行中(Running):pod 已經(jīng)被調(diào)度至某節(jié)點(diǎn),并且所有容器都已經(jīng)被kubelet創(chuàng)建完成
- 成功(Successded):pod中的所有容器都已經(jīng)成功終止并且不會被重啟
- 失敻歉摺(Failed):所有容器都已經(jīng)終止慎陵,但至少有一個容器終止失敗,即容器返回了非0值得退出狀態(tài)
- 未知(Unknown):apiserver無法正常獲取到pod對象的狀態(tài)信息喻奥,通常由網(wǎng)絡(luò)通信失敗所導(dǎo)致
創(chuàng)建和終止
pod的創(chuàng)建過程
- 用戶通過kubectl或其他api客戶端提交需要創(chuàng)建的pod信息給apiServer
- apiServer開始生成pod對象的信息席纽,并將信息存入etcd,然后返回確認(rèn)信息至客戶端
- apiServer開始反映etcd中的pod對象的變化撞蚕,其它組件使用watch機(jī)制來跟蹤檢查apiServer上的變動
- scheduler發(fā)現(xiàn)有新的pod對象要創(chuàng)建润梯,開始為Pod分配主機(jī)并將結(jié)果信息更新至apiServer
- node節(jié)點(diǎn)上的kubelet發(fā)現(xiàn)有pod調(diào)度過來,嘗試調(diào)用docker啟動容器甥厦,并將結(jié)果回送至apiServer
- apiServer將接收到的pod狀態(tài)信息存入etcd中
pod的終止過程
- 用戶向apiServer發(fā)送刪除pod對象的命令
- apiServcer中的pod對象信息會隨著時間的推移而更新纺铭,在寬限期內(nèi)(默認(rèn)30s),pod被視為dead
- 將pod標(biāo)記為terminating狀態(tài)
- kubelet在監(jiān)控到pod對象轉(zhuǎn)為terminating狀態(tài)的同時啟動pod關(guān)閉過程
- 端點(diǎn)控制器監(jiān)控到pod對象的關(guān)閉行為時將其從所有匹配到此端點(diǎn)的service資源的端點(diǎn)列表中移除
- 如果當(dāng)前pod對象定義了preStop鉤子處理器刀疙,則在其標(biāo)記為terminating后即會以同步的方式啟動執(zhí)行
- pod對象中的容器進(jìn)程收到停止信號
- 寬限期結(jié)束后舶赔,若pod中還存在仍在運(yùn)行的進(jìn)程,那么pod對象會收到立即終止的信號
- kubelet請求apiServer將此pod資源的寬限期設(shè)置為0從而完成刪除操作谦秧,此時pod對于用戶已不可見
初始化和容器
初始化容器是在pod的主容器啟動之前要運(yùn)行的容器竟纳,主要是做一些主容器的前置工作,它具有兩大特征:
- 初始化容器必須運(yùn)行完成直至結(jié)束油够,若某初始化容器運(yùn)行失敗蚁袭,那么kubernetes需要重啟它直到成功完成
- 初始化容器必須按照定義的順序執(zhí)行,當(dāng)且僅當(dāng)前一個成功之后石咬,后面的一個才能運(yùn)行
初始化容器有很多的應(yīng)用場景揩悄,下面列出的是最常見的幾個:
- 提供主容器鏡像中不具備的工具程序或自定義代碼
- 初始化容器要先于應(yīng)用容器串行啟動并運(yùn)行完成,因此可用于延后應(yīng)用容器的啟動直至其依賴的條件得到滿足
假設(shè)要以主容器來運(yùn)行nginx鬼悠,但是要求在運(yùn)行nginx之前先要能夠連接上相應(yīng)的服務(wù)器是可以ping通的删性。
創(chuàng)建 pod-initcontainer.yaml亏娜,內(nèi)容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
# 初始化容器
initContainers:
# 模擬mysql連接
- name: test-mysql
image: busybox:1.30
command: ['sh', '-c', 'until ping 192.168.81.102 -c 1 ; do echo waiting for mysql...; sleep 2; done;']、
# 模擬redis連接
- name: test-redis
image: busybox:1.30
command: ['sh', '-c', 'until ping 192.168.81.103 -c 1 ; do echo waiting for reids...; sleep 2; done;']
# 創(chuàng)建pod
[root@k8s-master01 ~]# kubectl create -f pod-initcontainer.yaml
pod/pod-initcontainer created
# 查看pod狀態(tài)
# 發(fā)現(xiàn)pod卡在啟動第一個初始化容器過程中蹬挺,后面的容器不會運(yùn)行
root@k8s-master01 ~]# kubectl describe pod pod-initcontainer -n dev
........
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
# 動態(tài)查看pod
[root@k8s-master01 ~]# kubectl get pods pod-initcontainer -n dev -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维贺,為當(dāng)前服務(wù)器新增兩個ip,觀察pod的變化巴帮,為網(wǎng)卡添加地址
[root@k8s-master01 ~]# ifconfig ens33:1 192.168.81.102 netmask 255.255.255.0 up
[root@k8s-master01 ~]# ifconfig ens33:2 192.168.81.103 netmask 255.255.255.0 up
鉤子函數(shù)
鉤子函數(shù)能夠感知自身生命周期中的事件溯泣,并在相應(yīng)的時刻到來時運(yùn)行用戶指定的程序代碼。
kubernetes在主容器的啟動之后和停止之前提供了兩個鉤子函數(shù):
- post start:容器創(chuàng)建之后執(zhí)行榕茧,如果失敗了會重啟容器
- pre stop :容器終止之前執(zhí)行垃沦,執(zhí)行完成之后容器將成功終止,在其完成之前會阻塞刪除容器的操作
鉤子處理器支持使用下面三種方式定義動作:
- Exec命令:在容器內(nèi)執(zhí)行一次命令
……
lifecycle:
postStart:
exec:
command:
- cat
- /tmp/healthy
……
- TCPSocket:在當(dāng)前容器嘗試訪問指定的socket
……
lifecycle:
postStart:
tcpSocket:
port: 8080
……
- HTTPGet:在當(dāng)前容器中向某url發(fā)起http請求
……
lifecycle:
postStart:
httpGet:
path: / #URI地址
port: 80 #端口號
host: 192.168.5.3 #主機(jī)地址
scheme: HTTP #支持的協(xié)議用押,http或者h(yuǎn)ttps
……
接下來肢簿,以exec方式為例,演示下鉤子函數(shù)的使用蜻拨,創(chuàng)建pod-hook-exec.yaml文件池充,內(nèi)容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-hook-exec
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
# 生命周期
lifecycle:
postStart:
exec: # 在容器啟動的時候執(zhí)行一個命令,修改掉nginx的默認(rèn)首頁內(nèi)容
command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
preStop:
exec: # 在容器停止之前停止nginx服務(wù)
command: ["/usr/sbin/nginx","-s","quit"]
# 創(chuàng)建pod
[root@k8s-master01 ~]# kubectl create -f pod-hook-exec.yaml
pod/pod-hook-exec created
# 查看pod
[root@k8s-master01 ~]# kubectl get pods pod-hook-exec -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-hook-exec 1/1 Running 0 29s 10.244.2.48 node2
# 訪問pod
[root@k8s-master01 ~]# curl 10.244.2.48
postStart...
容器探測
容器探測用于檢測容器中的應(yīng)用實(shí)例是否正常工作缎讼,是保障業(yè)務(wù)可用性的一種傳統(tǒng)機(jī)制收夸。如果經(jīng)過探測,實(shí)例的狀態(tài)不符合預(yù)期血崭,那么kubernetes就會把該問題實(shí)例" 摘除 "咱圆,不承擔(dān)業(yè)務(wù)流量。kubernetes提供了兩種探針來實(shí)現(xiàn)容器探測功氨,分別是:
- liveness probes:存活性探針序苏,用于檢測應(yīng)用實(shí)例當(dāng)前是否處于正常運(yùn)行狀態(tài),如果不是捷凄,k8s會重啟容器
- readiness probes:就緒性探針忱详,用于檢測應(yīng)用實(shí)例當(dāng)前是否可以接收請求,如果不能跺涤,k8s不會轉(zhuǎn)發(fā)流量
livenessProbe 決定是否重啟容器匈睁,readinessProbe 決定是否將請求轉(zhuǎn)發(fā)給容器。
上面兩種探針目前均支持三種探測方式:
- Exec命令:在容器內(nèi)執(zhí)行一次命令桶错,如果命令執(zhí)行的退出碼為0航唆,則認(rèn)為程序正常,否則不正常
……
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
……
- TCPSocket:將會嘗試訪問一個用戶容器的端口院刁,如果能夠建立這條連接糯钙,則認(rèn)為程序正常,否則不正常
……
livenessProbe:
tcpSocket:
port: 8080
……
- HTTPGet:調(diào)用容器內(nèi)Web應(yīng)用的URL,如果返回的狀態(tài)碼在200和399之間任岸,則認(rèn)為程序正常再榄,否則不正常
……
livenessProbe:
httpGet:
path: / #URI地址
port: 80 #端口號
host: 127.0.0.1 #主機(jī)地址
scheme: HTTP #支持的協(xié)議,http或者h(yuǎn)ttps
……
下面以liveness probes為例享潜,做幾個演示:
方式一:Exec
創(chuàng)建pod-liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-exec
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
exec:
command: ["/bin/cat","/tmp/hello.txt"] # 執(zhí)行一個查看文件的命令
創(chuàng)建pod困鸥,觀察效果
# 創(chuàng)建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-exec.yaml
pod/pod-liveness-exec created
# 查看Pod詳情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-exec -n dev
......
Normal Created 20s (x2 over 50s) kubelet, node1 Created container nginx
Normal Started 20s (x2 over 50s) kubelet, node1 Started container nginx
Normal Killing 20s kubelet, node1 Container nginx failed liveness probe, will be restarted
Warning Unhealthy 0s (x5 over 40s) kubelet, node1 Liveness probe failed: cat: can't open '/tmp/hello11.txt': No such file or directory
# 觀察上面的信息就會發(fā)現(xiàn)nginx容器啟動之后就進(jìn)行了健康檢查
# 檢查失敗之后,容器被kill掉剑按,然后嘗試進(jìn)行重啟(這是重啟策略的作用疾就,后面講解)
# 稍等一會之后,再觀察pod信息艺蝴,就可以看到RESTARTS不再是0虐译,而是一直增長
[root@k8s-master01 ~]# kubectl get pods pod-liveness-exec -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 0/1 CrashLoopBackOff 2 3m19s
# 當(dāng)然接下來,可以修改成一個存在的文件吴趴,比如/tmp/hello.txt,再試侮攀,結(jié)果就正常了......
方式二:TCPSocket
創(chuàng)建pod-liveness-tcpsocket.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-tcpsocket
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
tcpSocket:
port: 8080 # 嘗試訪問8080端口
創(chuàng)建pod锣枝,觀察效果
# 創(chuàng)建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created
# 查看Pod詳情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-tcpsocket -n dev
......
Normal Scheduled 31s default-scheduler Successfully assigned dev/pod-liveness-tcpsocket to node2
Normal Pulled <invalid> kubelet, node2 Container image "nginx:1.17.1" already present on machine
Normal Created <invalid> kubelet, node2 Created container nginx
Normal Started <invalid> kubelet, node2 Started container nginx
Warning Unhealthy <invalid> (x2 over <invalid>) kubelet, node2 Liveness probe failed: dial tcp 10.244.2.44:8080: connect: connection refused
# 觀察上面的信息,發(fā)現(xiàn)嘗試訪問8080端口,但是失敗了
# 稍等一會之后兰英,再觀察pod信息撇叁,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~]# kubectl get pods pod-liveness-tcpsocket -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 0/1 CrashLoopBackOff 2 3m19s
# 當(dāng)然接下來畦贸,可以修改成一個可以訪問的端口陨闹,比如80,再試薄坏,結(jié)果就正常了......
方式三:HTTPGet
創(chuàng)建pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet: # 其實(shí)就是訪問http://127.0.0.1:80/hello
scheme: HTTP #支持的協(xié)議趋厉,http或者h(yuǎn)ttps
port: 80 #端口號
path: /hello #URI地址
創(chuàng)建pod,觀察效果
# 創(chuàng)建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created
# 查看Pod詳情
[root@k8s-master01 ~]# kubectl describe pod pod-liveness-httpget -n dev
.......
Normal Pulled 6s (x3 over 64s) kubelet, node1 Container image "nginx:1.17.1" already present on machine
Normal Created 6s (x3 over 64s) kubelet, node1 Created container nginx
Normal Started 6s (x3 over 63s) kubelet, node1 Started container nginx
Warning Unhealthy 6s (x6 over 56s) kubelet, node1 Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 6s (x2 over 36s) kubelet, node1 Container nginx failed liveness probe, will be restarted
# 觀察上面信息胶坠,嘗試訪問路徑君账,但是未找到,出現(xiàn)404錯誤
# 稍等一會之后,再觀察pod信息沈善,就可以看到RESTARTS不再是0乡数,而是一直增長
[root@k8s-master01 ~]# kubectl get pod pod-liveness-httpget -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 5 3m17s
# 當(dāng)然接下來,可以修改成一個可以訪問的路徑path闻牡,比如/净赴,再試,結(jié)果就正常了......
至此罩润,已經(jīng)使用liveness Probe演示了三種探測方式玖翅,但是查看livenessProbe的子屬性,會發(fā)現(xiàn)除了這三種方式,還有一些其他的配置烧栋,在這里一并解釋下:
[root@k8s-master01 ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:
exec <Object>
tcpSocket <Object>
httpGet <Object>
initialDelaySeconds <integer> # 容器啟動后等待多少秒執(zhí)行第一次探測
timeoutSeconds <integer> # 探測超時時間写妥。默認(rèn)1秒,最小1秒
periodSeconds <integer> # 執(zhí)行探測的頻率审姓。默認(rèn)是10秒珍特,最小1秒
failureThreshold <integer> # 連續(xù)探測失敗多少次才被認(rèn)定為失敗。默認(rèn)是3魔吐。最小值是1
successThreshold <integer> # 連續(xù)探測成功多少次才被認(rèn)定為成功扎筒。默認(rèn)是1
- initialDelaySeconds 容器啟動后等待多少秒執(zhí)行第一次探測
- timeoutSeconds 探測超時時間。默認(rèn)1秒酬姆,最小1秒
- periodSeconds 執(zhí)行探測的頻率嗜桌。默認(rèn)是10秒,最小1秒
- failureThreshold 連續(xù)探測失敗多少次才被認(rèn)定為失敗辞色。默認(rèn)是3骨宠。最小值是1
- successThreshold 連續(xù)探測成功多少次才被認(rèn)定為成功。默認(rèn)是1
下面稍微配置兩個相满,演示下效果即可:
[root@k8s-master01 ~]# more pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet:
scheme: HTTP
port: 80
path: /
initialDelaySeconds: 30 # 容器啟動后30s開始探測
timeoutSeconds: 5 # 探測超時時間為5s
重啟策略
一旦容器探測出現(xiàn)了問題层亿,kubernetes就會對容器所在的Pod進(jìn)行重啟,其實(shí)這是由pod的重啟策略決定的立美,pod的重啟策略有 3 種匿又,分別如下:
- Always :容器失效時,自動重啟該容器建蹄,這也是默認(rèn)值碌更。
- OnFailure : 容器終止運(yùn)行且退出碼不為0時重啟
- Never : 不論狀態(tài)為何,都不重啟該容器
重啟策略適用于pod對象中的所有容器洞慎,首次需要重啟的容器痛单,將在其需要時立即進(jìn)行重啟,隨后再次需要重啟的操作將由kubelet延遲一段時間后進(jìn)行劲腿,且反復(fù)的重啟操作的延遲時長以此為10s桦他、20s、40s谆棱、80s快压、160s和300s,300s是最大延遲時長垃瞧。
創(chuàng)建pod-restartpolicy.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-restartpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet:
scheme: HTTP
port: 80
path: /hello
restartPolicy: Never # 設(shè)置重啟策略為Never
運(yùn)行Pod測試
# 創(chuàng)建Pod
[root@k8s-master01 ~]# kubectl create -f pod-restartpolicy.yaml
pod/pod-restartpolicy created
# 查看Pod詳情蔫劣,發(fā)現(xiàn)nginx容器失敗
[root@k8s-master01 ~]# kubectl describe pods pod-restartpolicy -n dev
......
Warning Unhealthy 15s (x3 over 35s) kubelet, node1 Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 15s kubelet, node1 Container nginx failed liveness probe
# 多等一會,再觀察pod的重啟次數(shù)个从,發(fā)現(xiàn)一直是0脉幢,并未重啟
[root@k8s-master01 ~]# kubectl get pods pod-restartpolicy -n dev
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Running 0 5min42s