0
0

More than 3 years have passed since last update.

[Kubernetes]Podデプロイ時のスケジューリング制御方法を確認する。

Last updated at Posted at 2020-06-11

はじめに

今回はPodデプロイ時のスケジューリングの動作について確認したいと思います。Podをどのノードにデプロイするかを制御するためには、以下の方法があります。それぞれの設定方法と動作について確認していきます。

  • nodeSelector
  • Node Affinity
  • node Anti-Affinity
  • Inter-Pod Affinity
  • Inter-Pod Anti-Affinity

nodeSelector

nodeSelectorでデプロイするノードを制御するには、ノードに付与されているラベルを利用します。

ラベルの確認と設定

まずは各ノードにデフォルトで設定されているラベルを確認します。

$ kubectl get nodes --show-labels
NAME           STATUS   ROLES    AGE    VERSION   LABELS
k8s-master     Ready    master   104d   v1.17.3   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-worker01   Ready    <none>   104d   v1.17.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-worker01,kubernetes.io/os=linux
k8s-worker02   Ready    <none>   104d   v1.17.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-worker02,kubernetes.io/os=linux

上記コマンドの他に、kubectl describeコマンドなどでも確認できます。

各Nodeにラベルを設定します。ここでは、「disktype」ラベルを設定して、k8s-worker01は「ssd」、k8s-worker02は「hdd」とします。
実際はどちらも同じディスクですけどね。検証ですので。

$ kubectl label node k8s-worker01 disktype=ssd
node/k8s-worker01 labeled
$ kubectl label node k8s-worker02 disktype=hdd
node/k8s-worker02 labeled

設定したラベルを確認します。

$ kubectl get nodes --show-labels
NAME           STATUS   ROLES    AGE    VERSION   LABELS
k8s-master     Ready    master   104d   v1.17.3   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-worker01   Ready    <none>   104d   v1.17.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-worker01,kubernetes.io/os=linux
k8s-worker02   Ready    <none>   104d   v1.17.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=hdd,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-worker02,kubernetes.io/os=linux

これだとちょっと見づらいので、disktypeラベルのみ確認します。

$ kubectl get node -L disktype
NAME           STATUS   ROLES    AGE    VERSION   DISKTYPE
k8s-master     Ready    master   104d   v1.17.3
k8s-worker01   Ready    <none>   104d   v1.17.3   ssd
k8s-worker02   Ready    <none>   104d   v1.17.3   hdd

nodeSelecterの動作確認

該当のPodのマニフェストに、nodeSelectorのセクションを追加し、「disktype: ssd」のNodeにデプロイするようにします。今回はDeploymentにしています。

nginx-dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-dep
spec:
  replicas: 4
  selector:
    matchLabels:
      app: app1
  template:
    metadata:
      labels:
        app: app1
    spec:
      containers:
        - name: nginx
          image: nginx:latest
      nodeSelector:
        disktype: ssd

applyして、PodがデプロイされているNodeを確認します。

$ kubectl apply -f nginx-dep.yaml
deployment.apps/nginx-dep created
$ kubectl get pod -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES
nginx-dep-8f97c98cd-b4z88   1/1     Running   0          21s   192.168.79.118   k8s-worker01   <none>           <none>
nginx-dep-8f97c98cd-bfg46   1/1     Running   0          21s   192.168.79.110   k8s-worker01   <none>           <none>
nginx-dep-8f97c98cd-jm22p   1/1     Running   0          21s   192.168.79.126   k8s-worker01   <none>           <none>
nginx-dep-8f97c98cd-vk8nj   1/1     Running   0          21s   192.168.79.117   k8s-worker01   <none>           <none>

4つのPodがすべてssdのNode(=k8s-worker01)にデプロイされていますね。

Node Affinity

Node AffinityもラベルでデプロイするNodeを制御しますが、nodeSelectorよりも細かな設定ができます。

設定

spec.affinityフィールドに設定を記載します。必須のスケジューリングポリシー(requiredDuringSchedulingIgnoredDuringExecution)と優先的に考慮されるスケジューリングポリシー(preferredDuringSchedulingIgnoredDuringExecution)があり、必須のポリシーを満たしたNodeのうち、優先的なポリシーに基づいてデプロイされるNodeが決定します。

nodeAffinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: #必須のスケジューリングポリシー
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype #ラベル名
            operator: In #In、NotIn、Exists、DoesNotExist、Gt、Ltを指定可能
            values: #複数設定可能
            - hdd
      preferredDuringSchedulingIgnoredDuringExecution: #優先的に考慮されるスケジューリングポリシー
      - weight: 1 #1~100で指定。条件を満たすノードのWeight値を合計し、一番スコアが高いノードが優先される
        preference:
          matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - k8s-worker01
  containers:
  - name: nginx-na
    image: nginx:latest

今回は以下の図のように必須と優先的なスケジューリングポリシーを別ノードにするようにしています。

image.png

operatorフィールドについて

以下の6つを指定できます。

operator 概要
In keyで指定したラベルが指定したvaluesのいづれか一つ以上に一致する。
NotIn keyで指定したラベルが指定したvaluesのいづれにも一致しない。
Exists keyで指定したラベルが存在する。valuesは指定しません。
DoesNotExist keyで指定したラベルが存在しない。valuesは指定しません。
Gt keyで指定したラベルの値(values)よりも大きい。valuesには整数値を指定します。
Lt keyで指定したラベルの値(values)よりも小さい。valuesには整数値を指定します。

Node Affinityの動作確認

applyして動作を確認します。

$ kubectl apply -f nodeAffinity.yaml
pod/with-node-affinity created
$ kubectl get pod -o wide
NAME                 READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES
with-node-affinity   1/1     Running   0          11s   192.168.69.210   k8s-worker02   <none>           <none>

想定通りk8s-worker02にデプロイされていますね。
requiredDuringSchedulingIgnoredDuringExecutionは満たさないといけませんが、preferredDuringSchedulingIgnoredDuringExecutionは必ずしも満たす必要はありません。

なお、requiredDuringSchedulingIgnoredDuringExecutionを満たすNodeがない場合、preferredDuringSchedulingIgnoredDuringExecutionを満たすNodeがあってもスケジューリングされません。

Podを削除して、k8s-worker02をスケジューリング対象から外してから、再度applyしてみます。

$ kubectl get node
NAME           STATUS                     ROLES    AGE    VERSION
k8s-master     Ready                      master   105d   v1.17.3
k8s-worker01   Ready                      <none>   105d   v1.17.3
k8s-worker02   Ready,SchedulingDisabled   <none>   105d   v1.17.3
$ kubectl apply -f nodeAffinity.yaml
pod/with-node-affinity created
$ kubectl get pod
NAME                 READY   STATUS    RESTARTS   AGE
with-node-affinity   0/1     Pending   0          5s
$ kubectl describe pod with-node-affinity
Name:         with-node-affinity
・・・
Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  66s (x2 over 66s)  default-scheduler  0/3 nodes are available: 1 node(s) were unschedulable, 2 node(s) didn't match node selector.

スケジューリングされず、Pendingで止まってますね。

Node Anti-Affinity

Node Anti-Affinityは、Node Affinityの条件(operator)を否定形にします。
以下の例では、「disktypeがhdd"ではない"Node」(=k8s-worker01)にデプロイされます。

nodeAntiAffinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: node-anti-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: NotIn
            values:
            - hdd
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - k8s-worker01
  containers:
  - name: nginx-na
    image: nginx:latest
$ kubectl apply -f nodeAntiAffinity.yaml
pod/node-anti-affinity created
$ kubectl get pod -o wide
NAME                 READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES
node-anti-affinity   1/1     Running   0          31s   192.168.79.116   k8s-worker01   <none>           <none>

Inter-Pod Affinity

Inter-Pod Affinityは既にデプロイされているPodのラベルをキーにして、新規にデプロイされるPodのスケジューリングを決定します。
例えば、あるサービスを構成するPodは同じノードにデプロイすることで、レイテンシーを極力小さくするなどの利用方法が考えられますね。

設定

ここでは、既に以下のPodがデプロイされています。

$ kubectl get pod -o wide -L env
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES   ENV
redis-prd                    1/1     Running   0          20s   192.168.79.111   k8s-worker01   <none>           <none>            prd
redis-stg                    1/1     Running   0          20s   192.168.69.225   k8s-worker02   <none>           <none>            stg

Production環境に新たにnginxをデプロイします。このとき、既にデプロイされているredisと同じノードにデプロイさせたいと思います。

マニフェストはNode Affinityと似ていますが、topologyKeyが追加となっています。ここではhostnameを指定していますので、prdラベルを持つPodと同じhostnameのNodeにデプロイされます。

interPodAffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-prd
spec:
  selector:
    matchLabels:
      env: prd
  replicas: 3
  template:
    metadata:
      labels:
        env: prd
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: env
                operator: In
                values:
                - prd
            topologyKey: "kubernetes.io/hostname" #スケジューリング対象の範囲を指定
      containers:
      - name: nginx-prd
        image: nginx:latest

Inter-Pod Affinityの動作確認

このマニフェストをapplyします。

$ kubectl apply -f interPodAffinity.yaml
deployment.apps/nginx-prd created
$ kubectl get pod -o wide -L env
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES   ENV
nginx-prd-7d498d98ff-djzvz   1/1     Running   0          16s   192.168.79.120   k8s-worker01   <none>           <none>            prd
nginx-prd-7d498d98ff-gtbzj   1/1     Running   0          16s   192.168.79.122   k8s-worker01   <none>           <none>            prd
nginx-prd-7d498d98ff-vtmk4   1/1     Running   0          16s   192.168.79.127   k8s-worker01   <none>           <none>            prd
redis-prd                    1/1     Running   0          10m   192.168.79.111   k8s-worker01   <none>           <none>            prd
redis-stg                    1/1     Running   0          10m   192.168.69.225   k8s-worker02   <none>           <none>            stg

3つのPod全てがredis-prdと同じNodeにデプロイされていますね。

なお、今回はrequiredDuringSchedulingIgnoredDuringExecutionのみを指定しましたが、Node Affinityと同様にpreferredDuringSchedulingIgnoredDuringExecutionも指定することができます。

Inter-Pod Anti-affinity

Inter-Pod Anti-affinityは、特定のPodがデプロイされていないノード上に新たなPodをスケジューリングするポリシーです。

設定

Node Anti-Affinityとは異なり、operatorを否定形にするのではなく、spec.affinityフィールドにpodAntiAffinityをマニフェストで指定できます。
以下の例は、inter-Pod Affinityのマニフェストの「podAffinity」を「podAntiAffinity」に変更しています。

interPodAntiAffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-prd
spec:
  selector:
    matchLabels:
      env: prd
  replicas: 3
  template:
    metadata:
      labels:
        env: prd
    spec:
      affinity:
        podAntiAffinity: #podAffinityから変更
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: env
                operator: In
                values:
                - prd
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: nginx-prd
        image: nginx:latest

Inter-Pod Anti-affinityの動作確認

このマニフェストをapplyして動作を確認します。初期状態として、redisのみがデプロイされています。

$ kubectl get pod -o wide -L env
NAME        READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES   ENV
redis-prd   1/1     Running   1          22h   192.168.79.121   k8s-worker01   <none>           <none>            prd
redis-stg   1/1     Running   1          22h   192.168.69.229   k8s-worker02   <none>           <none>            stg
$ kubectl apply -f interPodAntiAffinity.yaml
deployment.apps/nginx-prd created
$ kubectl get pod -o wide -L env
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES   ENV
nginx-prd-7df77bb75f-69gcw   0/1     Pending   0          54s   <none>           <none>         <none>           <none>            prd
nginx-prd-7df77bb75f-7gvq6   0/1     Pending   0          54s   <none>           <none>         <none>           <none>            prd
nginx-prd-7df77bb75f-xqj5v   1/1     Running   0          54s   192.168.69.221   k8s-worker02   <none>           <none>            prd
redis-prd                    1/1     Running   1          22h   192.168.79.121   k8s-worker01   <none>           <none>            prd
redis-stg                    1/1     Running   1          22h   192.168.69.229   k8s-worker02   <none>           <none>            stg

replica数3のうち、1つだけPodがRunningになって、残りの2つはPendingで止まってますね。
詳細を確認します。

$ kubectl describe replicasets.apps nginx-prd-7df77bb75f
Name:           nginx-prd-7df77bb75f
・・・
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  84s   replicaset-controller  Created pod: nginx-prd-7df77bb75f-xqj5v
  Normal  SuccessfulCreate  83s   replicaset-controller  Created pod: nginx-prd-7df77bb75f-69gcw
  Normal  SuccessfulCreate  83s   replicaset-controller  Created pod: nginx-prd-7df77bb75f-7gvq6
$ kubectl describe pod nginx-prd-7df77bb75f-69gcw
Name:           nginx-prd-7df77bb75f-69gcw
・・・
Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  35s (x2 over 101s)  default-scheduler  0/3 nodes are available: 1 node(s) had taints that the pod didn't tolerate, 2 node(s) didn't match pod affinity/anti-affinity.

ReplicaSetまでは正常ですが、PodのデプロイでFailedSchedulingになっています。
あまり考えずに、「podAffinity」を「podAntiAffinity」に変更しましたが、よくよく考えると「prdラベルのPodがないノードにprdラベルのPodをデプロイする」という設定なので、1つ目のnginx-prdがデプロイされた時点で、全てのノード(2ノード)にprdラベルのPodが存在することになりますね。なので、2つ目、3つ目のPodがPendingで止まるのは正常な動作です。

まとめ

今回は5つのスケジューリングの制御方法について確認しました。環境や要件に応じて、色々な制御ができそうですね。
また、Node Affinity/Node Anti-Affinity/Inter-Pod Affinity/Inter-Pod Anti-Affinityの4つは組み合わせて利用することもできますので、さらに細かく制御することができます。
まあ、細かくすればするほど設計が大変になりそうではありますが。。。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0