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