# Node 污点与 Pod 容忍度

本章涉及的两个高级调度特性为：

1. Node 污点：在 `nodes.spec.taints` 进行相关配置；
2. Pod 容忍度：在 `pods.spec.tolerations` 进行相关配置；

🦁 只有当一个 Pod 能够容忍某个 Node 中的污点，才能够被调度至该 Node 上。

## Node 污点

🍓 一个污点信息的结构是 **=:**，其中  可以为空。

💫 每个污点都可以关联一个效果，即 ，共有下面三种效果：

* **NoSchedule**：（调度期间生效）如果某个 Pod 无法容忍该 Node 污点，则这个 Pod 不能被调度至该 Node 上；
* **PreferNoSchedule**：（调度期间生效）是上面的宽松版本，表示尽量阻止无法容忍该 Node 污点的 Pod 调度至该 Node 上。如果某个 Pod 无法容忍该 Node 污点，但是该 Pod 已经没有其他 Node 可以调度，那么该 Pod 依然会被调度至该 Node 上；
* **NoExecute**：（调度及运行期间生效）若某个 Node 添加了该效果的污点，则无法容忍该污点的 Pod 将不会被调度至该 Node 上，并且该 Node 上原本已存在的 Pod 中若出现无法容忍该污点的 Pod，也会被去除；

{% hint style="info" %}
对 **调度期间生效** 和 **运行期间生效** 再次解释：

* 调度期间生效：是指配置 Node 污点后，仅无法容忍该污点的新 Pod 调度时不会或尽可能不会被调度至该 Node 上，但是不影响该 Node 中已经运行的无法容忍该污点的 Pod；
* 运行期间生效：是指配置 Node 污点后，不仅无法容忍该污点的新 Pod 无法调度至该 Node，该 Node 中正在运行的无法容忍该污点的 Pod 同样会被去除（视 Pod 的 `pods.spec.tolerations.tolerationSeconds` 配置而定），重新调度；
  {% endhint %}

### 展示案例

首先展示一个 Master 节点的 污点信息。

下面是 `get nodes -o yaml` 的结果：

```bash
taints:
- effect: NoSchedule
  key: node-role.kubernetes.io/master
```

下面是 `describe nodes` 的结果：

```bash
Taints:             node-role.kubernetes.io/master:NoSchedule
```

### 为 Node 添加污点

有两种方式可以为 Node 添加污点：

1. 使用 `kubectl taint` 命令；
2. 修改 YAML 配置文件；

#### 命令修改

为 名为 `node1.k8s` 的 Node 添加 `node-type=production:NoSchedule` 污点：

```bash
kubectl taint node node1.k8s node-type=production:NoSchedule
```

#### 修改配置文件

在 `nodes.spec.taints.key` 中配置污点的 ；在 `nodes.spec.taints.value` 中配置污点的 ；在 `nodes.spec.taints.effect` 中配置污点的 ：

```yaml
spec:
  taints:
  # 污点 node-type=production:NoSchedule
  - effect: NoSchedule    # (必须)
    key: node-type    # (必须)
    value: production
```

## Pod 容忍度

### 展示案例

下面展示一个可以运行在 Master 节点的 Pod 的容忍度信息（即容忍 Node 的 `node-role.kubernetes.io/master:NoSchedule` 污点）。

下面是 `get pods -o yaml` 的结果：

```yaml
tolerations:
# 容忍污点 node-role.kubernetes.io/master:NoSchedule
- effect: NoSchedule
  key: node-role.kubernetes.io/master
```

下面是 `describe pods` 的结果：

```bash
Tolerations:     node-role.kubernetes.io/master:NoSchedule
```

### 为 Pod 添加容忍度

可以通过 YAML 配置文件为 Pod 添加容忍度，下面展示一个例子。下面这个例子创建了一个 Deployment，其负责的 Pod 能够容忍的污点为 `node-type=production:NoSchedule`、任何 key 为 `CriticalAddonsOnly` 的污点、任何 key 为 `node.kubernetes.io/not-ready`, effect 为 `NoExecute` 的污点：

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prod
spec:
  selector:
    matchLabels:
      app: prod
  replicas: 5
  template:
    metadata:
      labels:
        app: prod
    spec:
      containers:
      - args:
        - sleep
        - "99999"
        image: busybox
        name: main
      tolerations:    # 配置容忍度
      # 容忍污点 node-type=production:NoSchedule
      - key: node-type
        operator: Equal    # Equal 操作符可以理解为 "="（默认操作符）
        value: production
        effect: NoSchedule
      # 容忍任何 key 为 CriticalAddonsOnly 的污点
      - key: CriticalAddonsOnly
        operator: Exists
      # 容忍任何 key 为 node.kubernetes.io/not-ready, effect 为 NoExecute 的污点 300s，300s之后，若 Node 还有该污点，则该 Pod 被去除
          - effect: NoExecute
            key: node.kubernetes.io/not-ready
            operator: Exists
            tolerationSeconds: 300    # 容忍时间，即若该 Pod 所在的 Node 被打上了该污点后，可以容忍 300s，超过这个时间，若污点还在，则 Pod 被去除。默认为 空，表示不去除；改为 0，则表示立刻被去除
```

{% hint style="info" %}
🏵 `pods.spec.tolerations.operator` 操作符有两种：

* Equal：(默认值)，在这种情况下需要完全指定污点的 key, value, effect，进行匹配；
* Exists：仅需要指定污点的 key 或者 key 和 effect，匹配包含这些信息的污点；
  {% endhint %}

{% hint style="info" %}
🐳 `tolerationSeconds` 是仅针对 **NoExecute** 效果的污点生效的，表示能够容忍该污点的时间：

* 空：(默认值)，在这种情况下 Pod 能够容忍该污点，并且若 Pod 已经处于该 Node 上，也不会被去除；
* 0：即使已经存在该 Node 上的 Pod 能够容忍该污点，也会被立刻去除；
* 大于 0 的值：即使已经存在该 Node 上的 Pod 能够容忍该污点，会容忍相应的时间，若污点在容忍时间内还存在，则 Pod 到达容忍时间后会被去除；
  {% endhint %}
