ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Kubernetes CoreDNS CrashLoopBackOff 故障排查与修复指南

Kubernetes CoreDNS CrashLoopBackOff 故障排查与修复指南 折腾过 k8s 的人应该都有这种经历kubeadm init 顺利跑完满心欢喜地准备部署第一个应用结果kubectl get pods -n kube-system一敲两个corednspod 齐刷刷地显示CrashLoopBackOff状态栏红得刺眼。我当时在 Ubuntu 22.04 上搭集群就被这个问题卡了一晚上日志像流水一样滚动看起来哪哪都是毛病但实际上往往就是几个非常具体的原因。这篇文章我会把你可能遇到的所有情况串一遍先从最常见、最隐蔽的容器运行时和 kubelet 配置冲突讲起再解决 Ubuntu 22.04 特有的 DNS 系统问题最后整理成一份可以直接对着操作的排查清单。无论你是刚接触 Kubernetes 的新手还是已经踩过几次坑的老手这套思路应该能帮你节省不少时间。1. 现象与第一反应CrashLoopBackOff 到底在说什么CrashLoopBackOff字面意思是“崩溃回退”容器启动后很快就异常退出然后 kubelet 按递增的退避时间反复重启它。这个状态本身不是在报某个特定的错误而是告诉你“容器起不来我在反复试探”。1.1 先看现象从哪里能观察现状最开始我执行的命令几乎成了肌肉记忆kubectl get pods -n kube-system输出里那两行 coredns 的状态是会变化的有时候是ContainerCreating卡很久然后变成CrashLoopBackOff重启几次退避时间变成 300 秒感觉就像集群在摆烂。Pod 本身能创建出来说明调度正常问题出在容器运行这一层。再精确一点看kubectl get pods -n kube-system -o wide kubectl describe pod -n kube-system coredns-xxxxxx-xxxxxdescribe 能告诉你很多上层信息比如镜像是否拉取成功、探针是否通过、有没有被 OOM、事件里有没有网络分配失败的提示。这一步做完基本就能判断出方向了。1.2 排查第一件事看日志和事件很多新手第一反应是去百度或复制报错其实最快的方式是直接看日志。CoreDNS 这种系统组件也就是几个副本日志量不会很大kubectl logs -n kube-system coredns-xxxxxx-xxxxx --previous关键是这个--previous参数因为 CrashLoopBackOff 状态下当前容器已经是挂掉重启后的新容器要看上一次挂掉时的输出必须加它。我在排查时第一次就忽略了看到的是一个空容器启动日志差点被带偏。日志里出现的关键信息大致有几类i/o timeout、no such host、cgroup相关报错、unknown、Fatal等。每一类背后对应的原因完全不同这也是为什么不能只靠“搜索报错”来解决问题得先搞清楚自己属于哪一类。2. 为什么 Ubuntu 22.04 上最容易踩这个坑不是说 CentOS 上不会遇到而是 Ubuntu 22.04 这个组合里有两颗雷特别容易引爆一是 containerd 和 kubelet 的 cgroup driver 不一致二是 systemd-resolved 把/etc/resolv.conf搞成了指向自己的软链接。2.1 cgroup driver 不一致排第一的元凶要说 CrashLoopBackOff 最常见的原因cgroup driver 不匹配绝对排第一。Kubernetes 管理 Pod 的资源限制、进程生命周期依赖容器运行时去操作 cgroup而 cgroup 的驱动有两种cgroupfs和systemd。kubelet 和容器运行时必须保持一致否则一个用 systemd 创建 cgroup另一个却直接在 cgroupfs 文件系统里找两边管理同一批进程时就会打架最终导致 sandbox 起不来Pod 崩溃。在 Ubuntu 22.04 上systemd 是默认的 init 系统kubelet 通过 kubeadm 初始化时默认配置趋向于使用systemd作为 cgroup driver。但 containerd 的默认配置却不一定开了SystemdCgroup true尤其在用 apt 安装containerd.io之后如果直接拿默认配置用很多版本下它仍然是cgroupfs或者根本没有显式开启。这两端一旦不一致CoreDNS 的 sandbox 容器每次创建都会被 kubelet 杀掉重来表现在外面就是无限 CrashLoopBackOff。2.2 systemd-resolved 的坑DNS 解析半路翻车Ubuntu 22.04 默认启动systemd-resolved服务它把/etc/resolv.conf变成指到自己的软链接读出来是这样的ls -l /etc/resolv.conf # /etc/resolv.conf - /run/systemd/resolve/stub-resolv.conf cat /etc/resolv.conf # nameserver 127.0.0.53问题来了CoreDNS 作为集群内部的 DNS 服务它向上游转发外部域名解析时使用的是从宿主机继承下来的resolv.conf。而 kubelet 在启动 Pod 的时候默认会把宿主机的/etc/resolv.conf作为 Pod 内的 DNS 配置来源之一。如果这个文件内容只有127.0.0.53那在 Pod 的网络命名空间里面访问127.0.0.53访问到的是 Pod 自己的 loopback 地址根本摸不到宿主机的 systemd-resolvedDNS 查询必然超时。这种现象虽然不一定直接导致 CoreDNS 进程崩溃但如果 readiness 探针一直失败kubelet 会认为容器不健康反复重启最终也会进入 CrashLoopBackOff。所以你会发现cgroup driver 的问题和 DNS 系统的问题经常是并发出现的解决一个另一个还继续爆。2.3 其他背景要素Ubuntu 22.04 较新的内核和 systemd 版本意味着一些老教程里针对 20.04 或 CentOS 7 的默认行为已经不适用了。比如 iptables 改用了 nftables 后端、swap 的处理方式、网络插件Calico/Flannel和内核模块的兼容性等都可能成为“压死骆驼的稻草”。所以在动手之前先确认你的版本信息和网络插件的搭配能少走很多弯路。3. 从零修复让 containerd 和 kubelet 老实配合既然确认了大概率是 cgroup driver 的问题那解决方案就很明确了把 containerd 的SystemdCgroup打开并确保 kubelet 也是同一个配置。3.1 确认当前配置检查 kubelet 和 containerd 的 cgroup driver不要上来就闷头改文件。先看看现在的配置到底是什么改起来心里才有底。先查 kubelet 的配置cat /var/lib/kubelet/config.yaml | grep -i cgroup这个文件是 kubeadm 初始化后生成的里面有一行类似cgroupDriver: systemd如果这行写着systemd那目标就很明确了让 containerd 也使用 systemd。如果这行不存在说明 kubelet 用了默认值不同版本默认值不一样稳妥的做法是把这一行补上并写成systemd。再查 containerd 的配置sudo cat /etc/containerd/config.toml | grep -n SystemdCgroup如果没有任何输出说明 containerd 跑的是内置默认配置并没有显式开启 SystemdCgroup。有些环境里/etc/containerd/config.toml文件甚至不存在或者内容非常空那说明它用的完全是默认值。需要特别提醒的是不能只凭 grep 的结果就断言一定是 cgroup driver 的问题。我见过一种情况grep 出来是空的但 containerd 版本较新、默认就已经开了 SystemdCgroup结果问题反而出在其它地方。所以这条命令只能作为一个参考更准确地验证方式是把 containerd 的实际运行参数打出来。3.2 修改 containerd 配置并重启如果/etc/containerd/config.toml存在但内容非常简单可以先用默认配置生成一份完整的sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml生成之后打开文件找到一段类似这样的结构[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc] ...在这段下面如果没有SystemdCgroup这一项就手动加上[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup true如果已经有这一项但值是false把它改成true。改完之后重启 containerdsudo systemctl restart containerd重启之后可以验证配置是否加载成功sudo systemctl status containerd这个修改的核心逻辑是containerd 在创建 Pod 的 sandbox 时如果SystemdCgroup true就会用 systemd 的接口去创建 cgroup和 kubelet 保持一致。反过来如果你坚持用cgroupfs那两边的配置统一成cgroupfs也行只是 Ubuntu 22.04 systemd 环境下官方推荐 systemd后续日志管理、资源回收都更稳。3.3 同步 kubelet 配置并重置节点光改 containerd 还不够如果 kubelet 侧的配置和它不一致依然会 CrashLoopBackOff。先看一下/var/lib/kubelet/kubeadm-flags.envcat /var/lib/kubelet/kubeadm-flags.env如果里面已经有--cgroup-driversystemd那基本一致了。如果没写可以检查/etc/default/kubeletsudo vim /etc/default/kubelet添加一行KUBELET_KUBEADM_ARGS--cgroup-driversystemd然后重启 kubeletsudo systemctl daemon-reload sudo systemctl restart kubelet这里面有个大坑如果你已经执行过kubeadm init节点上的 kubelet 已经完成了注册部分配置是固化在集群节点对象里的。只改本地配置重启 kubelet有时候能生效有时候却因为集群里的 node 对象还记录着旧的 cgroup driver 而产生冲突。我遇到的情况是一次性直接改完 containerd 和 kubelet 再重启CoreDNS 恢复了。但如果你改了之后重启了好几次还是不恢复最稳妥的操作是重新初始化集群。重置的命令sudo kubeadm reset -f sudo rm -rf /etc/cni/net.d ~/.kube然后重新 initsudo kubeadm init --pod-network-cidr10.244.0.0/16注意重置会清掉之前集群里的所有东西如果你已经部署过一些工作负载务必备份好配置。这也是为什么建议在刚初始化完、还没部署任何业务的时候发现问题赶紧重置代价最小。重新初始化之后再把网络插件装一遍然后重复kubectl get pods -n kube-system观察状态。3.4 验证 CoreDNS 恢复恢复之后的状态大概是kubectl get pods -n kube-system | grep coredns # coredns-xxxxxxxx-xxxxx 1/1 Running 0 3m # coredns-xxxxxxxx-xxxxx 1/1 Running 0 3m别高兴太早还要验证一下 DNS 是否真能解析。可以起一个临时的测试 Podkubectl run testdns --imagebusybox:1.28 --rm -it --restartNever -- nslookup kubernetes.default.svc.cluster.local如果返回了正常的解析结果说明 CoreDNS 是真的活了而不只是 pod 状态显示 Running。4. 其他高频原因网络、镜像、探针逐个排查cgroup driver 的问题解决之后仍然会遇到顽固不化的 CrashLoopBackOff。这时候要沉住气换另一条思路继续排查。4.1 CNI 网络插件没装好CoreDNS 的 Pod 虽然调度到节点上了但容器要正常运行必须有 CNI容器网络接口插件给它分配 Pod IP、创建网络命名空间。如果集群初始化后一直没有安装 Calico、Flannel 之类的网络插件Pod 的网络初始化会失败容器反复创建不成功表面上也会表现为 CrashLoopBackOff。排查方法很直接kubectl get pods -n kube-system | grep -E calico|flannel如果根本没有网络插件的 Pod或者它们本身也在 CrashLoopBackOff那就把网络插件补上。以 Flannel 为例kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml注意Flannel 的默认网段是10.244.0.0/16这要求你在kubeadm init时传了--pod-network-cidr10.244.0.0/16如果当初初始化时用的网段不对后面装啥网络插件都会出现 IP 分配混乱CoreDNS 也起不来。装完之后再观察状态多数情况下 DNS 问题会跟着网络插件一起消失。4.2 镜像拉不下来CrashLoopBackOff也有可能是镜像根本没有拉取成功导致的。查看 Pod 事件时如果看到Failed to pull image相关的报错比如Failed to pull image registry.k8s.io/coredns/coredns:v1.10.1这通常意味着节点无法从镜像仓库拉取镜像。不同网络环境下registry.k8s.io的访问速度差异很大甚至直接拉不下来。CoreDNS 镜像拉不下来容器当然起不来反反复复就成了 CrashLoopBackOff。解决方案有几种一是给 containerd 配置有效的镜像加速器在/etc/containerd/config.toml里的[plugins.io.containerd.grpc.v1.cri.registry.mirrors]段落中加入可用的镜像源二是手动下载镜像后导入crictl pull registry.k8s.io/coredns/coredns:v1.10.1 crictl images还有一种做法是改 kubeadm 的imageRepository参数在初始化之前就指定加速镜像源。不过这些都属于部署习惯问题和生产环境是否复杂没有直接关系我个人的建议是优先配置镜像加速器一劳永逸。4.3 探针失败与 DNS 配置问题排除完 cgroup driver、CNI、镜像问题之后另一种常见原因是 readiness/liveness 探针失败。CoreDNS 的探针会检查它能否正常响应 DNS 查询如果它在集群内部的监听地址或 Corefile 配置有问题探针失败后 kubelet 会认为容器不健康反复重启。面对这种情况先看 CoreDNS 日志kubectl logs -n kube-system coredns-xxxxxx-xxxxx | tail -50日志里如果出现[ERROR] plugin/errors: 2 10.96.0.1:53: read udp 10.244.x.x:...: i/o timeout这个10.96.0.1是 Kubernetes 的 Service 网段入口一般是 kube-apiserver 的 Service IP。如果 CoreDNS 连它都连不通说明集群的网络层还有问题不能简单归结为 CoreDNS 本身坏了。从宿主机层面检查一下到 Service 网段的连通性ping -c 3 10.96.0.1虽然 ping 不一定通因为 Service 不响应 ICMP但至少能看出路由有没有问题。另一个排查方向是iptables规则是否完整。Ubuntu 22.04 上如果还有旧的 docker/containerd 规则残留可能会干扰 kube-proxy 写入的转发规则导致 CoreDNS 无法访问 apiserver。这种情况下降低排查成本的办法是查看节点上的 kube-proxy 是否正常工作kubectl get pods -n kube-system | grep kube-proxy如果 kube-proxy 出了问题它的 BUG 也会间接导致 CoreDNS 无法访问 Service 网段探针自然失败。4.4 系统资源不足还有一种现象容易被忽略CoreDNS 被 OOM 杀掉。kubectl describe pod -n kube-system coredns-xxxxxx-xxxxx如果事件里出现了OOMKilled说明节点内存太紧张。CoreDNS 默认有 requests 和 limits可以在 Deployment 里调低 limits 或加内存。不过这种属于治标不治本节点内存如果长期不足后面跑业务应用也会很吃力。我在一台只有 2G 内存的小机器上遇到过这个问题把机器内存加到 4G 之后所有现象都消失了。5. 一份“速查 避坑”指南考虑到以后可能还会遇到类似问题也方便你在网络上搜索时快速对照我把这些排查点整理成了一份速查表。5.1 常见问题速查表现象主要原因排查命令/关键点解决方向CoreDNS CrashLoopBackOff日志无明确报错kubelet 与 containerd 的 cgroup driver 不一致cat /var/lib/kubelet/config.yaml与grep SystemdCgroup /etc/containerd/config.toml统一为 systemd重启 containerd必要时重置集群CoreDNS 日志大量i/o timeoutUbuntu systemd-resolved 导致上游 DNS 地址为 127.0.0.53cat /etc/resolv.conf看是否有 127.0.0.53修改 kubelet 的--resolv-conf或调整系统 DNSPod 一直 ContainerCreating 后转 CrashLoopBackOff未安装 CNI 网络插件ls /etc/cni/net.d或查看 calico/flannel pod安装对应网络插件确认 pod-network-cidr 一致事件显示Failed to pull image镜像仓库不可达crictl images检查有没有 CoreDNS 镜像配置镜像加速器或手动导入镜像探针失败但容器本身没崩kube-proxy 或网络规则异常kubectl logs看插件报错检查 kube-proxy检查集群网络组件清理残留 iptables 规则事件里出现OOMKilled节点内存不足kubectl describe pod查看 OOM 事件增加内存或调整 CoreDNS 资源限制5.2 我踩过的坑和最终建议根据我个人的实际经验这个问题的排查顺序应该是先看日志和事件再确认 cgroup driver 是否一致然后检查 CNI 和 DNS 系统最后才去折腾镜像和资源。踩过一次的坑是一开始没看重置这个选项反复在 containerd 和 kubelet 配置里打补丁虽然配置都改了但集群就是不恢复。后来想通了——kubeadm 初始化的集群里节点状态、证书、kube-proxy 规则都是环环相扣的如果底层容器运行时的驱动都变了最有把握的做法就是kubeadm reset之后重新初始化。这听起来很“重”但比起反复猜测实际时间成本更低。再有就是Ubuntu 22.04 上一定要提前处理 systemd-resolved。我建议在初始化集群之前就把 kubelet 的--resolv-conf参数指向真实可用的 DNS 配置文件不要等到 CoreDNS 起不来再来补。如果你只是本地测试环境最省事的组合是containerd 开启SystemdCgroup truekubeadm init 时指定--pod-network-cidr10.244.0.0/16然后装 Flannel。这一套下来CoreDNS 基本不会再出幺蛾子。最后再分享一个小技巧即使 CoreDNS 已经 Running也建议顺手验证一下集群 DNS 是否真的在干活。因为状态显示 Running 只代表容器没挂不代表解析一定正常。用busybox跑一次nslookup kubernetes.default几秒钟就能测完心里踏实很多。以后遇到任何 DNS 相关问题这套 PostgreSQL 一样的验证流程都通用。
返回列表