• 分析ExitCode定位Pod异常退出原因

    查看ExitCode

    使用kubectl describe pod查看到pod对应退出状态码,如果不为0,表示异常退出

    退出状态码区间,0-255,0表示正常退出

    外界中断将程序退出的时候状态码区间是129-255

    程序自身原因导致的异常退出状态码区间在1-128

    常见异常状态码:

        137:被SIGKILL中断信号杀死,常为内存溢出,CPU达到限制,在describe pod中的表现就是OOMKilled

  • 容器内抓包定位网络问题

    在kubernetes中运行容器应用,当容器应用中没有一些常见的问题盘查命令,出现问题后是很难定位的,这个时候,我们就能进入容器的网络命名空间netns,再使用宿主机上的命令进行问题排查。

    解决方法:

        1、kubectl get pod -o wide查看到应用所在主机节点

        2、在应用所在主机节点上docker ps | grep 应用名,定位到应用docker容器,获取docker id

        3、docker inspect -f {{.State.Pid}} docker-id获取到应用容器对应的进程pid

        4、nsenter -n -t pid进入容器网络命名空间,进行curl,tcpdump,ping等抓包排查操作

  • PID耗尽

    检查当前PID限制

    [root@k8s-master1 ~]# cat /proc/sys/kernel/pid_max 

    32768

    [root@k8s-master1 ~]# cat /proc/sys/kernel/threads-max 

    14531

    检查系统当前进程数量,若超过内核限制则会导致进程无法启动,可以通过调整最大阈值解决

    ps aux | wc -l

    解决方法:

        echo "kernel.pid_mac=65535" >> /etc.sysctl.conf && sysctl -p

        echo "kernel.tjreads-max=65535" >> /etc/sysctl.conf && sysctl -p

  • 内存碎片化

    内存分页失败,内核日志报错如下

    page allocate failure

    无法申请到内存,pod就会一直维持在ContainerCreating状态

    内存碎片化过多会导致,即便现在看上去系统内存很多,但是无法申请到大块的内存给进程使用,系统就会一直杀掉一些进程来释放内存,导致OOM

    解决方法:

        周期性进行drop cache操作

        echo 3 > /proc/sys/vm/drop_caches

  • 磁盘爆满
    容器运行时的磁盘爆满会导致docker无响应,docker Hang住,kubelet日志也能看到PLEG unhealthy,因为CRI调用timeout,就无法创建或者销毁容器,一直处于ContainerCreating以及Terminating。如果kubelet所在目录磁盘空间不足,Sandbox也会创建失败。
    关注路径/var/lib/docker /var/lib/kubelet
    解决方法:
        手动删除docker log文件,不要将容器日志写在容器内,需要另外挂载数据盘
        清理docker无用数据docker system prune -a
        设置正确的kubelet gc机制
        给docker设置日志归档配置,只保留固定大小的日志内容
  • Node NotReady
    kubelet,docker异常都会导致Node NotReady
    解决方法:
        查看docker响应速度是否正常,查看节点是否有大量日志写入,iotop,pidstat进行定位分析
        查看kubelet日志journalctl -u kubelet
  • Pod一直处于Terminating,ContainerCreating,Error,ImagePullBackOff,Pending,Unknown状态
    Terminating
        进程通过bash -c启动导致kill信号无法传递给进程
        进程还存在挂载点,被其他进程占用或者写入,无法卸载
    解决方法:
        停止数据写入,释放挂载点
    ContainerCreating
        解决方法:
            检查镜像正确性
            挂载volume失败
            磁盘满了,容器创建失败
            节点内存碎片化
            拉取镜像失败
            CNI网络错误:检查运行节点网络组件是否正常
            查看controller-manager是否正常
    Error
        解决方法:
            说明Pod启动过程产生错误
            依赖的ConfigMap,Secret,Volume产生错误
            违反集群设置的安全策略,比如违反了PodSecurityPolicy等
    ImagePullBackOff
        解决方法:
            镜像拉取失败,认证失败,镜像不存在等
            docker默认以https类型的registry拉取。,如果不支持https则必须配置非安全认证docker,如果时https类型,则dockerd会校验registry的证书,在/etc/docker/cert.d/<registry:port>/ca.crt
            如果镜像需要认证,但是没有配置ImagePullSecret也会拉取失败
            镜像拉取超时中断管,查看镜像仓库前面的负载均衡保持长连接的超时时长,进行调整
    Pending
        解决方法:
            容器没有足够的CPU,内存资源进行调度
            nodeSelector,污点容忍,亲和性不匹配
            kube-scheduler异常导致
      Unknown
          通常时节点失联,没法与apiserver通信,到达阈值后,controller-manager认为节点失联,并将其状态置为Unknown
          解决方法:
              查看节点kubelet状态
  • 容器进程主动退出
        容器进程主动退出,不是被外界中断杀死的话,退出码一般在0-128,一般都是业务程序的bug
        容器启动强依赖于第三方应用以及coredns解析失败,导致的应用重启
  • Apiserver响应慢
        查看apiserver日志具体问题具体分析
        通过VIP访问缓慢,有可能时VIP对应的nginx,LVS异常,可以通过kubectl get pod -s apiserver:8080绕过负载均衡进行验证
  • Namespace Terminating
        解决方法:
            Namespace上存在Finalizers,将其清理后就可以正常删除
            高版本的k8s集群不支持在线修改finalizers删除ns,要使用如下方法
           curl -H "Content-Type: application/json" -XPUT -d '{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"delete-me"},"spec":{"finalizers":[]}}' http://localhost:8001/api/v1/namespaces/delete-me/finalize
  • cgroup泄露

        在低版本内核(3.10)中,一旦开启了cgroup,就可能会导致不能彻底清理memcg和对应的cssid,也就是说应用即使删除了cgroup(/sys/fs/cgroup/memory下对应的cgroup目录)但在内核中没有释放cssid,导致内核认为的从group实际数量不一致。这个问题会导致容器创建失败,因为创建容器会为其创建cgroup进行隔离,而低版本内核中有个限制:允许创建的cgroup最大数量写死为65535,如果节点上经常创建销毁容器导致创建很多cgroup造成泄露,创建容器的时候就会导致cgroup创建失败并报错“no space left on device”

        解决方法:

             升级内核到3.10.1064以上版本并且关闭kernel,kmem特性,重启主机

  • arp缓存爆满导致健康检查失败

        某集群节点数 1200+,用户监控方案是 daemonset 部署 node-exporter 暴露节点监控指标,使用 hostNework 方式,statefulset 部署 promethues 且仅有一个实例,落在了一个节点上,promethues 请求所有节点 node-exporter 获取节点监控指标,也就是或扫描所有节点,导致 arp cache 需要存所有 node 的记录,而节点数 1200+,大于了 net.ipv4.neigh.default.gc_thresh3 的默认值 1024,这个值是个硬限制,arp cache记录数大于这个就会强制触发 gc,所以会造成频繁gc,当有数据包发送会查本地 arp,如果本地没找到 arp 记录就会判断当前 arp cache 记录数+1是否大于 gc_thresh3,如果没有就会广播 arp 查询 mac 地址,如果大于了就直接报 arp_cache: neighbor table overflow!,并且放弃 arp 请求,无法获取 mac 地址也就无法知道探测报文该往哪儿发(即便就在本机某个 veth pair),kubelet 对本机 pod 做存活检查发 arp 查 mac 地址,在 arp cahce 找不到,由于这时 arp cache已经满了,刚要 gc 但还没做所以就只有报错丢包,导致存活检查失败重启 pod

         解决方法:

             调整gc_thresh阈值           

            net.ipv4.neigh.default.gc_thresh1 = 128

            net.ipv4.neigh.default.gc_thresh2 = 512

            net.ipv4.neigh.default.gc_thresh3 = 4096

  • DNS解析异常
       如果DNS解析经常延时5s才返回,可能时conntrack冲突导致的丢包

         解析超时,查看coredns服务是否正常

         解析外部域名超时,若容器应用使用的dnspolicy时clusterfirst,则需要查看coredns所在节点的dns解析是否能够解析到外部域名,若dnspolicy为default,则需要查看容器所在主机的dns是否能够解析到外部域名,确认上游dns状态,是否有ACL限制