Help us understand the problem. What is going on with this article?

KubernetesのNode Affinity, Inter-Pod Affinityについて

More than 1 year has passed since last update.

Node Affinity

taints/tolerations(参考)は特定のNodeにPodをscheduleされるのを避けるための仕組み。
それに対してNode Affinityという仕組みは、Podを特定のNode集合へscheduleするための仕組みだ。
特定のNodeにPodをscheduleするための仕組みとしてはnode selectorがある。Node Affinitynode selectorよりももっとパワフル。

Node AffinityはNodeのlabelとして設定する。
そして(node selectorはpodにselectorを設定したが、同様に)Node Affinity ruleはpodに設定する。
podのNode Affinity ruleにnodeのlabelのNode Affinityがマッチした場合、そのnodeへpodがscheduleされる。

まずnodeのlabelに設定されているNode Affinityを試しに見てみよう。
以下はGKEのnodeに設定されているlabelだ。

$ kubectl describe node gke-kubia-default-pool-db274c5a-mjnf
Name:     gke-kubia-default-pool-db274c5a-mjnf
Role:
Labels: beta.kubernetes.io/arch=amd64
        beta.kubernetes.io/fluentd-ds-ready=true
        beta.kubernetes.io/instance-type=f1-micro
        beta.kubernetes.io/os=linux
        cloud.google.com/gke-nodepool=default-pool
        failure-domain.beta.kubernetes.io/region=europe-west1
        failure-domain.beta.kubernetes.io/zone=europe-west1-d
        kubernetes.io/hostname=gke-kubia-default-pool-db274c5a-mjnf

沢山labelが設定されているがaffiniyとして重要なのが下の3つのlabel。詳細はあとで説明するが、この3つが意味するのは以下だ。

  • failure-domain.beta.kubernetes.io/region はnodeが位置するregionを指す
  • failure-domain.beta.kubernetes.io/zone はnodeが属するavailability zoneを指す
  • kubernetes.io/hostname はnodeのhostnameを指す

podには以下のようにnodeAffinityを設定する。

apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod
spec:
  containers:
  - name: main
    image: alpine
    command: ["sleep", "9999999"]
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: failure-domain.beta.kubernetes.io/zone
            operator: In
            values:
            - "europe-west1-d"
            - "asia-northeast1-a"

matchExpressionsを使って柔軟に条件を設定することができる。
requiredDuringSchedulingIgnoredDuringExecutionは2つの意味に分解できる。

前半のrequiredDuringSchedulingはnodeをscheduleする際、このaffinity ruleに合致するnodeにしかscheduleされないことを意味する。
後半のIgnoredDuringExecutionはこのaffinity ruleはnodeで実行中のpodには影響を与えないことを意味する。

preferredDuringSchedulingIgnoredDuringExecutionというのもある。
前半のpreferredDuringSchedulingはnodeをscheduleする際、このaffinity ruleに合致するnodeに優先的にscheduleするが合致するnodeがなければ他のnodeへscheduleすることを意味する。
またIgnoredDuringExecutionとは対象的なRequiredDuringExecutionもある。これはnod上で実行中のpodに影響を与えることを意味するが、まだKubernetesで実装されていなく、今後実装される予定。

operator: Inという設定が上記でされているが、operatorにはIn以外にもNotInExistsDoesNotExistsGtLtが使用できる

またNode Affinityの設定は複数定義することができ、それぞれにweightを設定することができる。

試してみる

node-affinity-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod
spec:
  containers:
  - name: main
    image: alpine
    command: ["sleep", "9999999"]
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - gke-wdpress-default-pool-ee3b6592-6n3t
$ kubectl create -f node-affinity-pod.yaml
pod "node-affinity-pod" created

nodeAffinityで指定したノードに配置されていることが分かる

$ kubectl get pod -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP           NODE
node-affinity-pod   1/1       Running   0          3s        10.24.8.10   gke-wdpress-default-pool-ee3b6592-6n3t

ちなみにaffinity定義にマッチしない場合、podを作成してもPending状態になる。

$ kubectl get pod -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP        NODE
node-affinity-pod   0/1       Pending   0          2s        <none>    <none>

# Eventsの欄に「No nodes are available that match all of the predicates: MatchNodeSelector (5).」と出ているのが分かる。
$ kubectl describe pod node-affinity-pod | tail
    SecretName:  default-token-2hc7t
    Optional:    false
QoS Class:       Burstable
Node-Selectors:  <none>
Tolerations:     node.alpha.kubernetes.io/notReady:NoExecute for 300s
                 node.alpha.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason            Age              From               Message
  ----     ------            ----             ----               -------
  Warning  FailedScheduling  2s (x4 over 5s)  default-scheduler  No nodes are available that match all of the predicates: MatchNodeSelector (5).

Node Anti-Affinity

Nod Anti-Affinityは Nod Affinityの反対で、Podを特定のNode以外へスケジューリングすることができます。

厳密にはNod Anti-Affinityは存在せず、Node Affinityのspec.affinity.nodeAffinityの条件部分に否定形のオペレータを指定することで実現する。

Inter-Pod Affinity

Node SelectorNode Affinityはpodとnodeの影響を与えるものだった。
Inter-Pod Affinityはpod間に影響を与える仕組み。
Inter-Pod Affinityを使うと、特定のPodが実行されているドメイン(Node、ラック、ゾーン、データセンター、etc)上へPodをschedulingさせることができる。
たとえばfrontend podとbackend podがあった場合それらのpodが近いほうがlatencyが減らせるので、それぞれのpodが近いほうがいい。そういうときにInter-Pod Affinityが使える。

以下のような定義。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 5
  template:
... 
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: rack
            labelSelector:
              matchLabels:
                app: backend

topologyKey: rackだが、こうすると同じrackにあるnodeにpodがscheduleされる。
上記でいうと、app: backendというlabelをもったpodがすでにrack=aというlabelを持ったnodeにscheduleされていたら、このfrontend podはrack=aというlabelをもったnodeにscheduleされる。

つまり、例えばまず2つのnodeにそれぞれrack=a, rack=bのようにLabelをつけたとする。
すると、app=backendというlabelをもつpodが先にrack=aのnodeにscheduleされている場合は、上記と同じInter-Pod Affinity ruleをもつ他のpodはrack=aのnodeにscheduleされる。

試してみる

nodeを5つ作る

$ kubectl get node
NAME                                     STATUS    ROLES     AGE       VERSION
gke-wdpress-default-pool-ee3b6592-6n3t   Ready     <none>    59m       v1.8.8-gke.0
gke-wdpress-default-pool-ee3b6592-mj3x   Ready     <none>    59m       v1.8.8-gke.0
gke-wdpress-default-pool-ee3b6592-pbrq   Ready     <none>    59m       v1.8.8-gke.0
gke-wdpress-default-pool-ee3b6592-q033   Ready     <none>    59m       v1.8.8-gke.0
gke-wdpress-default-pool-ee3b6592-th89   Ready     <none>    59m       v1.8.8-gke.0

app: backend labelを設定したbackend-pod.yamlを作り、

backend-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: backend-pod
  labels:
    app: backend
spec:
  containers:
  - name: main
    image: alpine
    command: ["sleep", "9999999"]

backend-podを作成する。

$ kubectl create -f backend-pod.yaml
pod "backend-pod" created

$ kubectl get pod -o wide
NAME                      READY     STATUS        RESTARTS   AGE       IP           NODE
backend-pod               1/1       Running       0          4s        10.24.5.16   gke-wdpress-default-pool-ee3b6592-mj3x

次にInter-Pod Affinityを設定したpodを作成する

pod-affinity-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-affinity-pod1
spec:
  containers:
  - name: main
    image: alpine
    command: ["sleep", "9999999"]
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: "kubernetes.io/hostname"
        labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - backend        

そしてpod-affinity-pod1を作成

$ kubectl create -f  pod-affinity-pod.yaml
pod "pod-affinity-pod1" created

同じノードに配置されていることが分かる(topologyKey: "kubernetes.io/hostname"なので同じホスト(=ノード)に配置されるということ)

$ kubectl get pod -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP           NODE
backend-pod         1/1       Running   0          56s       10.24.5.16   gke-wdpress-default-pool-ee3b6592-mj3x
pod-affinity-pod1   1/1       Running   0          4s        10.24.5.17   gke-wdpress-default-pool-ee3b6592-mj3x

pod-affinity-podを増やして見ても同じノードに配置されることが分かる。

$ kubectl get pod -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP           NODE
backend-pod         1/1       Running   0          1m        10.24.5.16   gke-wdpress-default-pool-ee3b6592-mj3x
pod-affinity-pod1   1/1       Running   0          19s       10.24.5.17   gke-wdpress-default-pool-ee3b6592-mj3x
pod-affinity-pod2   1/1       Running   0          8s        10.24.5.18   gke-wdpress-default-pool-ee3b6592-mj3x
pod-affinity-pod3   1/1       Running   0          2s        10.24.5.19   gke-wdpress-default-pool-ee3b6592-mj3x

Inter-Pod Anti-Affinity

Inter-Pod Anti-AffinityはInter-Pod Affinityと反対で、特定のPodがいないドメイン(Node、ラック、ゾーン、データセンター、etc)上へPodをschedulingさせることができる。

またこのようにInter-Pod AffinityInter-Pod Anti-Affinityを合わせて指定することもできる。

参考

studyplus
学習管理アプリ「Studyplus」を開発・運営する会社です
https://info.studyplus.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away