KubernetesのpodAffinity/podAntiAffinityが機能しない
結論
- podAffinity/podAntiAffinityはある条件下では機能しなくなります
- こちらのissueで議論されている内容です
podAffinity/podAntiAffinityが機能しない状況
node-1
とnode-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-2
はnode-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になる
- 自分自身がpodAffinity/antiPodAffinityの条件に一致するか?
(GitHubのissueにかいてあった内容でソースコードを読み込んだわけではありませんが、kubernetesのv1.18.15で動作確認はしました)
(Topology Domainって何?って人はこちらを参考にしてください。 https://zenn.dev/nekoshita/articles/599080c3d0f13e)
今回の状況の場合
今回の状況の場合は、node-1
とnode-2
があり、pod-1
がnode-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