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)の作成
と進めていくことが大事ですね!