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 状态。