在Kubernetes平臺上,我們很少會直接創(chuàng)建一個Pod,在大多數(shù)情況下會通過RC潭千、Deployment、DaemonSet借尿、Job等控制器完成對一組Pod副本的創(chuàng)建刨晴、調度及全生命周期的自動控制任務
在最早的Kubernetes版本里是沒有這么多Pod副本控制器的屉来,只有一個Pod副本控制器RC(Replication Controller),這個控制器是這樣設計實現(xiàn)的:RC獨立于所控制的Pod狈癞,并通過Label標簽這個松耦合關聯(lián)關系控制目標Pod實例的創(chuàng)建和銷毀茄靠,隨著Kubernetes的發(fā)展,RC也出現(xiàn)了新的繼任者——Deployment亿驾,用于更加自動地完成Pod副本的部署嘹黔、版本更新、回滾等功能莫瞬。
嚴謹?shù)卣f儡蔓,RC的繼任者其實并不是Deployment,而是ReplicaSet疼邀,因為 ReplicaSet進一步增強了 RC標簽選擇器的靈活性喂江。之前RC的標簽選擇器只能選擇一個標簽,而ReplicaSet擁有集合式的標簽選擇器旁振,可以選擇多個Pod標簽获询,如下所示:
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
與RC不同,ReplicaSet被設計成能控制多個不同標簽的Pod副本拐袜。一種常見的應用場景是吉嚣,應用MyApp目前發(fā)布了v1與v2兩個版本,用戶希望MyApp的Pod副本數(shù)保持為3個蹬铺,可以同時包含v1和v2版本的Pod尝哆,就可以用ReplicaSet來實現(xiàn)這種控制,寫法如下:
selector:
matchLabels:
version: v2
matchExpressions:
- {key: version, operator: In, values: [v1, v2]}
Kubernetes的滾動升級就是巧妙運用ReplicaSet的這個特性來實現(xiàn)的甜攀,同時秋泄,Deployment也是通過ReplicaSet來實現(xiàn)Pod副本自動控制功能的。
在大多數(shù)情況下规阀,我們希望Deployment創(chuàng)建的Pod副本被成功調度到集群中的任何一個可用節(jié)點恒序,而不關心具體會調度到哪個節(jié)點。但是谁撼,在真實的生產環(huán)境中的確也存在一種需求:希望某種Pod的副本全部在指定的一個或者一些節(jié)點上運行歧胁,比如希望將MySQL數(shù)據庫調度到一個具有SSD磁盤的目標節(jié)點上,此時Pod模板中的NodeSelector屬性就開始發(fā)揮作用了厉碟,上述MySQL定向調度案例的實現(xiàn)方式可分為以下兩步喊巍。
(1)把具有SSD磁盤的Node都打上自定義標簽“disk=ssd”。
(2)在Pod模板中設定NodeSelector的值為“disk: ssd”墨榄。
在真實的生產環(huán)境中可能面臨以下令人尷尬的問題玄糟。
(1)如果NodeSelector選擇的Label不存在或者不符合條件勿她,比如這些目標節(jié)點此時宕機或者資源不足袄秩,該怎么辦?
(2)如果要選擇多種合適的目標節(jié)點,比如SSD磁盤的節(jié)點或者超高速硬盤的節(jié)點之剧,該怎么辦郭卫?Kubernates引入了NodeAffinity(節(jié)點親和性設置)來解決該需求。
在真實的生產環(huán)境中還存在如下所述的特殊需求背稼。
(1)不同Pod之間的親和性(Affinity)贰军。比如MySQL數(shù)據庫與Redis中間件不能被調度到同一個目標節(jié)點上,或者兩種不同的Pod必須被調度到同一個Node上蟹肘,以實現(xiàn)本地文件共享或本地網絡通信等特殊需求词疼,這就是PodAffinity要解決的問題。
(2)有狀態(tài)集群的調度帘腹。對于ZooKeeper贰盗、Elasticsearch、MongoDB阳欲、Kafka等有狀態(tài)集群舵盈,雖然集群中的每個Worker節(jié)點看起來都是相同的,但每個Worker節(jié)點都必須有明確的球化、不變的唯一ID(主機名或IP地址)秽晚,這些節(jié)點的啟動和停止次序通常有嚴格的順序。此外筒愚,由于集群需要持久化保存狀態(tài)數(shù)據赴蝇,所以集群中的Worker節(jié)點對應的Pod不管在哪個Node上恢復,都需要掛載原來的Volume锨能,因此這些Pod還需要捆綁具體的PV扯再。針對這種復雜的需求,Kubernetes提供了StatefulSet這種特殊的副本控制器來解決問題址遇,在Kubernetes 1.9版本發(fā)布后熄阻,StatefulSet才可用于正式生產環(huán)境中。
(3)在每個Node上調度并且僅僅創(chuàng)建一個Pod副本倔约。這種調度通常用于系統(tǒng)監(jiān)控相關的Pod秃殉,比如主機上的日志采集、主機性能采集等進程需要被部署到集群中的每個節(jié)點浸剩,并且只能部署一個副本钾军,這就是DaemonSet這種特殊Pod副本控制器所解決的問題。
(4)對于批處理作業(yè)绢要,需要創(chuàng)建多個Pod副本來協(xié)同工作吏恭,當這些Pod副本都完成自己的任務時,整個批處理作業(yè)就結束了重罪。這種Pod運行且僅運行一次的特殊調度樱哼,用常規(guī)的RC或者Deployment都無法解決哀九,所以Kubernates引入了新的Pod調度控制器Job來解決問題,并繼續(xù)延伸了定時作業(yè)的調度控制器CronJob搅幅。
與單獨的Pod實例不同阅束,由RC、ReplicaSet茄唐、Deployment息裸、DaemonSet等控制器創(chuàng)建的Pod副本實例都是歸屬于這些控制器的,這就產生了一個問題:控制器被刪除后沪编,歸屬于控制器的Pod副本該何去何從呼盆?在Kubernates 1.9之前,在RC等對象被刪除后蚁廓,它們所創(chuàng)建的Pod副本都不會被刪除宿亡;在Kubernates 1.9以后,這些Pod副本會被一并刪除纳令。如果不希望這樣做挽荠,則可以通過kubectl命令的--cascade=false參數(shù)來取消這一默認特性:
kubectl delete replicaset my-repset --cascade=false
Deployment或RC:全自動調度
Deployment或RC的主要功能之一就是自動部署一個容器應用的多份副本,以及持續(xù)監(jiān)控副本的數(shù)量平绩,在集群內始終維持用戶指定的副本數(shù)量
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
kubectl apply -f nginx-deployment.yaml
[root@k8s-master01 pod]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5bf87f5f59 3 3 2 7m53s
[root@k8s-master01 pod]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 9m4s
[root@k8s-master01 pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-5bf87f5f59-kvsmf 1/1 Running 0 9m13s 10.244.1.7 k8s-node02 <none> <none>
nginx-deployment-5bf87f5f59-rp229 1/1 Running 0 9m13s 10.244.1.8 k8s-node02 <none> <none>
nginx-deployment-5bf87f5f59-wp445 1/1 Running 0 9m13s 10.244.2.12 k8s-node01 <none> <none>
從調度策略上來說圈匆,這3個Nginx Pod由系統(tǒng)全自動完成調度。它們各自最終運行在哪個節(jié)點上捏雌,完全由Master的Scheduler經過一系列算法計算得出跃赚,用戶無法干預調度過程和結果。
除了使用系統(tǒng)自動調度算法完成一組Pod的部署性湿,Kubernetes也提供了多種豐富的調度策略纬傲,用戶只需在Pod的定義中使用NodeSelector、NodeAffinity肤频、PodAffinity叹括、Pod驅逐等更加細粒度的調度策略設置,就能完成對Pod的精準調度宵荒。
NodeSelector: 定向調度
Kubernetes Master上的Scheduler服務(kube-scheduler進程)負責實現(xiàn)Pod的調度汁雷,整個調度過程通過執(zhí)行一系列復雜的算法,最終為每個Pod都計算出一個最佳的目標節(jié)點报咳,這一過程是自動完成的侠讯,通常我們無法知道Pod最終會被調度到哪個節(jié)點上。
在實際情況下暑刃,也可能需要將Pod調度到指定的一些Node上厢漩,可以通過Node的標簽(Label)和Pod的nodeSelector屬性相匹配,來達到上述目的岩臣。
(1)首先通過kubectl label命令給目標Node打上一些標簽:
# 語法
kubectl label nodes <node-name> <label-key>=<label-value>
kubectl label node k8s-node01 zone=north
2)然后溜嗜,在Pod的定義中加上nodeSelector的設置柴底,以redis-master-controller.yaml為例:
# redis-master-controller.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: redis-master
labels:
name: redis-master
spec:
replicas: 1
selector:
name: redis-master
template:
metadata:
labels:
name: redis-master
spec:
containers:
- name: master
image: kubeguide/redis-master
ports:
- containerPort: 6379
nodeSelector: # 重點1
zone: north # 重點2
kubectl apply -f redis-master-controller.yaml
[root@k8s-master01 pod]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-master-zkkkj 1/1 Running 0 90s 10.244.1.10 k8s-node02 <none> <none>
如果我們給多個Node都定義了相同的標簽(例如zone=north),則scheduler會根據調度算法從這組Node中挑選一個可用的Node進行Pod調度粱胜。
通過基于Node標簽的調度方式,我們可以把集群中具有不同特點的Node都貼上不同的標簽狐树,例如“role=frontend”“role=backend”“role=database”等標簽焙压,在部署應用時就可以根據應用的需求設置NodeSelector來進行指定Node范圍的調度。
需要注意的是抑钟,如果我們指定了Pod的nodeSelector條件涯曲,且在集群中不存在包含相應標簽的Node,則即使在集群中還有其他可供使用的Node在塔,這個Pod也無法被成功調度幻件。
NodeSelector通過標簽的方式,簡單實現(xiàn)了限制Pod所在節(jié)點的方法蛔溃。親和性調度機制則極大擴展了Pod的調度能力绰沥,主要的增強功能如下。
- 更具表達力(不僅僅是“符合全部”的簡單情況)贺待。
- 可以使用軟限制徽曲、優(yōu)先采用等限制方式,代替之前的硬限制麸塞,這樣調度器在無法滿足優(yōu)先需求的情況下秃臣,會退而求其次,繼續(xù)運行該Pod哪工。
- 可以依據節(jié)點上正在運行的其他Pod的標簽來進行限制奥此,而非節(jié)點本身的標簽。這樣就可以定義一種規(guī)則來描述Pod之間的親和或互斥關系雁比。
親和性調度功能包括節(jié)點親和性(NodeAffinity)和Pod親和性(PodAffinity)兩個維度的設置稚虎。節(jié)點親和性與NodeSelector類似,增強了上述前兩點優(yōu)勢偎捎;Pod的親和與互斥限制則通過Pod標簽而不是節(jié)點標簽來實現(xiàn)祥绞,也就是上面第4點內容所陳述的方式,同時具有前兩點提到的優(yōu)點鸭限。
NodeSelector將會繼續(xù)使用蜕径,隨著節(jié)點親和性越來越能夠表達nodeSelector的功能,最終NodeSelector會被廢棄败京。
NodeAffinity:Node親和性調度
NodeAffinity意為Node親和性的調度策略兜喻,是用于替換NodeSelector的全新調度策略。目前有兩種節(jié)點親和性表達赡麦。
- RequiredDuringSchedulingIgnoredDuringExecution:必須滿足指定的規(guī)則才可以調度Pod到Node上(功能與nodeSelector很像朴皆,但是使用的是不同的語法)帕识,相當于硬限制。
- PreferredDuringSchedulingIgnoredDuringExecution:強調優(yōu)先滿足指定規(guī)則遂铡,調度器會嘗試調度Pod到Node上肮疗,但并不強求,相當于軟限制扒接。多個優(yōu)先級規(guī)則還可以設置權重(weight)值伪货,以定義執(zhí)行的先后順序。
IgnoredDuringExecution的意思是:如果一個Pod所在的節(jié)點在Pod運行期間標簽發(fā)生了變更钾怔,不再符合該Pod的節(jié)點親和性需求碱呼,則系統(tǒng)將忽略Node上Label的變化,該Pod能繼續(xù)在該節(jié)點運行宗侦。
下面的例子設置了NodeAffinity調度的如下規(guī)則愚臀。
- requiredDuringSchedulingIgnoredDuringExecution要求只運行在amd64的節(jié)點上(beta.kubernetes.io/arch In amd64)
- preferredDuringSchedulingIgnoredDuringExecution的要求是盡量運行在磁盤類型為ssd(disk-type In ssd)的節(jié)點上。
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity: # 1
nodeAffinity: # 2
requiredDuringSchedulingIgnoredDuringExecution: # 3
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
- amd64
preferredDuringSchedulingIgnoredDuringExecution: # 4
- weight: 1
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd
containers:
- name: with-node-affinity
image: gcr.io/google_containers/pause:2.0
從上面的配置中可以看到In操作符矾利,NodeAffinity語法支持的操作符包括In姑裂、NotIn、Exists男旗、DoesNotExist炭分、Gt、Lt剑肯。雖然沒有節(jié)點排斥功能捧毛,但是用NotIn和DoesNotExist就可以實現(xiàn)排斥的功能了。
NodeAffinity規(guī)則設置的注意事項如下
- 如果同時定義了nodeSelector和nodeAffinity让网,那么必須兩個條件都得到滿足呀忧,Pod才能最終運行在指定的Node上。
- 如果nodeAffinity指定了多個nodeSelectorTerms溃睹,那么其中一個能夠匹配成功即可而账。
- 如果在nodeSelectorTerms中有多個matchExpressions,則一個節(jié)點必須滿足所有matchExpressions才能運行該Pod因篇。
PodAffinity:Pod親和與互斥調度策略
Pod間的親和與互斥從Kubernetes 1.4版本開始引入泞辐。這一功能讓用戶從另一個角度來限制Pod所能運行的節(jié)點:根據在節(jié)點上正在運行的Pod的標簽而不是節(jié)點的標簽進行判斷和調度,要求對節(jié)點和Pod兩個條件進行匹配竞滓。這種規(guī)則可以描述為:如果在具有標簽X的Node上運行了一個或者多個符合條件Y的Pod咐吼,那么Pod應該(如果是互斥的情況,那么就變成拒絕)運行在這個Node上商佑。
X指的是一個集群中的節(jié)點锯茄、機架、區(qū)域等概念,通過Kubernetes內置節(jié)點標簽中的key來進行聲明肌幽,這個key的名字為topologyKey晚碾,用來表達節(jié)點所屬的topology范圍。
kubernetes內置標簽:
? ○ kubernetes.io/hostname
? ○ failure-domain.beta.kubernetes.io/zone
? ○ failure-domain.beta.kubernetes.io/region
# kubernetes.io/hostname
[root@k8s-master01 pod]# kubectl edit nodes k8s-node02
apiVersion: v1
kind: Node
metadata:
annotations:
flannel.alpha.coreos.com/backend-data: '{"VtepMAC":"16:83:50:6b:53:8d"}'
flannel.alpha.coreos.com/backend-type: vxlan
flannel.alpha.coreos.com/kube-subnet-manager: "true"
flannel.alpha.coreos.com/public-ip: 10.0.0.242
kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
node.alpha.kubernetes.io/ttl: "0"
volumes.kubernetes.io/controller-managed-attach-detach: "true"
creationTimestamp: "2020-06-24T18:05:26Z"
labels:
beta.kubernetes.io/arch: amd64
beta.kubernetes.io/os: linux
kubernetes.io/arch: amd64
kubernetes.io/hostname: k8s-node02
kubernetes.io/os: linux
zone: south
與節(jié)點不同的是,Pod是屬于某個命名空間的.條件Y表達的是pod對應的一個或者全部命名空間中的一個Label Selector喂急。
和 NodAffinity 相同:Pod親和和互斥的條件設置也是requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution格嘁。Pod 的親和性被定義于 PodSpec 的 Affinity 字段下的 podAffinity 字段中。Pod 間的互斥性則被定義于同一層次的 podAntiAffinity 子字段中廊移。
1.參照目標Pod
- 創(chuàng)建一個帶有標簽security=S1和app=nginx的Pod
# pod-flag.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-flag
labels: # 定義多個標簽糕簿,以便其它pod設置親和與互斥
app: nginx
security: s1
spec:
containers:
- name: nginx
image: nginx
2.Pod的親和性調度
# pod-flag-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity
spec:
affinity: #親和性設置
podAffinity: #設置pod親和性
requiredDuringSchedulingIgnoredDuringExecution: #必須要滿足的條件
- labelSelector: #與哪個pod有親和性,在此設置此pod具有的標簽
matchExpressions: #要匹配的pod的画机,標簽定義,如果定義了多個matchExpressions新症,則所有標簽必須同時滿足步氏。
- key: security #標簽的key
operator: In #操作符
values:
- s1 #標簽的值,即擁有l(wèi)abel security=s1
topologyKey: kubernetes.io/hostname #節(jié)點所屬拓樸域topology
#上面表達的意思是此處創(chuàng)建的Pod必須要與擁有標簽security=s1的pod在同一Node上.
containers:
- name: wish-pod-affinity
image: nginx
# 可以看到兩個Pod運行在同一個Node
[root@k8s-master01 pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-affinity 1/1 Running 0 18s 10.244.1.14 k8s-node02
pod-flag 1/1 Running 0 3m14s 10.244.1.13 k8s-node02
3.Pod的互斥性調度
# pod-flag-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: anti-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
containers:
- name: anti-affinity
image: nginx
這里要求這個新Pod與security=S1的Pod為同一個zone徒爹,但是不與 app=nginx的Pod為同一個Node荚醒。創(chuàng)建Pod之后,同樣用kubectl get pods -o wide來查看隆嗅,會看到新的Pod被調度到了同一Zone內的不同Node上.
與節(jié)點親和性類似界阁,Pod親和性的操作符也包括In、NotIn胖喳、Exists泡躯、 DoesNotExist、Gt丽焊、Lt
topologyKey 設置注意事項
原則上较剃,topologyKey可以使用任何合法的標簽Key賦值,但是出于性能和安全方面的考慮技健,對topologyKey有如下限制:
- 在Pod親和性和requiredDuringSchedulingIgnoredDuringExecution的互斥性定義中写穴,不允許使用空的topologyKey。
- 如果Admisson controller 包含了LimitPodHardAntiAffinityTopology雌贱,那么針對requiredDuringSchedulingIgnoredDuringExecution的Pod互斥性定義就被限制為kubernetes.io/hostname啊送,要使用自定義的topologyKey,就要改寫或禁用該控制器欣孤。
- 在preferredDuringSchedulingIgnoredDuringExecution 類型的pod互斥性定義中馋没,空的topologyKey會被解釋為kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone 和 failure-domain.beta.kubernetes.io/region的組合。
- 如果不是上述情況降传,就可以采用任意合法的topologyKey 了披泪。