Opensourcetechブログ

OpensourcetechによるNGINX/Kubernetes/Zabbix/Neo4j/Linuxなどオープンソース技術に関するブログです。

Node AffinityによるPod起動ノードの管理(kubernetes)


LinuCエヴァンジェリスト・Open Source Summit Japan 2022ボランティアリーダー鯨井貴博@opensourcetechです。


はじめに
今回は、kubernetesにおけるNode Affinityを使ったPod起動ノートの制御をしてみます。


Node Affinityとは
Node Affinityですが、ノードや既存Podに付与されたラベルを使用してPodを起動するノードを制御する方法です。

同じようなものとして、nodeSelectorがありますが、
それよりも柔軟・複雑な制御が可能です。
※必須条件・オプション条件の指定や複数条件(OR/AND)の指定など
https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/

サンプルを示すと、以下のようになります。

  • requiredDuringSchedulingIgnoredDuringExecution:必須の条件(スケジューリングポリシー)
  • preferredDuringSchedulingIgnoredDuringExecution:オプションで考慮される条件
  • key:キーとなるラベルの指定
  • operator:ラベルの値の判断(In:一致、NotIn:不一致、Exists:ラベルが存在、DoesNotExist:ラベルが存在しない、Gt:ラベルの数値が指定値より大きい、Lt:ラベルの数値が指定値より小さい)
  • value:ラベルの値
  • weight:条件の重みづけ(1~100で付与でき、大きいものが優先)
kubeuser@master01:~/nodeaffinity$ cat testpod2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: testpod2
  labels:
    run: testpod2
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: cpu
            operator: In
            values:
            - low
  containers:
  - name: testpod2
    image: nginx



Node Affinityの種類
実行する内容によって、以下のような呼び方をします。
※前述の条件の使い方次第

  • Node Affinity:特定のノード上だけでPodを実行させる
  • Node Anti-Affinity:特定のノード以外でPodを実行させる
  • Inter-Pod Affinity:特定のPodが起動しているノードなどでPodを実行させる
  • Inter-Pod Anti-Affinity:特定のPodが起動しているノードなど以外でPodを実行させる

    また、上記より簡易な制御として、以下のようなものもあります。
    nodeSelectorによるPod起動ノードの制御(kubernetes)
    TaintとTolerationsによるPodの配置管理(kubernetes)


    Node Affinityの実施
    まず、Node Affinity(特定のノード上だけでPodを実行させる)をやってみます。

    ノードのラベル付与状況は以下のようになります。
kubeuser@master01:~/nodeaffinity$ kubectl get nodes --show-labels
NAME       STATUS   ROLES           AGE    VERSION   LABELS
master01   Ready    control-plane   153d   v1.27.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master01,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
worker01   Ready    <none>          153d   v1.27.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,cpu=high,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker01,kubernetes.io/os=linux
worker02   Ready    <none>          153d   v1.27.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,cpu=low,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker02,kubernetes.io/os=linux


具体的には、Podのyamlでkubernetes.io/hostname=worker01というラベルがあるノード上で起動してって書いてます。

kubeuser@master01:~/nodeaffinity$ cat testpod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: testpod
  labels:
    run: testpod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - worker01
  containers:
  - name: testpod
    image: nginx


想定通り、Pod起動してますね!

kubeuser@master01:~/nodeaffinity$ kubectl apply -f testpod.yaml 
pod/testpod created

kubeuser@master01:~/nodeaffinity$ kubectl get pods testpod -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP          NODE       NOMINATED NODE   READINESS GATES
testpod   1/1     Running   0          9s    10.0.5.61   worker01   <none>           <none>



Node Affinity(複数条件)の実施
続いて、Node Affinity(特定のノード上だけでPodを実行させる)で必須条件・オプション条件を使った制御をやってみます。

具体的には、必須条件(ラベル disktype=ssdがある)・オプション条件(かつできればラベル cpu=lowがある)というものになります。
※想定だと、worker02上でPodが起動するはず。

kubeuser@master01:~/nodeaffinity$ vi testpod2.yaml 

kubeuser@master01:~/nodeaffinity$ cat testpod2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: testpod2
  labels:
    run: testpod2
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: cpu
            operator: In
            values:
            - low
  containers:
  - name: testpod2
    image: nginx


こちらも想定通り!

kubeuser@master01:~/nodeaffinity$ kubectl apply -f testpod2.yaml 
pod/testpod2 created

kubeuser@master01:~/nodeaffinity$ kubectl get pods testpod2 -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
testpod2   1/1     Running   0          11s   10.0.30.80   worker02   <none>           <none>



Node Anti-Affinity(特定のノード以外でPodを実行させる)の実施
続いて、Node Anti-Affinity(特定のノード以外でPodを実行させる)で必須条件・オプション条件を使った制御をやってみます。

具体的には、必須条件(ラベル disktype=ssdがある)・オプション条件(かつできればラベル cpu=lowがない)というものになります。 ※想定だと、worker01上でPodが起動するはず。

kubeuser@master01:~/nodeaffinity$ vi testpod3.yaml 

kubeuser@master01:~/nodeaffinity$ cat testpod3.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: testpod3
  labels:
    run: testpod3
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: cpu
            operator: NotIn
            values:
            - low
  containers:
  - name: testpod3
    image: nginx


こちらも想定通り!

kubeuser@master01:~/nodeaffinity$ kubectl apply -f testpod3.yaml 
pod/testpod3 created

kubeuser@master01:~/nodeaffinity$ kubectl get pod testpod3 -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP          NODE       NOMINATED NODE   READINESS GATES
testpod3   1/1     Running   0          9s    10.0.5.62   worker01   <none>           <none>



Inter-Pod Affinity(特定のPodが起動しているノードなどでPodを実行させる)の実施
続いて、Inter-Pod Affinity(特定のPodが起動しているノードなどでPodを実行させる)をやってみます。

具体的には、必須条件(ラベルpod-template-hash=668b69fd79を持つPodと同じノードで起動)というものになります。 他と少し違うのは、topologyKey: kubernetes.io/hostnameというパラメータを持つノード(正式にはドメインと呼ばれるらしい)という指定が必要です。
※想定だと、worker02上でPodが起動するはず。

kubeuser@master01:~/nodeaffinity$ kubectl get pod -o wide --show-labels
NAME                         READY   STATUS    RESTARTS      AGE     IP            NODE       NOMINATED NODE   READINESS GATES   LABELS
hpa-nginx-668b69fd79-k7d2q   1/1     Running   1 (92d ago)   131d    10.0.30.66    worker02   <none>           <none>            pod-template-hash=668b69fd79,run=hpa-nginx
nginx-6cbc9bb4f5-jm8mr       1/1     Running   0             144d    10.0.5.32     worker01   <none>           <none>            app=nginx,pod-template-hash=6cbc9bb4f5
nginx-6cbc9bb4f5-pfhfz       1/1     Running   0             144d    10.0.30.106   worker02   <none>           <none>            app=nginx,pod-template-hash=6cbc9bb4f5
nginx2-f96cfc57b-vnsfm       1/1     Running   0             144d    10.0.30.107   worker02   <none>           <none>            app=nginx,pod-template-hash=f96cfc57b
test-webserver               1/1     Running   2 (92d ago)   128d    10.0.5.47     worker01   <none>           <none>            run=test-webserver
testpod                      1/1     Running   0             10m     10.0.5.61     worker01   <none>           <none>            run=testpod
testpod2                     1/1     Running   0             9m8s    10.0.30.80    worker02   <none>           <none>            run=testpod2
testpod3                     1/1     Running   0             6m55s   10.0.5.62     worker01   <none>           <none>            run=testpod3
webserver-master01           1/1     Running   0             94d     10.0.241.69   master01   <none>           <none>            run=webserver

kubeuser@master01:~/nodeaffinity$ vi testpod4.yaml 

kubeuser@master01:~/nodeaffinity$ cat testpod4.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: testpod4
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: pod-template-hash
            operator: In
            values:
            - 668b69fd79
        topologyKey: kubernetes.io/hostname
  containers:
  - name: testpod4
    image: nginx


そして、起動。
想定通り!!

kubeuser@master01:~/nodeaffinity$ kubectl apply -f testpod4.yaml 
pod/testpod4 created

kubeuser@master01:~/nodeaffinity$ kubectl get pods testpod4 -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
testpod4   1/1     Running   0          15s   10.0.30.82   worker02   <none>           <none>



Inter-Pod Anti-Affinity(特定のPodが起動しているノードなどでPodを実行させる)の実施
最後は、Inter-Pod Affinity(特定のPodが起動しているノードなど以外でPodを実行させる)をやってみます。

基本的な構文はInter-Pod Affinityと似ていますが、
podAffinityという部分を podAntiAffinityに変更しています。 ※想定だと、worker01上でPodが起動するはず。

kubeuser@master01:~/nodeaffinity$ vi testpod5.yaml 

kubeuser@master01:~/nodeaffinity$ cat testpod5.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: testpod5
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: pod-template-hash
            operator: In
            values:
            - 668b69fd79
        topologyKey: kubernetes.io/hostname
  containers:
  - name: testpod5
    image: nginx


こちらも想定通り!

kubeuser@master01:~/nodeaffinity$ kubectl apply -f testpod5.yaml 
pod/testpod5 created

kubeuser@master01:~/nodeaffinity$ kubectl get pods testpod5 -o wide 
NAME       READY   STATUS    RESTARTS   AGE   IP          NODE       NOMINATED NODE   READINESS GATES
testpod5   1/1     Running   0          29s   10.0.5.63   worker01   <none>           <none>



おわりに
やってみる前には、同じような呼び方で区別しにくいなという印象がありました。
しかし、実際にやってみることで理解を深めることが出来ました。

併せて気が付いたことは、機能の呼び方以上に
・管理するクラスター上のノードでどのようなラベル管理をするか
・上記を使ってどのような制御をするか
という点(要件)をしっかりと管理することが大切であるということ。

当たり前ですが、
kubernetesでなどのような機能があるか知る
 ⇒ 起動Pod(リソース)の特性を考慮してどのノードで起動するか検討する
  ⇒実装(yaml)の作成
と進めていくことが大事ですね!

Opensourcetech by Takahiro Kujirai