1.簡介:
一般情況下我們部署的 Pod 是通過集群的自動調度策略來選擇節點的,默認情況下調度器考慮的是資源足夠,並且負載盡量平均,但是有的時候我們需要能夠更加細粒度的去控制 Pod 的調度,比如我們希望一些機器學習的應用只跑在有 GPU 的節點上;但是有的時候我們的服務之間交流比較頻繁,又希望能夠將這服務的 Pod 都調度到同一個的節點上。這就需要使用一些調度方式來控制 Pod 的調度了,主要有兩個概念:親和性和反親和性,親和性又分成節點親和性(nodeAffinity)和 Pod 親和性(podAffinity)。
2.nodeSelector:
在了解親和性之前,我們先來了解一個非常常用的調度方式:nodeSelector。我們知道 label 標簽是 kubernetes 中一個非常重要的概念,用戶可以非常靈活的利用 label 來管理集群中的資源,比如最常見的 Service 對象通過 label 去匹配 Pod 資源,而 Pod 的調度也可以根據節點的 label 來進行調度。
我們可以通過下面的命令查看我們的 node 的 label:
[root@k8s-master01 ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master01 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master01,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-master02 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master02,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-master03 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master03,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-node01 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node01,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-node02 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node02,kubernetes.io/os=linux,node.kubernetes.io/node=
現在我們先給節點k8s-node01增加一個cm=test的標簽,命令如下:
[root@k8s-master01 ~]# kubectl label nodes k8s-node01 cm=test # 創建標簽
node/k8s-node01 labeled
[root@k8s-master01 ~]# kubectl label nodes k8s-node01 cm- # 刪除標簽
node/k8s-node01 labeled
[root@k8s-master01 ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master01 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master01,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-master02 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master02,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-master03 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master03,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-node01 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,cm=test,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node01,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-node02 Ready <none> 11d v1.20.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node02,kubernetes.io/os=linux,node.kubernetes.io/node=
我們可以通過上面的 --show-labels 參數可以查看上述標簽是否生效。當節點被打上了相關標簽后,在調度的時候就可以使用這些標簽了,只需要在 Pod 的 spec 字段中添加 nodeSelector 字段,里面是我們需要被調度的節點的 label 標簽,比如,下面的 Pod 我們要強制調度到 node2 這個節點上去,我們就可以使用 nodeSelector 來表示了:(node-selector-demo.yaml)
apiVersion: v1
kind: Pod
metadata:
labels:
app: busybox-pod
name: test-busybox
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
imagePullPolicy: Always
name: test-busybox
nodeSelector:
com: test # 這里可以寫我們定義的標簽,或者hostname
我們可以通過命令查看調度結果
[root@k8s-master01 ~]# kubectl apply -f node-selector-demo.yaml
pod/test-busybox created
[root@k8s-master01 ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox 1/1 Running 261 11d 172.17.125.9 k8s-node01 <none> <none>
nginx-68db656dd8-qjhs9 1/1 Running 1 10d 172.25.244.200 k8s-master01 <none> <none>
nginx-68db656dd8-znwgp 1/1 Running 1 10d 172.18.195.11 k8s-master03 <none> <none>
test-busybox 1/1 Running 0 19s 172.27.14.215 k8s-node02 <none> <none>
web 2/2 Running 0 3d17h 172.18.195.14 k8s-master03 <none> <none>
[root@k8s-master01 ~]# kubectl describe pod test-busybox
Name: test-busybox
Namespace: default
Priority: 0
Node: k8s-node02/192.168.0.111
Start Time: Tue, 30 Mar 2021 18:37:27 +0800
Labels: app=busybox-pod
Annotations: <none>
Status: Running
IP: 172.27.14.215
IPs:
IP: 172.27.14.215
Containers:
test-busybox:
Container ID: docker://f85fa6688eb7b014b5c710354243528c6c0df555b3300f168773bc0f461581d1
Image: busybox
Image ID: docker-pullable://busybox@sha256:ce2360d5189a033012fbad1635e037be86f23b65cfd676b436d0931af390a2ac
Port: <none>
Host Port: <none>
Command:
sleep
3600
State: Running
Started: Tue, 30 Mar 2021 18:37:44 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-pn9sp (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-pn9sp:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-pn9sp
Optional: false
QoS Class: BestEffort
Node-Selectors: cm=test #
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 55s default-scheduler Successfully assigned default/test-busybox to k8s-node02
Normal Pulling 54s kubelet Pulling image "busybox"
我們可以看到 Events 下面的信息,我們的 Pod 通過默認的 default-scheduler 調度器被綁定到了 node2 節點。不過需要注意的是nodeSelector 屬於強制性的,如果我們的目標節點沒有可用的資源,我們的 Pod 就會一直處於 Pending 狀態。