K8s节点亲和性

245 阅读4分钟

这是我参与更文挑战的第17天,活动详情查看: 更文挑战

一些概念 Affinity对应pod的pod.spec.affinity字段,下面有多种组合配置,需要先明白一些概念

Affinity和Anti-affinity 亲和性分为正向的Affinity,表示愿意被分配至目标node,和反向的Anti-affinity,表示不愿意被分配至目标node。

NodeAffinity和PodAffinity 两个不同维度的亲和性策略,NodeAffinity是根据node的标签去决定是否分配。而PodAffinity是根据pod的标签去决定要不要和别的pod分配到一个node。

软策略和硬策略 软策略字段以preferred开头,表示尽量达到,没满足也没关系,多个软策略之间会有各自权重。硬策略字段以required开头,表示必须满足。

实际操作 因为本节的概念理解起来相对容易,所以直接上手操作。不过因为字段组合较多,建议多利用kubectl explain pod.spec.affinity查看各个字段的含义。

以下所有yaml文件托管在我的Github仓库

NodeAffinity实际操作

硬策略

通过下面的yaml文件test-nodeaffinity-hard.yaml来验证下node亲和性的硬策略

apiVersion: v1
kind: Pod
metadata:
  name: test-nodeaffinity-hard
  labels:
    app: app1
spec:
  containers:
    - name: mynginx
      image: mynginx:v2
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                  - k8s-node3

这里的matchExpressions是比配node的labels,底下的三个字段如下

字段 类型 说明 key string node的标签的key operator string 判断符号,可以选In/NotIn/Exists/DoesNotExist/Gt/Lt values list 一组值,结合上面的运算符号进行判断 node的labels可以用如下方式查看

[root@k8s-master affinity]# kubectl get node --show-labels
NAME         STATUS   ROLES    AGE   VERSION    LABELS
k8s-master   Ready    master   14d   v1.15.11   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux,node-role.kubernetes.io/master=
k8s-node1    Ready    <none>   14d   v1.15.11   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux

我这里有两个node,它们的hostname都不是k8s-nodes,在这种硬策略下该pod没有node可以分配

[root@k8s-master affinity]# kubectl apply -f test-nodeaffinity-hard.yaml
pod/test-nodeaffinity-hard created
[root@k8s-master affinity]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
test-nodeaffinity-hard   0/1     Pending   0          6s    <none>   <none>   <none>           <none>

再启动,无论启动多少次都只会在master节点

[root@k8s-master affinity]# kubectl apply -f test-nodeaffinity-hard.yaml
pod/test-nodeaffinity-hard created
[root@k8s-master affinity]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
test-nodeaffinity-hard   1/1     Running   0          5s    10.244.0.19   k8s-master   <none>           <none>

我这里没有足够节点所以选择master节点,一般master节点是不会分配pod的

软策略

将上面的yaml文件稍微修改成如下的test-nodeaffinity-soft.yaml

apiVersion: v1
kind: Pod
metadata:
  name: test-nodeaffinity-soft
  labels:
    app: app1
spec:
  containers:
    - name: mynginx
      image: mynginx:v2
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 1
          preference:
            matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                  - k8s-node3

这里虽然还是希望能被调度到node3,但是没有这么一个node存在的时候,还是能被成功调度的

[root@k8s-master affinity]# kubectl apply -f test-nodeaffinity-soft.yaml
pod/test-nodeaffinity-soft created
[root@k8s-master affinity]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP             NODE         NOMINATED NODE   READINESS GATES
test-nodeaffinity-hard   1/1     Running   0          9m40s   10.244.0.19    k8s-master   <none>           <none>
test-nodeaffinity-soft   1/1     Running   0          5s      10.244.1.133   k8s-node1    <none>           <none>

因为有权重的存在,所以可以设置多条策略,按照权重高低进行选择。权重可配置1-100。

当然硬策略和软策略可以一起配置,优先考虑硬策略。

PodAffinity实际操作 pod与pod之间就开始出现Affinity和Anti-Affinity了

硬策略 目前已存在的两个pod如下,用作参照

[root@k8s-master affinity]# kubectl get pod --show-labels
NAME                     READY   STATUS    RESTARTS   AGE   LABELS
test-nodeaffinity-hard   1/1     Running   0          49m   app=app1
test-nodeaffinity-soft   1/1     Running   0          39m   app=app1

用如下的yaml文件test-podaffinity-hard.yaml去创建一个pod

apiVersion: v1
kind: Pod
metadata:
  name: test-podaffinity-hard
  labels:
    app: app2
spec:
  containers:
    - name: mynginx
      image: mynginx:v2
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: app
                operator: In
                values:
                  - app1
          topologyKey: kubernetes.io/hostname

注意,这里用的是Anti-Affinity,所以是不想和目标pod在一起。然后这里还有个topologyKey字段,表示node的一个label,表示要被创建的pod所在的node的这个label值和被比较pod所在的node这个label值要不一致才行。这个逻辑关系有点绕,就是先选出目标pod,然后根据目标pod所在node的label来决定去哪个node。

这里选出的pod是满足app: app1的pod,上面两个pod都满足,然后是查看node的hostname,上述两个pod已经把集群内的两个node都占据了,所以新的pod没有node可以被分配

[root@k8s-master affinity]# kubectl apply -f test-podaffinity-hard.yaml
pod/test-podaffinity-hard created
[root@k8s-master affinity]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
test-nodeaffinity-hard   1/1     Running   0          53m   10.244.0.19    k8s-master   <none>           <none>
test-nodeaffinity-soft   1/1     Running   0          43m   10.244.1.133   k8s-node1    <none>           <none>
test-podaffinity-hard    0/1     Pending   0          8s    <none>         <none>       <none>           <none>

修改其中一个pod的label为app:test

[root@k8s-master affinity]# kubectl edit pod test-nodeaffinity-hard
pod/test-nodeaffinity-hard edited
[root@k8s-master affinity]# kubectl get pod --show-labels -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES   LABELS
test-nodeaffinity-hard   1/1     Running   0          63m   10.244.0.19    k8s-master   <none>           <none>            app=test
test-nodeaffinity-soft   1/1     Running   0          54m   10.244.1.133   k8s-node1    <none>           <none>            app=app1
test-podaffinity-hard    1/1     Running   0          10m   10.244.0.20    k8s-master   <none>           <none>            app=app2

可以看到新的pod被创建了,而且是分配在master