k8s での Pod のスケジューリングについて確認した時のメモ(Inter-Pod Affinity/Anti-Affinity)

メモ。
以下の続きです。

[k8s での Pod のスケジューリングについて確認した時のメモ(nodeSelector まで)](https://qiita.com/toshihirock/items/0f38aea8d069d434d81d)
[k8s での Pod のスケジューリングについて確認した時のメモ(NodeAffinity)](https://qiita.com/toshihirock/items/e4105e2a78ca5067b1fe)

# まとめ

+ Inter-Pod Affinity/Anti-Affinity を使うことですでにノードで稼働している Pod のラベルに従って Pod がスケジュールされるノードを制限
+ Inter-Pod Affinity では **スケジュールの条件を指定** する(例: app=sample-app というラベルを持つ Pod と同じ AZ に Pod をスケジューリングしたい)
+ Inter-Pod Anti-Affinity では **スケジュールして欲しくない条件を指定** する(例: app=sample-app というラベルを持つ Pod と同じ AZ 以外に Pod をスケジューリングしたい)
+ 条件は必須条件と優先条件がある

# 環境

以下の記事と同じ。

[k8s での Pod のスケジューリングについて確認した時のメモ(nodeSelector まで)](https://qiita.com/toshihirock/items/0f38aea8d069d434d81d)

# Inter-Pod Affinity/Anti-Affinity

[Inter-Pod Affinity/Anti-Affinity](https://kubernetes.io/ja/docs/concepts/configuration/assign-pod-node/#inter-pod-affinity-anti-affinity)

nodeSelector や Nodeaffinity/Node Anti-Affinity は **Node に設定されたラベル** を利用してスケジューリングの制御を行う。
一方、Inter-Pod Affinity/Anti-Affinity は **すでにNodeで稼働しているPodのラベルに従ってPodがスケジュールされるNodeを制限** する。

Node Affinityと同様に、Pod AffinityとPod Anti-Affinityにも必須条件と優先条件を示すものがある。

+ requiredDuringSchedulingIgnoredDuringExecution:スケジューリングの必須要件
+ preferredDuringSchedulingIgnoredDuringExecution:スケジューリング時の優先要件

# 検証

## 検証に利用する Node

以下の通り2つの Node が配置された環境を利用する。
デフォルトで付与されてるラベルは以下の通り

```bash
$k get nodes -o json|jq ".items[]|.metadata.labels"
{
  "alpha.eksctl.io/cluster-name": "test-cluster",
  "alpha.eksctl.io/instance-id": "i-0282a2ef8130cbff1",
  "alpha.eksctl.io/nodegroup-name": "standard-workers",
  "beta.kubernetes.io/arch": "amd64",
  "beta.kubernetes.io/instance-type": "t3.medium",
  "beta.kubernetes.io/os": "linux",
  "failure-domain.beta.kubernetes.io/region": "ap-northeast-1",
  "failure-domain.beta.kubernetes.io/zone": "ap-northeast-1c",
  "kubernetes.io/arch": "amd64",
  "kubernetes.io/hostname": "ip-192-168-48-33.ap-northeast-1.compute.internal",
  "kubernetes.io/os": "linux"
}
{
  "alpha.eksctl.io/cluster-name": "test-cluster",
  "alpha.eksctl.io/instance-id": "i-03a676eba6a541672",
  "alpha.eksctl.io/nodegroup-name": "standard-workers",
  "beta.kubernetes.io/arch": "amd64",
  "beta.kubernetes.io/instance-type": "t3.medium",
  "beta.kubernetes.io/os": "linux",
  "failure-domain.beta.kubernetes.io/region": "ap-northeast-1",
  "failure-domain.beta.kubernetes.io/zone": "ap-northeast-1a",
  "kubernetes.io/arch": "amd64",
  "kubernetes.io/hostname": "ip-192-168-74-169.ap-northeast-1.compute.internal",
  "kubernetes.io/os": "linux"
}
```

上記の通り、Node は ap-northeast-1c 及び ap-northeast-1d に起動されている。

## やってみる(Inter-Pod Affinity)

まずは以下のような `app = sample` というラベルが付与された Pod を作成する。

```web.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    app: sample-app
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP
```

```bash
$k apply -f web.yaml

$k get pods -l app=sample-app -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP             NODE                                               NOMINATED NODE   READINESS GATES
static-web   1/1     Running   0          87s   192.168.60.7   ip-192-168-48-33.
ap-northeast-1.compute.internal   <none>           <none>
```

ap-northeast-1c のインスタンス上に Pod が起動された事が分かる。
次に **app=sample-app のラベルを持つ Pod と同じノードにスケジュールする** 為に Inter-Pod Affinity を使う。

```
apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - sample-app
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0
```

```bash
$k apply -f pod-with-pod-affinity.yaml
pod/with-pod-affinity created

$k get pods -o wide
NAME                READY   STATUS    RESTARTS   AGE     IP              NODE                                               NOMINATED NODE   READINESS GATES
static-web          1/1     Running   0          7m21s   192.168.60.7    ip-192-168-48-33.ap-northeast-1.compute.internal   <none>           <none>
with-pod-affinity   1/1     Running   0          12s     192.168.34.56   ip-192-168-48-33.ap-northeast-1.compute.internal   <none>           <none>
```

同じ先程起動した Pod と同じノード上に起動する事を確認出来た。

今回 `topologyKey` が `kubernetes.io/hostname` で合ったため、同じノード上に起動した。
もし値が `failure-domain.beta.kubernetes.io/zone` となる場合、**app=sample-app のラベルを持つ Pod と同じ AZ にスケジュールする** という条件でスケジューリングする。
一応試す

```bash
# 削除
$k delete pod with-pod-affinity
pod "with-pod-affinity" deleted

# topologyKey を failure-domain.beta.kubernetes.io/zone に変える
$vi pod-with-pod-affinity.yaml

$k apply -f pod-with-pod-affinity.yaml
pod/with-pod-affinity created

$k get pods -o wide
NAME                READY   STATUS    RESTARTS   AGE   IP              NODE                                               NOMINATED NODE   READINESS GATES
static-web          1/1     Running   0          12m   192.168.60.7    ip-192-168-48-33.ap-northeast-1.compute.internal   <none>           <none>
with-pod-affinity   1/1     Running   0          4s    192.168.34.56   ip-192-168-48-33.ap-northeast-1.compute.internal   <none>           <none>

```

今回の検証では各 AZ にノードが一つしかないので、Pod と同じノード上に起動している。
ただし、こちらのマニュフェストの場合、Pod が起動してる AZ に他のノードがある場合、こちらへの起動も許容する。
逆に最初の `kubernetes.io/hostname` の場合、上記は許容せず、同じノード上への起動とさせる（その場合、Pod で複数コンテナを定義した方が良いのではという気がしなくもない...)

## やってみる(Inter-Pod Anti-Affinity)

今度は Inter-Pod Anti-Affinity を使ってみる
先程のマニュフェストを変えて　**app=sample-app のラベルを持つ Pod と同じ AZ にスケジューリングしない** ようにする
具体的には `podAffinity` を `podAntiAffinity`　に変えた

これを使う。

```bash
# 削除
$k delete pod with-pod-affinity
pod "with-pod-affinity" deleted

# podAffinity を podAntiAffinity にする
$vi pod-with-pod-anti-affinity.yaml

# diff でも確認
$ diff pod-with-pod-affinity.yaml pod-with-pod-anti-affinity.yaml
7c7
<     podAffinity:
---
>     podAntiAffinity:

$k apply -f pod-with-pod-affinity.yaml
pod/with-pod-affinity created

$k get pods -o wide
NAME                READY   STATUS    RESTARTS   AGE     IP               NODE                                                NOMINATED NODE   READINESS GATES
static-web          1/1     Running   0          35m     192.168.60.7     ip-192-168-48-33.ap-northeast-1.compute.internal    <none>           <none>
with-pod-affinity   1/1     Running   0          3m31s   192.168.71.173   ip-192-168-74-169.ap-northeast-1.compute.internal   <none>           <none>
```

別 AZ で動作するノード上に Pod が起動する事が確認出来た。