🏗️

KubernetesのpodAffinity/podAntiAffinityが機能しない

2021/03/27に公開

結論

podAffinity/podAntiAffinityが機能しない状況

node-1node-2というふたつのNodeがあるとします。
まずは、pod-1というPodをnode-1に作成します。

apiVersion: v1
kind: Pod
metadata:
  name: pod-1
  labels:
    app: pod-1
spec:
  containers:
    - name: pod-1
      image: gcr.io/google-samples/hello-app:1.0

nodeNameで配置すべきNodeを指定しているので、このPodはnode-1に配置されます。
次に、pod-2を作成します。
podAffinityを使い、pod-1が配置さていないNodeであるnode-2に配置することを試みます。

apiVersion: v1
kind: Pod
metadata:
  name: pod-2
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: app
                operator: DoesNotExist
          topologyKey: kubernetes.io/hostname
  containers:
    - name: pod-2
      image: gcr.io/google-samples/hello-app:1.0

topologyKeyはkubernetes.io/hostnameなので、それぞれのNodeがTopologyDomainとなります。
labelSelectorは、appというキーのLabelが存在しないTopologyDomainを指定しているように見えます。
node-1には、app=pod-1というLabelを持つPodが起動しています。
なので、pod-2node-2に必ず起動するかのように見えます。

しかし、これは間違いです。このままでは意図通りに機能しません。

なぜ機能しないのか

podAffinity/antiPodAffinityの実際の挙動

podAffinity/antiPodAffinityが動作する時のスケジューラーの流れは以下のようになるようです

  • podAffinity/antiPodAffinityの条件に一致するPodが存在するかをTopology Domainごとに探す
  • 1つでも一致するPodが存在する場合
    • podAffinityであれば、一致するPodが存在するTopology DomainにPodを配置する
    • podAntiAffinityであれば、一致するPodが存在しないTopology DomainにPodを配置する
  • 1つも一致するPodが存在しない場合
    • 自分自身がpodAffinity/antiPodAffinityの条件に一致するか?
      • する -> affinityは無視して配置される
      • しない -> pendingになる

(GitHubのissueにかいてあった内容でソースコードを読み込んだわけではありませんが、kubernetesのv1.18.15で動作確認はしました)
(Topology Domainって何?って人はこちらを参考にしてください。 https://zenn.dev/nekoshita/articles/599080c3d0f13e)

今回の状況の場合

今回の状況の場合は、node-1node-2があり、pod-1node-1に配置されています。
pod-1にはapp=pod-1というLabelが付与されています。
pod-2にはpodAffinityが付与されており、その条件は

  • appというキーのLabelが存在しない
    というものです。

なので、pod-2をスケジュールするときに、

  • Topology Domainごとに、appというキーのLabelを持たないPodを探す
  • 条件に満たすPodはひとつもない(唯一存在するPodはappというキーのLabelを持っているので)
  • 自分自身もpodAffinity/antiPodAffinityの条件に当てはまらない
  • affinityは無視して配置する

という流れになります。
なので、pod-1と違うNodeに配置される場合もあれば、同じNodeに配置される場合もあります。

どうすればよいのか?

podAffinityとpodAntiAffinityをうまく使い分けます。
今回の場合であれば、podAntiAffinityを使い、以下のようにすればよいです

apiVersion: v1
kind: Pod
metadata:
  name: pod-2
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: app
                operator: Exists
          topologyKey: kubernetes.io/hostname
  containers:
    - name: pod-2
      image: gcr.io/google-samples/hello-app:1.0

すると、スケジューラーの処理の流れとしては

  • Topology Domainごとに、appというキーのLabelを持たないPodを探す
  • 条件に満たすPodをnode-1が所属するTopology Domain内に対象のPodを発見する
  • node-1が所属するTopology Domain以外のTopology Domainにpod-2を配置する
  • よって、pod-2は必ずnode-2の配置されます

最後に

僕の直感的な理解と挙動が異なっていてたので戸惑いました。
podAffinityにpodAntiAffinityが存在する理由はこのためだったことを理解しました。

Discussion