# 工作负载异常：实例驱逐异常（Evicted）
#### 驱逐原理
当节点出现异常时，为了保证工作负载的可用性，Kubernetes会通过驱逐机制（Eviction）将该节点上的Pod调离异常节点。
目前Kubernetes中存在两种Eviction机制，分别由**kube-controller-manager** 和**kubelet**实现。
- **kube-controller-manager实现的驱逐**
  kube-controller-manager主要由多个控制器构成，而驱逐的功能主要由node controller这个控制器实现，它会周期性检查所有节点状态，当节点处于NotReady状态超过一段时间后，驱逐该节点上所有Pod。
  kube-controller-manager提供了以下启动参数控制驱逐：
  - **pod-eviction-timeout：**即当节点宕机时间超过一定的时间间隔后，开始驱逐宕机节点上的Pod，默认为5min。
  
  - **node-eviction-rate：**每秒需要排空的节点数量，默认为0.1，即每10s从一个节点驱逐Pod。
  
  - **secondary-node-eviction-rate：**第二档的排空节点的速率。当集群中宕机节点过多时，排空节点的速率会降低至第二档，默认为0.01。
  
  - **unhealthy-zone-threshold：**可用区的不健康阈值，默认为0.55，即当该可用区中节点宕机数目超过55%时，认为该可用区不健康。
  
  - **large-cluster-size-threshold：**集群的大规模阈值，默认为50，当集群节点数量超过该阈值时认为集群属于大规模集群。大规模集群的可用区节点宕机数目超过55%时，则将排空节点速率降为0.01；假如是小规模集群，则将速率直接降为0，即停止驱逐节点上的Pod。
   
- **kubelet的eviction机制**
  如果节点处于资源压力，那么kubelet就会执行驱逐策略。驱逐会考虑Pod的优先级，资源使用和资源申请。当优先级相同时，资源使用/资源申请最大的Pod会被首先驱逐。
  kube-controller-manager的驱逐机制是粗粒度的，即驱逐一个节点上的所有Pod，而kubelet则是细粒度的，它驱逐的是节点上的某些Pod。此类驱逐会周期性检查本节点内存、磁盘等资源，当资源不足时，按照优先级驱逐部分Pod。关于Pod驱逐优先级，请参见[kubelet驱逐时Pod的选择](https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/node-pressure-eviction/#kubelet-驱逐时-pod-的选择)。
  驱逐阈值分为软驱逐条件（Soft Eviction Thresholds）和硬驱逐条件（Hard Eviction Thresholds）两种机制，如下：
  - **软驱逐条件：** 当节点的内存/磁盘空间达到一定的阈值后，kubelet不会马上回收资源，如果改善到低于阈值就不进行驱逐，若这段时间一直高于阈值就进行驱逐。
    您可以通过以下参数配置软驱逐条件：
    - **eviction-soft：** 软驱逐阈值设置。当节点[驱逐信号](https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/node-pressure-eviction/#eviction-signals)满足一定阈值时，例如memory.available\<1.5Gi时，kubelet不会立即执行Pod驱逐，而会等待eviction-soft-grace-period时间，假如该时间过后，依然还是达到了软驱逐阈值，则触发一次Pod驱逐。
    
    - **eviction-soft-grace-period：**当达到软驱逐阈值时，允许Pod优雅终止的时间，即软驱逐宽限期，软驱逐信号与驱逐处理之间的时间差。
    
    - **eviction-max-pod-grace-period：**最大驱逐pod宽限期，停止信号与kill之间的时间差。
      
  
  - **硬驱逐条件：** 硬驱逐机制则简单得多，一旦达到阈值，直接把Pod从本地驱逐。
    您可以通过以下参数配置硬驱逐条件：
    **eviction-hard** **：** 硬驱逐阈值设置。当节点[驱逐信号](https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/node-pressure-eviction/#eviction-signals)满足一定阈值时，例如memory.available\<1Gi，即当节点可用内存低于1Gi时，会立即触发一次Pod驱逐。
    kubelet 具有以下默认硬驱逐条件：
    - memory.available\<100Mi
    
    - nodefs.available\<10%
    
    - imagefs.available\<15%
    
    - nodefs.inodesFree\<5%（Linux 节点）
     
  
  
  除此之外，kubelet还提供了其他的驱逐参数：
  - **eviction-pressure-transition-period：**驱逐等待时间。当出现节点压力驱逐时，节点需要等待一定的时间，才会被设置为DiskPressure或者MemoryPressure，然后开启Pod驱逐，该时间默认为5分钟。该参数可以防止在某些情况下，节点在软驱逐条件上下振荡而出现错误的驱逐决策。
  
  - **eviction-minimum-reclaim：**表示每一次驱逐必须至少回收多少资源。该参数可以避免在某些情况下，驱逐Pod只会回收少量的资源，导致kubelet反复触发多次驱逐。
   
 
#### 问题定位
若节点故障时，实例未被驱逐，请先按照如下方法进行问题定位。
使用如下命令发现很多pod的状态为Evicted：
```
kubectl get pods
```
在节点的kubelet日志中会记录Evicted相关内容，搜索方法可参考如下命令：
```
cat /var/log/cce/kubernetes/kubelet.log | grep -i Evicted -C3
```
#### 排查思路
以下排查思路根据原因的出现概率进行排序，建议您从高频率原因往低频率原因排查，从而帮助您快速找到问题的原因。
如果解决完某个可能原因仍未解决问题，请继续排查其他可能原因。
- [排查项一：节点是否存在资源压力]
- [排查项二：是否在实例上设置了tolerations]
- [排查项三：是否满足停止驱逐实例的条件]
- [排查项四：容器与节点上的"资源分配量"是否一致]
- [排查项五：工作负载实例不断失败并重新部署]
 
 #### 排查项一：节点是否存在资源压力
当满足硬性或软性驱逐条件时，即存在资源压力时，kubelet会根据驱逐信号将节点设置为相应的[节点状况](https://kubernetes.io/zh-cn/docs/reference/node/node-status/#condition)，并为节点打上对应的污点。请通过以下步骤查看节点是否存在对应的污点。
```
$ kubectl describe node 192.168.0.37
Name:               192.168.0.37
...
Taints:             key1=value1:NoSchedule
...
```
表1存在资源压力的节点状况及解决方案 
| 节点状况            | 节点污点                            | 驱逐信号                                                                      | 描述                                       | 解决方案                                                                                                                  |
|:---|:---|:---|:---|:---|
| MemoryPressure | node.kubernetes.io/memory-pressure | memory.available                                                           | 节点上的可用内存已满足驱逐条件。                        | 您可以扩容节点规格，详情请参见[如何变更CCE集群中的节点规格？](https://support.huaweicloud.com/cce_faq/cce_faq_00030.html)。                      |
| DiskPressure    | node.kubernetes.io/disk-pressure  | nodefs.available、nodefs.inodesFree、imagefs.available 或 imagefs.inodesFree | 节点的根文件系统或镜像文件系统上的可用磁盘空间和 inode 已满足驱逐条件。 | 您可以扩容节点磁盘空间，详情请参见[存储扩容](https://support.huaweicloud.com/bestpractice-cce/cce_bestpractice_00198.html)。              |
| PIDPressure    | node.kubernetes.io/pid-pressure  | pid.available                                                               | 节点上的可用进程标识符已低于驱逐条件。                     | 您可以修改节点进程ID上限，详情请参见[修改节点进程 ID数量上限kernel.pid_max](https://support.huaweicloud.com/usermanual-cce/cce_10_0401.html)。 |
   
 #### 排查项二：是否在实例上设置了tolerations
通过kubectl工具或单击对应工作负载后的"更多 \> 编辑YAML"，检查工作负载上是不是设置了容忍度，具体请参见[污点和容忍度](https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/taint-and-toleration/)。
 #### 排查项三：是否满足停止驱逐实例的条件
若属于小规格的集群（集群节点数小于50个节点），如果故障的节点大于总节点数的55%，实例的驱逐将会被暂停。此情况下Kubernetes将不再尝试驱逐故障节点的工作负载，具体请参见[节点驱逐速率限制](https://kubernetes.io/zh-cn/docs/concepts/architecture/nodes/#rate-limits-on-eviction)。
 #### 排查项四：容器与节点上的"资源分配量"是否一致
容器被驱逐后还会频繁调度到原节点。
**问题原因：**
节点驱逐容器是根据节点的"资源使用率"进行判断；容器的调度规则是根据节点上的"资源分配量"进行判断。由于判断标准不同，所以可能会出现被驱逐后又再次被调度到原节点的情况。
**解决方案：**
遇到此类问题时，请合理分配各容器的资源分配量即可解决。
 #### 排查项五：工作负载实例不断失败并重新部署
工作负载实例出现不断失败，不断重新部署的情况。
**问题分析：**
pod驱逐后，如果新调度到的节点也有驱逐情况，就会再次被驱逐；甚至出现pod不断被驱逐的情况。
如果是由kube-controller-manager触发的驱逐，会留下一个状态为Terminating的pod；直到容器所在节点状态恢复后，pod才会自动删除。如果节点已经删除或者其他原因导致的无法恢复，可以使用"强制删除"删除pod。
如果是由kubelet触发的驱逐，会留下一个状态为Evicted的pod，此pod只是方便后期定位的记录，可以直接删除。
**解决方案：**
使用如下命令删除旧驱赶的遗留：
```
kubectl get pods -n <namespace> | grep Evicted | awk '{print $1}' | xargs kubectl delete pod -n <namespace>
```
*\<namespace\>*为命名空间名称，请根据需要指定。
#### 参考
[Kubelet does not delete evicted pods](https://github.com/kubernetes/kubernetes/issues/55051)
#### 提交工单
如果上述方法均不能解决您的疑问，请[提交工单](https://console.huaweicloud.com/ticket/#/ticketindex/createIndex)寻求更多帮助。
