一般情况下我们部署的 Pod 是通过集群的自动调度策略来选择节点的,默认情况下调度器考虑的是资源足够,并且负载尽量平均,但是有的时候我们需要能够更加细粒度的去控制 Pod 的调度,比如我们内部的一些服务 gitlab 之类的也是跑在Kubernetes集群上的,我们就不希望对外的一些服务和内部的服务跑在同一个节点上了,害怕内部服务对外部的服务产生影响;但是有的时候我们的服务之间交流比较频繁,又希望能够将这两个服务的 Pod 调度到同一个的节点上。这就需要用到 Kubernetes 里面的一个概念:亲和性和反亲和性。
亲和性有分成节点亲和性(nodeAffinity)和 Pod 亲和性(podAffinity)。
nodeSelector
在了解亲和性之前,我们先来了解一个非常常用的调度方式:nodeSelector。我们知道label是kubernetes中一个非常重要的概念,用户可以非常灵活的利用 label 来管理集群中的资源,比如最常见的一个就是 service 通过匹配 label 去匹配 Pod 资源,而 Pod 的调度也可以根据节点的 label 来进行调度。
我们可以通过下面的命令查看我们的 node 的 label:
1 2 3 4 5
$ 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
$ kubectl create -f node-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 300s node.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 node02 Normal Pulling 54s kubelet, node02 pulling image "busybox" Normal Pulled 40s kubelet, node02 Successfully pulled image "busybox" Normal Created 40s kubelet, node02 Created container Normal Started 40s kubelet, node02 Started container
我们可以看到 Events 下面的信息,我们的 Pod 通过默认的 default-scheduler 调度器被绑定到了node02节点。不过需要注意的是nodeSelector属于强制性的,如果我们的目标节点没有可用的资源,我们的 Pod 就会一直处于 Pending 状态,这就是nodeSelector的用法。
pod 亲和性主要解决 pod 可以和哪些 pod 部署在同一个拓扑域中的问题(其中拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的 cluster、zone 等等),而 pod 反亲和性主要是解决 pod 不能和哪些 pod 部署在同一个拓扑域中的问题,它们都是处理的 pod 与 pod 之间的关系,比如一个 pod 在一个节点上了,那么我这个也得在这个节点,或者你这个 pod 在节点上了,那么我就不想和你待在同一个节点上。
由于我们这里只有一个集群,并没有区域或者机房的概念,所以我们这里直接使用主机名来作为拓扑域,把 pod 创建在同一个主机上面。
1 2 3 4 5
$ 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=youdianzhishi,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
同样,还是针对上面的资源对象,我们来测试下 pod 的亲和性:(pod-affinity-demo.yaml)
我们这个地方使用的是kubernetes.io/hostname这个拓扑域,意思就是我们当前调度的 pod 要和目标的 pod 处于同一个主机上面,因为要处于同一个拓扑域下面,为了说明这个问题,我们把拓扑域改成beta.kubernetes.io/os,同样的我们当前调度的 pod 要和目标的 pod 处于同一个拓扑域中,目标的 pod 是不是拥有beta.kubernetes.io/os=linux的标签,而我们这里3个节点都有这样的标签,这也就意味着我们3个节点都在同一个拓扑域中,所以我们这里的 pod 可能会被调度到任何一个节点:
1 2 3 4 5
$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE affinity-7d86749984-glkhz 1/1 Running 0 3m 10.244.2.16 node03 affinity-7d86749984-h4fb9 1/1 Running 0 3m 10.244.4.219 node02 affinity-7d86749984-tj7k2 1/1 Running 0 3m 10.244.2.14 node03
podAntiAffinity
这就是 pod 亲和性的用法,而 pod 反亲和性则是反着来的,比如一个节点上运行了某个 pod,那么我们的 pod 则希望被调度到其他节点上去,同样我们把上面的 podAffinity 直接改成 podAntiAffinity,(pod-antiaffinity-demo.yaml)