K8s的亲和性调度

一般情况下我们部署的 Pod 是通过集群的自动调度策略来选择节点的,默认情况下调度器考虑的是资源足够,并且负载尽量平均,但是有的时候我们需要能够更加细粒度的去控制 Pod 的调度,比如我们内部的一些服务gitlab、jenkins 之类的也是跑在Kubernetes 集群上的,我们就不希望对外的一些服务和内部的服务跑在同一个节点上了,害怕内部服务对外部的服务产生影响;但是有的时候我们的服务之间交流比较频繁,又希望能够将这两个服务的 Pod 调度到同一个的节点上。这就需要用到 Kubernetes 里面的一个概念:亲和性和反亲和性。

亲和性又分成节点亲和性( nodeAffinity )和 Pod 亲和性( podAffinity )。

nodeSelector

非常常用的调度方式:nodeSelector。我们知道label 是kubernetes 中一个非常重要的概念,用户可以非常灵活的利用 label 来管理集群中的资源,比如最常见的一个就是 service 通过匹配 label 去匹配 Pod 资源,而 Pod 的调度也可以根据节点的 label 来进行调度。可以通过下面的命令查看我们的 node 的 label:

kubectl get nodes --show-labels

#给节点增加标签,命令如下:
kubectl label nodes k8s-node1 com=jjy1

#给节点删除标签
kubectl label nodes k8s-node1 com-
vim 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: jjy1

#查看调度节点
kubectl get po -o wide

如果目标节点没有可用的资源, Pod 就会一直处于 Pending 状态,这就是nodeSelector 的用法

亲和性和反亲和性调度

在 kubernetes 默认调度器的一个调度流程中,经过了预选(predicates) 和 优选(priorities) 两个阶段,但是在实际的生产环境中,往往需要根据自己的一些实际需求来控制 pod 的调度,这就需要用到 nodeAffinity (节点亲和性)podAffinity(pod 亲和性) 以及podAntiAffinity(pod 反亲和性)

亲和性调度可以分成软策略和硬策略两种方式:

软策略就是如果你没有满足调度要求的节点的话,pod 就会忽略这条规则,继续完成调度过程,说白了就是满足条件最好了,没有的话也无所谓了的策略

硬策略 就比较强硬了,如果没有满足条件的节点的话,就不断重试直到满足条件为止,不满足就是pending状态

对于亲和性和反亲和性都有这两种规则可以设置:

软策略 :preferredDuringSchedulingIgnoredDuringExecution

硬策略: requiredDuringSchedulingIgnoredDuringExecution

apiVersion: apps/v1
kind: Deployment
metadata:
  name: affinity
  labels:
    app: affinity
spec:
  replicas: 2
  revisionHistoryLimit: 15    #版本记录15个
  selector:
    matchLabels:
      app: affinity
      role: test
  template:
    metadata:
      labels:
        app: affinity
        role: test
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: nginxweb
      affinity:
        nodeAffinity:           #这是一个pod亲和
          requiredDuringSchedulingIgnoredDuringExecution:     #硬策略
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: NotIn    #不能在node1
                values:
                - k8s-node1
          preferredDuringSchedulingIgnoredDuringExecution:    #软策略
            - weight: 1    #权重为1
              preference:
                matchExpressions:
                - key: com
                  operator: In      #最好在node标签为jjy1上
                  values:
                  - jjy1

污点(taints)与容忍(tolerations

对于nodeAffinity 无论是硬策略还是软策略方式,都是调度 pod 到预期节点上,而Taints 恰好与之相反,如果一个节点标记为 Taints ,除非Pod 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度pod。

比如用户希望把 Master 节点保留给 Kubernetes 系统组件使用,或者把一组具有特殊资源预留给某些 pod,则污点就很有用了,pod 不会再被调度到 taint 标记过的节点。我们使用kubeadm搭建的集群默认就给master 节点添加了一个污点标记,所以我们看到我们平时的 pod 都没有被调度到 master 上去:

kubectl describe node k8s-master | grep Taints
Taints:             node-role.kubernetes.io/master:NoSchedule

#添加污点
kubectl taint node k8s-node1 node-role.kubernetes.io/master:NoSchedule     #对旧的pod是没有影响的,只对新的pod有影响不会被调度

NoSchedule :表示 pod 不会被调度到标记为 taints 的节点

PreferNoSchedule :NoSchedule 的软策略版本,表示尽量不调度到污点节点上去

NoExecute :该选项意味着一旦 Taint 生效,如该节点内正在运行的pod 没有对应 Tolerate 设置,会直接被逐出 (可能会对静态pod有影响,未实践)

kubectl taint nodes k8s-node02 test=node02:NoSchedule2 node "k8s-node02" tainted
#上面的命名将 node02 节点标记为了污点,影响策略是 NoSchedule,只会影响新的 pod 调度,如果仍然希望某个 pod 调度到 taint 节点上,则必须在 Spec 中做出Toleration 定义,才能调度到该节点,比如现在我们想要将一个 pod 调度到 node02 节点:

vim taint-demo.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: taint
  labels:
    app: taint
spec:
  replicas: 3
  revisionHistoryLimit: 10
  selector:
    matchLabels:
......
......
    spec:
      containers:
      - name: nginx
        image:
.....
      tolerations:     #宽容
      - key: "test"
        operator: "Exists"
        effect: "NoSchedule"