k8s调度之node的亲和性以及pod的亲和性/反亲和性
k8s調度之node的親和性以及pod的親和性/反親和性
通常情況下,使用的都是k8s默認的調度調度方式,但是在有些情況下,我們需要將pod運行在具有特定的標簽的node上才能都運行,這個時候,pod的調度策略就不能使用k8s默認的調度策略了,這個時候,就需要指定調度策略,告訴k8s需要將pod調度到那些node(節點)上。
nodeSelector
常規情況下,會直接使用nodeSelector這種調度策略。labels(標簽) 是k8s里面用來標記資源的一種常用的方式,我們可以給node標記特殊的標簽,然后nodeSelector會將pod調度到帶有指定labels的node上的。
下面看個示例:
首先,查看node的label信息,通過下面的命令查看的 node 的 label:
$ kubectl get nodes --show-labels NAME STATUS ROLES AGE VERSION LABELS master Ready master 147d v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=master,node-role.kubernetes.io/master= node02 Ready <none> 67d v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,course=k8s,kubernetes.io/hostname=node02 node03 Ready <none> 127d v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,jnlp=haimaxy,kubernetes.io/hostname=node03然后,可以給node02節點新增一個label:
$ kubectl label nodes node02 com=yijiadashuju node "node02" labeled然后通過上面的--show-labels參數可以查看上述標簽是否生效。當 node 被打上了相關標簽后,在調度的時候就可以使用這些標簽了,只需要在 Pod 的spec字段中添加nodeSelector字段,里面是我們需要被調度的節點的 label 即可。比如,要將 Pod 我們要強制調度到 node02 這個節點上去,可以使用 nodeSelector 來表示了:(pod-selector-demo.yaml)
apiVersion: v1 kind: Pod metadata:labels:app: busybox-podname: test-busybox spec:containers:- command:- sleep- "3600"image: busyboximagePullPolicy: Alwaysname: test-busyboxnodeSelector:com: yijiadashuju然后,執行pod-selector-demo.yaml文件后,可以通過下面的命令查看pod運行的節點信息
kubectl get pod -o wide -n default也可以使用describe命令查看pod被調度到哪個節點上:
$ kubectl create -f pod-selector-demo.yaml pod "test-busybox" created $ kubectl describe pod test-busybox Name: test-busybox Namespace: default Node: node02/10.151.30.63 ...... QoS Class: BestEffort Node-Selectors: com=youdianzhishi Tolerations: node.kubernetes.io/not-ready:NoExecute for 300snode.kubernetes.io/unreachable:NoExecute for 300s Events:Type Reason Age From Message---- ------ ---- ---- -------Normal SuccessfulMountVolume 55s kubelet, node02 MountVolume.SetUp succeeded for volume "default-token-n9w2d"Normal Scheduled 54s default-scheduler Successfully assigned test-busybox to node02Normal Pulling 54s kubelet, node02 pulling image "busybox"Normal Pulled 40s kubelet, node02 Successfully pulled image "busybox"Normal Created 40s kubelet, node02 Created containerNormal Started 40s kubelet, node02 Started container從上面的執行結果可以看出,pod 通過默認的 default-scheduler 調度器到了node02節點上。不過,這種調度方式屬于強制性的。如果node02上的資源不足,那么pod的狀態將會一直是pending狀態。這就是nodeselector的用法了。
通過上面的介紹,可以看出nodeselector使用起來非常方便,但是還有很多的不足,那就是不夠靈活,控制粒度偏大,在實際使用中還是有許多的不便。接下來一起看下親和性和反親和性調度。
親和性和反親和性調度
k8s的默認調度流程實際上是經過了兩個階段:predicates 和 priorities 。使用默認的調度流程的話,k8s會將pod調度到資源充裕的節點上,使用nodeselector的調度方法,又會將pod調度到具有指定標簽的pod上。然后在實際生產環境中,我們需要將pod調度到具有某些label的一組node才能滿足實際需求,這個時候就需要nodeAffinity(節點親和性)、podAffinity(pod 親和性) 以及 podAntiAffinity(pod 反親和性)。
親和性可以分為具體可以細分為硬和軟兩種親和性,
- 軟親和性:如果調度的時候,沒有滿足要求,也可以繼續調度,即能滿足最好,不能也無所謂
- 硬親和性:是指調度的時候必須滿足特定的要求,如果不滿足,那么pod將不會被調度到當前node
規則可以設置:
軟策略: preferredDuringSchedulingIgnoredDuringExecution
硬策略: requiredDuringSchedulingIgnoredDuringExecution
nodeAffinity 節點親和性
節點親和性主要是用來控制 pod 能部署在哪些節點上,以及不能部署在哪些節點上的。它可以進行一些簡單的邏輯組合了,不只是簡單的相等匹配。
接下來看一個示例,使用 Deployment 來管理3個 pod 副本,使用nodeAffinity控制 pod 的調度,如下例子:(node-affinity-demo.yaml)
apiVersion: apps/v1beta1 kind: Deployment metadata:name: affinitylabels:app: affinity spec:replicas: 3revisionHistoryLimit: 15template:metadata:labels:app: affinityrole: testspec:containers:- name: nginximage: nginx:1.7.9ports:- containerPort: 80name: nginxwebaffinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution: # 硬策略nodeSelectorTerms:- matchExpressions:- key: kubernetes.io/hostnameoperator: NotInvalues:- node03preferredDuringSchedulingIgnoredDuringExecution: # 軟策略- weight: 1preference:matchExpressions:- key: comoperator: Invalues:- yijiadashuju這個pod調度的時候,首先要求不能運行在node03節點上,但是如果有節點滿足labels為com:yijiadashuju 的話,就會優先調度到這個節點上。
接下來看下節點信息:
$ kubectl get nodes --show-labels NAME STATUS ROLES AGE VERSION LABELS master Ready master 154d v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=master,node-role.kubernetes.io/master= node02 Ready <none> 74d v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,com=yijiadashuju,course=k8s,kubernetes.io/hostname=node02 node03 Ready <none> 134d v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,jnlp=haimaxy,kubernetes.io/hostname=node03可以看到 node02 節點有com=yijiadashuju的 label,按要求會優先調度到這個節點,接下來創建 pod,然后使用descirbe命令查看調度情況。
$ kubectl create -f node-affinity-demo.yaml deployment.apps "affinity" created $ kubectl get pods -l app=affinity -o wide NAME READY STATUS RESTARTS AGE IP NODE affinity-7b4c946854-5gfln 1/1 Running 0 47s 10.244.4.214 node02 affinity-7b4c946854-l8b47 1/1 Running 0 47s 10.244.4.215 node02 affinity-7b4c946854-r86p5 1/1 Running 0 47s 10.244.4.213 node02從結果可以看到 pod 均被部署到了 node02節點。
現在Kubernetes提供的操作符有下面的幾種
In:label 的值在某個標簽中 NotIn:label 的值不在某個標簽中 Gt:label 的值大于某個值 Lt:label 的值小于某個值 Exists:某個 label 存在 DoesNotExist:某個 label 不存在如果nodeSelectorTerms下面有多個選項的話,滿足任何一個條件就可以了;如果matchExpressions有多個選項的話,則必須同時滿足這些條件才能正常調度 POD。
podAffinity pod親和性
pod的親和性主要用來解決pod可以和哪些pod部署在同一個集群里面,即拓撲域(由node組成的集群)里面;而pod的反親和性是為了解決pod不能和哪些pod部署在一起的問題,二者都是為了解決pod之間部署問題。需要注意的是,Pod 間親和與反親和需要大量的處理,這可能會顯著減慢大規模集群中的調度,不建議在具有幾百個節點的集群中使用,而且Pod 反親和需要對節點進行一致的標記,即集群中的每個節點必須具有適當的標簽能夠匹配 topologyKey。如果某些或所有節點缺少指定的 topologyKey 標簽,可能會導致意外行為。
下面是pod間親和的示例:
apiVersion: v1 kind: Pod metadata:name: with-pod-affinity spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: securityoperator: Invalues:- S1topologyKey: failure-domain.beta.kubernetes.io/zonepodAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: securityoperator: Invalues:- S2topologyKey: failure-domain.beta.kubernetes.io/zonecontainers:- name: with-pod-affinityimage: k8s.gcr.io/pause:2.0podAntiAffinity pod反親和性
下面是一個pod反親和yaml文件示例:
apiVersion: apps/v1 kind: Deployment metadata:name: redis-cache spec:selector:matchLabels:app: storereplicas: 3template:metadata:labels:app: storespec:affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues:- storetopologyKey: "kubernetes.io/hostname"containers:- name: redis-serverimage: redis:3.2-alpinePod Affinity補充
通過《K8S調度之節點親和性》,我們知道怎么在調度的時候讓pod靈活的選擇node,但有些時候我們希望調度能夠考慮pod之間的關系,而不只是pod與node的關系。于是在kubernetes 1.4的時候引入了pod affinity。
為什么有這樣的需求呢?舉個例子,我們系統服務 A 和服務 B 盡量部署在同個主機、機房、城市,因為它們網絡溝通比較多;再比如,我們系統數據服務 C 和數據服務 D 盡量分開,因為如果它們分配到一起,然后主機或者機房出了問題,會導致應用完全不可用,如果它們是分開的,應用雖然有影響,但還是可用的。
pod affinity 可以這樣理解:調度的時候選擇(或者不選擇)這樣的節點 N ,這些節點上已經運行了滿足條件 X。條件 X 是一組 label 選擇器,它必須指明作用的 namespace(也可以作用于所有的 namespace),因為 pod 是運行在某個 namespace 中的。
這里的X指的是集群中的節點、機架、區域等概念,通過kubernetes內置節點標簽中的key來進行聲明。這個key的名字為topologyKey,意為表達節點所屬的topology范圍:
- kubernetes.io/hostname
- failure-domain.beta.kubernetes.io/zone
- failure-domain.beta.kubernetes.io/region
和 node affinity 相似,pod affinity 也有 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,意義也和之前一樣。如果有使用親和性,在 affinity 下面添加 podAffinity 字段,如果要使用互斥性,在 affinity 下面添加 podAntiAffinity 字段。
先定義一個參照目標pod:
apiVersion: v1 kind: Pod metadata:name: pod-flaglabels:security: "S1"app: "nginx" spec:containers:- name: nginximage: nginxPod親和性調度
下面是一個親和性調度的示例
apiVersion: v1 kind: Pod metadata:name: pod-affinity spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: securityoperator: Invalues:- S1topologyKey: kubernetes.io/hostnamecontainers:- name: with-pod-affinityimage: gcr.io/google_containers/pause:2.0創建后可以看到這個pod與上面那個參照的pod位于同一個node上,另外,如果將這個node上的kubernetes.io/hostname標簽干掉,將會發現pod會一直處于pending狀態,這是因為找不到滿足條件的node了。
pod互斥性調度
下面是一個互斥性調度的示例:
apiVersion: v1 kind: Pod metadata:name: with-pod-affinity spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: securityoperator: Invalues:- S1topologyKey: "failure-domain.beta.kubernetes.io/zone"podAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: securityoperator: Invalues:- S2topologyKey: kubernetes.io/hostnamecontainers:- name: with-pod-affinityimage: gcr.io/google_containers/pause:2.0這個例子要求這個新pod與security=S1的pod為同一個zone,但是不與security=S2的pod為同一個node。
原則上,topologyKey可以使用任何合法的標簽Key賦值,但是出于性能和安全方面的考慮,對topologyKey有如下限制:
- 在pod親和性和RequiredDuringScheduling的pod互斥性的定義中,不允許使用空的topologyKey
- 如果admission controller包含了LimitPodHardAntiAffinityTopology,那么針對RequiredDuringScheduling的pod互斥性定義就被限制為kubernetes.io/hostname,要使用自定義的topologyKey,就要改寫或禁用該控制器
- 在PerferredDuringScheduling類型的Pod互斥性定義中,空的topologyKey會被解釋為kubernetes.io/hostname、failure-domain.beta.kubernetes.io/zone及failure-domain.beta.kubernetes.io/region的組合
podAffinity規則設置的注意事項:
- 在labelSelector和topologyKey同級,還可以定義namespaces列表,表示匹配哪些namespace里面的pod,默認情況下,會匹配定義的pod所在的namespace,如果定義了這個字段,但是它的值為空,則匹配所有的namespaces。
- 所有關聯requiredDuringSchedulingIgnoredDuringExecution的matchExpressions全都滿足之后,系統才能將pod調度到某個node上。
總結
以上是生活随笔為你收集整理的k8s调度之node的亲和性以及pod的亲和性/反亲和性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux解决root用户无法ssh l
- 下一篇: scala伴生类和半生对象详解