Pod 是 kubernetes 中的基本單位,容器本身不會(huì)直接分配到主機(jī)上,而是會(huì)封裝到 Pod 對(duì)象中。一個(gè) Pod 通常表示單個(gè)應(yīng)用程序,有一個(gè)或者多個(gè)相關(guān)的容器組成,這些容器的生命周期都是相同的,而且會(huì)作為一個(gè)整體在同一個(gè) node 上調(diào)度起來(lái),這些容器共享環(huán)境、存儲(chǔ)卷和 IP 控件。盡管 Pod 中可能存在多個(gè)容器,但是在 kubernetes 中是以 Pod 為最小單位進(jìn)行調(diào)度、伸縮并共享資源、管理生命周期。
Pod 的基本操作
我們先來(lái)看一下 Pod 的創(chuàng)建、查詢、修改和刪除操作。
創(chuàng)建 Pod
# expod.yml
apiVersion: v1
kind: Pod
metadata:
name: expod
spec:
containers:
- name: expod-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "Hello Kubernetes!"; sleep 3600']
簡(jiǎn)單的模板含義:
- apiVersion 表示 API 版本,v1 表示使用 kubernetes API 的穩(wěn)定版本。
- kind 表示要?jiǎng)?chuàng)建的資源對(duì)象。
- metadata 表示該資源對(duì)象的元數(shù)據(jù)。可以擁有多個(gè)元數(shù)據(jù),name 表示當(dāng)前資源的名稱。
- spec 表示該資源對(duì)象的具體設(shè)置。其中 containers 表示容器的集合,我們這里設(shè)置了一個(gè)簡(jiǎn)單的容器。name: 要?jiǎng)?chuàng)建的容器名稱。image: 容器的鏡像地址。imagePullPolicy: 鏡像的下載策略,支持3種策略:Always、Never、IfNotPresent。command: 容器的啟動(dòng)命令列表,不配置的話就使用鏡像內(nèi)部的命令。args: 啟動(dòng)參數(shù)列表
運(yùn)行命令,創(chuàng)建 Pod。
kubectl Apply -f expod.yml
創(chuàng)建成功后,查詢一下當(dāng)前運(yùn)行的所有 Pod
kubectl get pod
查詢 Pod
Pod 信息查詢的命令有多個(gè),查詢的詳細(xì)度也不一樣:
- 查詢默認(rèn)命名空間 default 的所有 Pod
kubectl get pod
- 查詢指定 Pod 的信息
kubectl get pod expod
- 對(duì) Pod 狀態(tài)進(jìn)行持續(xù)監(jiān)控
kubectl get pod -w
- 顯示更多概要信息
kubectl get pod -o wide
比簡(jiǎn)要信息多顯示集群內(nèi) IP 地址、所屬 node
- 按照指定格式輸出 Pod 信息
kubectl get pod expod -o yaml
- 最詳細(xì)信息顯示,包括 Event
kubectl describe pod expod
這是最常用的資源信息查詢命令,顯示信息比較全面,包括資源的基本信息、容器信息、準(zhǔn)備情況、存儲(chǔ)卷信息和相關(guān)的事件列表。在資源部署的時(shí)候,如果遇到問(wèn)題,可以用這個(gè)命令查詢?cè)斍椋治鲥e(cuò)誤原因。
- 查詢Pod的日志信息
kubectl logs Pod名稱
修改 Pod
修改已存在的 Pod 屬性可以使用 replace 命令
kubectl replace -f pod的yaml文件
我們修改一下前面創(chuàng)建 Pod 的 yaml 文件,把輸出 “Hello Kubernetes!" 修改為 "Hello Kubernetes replaced!"。
# expod.yml
apiVersion: v1
kind: Pod
metadata:
name: expod
spec:
containers:
- name: expod-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "Hello Kubernetes replaced!"; sleep 3600']
Pod 有很多屬性是無(wú)法修改的,如果一定要修改,需要加上 --force 參數(shù),相當(dāng)于重建 Pod。
kubectl replace -f expod.yml --force
可以看一下命令輸出結(jié)果
先刪除了 expod 這個(gè) Pod,然后創(chuàng)建一個(gè)替換的 expod 的 Pod,我們?cè)俨榭匆韵?Pod 的日志。
kubectl logs expod
可以看到 Pod 信息已經(jīng)修改了。
刪除 Pod
刪除 Pod 非常簡(jiǎn)單,執(zhí)行以下命令即可:
kubectl delete pod expod
如果我們是通過(guò) Pod 模板文件創(chuàng)建的,推薦使用基于模板文件的刪除命令。
kubectl delete -f expod.yml
Pod 與容器
我們可能已經(jīng)發(fā)現(xiàn)了,Pod 模板和 Docker-Compose 非常相似,但是 Pod 模板可以配置的參數(shù)更多、更復(fù)雜。
Pod 創(chuàng)建容器的方式
在 Pod 模板的 Containers 部分,指明容器的部署方式,在部署的過(guò)程中會(huì)轉(zhuǎn)換成對(duì)應(yīng)的容器運(yùn)行命令,就以我們最開(kāi)始的 Pod 模板為例:
apiVersion: v1
kind: Pod
metadata:
name: expod
spec:
containers:
- name: expod-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "Hello Kubernetes!"; sleep 3600']
在 kubernetes 進(jìn)行調(diào)度的時(shí)候,會(huì)執(zhí)行如下命令:
docker run --name expod-container busybox sh -c 'echo "Hello Kubernetes!"; sleep 3600'
command 和 args 的設(shè)置會(huì)分別覆蓋 Docker 鏡像中定義的 EntryPoint 與 CMD。
Pod 組織容器的方式
Pod 是由各個(gè)容器組成的一個(gè)整體,同時(shí)調(diào)度到某臺(tái) Node 上,容器之間可以共享資源、網(wǎng)絡(luò)環(huán)境和依賴,并擁有相同的生命周期。
容器如何組成一個(gè) Pod
每個(gè) Pod 都包含一個(gè)或一組密切相關(guān)的業(yè)務(wù)容器,但是還有一個(gè)成為”根容器“的特殊 Pause 容器。Pause 容器是屬于 Kubernetes 的一部分,如果一組業(yè)務(wù)容器作為一個(gè)整體,我們很難對(duì)整個(gè)容器進(jìn)行判斷,假如一個(gè)業(yè)務(wù)組容器當(dāng)中的某個(gè)容器掛載了能代表整個(gè) Pod 都掛載了嗎?如果引入一個(gè)和業(yè)務(wù)無(wú)關(guān)的 Pause 容器,用它作為 Pod 的根容器,用它的狀態(tài)代表整組容器的狀態(tài),就能解決這個(gè)問(wèn)題。而且一個(gè) Pod 中的所有容器都共享 Pause 容器的 IP 地址及其掛載的存儲(chǔ)卷,這樣也簡(jiǎn)化了容器之間的通信和數(shù)據(jù)共享問(wèn)題,類似于 Docker 容器網(wǎng)絡(luò)的 Container 模式。
我們?cè)陂_(kāi)始創(chuàng)建的 Pod,可以登錄上對(duì)應(yīng)的 Node 機(jī)器,查看容器信息。
kubectl get pod -o wide
登錄 k8s-node2 以后,執(zhí)行 docker ps 命令查看詳細(xì)信息:
可以看到有三個(gè) pause 容器,其中兩個(gè)是 flannel 和 proxy 容器,還有一個(gè)是我們的 expod 的容器,它與另一個(gè) expod 容器共同組成了一個(gè) Pod。
Pod 中的容器共享兩種資源-存儲(chǔ)和網(wǎng)絡(luò)。
- 存儲(chǔ)
在 Pod 里面,我們可以指定一個(gè)或者多個(gè)存儲(chǔ)卷,Pod 中的所有容器都可以訪問(wèn)這些存儲(chǔ)卷,存儲(chǔ)卷可以分為臨時(shí)和持久化兩種。
- 網(wǎng)絡(luò)
在 kubernetes 集群中,每個(gè) Pod 都分配了唯一的 IP 地址,Pod 中的容器都共享網(wǎng)絡(luò)命名空間,包括 IP 地址和網(wǎng)絡(luò)端口。在 Pod 內(nèi)部各容器之間可以通過(guò) localhost 互相通信。我們可以對(duì)比一下 Docker 和 kubernetes 在網(wǎng)絡(luò)空間上的差異。
Docker的網(wǎng)絡(luò)空間
從圖中可以看出,容器之間通過(guò)docker0網(wǎng)卡連接,每個(gè)容器擁有獨(dú)立的內(nèi)部網(wǎng)絡(luò)地址
kubernetes 的網(wǎng)絡(luò)空間
從圖中可以看出,Pod 中的所有容器共享一個(gè)網(wǎng)絡(luò)地址
Pod 之間如何通信
Pod 之間的通信主要分為兩種情況:
- 同一個(gè) Node 上 Pod 之間的通信
同一個(gè) Node 上的 Pod 使用的都是相同的網(wǎng)橋( docker0 )進(jìn)行連接,相當(dāng)于 Docker 的網(wǎng)絡(luò)空間,只不過(guò)是以 Pod 為基礎(chǔ)。每個(gè) Pod 都有一個(gè)全局 IP 地址,同一個(gè) Node 內(nèi)不同 Pod 之間通過(guò) veth 連接在同一個(gè) docker0 網(wǎng)橋上,其 IP 地址都是從 docker0 網(wǎng)橋上動(dòng)態(tài)獲取的,并且關(guān)聯(lián)在同一個(gè) docker0 網(wǎng)橋上,地址段也相同,所以它們之間能直接通信。
- 不同 Node 之間的 Pod 通信
要實(shí)現(xiàn)不同 Node 的 Pod 之間通信,首先要保證 Pod 在一個(gè) kubernetes 集群中擁有全局唯一的 IP 地址。又因?yàn)橐粋€(gè) Node 上的 Pod 是通過(guò) Docker 網(wǎng)橋與外部進(jìn)行通信的,所以只要將不同 Node 上的 Docker 網(wǎng)橋配置成不同的 IP 地址段就可以實(shí)現(xiàn)這個(gè)功能。 Flannel 會(huì)配置 Docker 網(wǎng)橋,通過(guò)修改 Docker 的啟動(dòng)參數(shù) bip 來(lái)實(shí)現(xiàn)這一點(diǎn),這樣就使得集群中機(jī)器的 Docker 網(wǎng)橋就得到了全局唯一的 IP 地址段,機(jī)器上所創(chuàng)建的容器也就擁有了全局唯一的 IP 地址。
跨 Node 的 Pod 之間的通信
我們?cè)诖罱?kubernetes 集群的時(shí)候,在每個(gè) Node 機(jī)器上都創(chuàng)建了一個(gè) kube-flannel 容器,這是在每個(gè) Node 機(jī)器上使用 Flannel 虛擬網(wǎng)卡接管容器并跨主機(jī)通信,當(dāng)一個(gè)節(jié)點(diǎn)的容器訪問(wèn)另一個(gè)節(jié)點(diǎn)的容器時(shí),源節(jié)點(diǎn)上的數(shù)據(jù)會(huì)從 docker0 網(wǎng)橋路由到 flannel0 網(wǎng)卡,在目的節(jié)點(diǎn)處會(huì)從 flannel0 網(wǎng)卡路由到 docker0 網(wǎng)橋,然后再轉(zhuǎn)發(fā)給目標(biāo)容器。Flannel 重新規(guī)劃了容器集群的網(wǎng)絡(luò),這樣既保證了容器 IP 的全局唯一性,又讓不同機(jī)器上的容器能通過(guò)內(nèi)網(wǎng) IP 地址互相通信。但是,容器的 IP 地址并不是固定的,F(xiàn)lannel 只分配子網(wǎng)段,所以容器的 IP 地址是在此網(wǎng)段的范圍內(nèi)進(jìn)行動(dòng)態(tài)分配的。
因?yàn)?Pod 的 IP 地址本身是虛擬 IP,所以只有 kubernetes 集群內(nèi)部的機(jī)器( Master 和 Node )和其他 Pod 可以直接訪問(wèn)這個(gè) IP 地址,集群之外的機(jī)器無(wú)法直接訪問(wèn) Pod 的 IP 地址。我們創(chuàng)建了一個(gè) Nginx Pod:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: exnginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: nginxport
containerPort: 80
通過(guò)命令創(chuàng)建后查詢以下 IP 地址:
[root@k8s-master]# kubectl apply -f exnginx.yml
[root@k8s-master]# kubectl get pod -o wide
集群中的所有機(jī)器和 Pod 都可以訪問(wèn)這個(gè)虛擬地址和 containerPort 暴露的端口
[root@k8s-master]# curl http://10.244.1.6
同時(shí)我們看到我們有兩個(gè) Pod,一個(gè)在 Node1 上,一個(gè)在 Node2 上,而 Node2 上的 Pod IP 地址是10.244.2.6,Node1 上的 Pod IP 地址是10.244.1.6,登錄到兩臺(tái)機(jī)器上,使用 ifconfig flannel.1 命令查看集群子網(wǎng)段。
k8s-node1
K8s-node2
要使集群外的機(jī)器訪問(wèn) Pod 提供的服務(wù),后面我們會(huì)介紹 Service 和 Ingress 來(lái)發(fā)布服務(wù)。
Pod 的生命周期
Pod 的狀態(tài)
一旦開(kāi)始在集群節(jié)點(diǎn)中創(chuàng)建 Pod,首先就會(huì)進(jìn)入 Pending 狀態(tài),只要 Pod 中的所有容器都已啟動(dòng)并正常運(yùn)行,則 Pod 接下來(lái)會(huì)進(jìn)入 Running 狀態(tài),如果 Pod 被要求終止,且所有容器終止退出時(shí)的狀態(tài)碼都為0,Pod 就會(huì)進(jìn)入 Succeeded 狀態(tài)。
如果進(jìn)入 Failed 狀態(tài),通常有以下3種原因。
- Pod 啟動(dòng)時(shí),只要有一個(gè)容器運(yùn)行失敗,Pod 將會(huì)從 Pending 狀態(tài)進(jìn)入 Failed 狀態(tài)。
- Pod 正處于 Running 狀態(tài),若 Pod 中的一個(gè)容器突然損壞或者在退出時(shí)狀態(tài)碼不為0,Pod 將會(huì)從 Running 進(jìn)入 Failed 狀態(tài)。
- 在要求 Pod 正常關(guān)閉的時(shí)候,只要有一個(gè)容器退出的狀態(tài)碼不為0,Pod 就會(huì)進(jìn)入 Failed 狀態(tài)。
Pod 的重啟策略
在配置 Pod 的模板文件中有個(gè) spec 模塊,其中有一個(gè)名為 restartPolicy 的字段,字段的值為 Always、OnFailure、Never。Node 上的 kubelet 通過(guò) restartPolicy 執(zhí)行重啟操作,由 kubelet 重新啟動(dòng)的已退出容器將會(huì)以遞增延遲的方式(10s,20s,40s,...)嘗試重新啟動(dòng),上限時(shí)間為 5min,延時(shí)的累加值會(huì)在成功運(yùn)行 10min 后重置,一旦 Pod 綁定到某個(gè)節(jié)點(diǎn)上,就絕對(duì)不會(huì)重新綁定到另一個(gè)節(jié)點(diǎn)上。
重啟策略對(duì) Pod 狀態(tài)的影響如下:
- 假設(shè)有1個(gè)運(yùn)行中的 Pod,包含1個(gè)容器,容器退出成功后。
Always:重啟容器,Pod 狀態(tài)仍為 Running。
OnFailure:Pod 狀態(tài)變?yōu)?Completed。
Never:Pod 狀態(tài)變?yōu)?Completed。
- 假設(shè)有1個(gè)運(yùn)行中的 Pod,包含1個(gè)容器,容器退出失敗后。
Always:重啟容器,Pod 狀態(tài)仍為 Running。
OnFailure:重啟容器,Pod 狀態(tài)仍為 Running。
Never:Pod 狀態(tài)變?yōu)?Failed。
- 假設(shè)有1個(gè)運(yùn)行中的 Pod,包含2個(gè)容器,第1個(gè)容器退出失敗后。
Always:重啟容器,Pod 狀態(tài)仍為 Running。
OnFailure:重啟容器,Pod 狀態(tài)仍為 Running。
Never:不會(huì)重啟容器,Pod 狀態(tài)仍為 Completed。
- 假設(shè)第1個(gè)容器沒(méi)有運(yùn)行起來(lái),而第2個(gè)容器也退出了。
Always:重啟容器,Pod 狀態(tài)仍為 Running。
OnFailure:重啟容器,Pod 狀態(tài)仍為 Running。
Never:Pod 狀態(tài)變?yōu)?Failed。
- 假設(shè)有1個(gè)運(yùn)行中的 Pod,包含1個(gè)容器,容器發(fā)生內(nèi)存溢出后。
Always:重啟容器,Pod 狀態(tài)仍為 Running。
OnFailure:重啟容器,Pod 狀態(tài)仍為 Running。
Never:記錄失敗事件,Pod 狀態(tài)變?yōu)?Failed。
Pod 的創(chuàng)建于銷毀過(guò)程
Pod 的創(chuàng)建過(guò)程:
- kubectl 命令將轉(zhuǎn)換為對(duì) API Server 的調(diào)用。
- API Server 驗(yàn)證請(qǐng)求并將其保存到 etcd 中。
- etcd 通知 API Server。
- API Server 調(diào)用調(diào)度器。
- 調(diào)度器決定在哪個(gè)節(jié)點(diǎn)上運(yùn)行 Pod,并將其返回給 API Server。
- API Server 將其對(duì)應(yīng)節(jié)點(diǎn)保存到 etcd 中。
- etcd 通知 API Server。
- API Server 在相應(yīng)的節(jié)點(diǎn)中調(diào)用 kubelet。
- kubelet 與容器運(yùn)行時(shí) API 發(fā)生交互,與容器守護(hù)進(jìn)程通信以創(chuàng)建容器。
- kubelet 將 Pod 狀態(tài)更新到 API Server 中。
- API Server 把最新的狀態(tài)保存到 etcd 中。
Pod 的銷毀過(guò)程:
- 用戶發(fā)送刪除 Pod 的命令。
- 將會(huì)更新 API Server 中的 Pod 對(duì)象,設(shè)定 Pod 被”銷毀“完成的大致時(shí)間(默認(rèn) 30s),超出這個(gè)寬限時(shí)間 Pod 將被強(qiáng)制終止。
- 同時(shí)觸發(fā)以下操作:Pod 被標(biāo)記為 Terminating。kubelet 發(fā)現(xiàn) Pod 已標(biāo)記為 Terminating 后,將會(huì)執(zhí)行 Pod 關(guān)閉過(guò)程。Endpoint 控制器監(jiān)控到 Pod 即將刪除,將溢出所有 Service 對(duì)象中與該 Pod 相關(guān)的 Endpoint。
- 如果 Pod 定義了 preStop 回調(diào),則這會(huì)在 Pod 中執(zhí)行,如果寬限時(shí)間到了 preStop 還在運(yùn)行,則會(huì)通知 API Server增加少量寬限時(shí)間(2s)。
- Pod 中的進(jìn)程接收到 TERM 信號(hào)。
- 如果寬限時(shí)間過(guò)期,Pod 中的進(jìn)行仍在運(yùn)行,則會(huì)被 SIGKILL 信號(hào)終止。
- kubelet 通過(guò) API Server 設(shè)置寬限時(shí)間為 0(立即刪除),完成 Pod 的刪除操作,Pod 從 API 中移除。
刪除操作的延遲時(shí)間默認(rèn)為 30s。kubectl delete 命令支持--grace-period=秒,用戶可以自定義延遲時(shí)間。如果這個(gè)值設(shè)置為0,則表示強(qiáng)制刪除 Pod,但是在使用--grace-period=0 時(shí)需要增加選項(xiàng) --force 才能執(zhí)行強(qiáng)制刪除。
Pod 的生命周期時(shí)間
Pod 在整個(gè)運(yùn)行過(guò)程中,會(huì)有兩個(gè)大的階段,第一階段是初始化容器運(yùn)行階段,第二階段是正式容器運(yùn)行階段,每個(gè)階段都會(huì)有不同的事件
初始化容器運(yùn)行階段
Pod 中可以包含一個(gè)或者多個(gè)初始化容器,這些容器是在應(yīng)用程序容器正式運(yùn)行之前運(yùn)行的,主要負(fù)責(zé)一些初始化工作,所有初始化容器執(zhí)行完后才能執(zhí)行應(yīng)用程序容器,因此初始化容器不能是長(zhǎng)期運(yùn)行的容器,而是執(zhí)行完一定操作后就必須結(jié)束的。如果是多個(gè)初始化容器,只能順序執(zhí)行,不能同時(shí)運(yùn)行。在應(yīng)用程序容器開(kāi)始前,所有初始化容器都必須正常結(jié)束。
初始化容器執(zhí)行失敗時(shí),如果 restartPolicy 是 OnFailure 或者 Always,那么會(huì)重復(fù)執(zhí)行失敗的初始化容器,直到成功;如果 restartPolicy 是 Never,則不會(huì)重啟失敗的初始化容器。
下面我們舉一個(gè)例子,在部署應(yīng)用程序前,檢測(cè) db 是否就緒,并執(zhí)行以下初始化腳本。
apiVersion: v1
kind: Pod
metadata:
name: expodinitcontainer
spec:
containers:
- name: maincontainer
image: busybox
command: ['sh', '-c']
args: ['echo "maincontainer is running!"; sleep 3600']
initContainers:
- name: initdbcheck
image: busybox
command: ['sh', '-c']
args: ['echo "checking db!"; sleep 30; echo "checking done!"']
- name: initscript
image: busybox
command: ['sh', '-c']
args: ['echo "init script exec!"; sleep 30; echo "init script exec done!"']
這里包含一個(gè)主容器,兩個(gè)初始化容器,每個(gè)初始化容器執(zhí)行 30s,接下來(lái)我們創(chuàng)建一下 Pod,再查看他們的狀態(tài):
kubectl apply -f expodinitcontainers.yml
在 30s 內(nèi)執(zhí)行第一個(gè)初始化容器,所以 Pod 狀態(tài)是 Init:0/2,在 30s-60s 之間執(zhí)行第二個(gè)初始化容器,所以 Pod 狀態(tài)是 Init:1/2,當(dāng)所有初始化容器執(zhí)行完畢后,狀態(tài)會(huì)先變?yōu)?PodInitializing,然后變?yōu)?Running 狀態(tài)。通過(guò) kubectl describe 命令查看 Pod 詳細(xì)信息,可以看到先執(zhí)行 initdbcheck,然后執(zhí)行 initscript,最后才運(yùn)行 maincontainer。
正式容器運(yùn)行階段
正式容器創(chuàng)建成功后,就會(huì)觸發(fā) PostStart 事件,在容器運(yùn)行的過(guò)程中,可以設(shè)置存活探針和就緒探針來(lái)持續(xù)監(jiān)測(cè)容器的健康狀況,在容器結(jié)束前,會(huì)觸發(fā) PreStop 事件。
- PostStart:容器剛創(chuàng)建成功后,觸發(fā)此事件,如果回調(diào)執(zhí)行失敗,則容器會(huì)被終止,然后根據(jù)重啟策略決定是否要重啟該容器。
- PreStop:容器開(kāi)始和結(jié)束前,觸發(fā)此事件,無(wú)論執(zhí)行結(jié)果如何,都會(huì)結(jié)束容器。
回調(diào)的方式有兩種:Exec 執(zhí)行一段腳本和 HttpGet 執(zhí)行特定的請(qǐng)求。
- Exec 回調(diào)會(huì)執(zhí)行特定的命令,如果 Exec 執(zhí)行的命令最后正常退出,則代表執(zhí)行成功;否則就認(rèn)為執(zhí)行異常,配置方式如下:
postStart/preStop:
exec:
command: xxxxx # 命令列表
- HttpGet 回調(diào)會(huì)執(zhí)行特定的 Http 請(qǐng)求,通過(guò)返回的狀態(tài)碼來(lái)判斷該請(qǐng)求執(zhí)行是否成功,配置方式如下:
postStart/preStop:
httpGet:
host: xxxx # 請(qǐng)求的 IP 地址或域名
port: xx # 請(qǐng)求的端口號(hào)
path: xxxxxx # 請(qǐng)求的路徑,比如github.com/xingxingzaixian,"/xingxingzaixian"就是路徑
scheme: http/https # 請(qǐng)求的協(xié)議,默認(rèn)為 HTTP
我們來(lái)舉例使用一下 PostStart 和 PreStop 事件
apiVersion: v1
kind: Pod
metadata:
name: expodpostpre
spec:
containers:
- name: postprecontainer
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "Hello Kubernetes!"; sleep 3600']
lifecycle:
postStart:
httpGet:
host: www.baidu.com
path: /
port: 80
scheme: HTTP
preStop:
exec:
command: ['sh', '-c', 'echo "preStop callback done!"; sleep 60']
postStart 中我們執(zhí)行 HttpGet 回調(diào),訪問(wèn)了百度首頁(yè),preStop 則執(zhí)行命令輸出一段文本,然后停留 60s。我們執(zhí)行創(chuàng)建命令,觀察一下 Pod 的狀態(tài)。
kubectl apply -f expodpostpre.yml
正常情況下是和之前的是一樣的,我們做一些測(cè)試操作,例如把 postStart 的網(wǎng)址寫(xiě)一個(gè)不可訪問(wèn)的網(wǎng)址,比如:
host: www.fackbook.com
創(chuàng)建后,查看日志信息
kubectl logs expodpostpre
還可以通過(guò) kubectl describe 來(lái)查看詳細(xì)情況
Pod 的健康檢查
在容器運(yùn)行的過(guò)程中,我們可以通過(guò)探針來(lái)持續(xù)檢查容器的狀況,kubernetes 為我們提供了兩種探針:存活探針、就緒探針。
- 存活探針livenessProbe:檢測(cè)容器是否正在運(yùn)行,如果返回 Failure,kubelet 會(huì)終止容器,然后容器會(huì)按照重啟策略執(zhí)行。如果沒(méi)有提供存活探針,默認(rèn)狀態(tài)就是 Success。
- 就緒探針readlinessProbe:檢測(cè)容器是否已經(jīng)可以啟動(dòng)了應(yīng)用服務(wù),如果返回 Failure,Endpoint 控制器就會(huì)從所有 Service 的 Endpoint 中移除此 Pod 的 IP 地址。從容器啟動(dòng)到第一次探測(cè)之前,默認(rèn)的就緒狀態(tài)是 Failure。如果沒(méi)有提供就緒探針,默認(rèn)狀態(tài)就是 Success。
容器配置當(dāng)中有 3 種方法來(lái)執(zhí)行探針檢測(cè):exec、tcpSocket、httpGet。
- exec:在容器內(nèi)部執(zhí)行指定的命令,如果命令以狀態(tài)碼“0”退出,則表示診斷成功。配置如下:
livenessProbe/readlinessProbe:
exec:
command: [xxxx] # 命令列表
- tcpSocket:對(duì)容器的指定端口執(zhí)行 TCP 檢測(cè)。如果端口是打開(kāi)的,則診斷成功。配置如下:
livenessProbe/readlinessProbe:
tcpSocket:
port: Number # 指定端口號(hào)
- httpGet:對(duì)容器內(nèi)的 HTTP 服務(wù)進(jìn)行檢測(cè),如果響應(yīng)的狀態(tài)碼范圍為 200~400,則診斷成功。配置如下:
livenessProbe/readlinessProbe:
httpGet:
port: Number # 指定的端口號(hào)
path: String # 指定路徑
下面舉幾個(gè)例子來(lái)展示一下探針的使用。
存活探針的使用
apiVersion: v1
kind: Pod
metadata:
name: expodlive
spec:
containers:
- name: livecontainer
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['mkdir /files_dir; echo "Hello Kubernetes!" > /files_dir/newfile; sleep 3600']
livenessProbe:
exec:
command: ['cat', '/files_dir/newfile']
先創(chuàng)建一個(gè)文件夾 files_dir,再新建一個(gè)文件 newfile,寫(xiě)入 Hello Kubernetes! 內(nèi)容,然后在存活探針中使用 cat 查看文件內(nèi)容。執(zhí)行創(chuàng)建 Pod 命令:
kubectl apply -f expodlive.yml
目前 Pod 運(yùn)行一切正常,現(xiàn)在我們可以做一些破壞性的操作,進(jìn)入 Pod 內(nèi)部,刪除 newfile 文件。
[root@k8s-master]# kubectl exec -it expodlive -- /bin/sh
/# rm -f /files_dir/newfile
/# exit
由于探針定期檢查 /files_dir/newfile 文件是否存在,而我們的 Pod 默認(rèn)是異常后重啟,因此可以通過(guò)以下命令查看 Pod 詳細(xì)信息:
kubectl describe pods expodlive
可以看到 Event 中打印了存活探針的運(yùn)行情況:存活探針失敗,并且準(zhǔn)備重啟。
就緒探針的使用
apiVersion: v1
kind: Pod
metadata:
name: expodread
spec:
containers:
- name: readcontainer
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: portnginx
containerPort: 80
readinessProbe:
httpGet:
port: 80
path: /
我們創(chuàng)建了一個(gè) Nginx 容器,通過(guò) containerPort 屬性,將 80 端口暴露出來(lái),然后設(shè)置一個(gè)就緒探針定期向 80 端口發(fā)送 HttpGet 請(qǐng)求,檢測(cè)響應(yīng)范圍是否為 200~400。使用 apply 命令創(chuàng)建成功后,我們同樣要進(jìn)行一些測(cè)試性的操作。
[root@k8s-master]# kubectl apply -f expodread.yml
[root@k8s-master]# kubectl exec -it expodread -- /bin/sh
/# nginx -s stop
執(zhí)行 nginx -s stop 命令后,容器就會(huì)退出,因?yàn)?Nginx 容器是持續(xù)提供服務(wù)的,服務(wù)停止后,容器就異常了,然后 kubernetes 又會(huì)重新拉起一個(gè)容器,在這個(gè)過(guò)程當(dāng)中,我們使用 kubectl get pod 命令就會(huì)發(fā)現(xiàn),容器的狀態(tài)先變?yōu)?Completed,然后變?yōu)?CrashLoopBackOff,最后變?yōu)?Running。
對(duì)于初學(xué)者來(lái)講,總是分不清存活探針和就緒探針的區(qū)別,什么情況下該使用存活探針?什么情況下應(yīng)該使用就緒探針?我給的建議如下:
- 如果容器中的進(jìn)程能夠在遇到問(wèn)題或異常的情況下自行崩潰,就像剛才的 Nginx 容器,那么不一定需要存活探針,kubelet 會(huì)根據(jù) Pod 的重啟策略自動(dòng)執(zhí)行正確的操作。
- 如果想在探測(cè)失敗時(shí)終止并重啟容器,則可以指定存活探針,并將重啟策略設(shè)置為 Always 或 OnFailure。
- 如果只想在探針成功時(shí)才對(duì) Pod 發(fā)送網(wǎng)絡(luò)請(qǐng)求,則可以指定就緒探針,例如 HttpGet。
- 如果容器需要在啟動(dòng)期間處理大型數(shù)據(jù)、配置文件或遷移,就使用就緒探針。
對(duì)于每種探針,還可以設(shè)置 5 個(gè)參數(shù):
- initialDelaySeconds:?jiǎn)?dòng)容器后首次監(jiān)控檢測(cè)的等待時(shí)間,單位為秒。
- timeoutSeconds:發(fā)送監(jiān)控檢測(cè)請(qǐng)求后等待響應(yīng)的超時(shí)時(shí)間,單位為秒,如果超時(shí)就認(rèn)為探測(cè)失敗,默認(rèn)值為 10s。
- periodSeconds:探針的執(zhí)行周期,默認(rèn) 10s 執(zhí)行一次。
- successThreshold:如果出現(xiàn)失敗,則需要連續(xù)探測(cè)成功多次才能確認(rèn)診斷成功。默認(rèn)值為1.
- failureThreshold:如果出現(xiàn)失敗,則要連續(xù)失敗多次才重啟 Pod(對(duì)于存活探針)或標(biāo)記為 Unready(對(duì)于就緒探針)。默認(rèn)值為3。
具體設(shè)置方法如下:
livenessProbe/readinessProbe:
exec/tcpSocket/httpGet:
initialDelaySeconds: Number
timeoutSeconds: Number
periodSeconds: Number
successThreshold: Number
failureThreshold: Number
總結(jié)
這篇文章我們主要講了 Pod 的增刪改查,Pod 與容器的關(guān)系,如何組織容器,Pod 的生命周期及對(duì)應(yīng)事件,以及 Pod 的健康檢查機(jī)制。