簡述
Kubernetes 是一種用于在一組主機(jī)上運(yùn)行和協(xié)同容器化應(yīng)用程序的系統(tǒng),提供應(yīng)用部署、規(guī)劃、更新維護(hù)的機(jī)制。應(yīng)用運(yùn)行在 kubernetes 集群之上,實(shí)現(xiàn)服務(wù)的擴(kuò)容、縮容,執(zhí)行滾動(dòng)更新以及在不同版本的應(yīng)用程序之間調(diào)度流量以測試功能或回滾有問題的部署。Kubernetes 實(shí)現(xiàn)管理服務(wù)的各項(xiàng)功能是通過定義各種類型的資源來實(shí)現(xiàn)的,如 deployment、pod、service、volume 等。下面通過該文章來簡述 pod 的基礎(chǔ)信息并詳述 pod 的生命周期。
Pod簡介
Pod 是 kubernetes 系統(tǒng)的基礎(chǔ)單元,是由用戶創(chuàng)建或部署的最小組件,也是 kubernetes 系統(tǒng)上運(yùn)行容器化應(yīng)用的資源對(duì)象。Kubernetes 集群中其他資源對(duì)象都是為 pod 這個(gè)資源對(duì)象做支撐來實(shí)現(xiàn) kubernetes 管理應(yīng)用服務(wù)的目的。Kubernetes 集群組件主要包括主節(jié)點(diǎn)組件API Server、Controller Manager、Scheduler 以及子節(jié)點(diǎn)組件 kubelet、container Runtime(如docker)、kube-proxy 等。從與集群各組件交互角度講述 pod 的創(chuàng)建、運(yùn)行、銷毀等生命周期,Pod 生命周期中的幾種不同狀態(tài)包括pending、running、succeeded、failed、Unknown。與API Server交互
API Server 提供了集群與外部交互的接口,通過 kubectl 命令或者其他 API 客戶端提交 pod spec 給 API Server 作為pod創(chuàng)建的起始。Pod 與 API Server 交互的主要流程如下:
API Server 在接收到創(chuàng)建pod的請求之后,會(huì)根據(jù)用戶提交的參數(shù)值來創(chuàng)建一個(gè)運(yùn)行時(shí)的pod對(duì)象。
根據(jù) API Server 請求的上下文的元數(shù)據(jù)來驗(yàn)證兩者的 namespace 是否匹配,如果不匹配則創(chuàng)建失敗。
Namespace 匹配成功之后,會(huì)向 pod 對(duì)象注入一些系統(tǒng)數(shù)據(jù),如果 pod 未提供 pod 的名字,則 API Server 會(huì)將 pod 的 uid 作為 pod 的名字。
API Server 接下來會(huì)檢查 pod 對(duì)象的必需字段是否為空,如果為空,創(chuàng)建失敗。
上述準(zhǔn)備工作完成之后會(huì)將在 etcd 中持久化這個(gè)對(duì)象,將異步調(diào)用返回結(jié)果封裝成 restful.response,完成結(jié)果反饋。
至此,API Server 創(chuàng)建過程完成,剩下的由 scheduler 和 kubelet 來完成,此時(shí) pod 處于 pending 狀態(tài)。
與scheduler交互
當(dāng)提交創(chuàng)建 pod 的請求與 API Server 的交互完成之后,接下來由 scheduler 進(jìn)行工作,該組件主要是完成 pod 的調(diào)度來決定 pod 具體運(yùn)行在集群的哪個(gè)節(jié)點(diǎn)上。注意,此處聲明一點(diǎn),API Server 完成任務(wù)之后,將信息寫入到 etcd 中,此時(shí) scheduler 通過 watch 機(jī)制監(jiān)聽到寫入到 etcd 的信息然后再進(jìn)行工作。Scheduler 讀取到寫入到 etcd 中的 pod 信息,然后基于一系列規(guī)則從集群中挑選一個(gè)合適的節(jié)點(diǎn)來運(yùn)行它,調(diào)度時(shí)主要通過三步來確定 pod 運(yùn)行節(jié)點(diǎn):
節(jié)點(diǎn)預(yù)選:基于一系列預(yù)選規(guī)則(如 PodFitsResource 和 MatchNode-Selector 等)對(duì)每個(gè)節(jié)點(diǎn)進(jìn)行檢查,將不符合的節(jié)點(diǎn)過濾掉從而完成節(jié)點(diǎn)預(yù)選。
節(jié)點(diǎn)優(yōu)選:對(duì)預(yù)選出的節(jié)點(diǎn)進(jìn)行優(yōu)先級(jí)排序,以便選出最適合運(yùn)行 pod 對(duì)象的節(jié)點(diǎn)。
從優(yōu)先級(jí)結(jié)果中挑選出優(yōu)先級(jí)最高的節(jié)點(diǎn)來運(yùn)行 pod 對(duì)象,當(dāng)此類節(jié)點(diǎn)多個(gè)時(shí)則隨機(jī)選擇一個(gè)。
注:如果有特殊 pod 資源需要運(yùn)行在特殊節(jié)點(diǎn)上,此時(shí)可以通過組合節(jié)點(diǎn)標(biāo)簽以及 pod 標(biāo)簽和標(biāo)簽選擇器等來實(shí)現(xiàn)高級(jí)調(diào)度,如 MatchInterPodAffinity、MatchNodeSelector 和 PodToleratesNodeTaints 等預(yù)選策略,他們?yōu)橛脩籼峁┳远x Pod 親和性或反親和性、節(jié)點(diǎn)親和性以及基于污點(diǎn)及容忍度的調(diào)度機(jī)制。
預(yù)選策略
預(yù)選策略就是節(jié)點(diǎn)過濾器,例如 MathNodeSelector 實(shí)現(xiàn)的規(guī)則,以及 PodFitsResources 實(shí)現(xiàn)的規(guī)則等。執(zhí)行預(yù)選操作時(shí),如果不存在適合的節(jié)點(diǎn),此時(shí) pod 會(huì)一直處于 pending 狀態(tài),直到至少有一個(gè)可用節(jié)點(diǎn)。支持的預(yù)選策略列舉一下(1.10版本):
CheckNodeConditionGeneralNoDiskConflict
PodToleratesNodeTaintsPodToleratesNodeNoExecuteTaints
CheckServiceAffinity
MaxEBsVolumeCount
MaxGCEPDVolumeCount
MaxAzureDiskVolumeCount
CheckVolumeBinding
NoVolumeZoneConflict
CheckNodeMemoryPressure
CheckNodePIDPressure
CheckNodeDiskPressure
MatchInterPodAffinity
簡單介紹幾種:
CheckNodeCondition:檢查是否可以在節(jié)點(diǎn)報(bào)告磁盤、網(wǎng)絡(luò)不可用或未準(zhǔn)備好的情況下將 pod 對(duì)象調(diào)度其上。
NoDiskConflict:檢查 pod 對(duì)象請求的存儲(chǔ)卷在此節(jié)點(diǎn)上是否可用,若不存在沖突則通過檢查。
MathNodeSelector:若 pod 對(duì)象定義了 spec.NodeSelector 屬性,則檢查節(jié)點(diǎn)標(biāo)簽是否能匹配此屬性值。
優(yōu)選函數(shù)
常用優(yōu)選函數(shù):
BalancedResourceAllocationLeaastRequstedPriorityNodePreferAvoidPodsPriority
NodeAffinityPriority
TaintTolerationPriority
InterPodAffinityPriority
SelectorSpreadPriority
NodeLabelPriority
MostRequestedPriority
ImageLoccalityPriority
此外調(diào)度器支持為每個(gè)優(yōu)選函數(shù)指定一個(gè)簡單的整數(shù)值表示權(quán)重,進(jìn)行節(jié)點(diǎn)優(yōu)先級(jí)分值的計(jì)算,計(jì)算公式如下:
FinalScoreNode = (weight1 * priorityFunc1) + (weight2 * priorityFunc2)+ ….
列舉說明幾個(gè)優(yōu)選函數(shù):
TaintToleraionPriority:基于Pod資源對(duì)節(jié)點(diǎn)的污點(diǎn)容忍調(diào)度偏好進(jìn)行其優(yōu)先級(jí)的評(píng)估,它將 Pod 對(duì)象的 tolerations 列表與節(jié)點(diǎn)的污點(diǎn)進(jìn)行匹配度檢查,成功匹配的條目越多,則節(jié)點(diǎn)得分越低。
NodeAffinityPriority:基于節(jié)點(diǎn)親和性調(diào)度偏好進(jìn)行優(yōu)先級(jí)評(píng)估,它將根據(jù) Pod 資源中的 nodeSelector 對(duì)給定節(jié)點(diǎn)進(jìn)行匹配度計(jì)算,成功匹配到的條目越多則節(jié)點(diǎn)得分越高。
對(duì)于上述節(jié)點(diǎn)調(diào)度中還包括一些節(jié)點(diǎn)親和度:硬親和度和軟親和性、資源親和調(diào)度。硬親和調(diào)度和軟親和調(diào)度以及反親和調(diào)度、污點(diǎn)容忍度等,都是 pod 調(diào)度的策略,不一一詳述。
當(dāng) scheduler 通過一系列策略選定 pod 運(yùn)行節(jié)點(diǎn)之后將結(jié)果信息更新至 API Server,由 API Server 更新至 etcd 中,并由 API Server 反映調(diào)度結(jié)果,接下來由 kubelet 在所選定的節(jié)點(diǎn)上啟動(dòng) pod。
Kubelet組件啟動(dòng)pod
kubelet 組件的作用不單單是創(chuàng)建 pod,另外還包括節(jié)點(diǎn)管理、cAdvisor 資源監(jiān)控管理、容器健康檢查等功能。
啟動(dòng)pod流程分析
kubelet 通過 API Server 監(jiān)聽 etcd 目錄,同步 pod 列表。如果發(fā)現(xiàn)有新的 pod 綁定到本節(jié)點(diǎn),則按照 pod 清單要求創(chuàng)建 pod,如果是發(fā)現(xiàn) pod 被更新,則做出相應(yīng)更改。讀取到 pod 的信息之后,如果是創(chuàng)建和修改 pod 的任務(wù),則做如下處理:
為該 pod 創(chuàng)建一個(gè)數(shù)據(jù)目錄
從 API Server 讀取該 pod 清單
為該 pod 掛載外部卷
下載 pod 所需的 Secret
檢查已經(jīng)運(yùn)行在節(jié)點(diǎn)中 pod,如果該 pod 沒有容器或者 Pause 容器沒有啟動(dòng),則先停止pod里所有的容器進(jìn)程。
使用 pause 鏡像為每個(gè)pod創(chuàng)建一個(gè)容器,該容器用于接管 Pod 中所有其他容器的網(wǎng)絡(luò)。
為 pod 中的每個(gè)容器做如下處理:1.為容器計(jì)算一個(gè) hash 值,然后用容器的名字去查詢對(duì)于 docker 容器的 hash 值。若查找到容器,且兩者的 hash 值不同,則停止 docker 中容器中進(jìn)程,并停止與之關(guān)聯(lián)的 pause 容器,若相同,則不做處理。若容器被終止了,且容器沒有指定的重啟策略,則不做任何處理調(diào)用 docker client 下載容器鏡像,并啟動(dòng)容器。
詳述pod聲明周期中的重要行為
除了創(chuàng)建應(yīng)用容器(主容器及輔助容器之外,注意,如果集群中部署了 istio,則會(huì)在 pod 啟動(dòng)的時(shí)候注入一個(gè)新的和 istio 相關(guān)的容器,那是另一個(gè)美好故事的開端),還可以為 pod 對(duì)象定義其聲明周期中的多種行為,如初始化容器、容器探測以及就緒性探測等。
容器生命周期的幾種行為
初始化容器初始化容器即 pod 內(nèi)主容器啟動(dòng)之前要運(yùn)行的容器,主要是做一些前置工作,初始化容器具有以下特征:
初始化容器必須首先執(zhí)行,若初始化容器運(yùn)行失敗,集群會(huì)一直重啟初始化容器直至完成,注意,如果 pod 的重啟策略為 Never,那初始化容器啟動(dòng)失敗后就不會(huì)重啟。
初始化容器必須按照定義的順序執(zhí)行,初始化容器可以通過 pod 的 spec.initContainers 進(jìn)行定義。
聲明周期鉤子函數(shù)
Kubernetes 為容器提供了兩種生命周期鉤子:
Poststart:于容器創(chuàng)建完成之后立即運(yùn)行的鉤子程序。
preStop:容器終止之前立即運(yùn)行的程序,是以同步方式的進(jìn)行,因此其完成之前會(huì)阻塞 刪除容器的調(diào)用
備注:鉤子程序的執(zhí)行方式有“Exec”和“HTTP”兩種。
容器探測
容器探測分為存活性探測和就緒性探測容器探測是kubelet對(duì)容器健康狀態(tài)進(jìn)行診斷,容器探測的方式主要以下三種:
ExecAction:在容器中執(zhí)行命令,根據(jù)返回的狀態(tài)碼判斷容器健康狀態(tài),返回0即表示成功,否則為失敗。
TCPSocketAction: 通過與容器的某TCP端口嘗試建立連接進(jìn)行診斷,端口能打開即為表示成功,否則失敗。
HTTPGetAction:向容器指定 URL 發(fā)起 HTTP GET 請求,響應(yīng)碼為2xx或者是3xx為成功,否則失敗。
Pod終止過程
終止過程主要分為如下幾個(gè)步驟:
用戶發(fā)出刪除 pod 命令
Pod 對(duì)象隨著時(shí)間的推移更新,在寬限期(默認(rèn)情況下30秒),pod 被視為“dead”狀態(tài)
將 pod 標(biāo)記為“Terminating”狀態(tài)
第三步同時(shí)運(yùn)行,監(jiān)控到 pod 對(duì)象為“Terminating”狀態(tài)的同時(shí)啟動(dòng) pod 關(guān)閉過程
第三步同時(shí)進(jìn)行,endpoints 控制器監(jiān)控到 pod 對(duì)象關(guān)閉,將pod與service匹配的 endpoints 列表中刪除
如果 pod 中定義了 preStop 鉤子處理程序,則 pod 被標(biāo)記為“Terminating”狀態(tài)時(shí)以同步的方式啟動(dòng)執(zhí)行;若寬限期結(jié)束后,preStop 仍未執(zhí)行結(jié)束,第二步會(huì)重新執(zhí)行并額外獲得一個(gè)2秒的小寬限期
Pod 內(nèi)對(duì)象的容器收到 TERM 信號(hào)
寬限期結(jié)束之后,若存在任何一個(gè)運(yùn)行的進(jìn)程,pod 會(huì)收到 SIGKILL 信號(hào)
Kubelet 請求 API Server 將此 Pod 資源寬限期設(shè)置為0從而完成刪除操作
此外 kubelet 除了啟動(dòng)之外,kubelet 中還有 cAdvisor,用于收集容器 CPU、內(nèi)存、文件系統(tǒng)和網(wǎng)絡(luò)使用情況等信息,與 prometheus 結(jié)合實(shí)現(xiàn)對(duì)集群內(nèi) pod 監(jiān)控。
此外,除了上述三個(gè)組件在創(chuàng)建 pod 過程中的交互,還有 controller-manager 來保證 pod 處于用戶期望狀態(tài)(即保證 pod 永遠(yuǎn)處于存活狀態(tài))等功能以及 proxy 用于集群內(nèi) pod 之間通信等。
pod 資源清單介紹
apiVersion: v1 # 必選,指定api接口資源版本
kind: Pod # 必選,定義資源接口類型/角色。pod為容器資源
metadata: # 必選,定義資源的元數(shù)據(jù)信息
name: nginx # 必選,定義資源名稱,在同一個(gè)namespace中必須是唯一的
namespace: web-testing # 可選,不指定默認(rèn)為default,指定資源所在的命名空間
labels: # 可選,定義資源標(biāo)簽
- app: nginx
annotations: # 可選,注釋列表
- app: nginx
spec: # 必選,用于定義容器的詳細(xì)信息
containers: # 必選,容器列表
- name: nginx # 必選,符合RFC 1035規(guī)范的容器名稱
image: nginx:v1 # 必選,容器所用的鏡像的地址
imagePullPolicy: Always # 可選,鏡像拉取策略
workingDir: /usr/share/nginx/html # 可選,容器的工作目錄
volumeMounts: # 可選,存儲(chǔ)卷配置
- name: webroot # 存儲(chǔ)卷名稱
mountPath: /usr/share/nginx/html # 掛載目錄
readOnly: true # 只讀
ports: # 可選,容器需要暴露的端口號(hào)列表
- name: http # 端口名稱
containerPort: 80 # 端口號(hào)
protocol: TCP # 端口協(xié)議,默認(rèn)TCP
env: # 可選,環(huán)境變量配置
- name: TZ # 變量名
value: Asia/Shanghai #變量
- name: LANG
value: en_US.utf8
resources: # 可選,資源限制和資源請求限制
limits: # 最大限制設(shè)置
cpu: 1000m
memory: 1024MiB
requests: # 啟動(dòng)所需的資源
cpu: 100m
memory: 512MiB
readinessProbe: # 可選,容器狀態(tài)檢查
httpGet: # 檢測方式
path: / # 檢查路徑
port: 80 # 監(jiān)控端口
timeoutSeconds: 2 # 超時(shí)時(shí)間
initialDelaySeconds: 60 # 初始化時(shí)間
livenessProbe: # 可選,監(jiān)控狀態(tài)檢查
exec: # 檢測方式
command:
- cat
- /health
httpGet: # 檢測方式
path: /_health
port: 8080
httpHeaders:
- name: end-user
value: jason
tcpSocket: # 檢測方式
port: 80
initialDelaySeconds: 60 # 初始化時(shí)間
timeoutSeconds: 2 # 超時(shí)時(shí)間
periodSeconds: 5 # 檢測間隔
successThreshold: 2 # 檢查成功為2次表示就緒
failureThreshold: 1 # 檢測失敗1次表示未就緒
securityContext: # 可選,限制容器不可信的行為
provoleged: false
restartPolicy: Always # 可選,默認(rèn)為Always
nodeSelector: # 可選,指定Node節(jié)點(diǎn)
region: subnet7
imagePullSecrets: # 可選,拉取鏡像使用的secret
- name: default-dockercfg-86258
hostNetwork: false # 可選,是否為主機(jī)模式,如是,會(huì)占用主機(jī)端口
volumes: # 共享存儲(chǔ)卷列表
- name: webroot # 名稱,與上述對(duì)應(yīng)
emptyDir: {} # 共享卷類型,空
hostPath: # 共享卷類型,本機(jī)目錄
path: /etc/hosts
secret: # 共享卷類型,secret模式,一般用于密碼
secretName: default-token-tf2jp # 名稱
defaultMode: 420 # 權(quán)限
configMap: # 一般用于配置文件
name: nginx-conf
defaultMode: 420