【KubernetesのAffinity】その１ - Node Affinityの使い方

## Affinityとは
Affinityとは、Podをスケジューリングの条件を設定する機能である。
以下の3種類があるが、このページではNode Affinityについて説明する。

- **Node Affinity**
- Pod Affinity
- Pod Anti-Affinity

Pod Affinity、Pod Anti-Affinityについては以下を参照。
[【KubernetesのAffinity】その２ - Pod Affinity / Pod Anti-Affinityの使い方](https://qiita.com/Esfahan/items/a673317a29ca407e5ae7)

似たような機能でTaint / Tolerationというものもある。
[KubernetesのTaint / Tolerationの使い方](https://qiita.com/Esfahan/items/843845bdbfefd8ea46d2)

## 目次
- [Kubernetesハンズオン目次](https://qiita.com/Esfahan/items/4fcf51622fed20dffb88)

## Node Affinity
PodをどのNodeにスケジューリングするのかを設定できる。
NodeSelectorと似ているが、Node Affinityのほうが柔軟な設定できる。

参考：[NodeSelectorの使い方](https://qiita.com/Esfahan/items/a7f7de0a941cc03f12b1#nodeselectorの使い方)

matchExpressions、matchFieldsの2つの条件設定方法がある。


## matchExpressionsの使い方
### NodeにLabelをセット
`environment=dev` というLabelをセットする。

```shell-session
$ sudo kubectl label node/minikube environment=dev
$ sudo kubectl get node --show-labels
```

### Podを定義
matchExpressionsで、敢えて`environment=prod`という存在しないLabelを指定する。

```yaml:pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod
spec:
  containers:
  - name: main
    image: nginx
  affinity:
    nodeAffinity:
      ###################################################
      # - requiredDuringSchedulingIgnoredDuringExecution
      #   - Affinityルールに合致したNodeにのみスケジュールする
      # - preferredDuringSchedulingIgnoredDuringExecution
      #   - Affinityルールに合致したNodeに優先的にスケジュールするが、合致しない場合他のNodeへスケジュールする
      # - IgnoredDuringExecutionというのは、実行中のPodには影響しないことを意味する。
      ###################################################
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          # NodeのLabelに対して集合ベースの比較
          - matchExpressions:
            - key: environment
              operator: In
              values:
                - prod
```

### apply

```shell-session
$ sudo kubectl apply -f pod.yml
pod/node-affinity-pod created
```

### Podの状態を確認
STATUSがPendingのまま。

```shell-session
$ sudo kubectl get pod
NAME                READY   STATUS    RESTARTS   AGE
node-affinity-pod   0/1     Pending   0          13s
```

マッチするNodeが存在しないというエラーが出ている。

```shell-session
$ sudo kubectl describe pod/node-affinity-pod
# **snip**
Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  18s (x2 over 18s)  default-scheduler  0/1 nodes are available: 1 node(s) didn't match node selector.
```

### Delete
一旦Podを削除する。

```shell-session
$ sudo kubectl delete -f pod.yml
pod "node-affinity-pod" deleted
```

### Podを修正
matchExpressionsの`environment`を`dev`に修正する。

```diff:pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod
spec:
  containers:
  - name: main
    image: nginx
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: environment
            operator: In
            values:
-           - prod
+           - dev
```

### 再度apply

```shell-session
$ sudo kubectl apply -f pod.yml
pod/node-affinity-pod created
```

### 再度Podの状態を確認
Runningになった。

```shell-session
$ sudo kubectl get pod
NAME                READY   STATUS    RESTARTS   AGE
node-affinity-pod   1/1     Running   0          11s
```

Podが指定したNodeにスケジューリングされた。

```shell-session
$ sudo kubectl describe pod/node-affinity-pod
# **snip**
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  43s   default-scheduler  Successfully assigned default/node-affinity-pod to minikube
  Normal  Pulling    42s   kubelet, minikube  Pulling image "nginx"
  Normal  Pulled     36s   kubelet, minikube  Successfully pulled image "nginx"
  Normal  Created    36s   kubelet, minikube  Created container main
  Normal  Started    35s   kubelet, minikube  Started container main
```

## matchFieldsの使い方
matchExpressionsと似ているが、こちらはkeyにオブジェクトのフィールド名を指定する。
matchFieldsサポートしているものとしていないオブジェクトがあるので注意。

以下の例では、nodeのNAMEを指定して、Podを関連付ける。

```shell-session
$ sudo kubectl get node
NAME       STATUS   ROLES    AGE    VERSION
minikube   Ready    master   5h2m   v1.14.3
```

nodeのNAMEが`minikube`なので、下記の用に、`key: metada.name`に対して`minikube`を指定する。当然、この値を違うものに変更すると、Podはスケジューリング先を見つけられず、起動しない。

```yaml:pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod
spec:
  containers:
  - name: main
    image: nginx
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        # Nodeオブジェクトのフィールドに対して集合ベースの比較
        nodeSelectorTerms:
          - matchFields:
            - key: metadata.name
              operator: In
              values:
                - minikube
```

### Podの状態を確認
Runningになった。

```shell-session
$ sudo kubectl get pod
NAME                READY   STATUS    RESTARTS   AGE
node-affinity-pod   1/1     Running   0          6s
```

```shell-session
$ sudo kubectl describe pod/node-affinity-pod
# **snip**
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  8s    default-scheduler  Successfully assigned default/node-affinity-pod to minikube
  Normal  Pulling    7s    kubelet, minikube  Pulling image "nginx"
  Normal  Pulled     4s    kubelet, minikube  Successfully pulled image "nginx"
  Normal  Created    4s    kubelet, minikube  Created container main
  Normal  Started    4s    kubelet, minikube  Started container main
```

## 関連記事
- [KubernetesのTaint / Tolerationの使い方](https://qiita.com/Esfahan/items/843845bdbfefd8ea46d2)
- [KubernetesのLabel / NodeSelector / Annotationの使い方](https://qiita.com/Esfahan/items/a7f7de0a941cc03f12b1)

## 参考
- [Assigning Pods to Nodes](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/)
- [KubernetesのNode Affinity, Inter-Pod Affinityについて](https://qiita.com/sheepland/items/ed12b3dc4a8f1df7c4ec)
- [KubernetesのNode AffinityとInternal Pod Affinityを使ってPodを高度スケジューリングする](https://www.1915keke.com/entry/2018/09/26/163447)
- [Kubernetes道場 18日目 - Affinity / Anti-Affinity / Taint / Tolerationについて](https://cstoku.dev/posts/2018/k8sdojo-18/)