Kube.NETes的資源對(duì)象包括Pod、Node、Namespace、Endpoints、Service等,Kubernetes也提供了各種資源對(duì)象的控制器,用來(lái)驅(qū)動(dòng)對(duì)象的當(dāng)前狀態(tài)(status)逼近提交的期望狀態(tài)(spec)。本文從原理、類型、使用這3個(gè)方面說(shuō)說(shuō)控制器。
一、控制器的原理
1.大致原理
在Kubernetes 集群中,Controller通過(guò) API Server提供的(List & Watch)接口實(shí)時(shí)監(jiān)控集群中資源對(duì)象的狀態(tài)變化,當(dāng)發(fā)生故障,導(dǎo)致資源對(duì)象的狀態(tài)變化時(shí),Controller會(huì)嘗試將其狀態(tài)調(diào)整為期望的狀態(tài)。
比如當(dāng)某個(gè)Pod出現(xiàn)故障時(shí),Deployment Controller會(huì)及時(shí)發(fā)現(xiàn)故障并執(zhí)行自動(dòng)化修復(fù)流程,確保集群里的Pod始終處于預(yù)期的工作狀態(tài)。
具體流程見(jiàn)下圖:
Kubernetes控制器是通過(guò)定期重復(fù)執(zhí)行如下 3 個(gè)步驟來(lái)完成控制任務(wù)的:
- 從API Server讀取資源對(duì)象的期望狀態(tài)和當(dāng)前狀態(tài)。
- 比較兩者的差異,然后運(yùn)行控制器操作現(xiàn)實(shí)中的資源對(duì)象,將資源對(duì)象的真實(shí)狀態(tài)修正為Spec中定義的期望狀態(tài)。
- 變動(dòng)執(zhí)行成功后,將結(jié)果狀態(tài)寫回到在API Server上的目標(biāo)資源對(duì)象的status字段中。
2.Controller Manager
Kubernetes 提供了名種資源對(duì)象的控制器,每種Controller 都負(fù)責(zé)一種特定資源的控制流程,而Controller Manager是這些控制器的管理者。
Controller Manager 發(fā)現(xiàn)資源的實(shí)際狀態(tài)和期望狀態(tài)有偏差之后,會(huì)觸發(fā)相應(yīng)Controller注冊(cè)的Event Handler,讓它們?nèi)ジ鶕?jù)資源本身的特點(diǎn)進(jìn)行調(diào)整。之所以叫Controller Manager,是因?yàn)镃ontroller Manager 由負(fù)責(zé)不同資源的多種Controller組成,如 Deployment Controller、Node Controller、Namespace Controller、Service Controller等,這些Controllers各自明確分工,負(fù)責(zé)集群內(nèi)各種資源的管理。
3.循環(huán)控制和立即控制
Kubernetes集群上運(yùn)行著大量的控制循環(huán),每個(gè)循環(huán)都有一組特定的任務(wù)要處理,為了避免API Server被大量的請(qǐng)求拖垮,需要設(shè)置控制循環(huán)以較低的頻率運(yùn)行,默認(rèn)每5分鐘一次。
同時(shí),為了能及時(shí)觸發(fā)由客戶端提交的期望狀態(tài),控制器向API Server注冊(cè)監(jiān)聽(tīng)資源對(duì)象事件,這些資源對(duì)象的期望狀態(tài)的任何變動(dòng)都會(huì)由Informer組件通知給控制器立即執(zhí)行而無(wú)須等到下一輪的控制循環(huán)。
控制器使用工作隊(duì)列將需要運(yùn)行的控制循環(huán)進(jìn)行排隊(duì),從而確保在受控資源對(duì)象很多時(shí) 或 資源對(duì)象變動(dòng)頻繁時(shí),不會(huì)漏掉控制任務(wù)。
這個(gè)流程類似于做大數(shù)據(jù)統(tǒng)計(jì)時(shí),一方面利用實(shí)時(shí)計(jì)算及時(shí)統(tǒng)計(jì)出結(jié)果,一方面利用批計(jì)算按日統(tǒng)計(jì),重新比對(duì)和矯正實(shí)時(shí)計(jì)算的結(jié)果。
整體流程如下圖:
二、控制器的類型
Kubernetes 提供了Replication Controller、Node Controller、ResourceOuota Controller、Namespace Controller、ServiceAccount Controller、Service Controller、Endpoint Controller、Deploymont Controller等各種資源對(duì)象的控制器。常用的控制器有以下幾種。
1.ReplicaSet Controller
ReplicaSet保證在所有時(shí)間內(nèi),都有特定數(shù)量的Pod副本在運(yùn)行。如果數(shù)量太多,ReplicaSet會(huì)刪除幾個(gè);如果數(shù)量太少,ReplicaSet將創(chuàng)建幾個(gè)。與直接創(chuàng)建的Pod不同的是,ReplicaSet會(huì)替換掉那些被刪除或者被終止的Pod。
建議通過(guò)Deployment管理ReplicaSet,一般無(wú)須直接操作ReplicaSet。
ReplicaSet Controller中的 Pod 模板就像一個(gè)模具,模具制作出來(lái)的東西一旦離開模具,它們之間就再也沒(méi)關(guān)系了。同樣,一旦Pod被創(chuàng)建完畢,無(wú)論模板如何變化也不會(huì)影響到已經(jīng)創(chuàng)建的 Pod 。
2.Deployment Controller
Deployment是管理應(yīng)用副本的API對(duì)象。管理員只需要在Deployment中描述期望的狀態(tài),Deployment就能根據(jù)一定的策略將ReplicaSet與Pod更新到管理員預(yù)期的狀態(tài)。Deployment提供了運(yùn)行Pod的能力,并且為Pod提供滾動(dòng)升級(jí)、伸縮、副本等功能,一般用于運(yùn)行無(wú)狀態(tài)的應(yīng)用。
需要說(shuō)明的是,Deployment并不直接控制Pod,而是通過(guò)上面介紹的ReplicaSet實(shí)現(xiàn)對(duì)Pod的管理。
在創(chuàng)建 Deployment 資源對(duì)象之后,Deployment Controller 也默默創(chuàng)建了對(duì)應(yīng)的ReplicaSet,Deployment 的滾動(dòng)升級(jí)是Deployment Controller通過(guò)自動(dòng)創(chuàng)建新的ReplicaSet來(lái)支持的。
3.StatefulSet Controller
StatefulSet用來(lái)管理一組Pod集合的部署和擴(kuò)縮容,并為這些Pod提供持久化存儲(chǔ)和持久化標(biāo)識(shí)符。與Deployment不同的是,StatefulSet為每個(gè)Pod維護(hù)了一個(gè)固定的ID。這些Pod是基于相同的模板來(lái)創(chuàng)建的,但是不能相互替換,無(wú)論怎么調(diào)度,每個(gè)Pod都有一個(gè)固定的ID。
4.DaemonSet Controller
DaemonSet確保全部Node上運(yùn)行一個(gè)Pod的副本。當(dāng)有Node加入集群時(shí),會(huì)自動(dòng)為它們新增一個(gè)Pod;當(dāng)有Node從集群移除時(shí),這些Pod也會(huì)被回收。刪除DaemonSet將會(huì)刪除它創(chuàng)建的所有Pod。
5.Job Controller
Job負(fù)責(zé)批量處理短暫的一次性任務(wù),即僅執(zhí)行一次的任務(wù),它保證批處理任務(wù)的一個(gè)或多個(gè)Pod成功結(jié)束。
6.CronJob Controller
CronJob負(fù)責(zé)周期性的處理任務(wù),根據(jù)Cron表達(dá)式定時(shí)執(zhí)行Job。
7.Node Controller
kubelet進(jìn)程在啟動(dòng)時(shí)通過(guò) API Server 注冊(cè)自身節(jié)點(diǎn)信息,并定時(shí)向API Server匯報(bào)狀態(tài)信息,API Server 在接收到這些信息后,會(huì)將這些信息更新到etcd中。在etcd 中存儲(chǔ)的節(jié)點(diǎn)信息包括節(jié)點(diǎn)健康狀況、節(jié)點(diǎn)資源、節(jié)點(diǎn)名稱、節(jié)點(diǎn)地址信息、操作系統(tǒng)版本、Dooker版本、kubelet版本等。
節(jié)點(diǎn)健康狀況包含就緒(True)、未就緒(False)、未知(Unknown)三種。
Node Controller通過(guò)API Server實(shí)時(shí)獲取 Node的相關(guān)信息,實(shí)現(xiàn)管理和監(jiān)控集群中各個(gè)Node的相關(guān)控制功能。
8.ResourceQuota Controller
ResourceQuota Controller(資源配額管理)確保指定的資源對(duì)象在任何時(shí)候都不會(huì)超量占用系統(tǒng)物理資源,避免由于某些業(yè)務(wù)在設(shè)計(jì)或?qū)崿F(xiàn)上的缺陷,導(dǎo)致整個(gè)系統(tǒng)運(yùn)行紊亂甚至宕機(jī)。比如可以對(duì)容器運(yùn)行的CPU和Memory進(jìn)行限制。對(duì)Namespace下的Pod數(shù)量、Service數(shù)量、PV數(shù)量等進(jìn)行限制。
9.Service Controller 與 Endpoints Controller
我們先看看Service、Endpoints與Pod 的關(guān)系。如圖下圖所示,Endpoints表示一個(gè)Service對(duì)應(yīng)的所有Pod副本的訪問(wèn)地址,Endpoints Controller就是負(fù)責(zé)生成和維護(hù)所有Endpoints對(duì)象的控制器。
Endpoints Controller負(fù)責(zé)監(jiān)聽(tīng)Service和對(duì)應(yīng)的Pod副本的變化,如果監(jiān)測(cè)到Service被刪除,則刪除和該Service同名的Endpoints對(duì)象。如果監(jiān)測(cè)到新的 Service 被創(chuàng)建或者修改,則根據(jù)該 Service 信息獲得相關(guān)的Pod列表,然后創(chuàng)建或者更新 Service 對(duì)應(yīng)的Endpoints 對(duì)象。如果監(jiān)測(cè)到Pod的事件,則更新它所對(duì)應(yīng)的Service的Endpoints對(duì)象(增加、刪除或者修改對(duì)應(yīng)的 Endpoint 列表)。
那么,Endpoints對(duì)象是在哪里被使用的呢?答案是每個(gè) Node 上的kube-proxy進(jìn)程,kube-proxy進(jìn)程獲取每個(gè)Service的Endpoints,實(shí)現(xiàn)了Service的負(fù)載均衡功能。
Service Controller則是監(jiān)聽(tīng)Service的變化,相應(yīng)地創(chuàng)建、刪除或者更新路由轉(zhuǎn)發(fā)表(根據(jù)Endpoints的列表)。
三、控制器的使用
一個(gè)工作負(fù)載控制器資源通常包含3 個(gè)基本的組成部分。
- 選擇器:匹配并關(guān)聯(lián) Pod 對(duì)象
- 期望的副本數(shù):期望在集群中運(yùn)行受控的 Pod 數(shù)量。
- Pod模板:用于新建 Pod 對(duì)象使用的模板。
工作負(fù)載控制器的資源的spec字段通常要包含如下字段:replicas、selector和template,其中template是用于定義 Pod模板。
常用控制器的編排yaml如下。
1.Deployment
管理一組Pod,假設(shè)設(shè)置副本數(shù) replicas=2,這時(shí) node01和node02上會(huì)各自分配一個(gè)Pod,現(xiàn)在將node02關(guān)閉,則會(huì)發(fā)現(xiàn)node01上會(huì)部署兩個(gè)Pod。
apiVersion: Apps/v1
kind: Deployment
metadata:
name: Nginx-deployment
namespace: test
labels:
app: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx-deployment-tp
template:
metadata:
labels:
app: nginx-deployment-tp
spec:
contAIners:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 30
2.StatefulSet
StatefulSet是幾種工作負(fù)載當(dāng)中較為復(fù)雜的一種,當(dāng)然核心也是管理一組Pod。舉個(gè)例子:假設(shè)設(shè)置副本數(shù)replicas=2,那么這一組Pod有如下特性:
- 啟動(dòng)的有序性:pod-0啟動(dòng)之后,再啟動(dòng)pod-1。
- 停止的有序性:pod-1停止之后,再停止pod-0。
- 有狀態(tài):pod的id不變,創(chuàng)建的PVC是是跟Pod綁定的,有獨(dú)特的volumeClaimTemplate配置,會(huì)利用storgeClass自動(dòng)創(chuàng)建PVC和PV 。
- 穩(wěn)定服務(wù)發(fā)現(xiàn): Service可以找到自己想找到的pod,因?yàn)镻od的id是固定的有狀態(tài)的。例如要訪問(wèn)某個(gè)Pod,可以直接使用curl pod名稱.svc名稱.命名空間.svc.cluster.local。
apiVersion: v1
kind: Service
metadata:
name: nginx-statefulset-svc
namespace: test
spec:
# ClusterIP | LoadBalancer |
type: ClusterIP
# headless service
clusterIP: None
selector:
app: nginx-statefulset-tp
ports:
- name: http
port: 80
targetPort: 80
# nodePort: 30080
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-statefulset
namespace: test
labels:
app: nginx-statefulset
spec:
replicas: 2
serviceName: nginx-statefulset-svc
selector:
matchLabels:
app: nginx-statefulset-tp
template:
metadata:
labels:
app: nginx-statefulset-tp
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 30
volumeClaimTemplates:
- metadata:
name: www
spec:
resources:
requests:
storage: 100Mi
accessModes:
- ReadWriteOnce
storageClassName: nfs-client
3.DaemonSet
管理一組Pod,相比Deployment和StatefulSet,沒(méi)有副本數(shù)的概念。它有如下特性:
- 每個(gè)node上部署一個(gè)pod
- node上部署的pod不會(huì)被驅(qū)逐,默認(rèn)添加對(duì)所有節(jié)點(diǎn)污點(diǎn)的容忍
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-daemonset
namespace: test
labels:
app: nginx-daemonset
spec:
selector:
matchLabels:
app: nginx-daemonset-tp
template:
metadata:
labels:
app: nginx-daemonset-tp
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
4.Job
執(zhí)行一次就結(jié)束的Pod
apiVersion: batch/v1
kind: Job
metadata:
name: hello
namespace: test
spec:
# 嘗試backoffLimit+1次 沒(méi)有成功 則退出Job
backoffLimit: 2
completions: 3
template:
spec:
# 僅支持 OnFailure Never
restartPolicy: Never
containers:
- name: busybox
image: busybox
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- echo "Hello My Job"
5.CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
namespace: test
spec:
# ┌───────────── 分鐘 (0 - 59)
# │ ┌───────────── 小時(shí) (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6)(周日到周一;在某些系統(tǒng)上,7 也是星期日)
# │ │ │ │ │ 或者是 sun,mon,tue,web,thu,fri,sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *
schedule: "* * * * *"
# Allow - 允許并發(fā)
# Forbid - 不允許并發(fā) 丟棄新的job
# Replace - 不允許并發(fā) 丟棄老的job
concurrencyPolicy: Allow
# 記錄成功的job數(shù)量
successfulJobsHistoryLimit: 3
# 記錄失敗的job數(shù)量
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
# 僅支持 OnFailure Never
restartPolicy: Never
containers:
- name: busybox
image: busybox
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- echo "Hello My Cron Job"
Job的基礎(chǔ)上定義Cron表達(dá)式,定時(shí)執(zhí)行Job