记一次磁盘空间不足引起的pod驱逐问题

现象:

 

观察到磁盘空间不足的节点有大量pod处于Evicted 0/1状态,但并未进行重新调度。

查看到我的集群中有如下事件:

failed to garbage collect required amount of images

原因描述:

当容器集群中的节点(宿主机)磁盘使用率达到85%之后,会触发自动的容器镜像回收策略,以便于释放足够的宿主机磁盘。该事件发生于当触发镜像回收策略之后,磁盘空间仍然不足以达到健康阈值(默认为80%)。通常该错误是由于宿主机磁盘被占用太多导致。当磁盘空间占用率持续增长(超过90%),会导致该节点上的所有容器被驱逐,也就是当前节点由于磁盘压力不再对外提供服务,直到磁盘空间释放。

磁盘空间不足是由于一些历史原因导致的。在我的K8S集群中采用EFK的日志架构去收集日志,但是由于历史原因(从虚拟机转型到K8S),日志文件通过hostpath挂载到主机目录,并且通过在K8S node上通过systemd启动flume logtail组件去抓取挂载到宿主机的日志。这就导致两个问题:

  1. pod不是固定于某个节点上的,而是会随机调度,这也就造成有可能多个pod调度到一个主机上,并打印日志到同一个文件中去。
  2. hostpath的主机目录中的日志文件会不断堆积,引发本文中遇到的问题。

pod驱逐但未被删除的原因可以参考以下链接:

Kubelet does not delete evicted pods · Issue #55051 · kubernetes/kubernetes · GitHub

这样设计作可能是为了方便定位问题原因,而且如果大面积的pod被驱逐导致重启,这将对apiserver和etcd造成不小的冲击压力。

值得一提的是,当statefulset进入failed状态会自动重启,而deployment和damonset就不会。这里不作深入讨论。

https://github.com/kubernetes/kubernetes/blob/52eea971c57580c6b1b74f0a12bf9cc6083a4d6b/pkg/controller/statefulset/stateful_set_control.go#L386-L393 记一次磁盘空间不足引起的pod驱逐问题_第1张图片

解决方法:

我们从多个纬度来看待和处理这个问题:

  1. 从日志系统角度看,更优雅的做法应该是通过logtail sidecar和业务容器共同挂载emptydir的方式去抓取日志,emptydir属于临时存储(ephemeral-storage),占用的是kubelet根目录下的空间或者内存空间,可以考虑在podtemlate下的ephemeral-storage limit为其加入最大存储用量来防止超出预期的用量。此外当pod重启时,这部分空间会自动回收,不会保留日志文件,也就不会有大文件堆积的担忧。
  2. kubelet驱逐条件默认磁盘可用空间在10%以下,我们会更倾向调整云监控磁盘告警阈值以提前告警,如果是部署在集群内的prometheus,必须在prometheus被驱逐前通过各种告警手段通知到运维。

  3. kube-controller-manager可以设置--terminated-pod-gc-threshold为1,这个参数的意义表示controller manager可以容忍的最大的被驱逐的pod数量,如果超出该值,则会触发pod gc,将这些pod删除,以达到pod被驱逐后自动重启的目的。

  4. kube-scheduler配置了--policy-config-file下的CheckNodeDiskPressure策略项,当pod在调度时将磁盘压力作为调度的指标,以达到优选的目的。

  5. 引入自动化脚本来定时删除被驱逐的pod也是个不错的选择。

    apiVersion: batch/v1beta1
    kind: CronJob
    metadata:
      name: delete-failed-pods
    spec:
      schedule: "*/30 * * * *"
      failedJobsHistoryLimit: 1
      successfulJobsHistoryLimit: 1
      jobTemplate:
        spec:
          template:
            spec:
              containers:
              - name: kubectl-runner
                image: wernight/kubectl
                command: ["sh", "-c", "kubectl get pods --all-namespaces --field-selector 'status.phase==Failed' -o json | kubectl delete -f -"]
                restartPolicy: OnFailure

你可能感兴趣的:(踩坑,k8s,运维,kubernetes)