首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
CoreDNS反复崩溃?一套方法论搞定K8s CrashLoopBackOff排查
📅 2026/10/20 8:31:37
✍️ 爱科研究院
👁 阅读 3,247
刚部署完一套 k8s 集群还没高兴几分钟kubectl get pods就看到 kube-system 命名空间里的几个 coredns 处于CrashLoopBackOff。那会儿我第一反应是网络插件没装好第二反应是 kube-proxy 出了问题结果排查了半天最后在一行 CoreDNS 日志里找到了答案。类似的事情后来在几个不同环境里又遇到过每次崩溃的原因都不一样但一套完整的排查思路用下来处理时间基本能控制在十分钟以内。这篇文章就把这些原因和对应解法完整整理出来适合正在被CrashLoopBackOff折磨的人也适合想提前给集群打个预防针的人。放心问题不复杂但前提是你要知道去看哪里、怎么读日志、怎么分辨那种看起来是网络问题、其实是 DNS 问题的假象。1. CoreDNS 一崩影响面远比无法解析域名大先花点时间理解为什么 CoreDNS 崩溃的动静这么大。很多人在CrashLoopBackOff出现后第一反应是查网络插件、查 kube-proxy、查防火墙唯独忽略了这个状态背后的 DNS 链路。等绕了一圈回来才发现集群根子上的 DNS 服务已经反复重启十分钟了。1.1 一个 DNS 失效的集群表面上会伪装成各种问题CoreDNS 在集群里的角色相当于总机通过 ClusterIP 暴露 DNS 服务所有 Pod 的/etc/resolv.conf都会被 kubelet 写入nameserver CoreDNS Service 的 ClusterIP。也就是说任何一个 Pod 要解析其他 Service 域名、外部域名第一步都会先找到 CoreDNS。如果没有它你会看到非常多样化的故障现象Service 之间互相调用报unknown host、getaddrinfo EAI_AGAIN应用日志里出现大量间歇性超时因为 Pod 内的 DNS 缓存过期后新查询全部失败新建 Pod 启动时依赖域名初始化结果启动失败显示CrashLoopBackOff有些应用表现为连接被拒绝或连接超时根本不直接提 DNS。为什么容易误判因为 DNS 故障的表现太像网络故障了。你看到服务 A 连不上服务 B自然先去查 Service、Endpoint、CNI、kube-proxy几乎没人第一时间想到 CoreDNS。所以排查这类问题之前先确认 CoreDNS 本身是不是健康是一个必须养成的肌肉记忆。1.2 CrashLoopBackOff 的重启算法为什么看起来时好时坏CrashLoopBackOff是 Kubelet 对反复启动失败的容器的状态描述。Kubelet 每次启动容器容器进程退出后会根据退出码判定失败然后按指数退避的方式等待下一次重启10 秒、20 秒、40 秒、80 秒、160 秒最长封顶 300 秒。只要容器每次都在启动后短期内退出这个循环就会一直持续下去。所以你会看到 Pod 状态在CrashLoopBackOff和Running之间反复横跳或者在事件流里看到Back-off restarting failed container。但注意CrashLoopBackOff只是结果不是原因。它本身不告诉我们容器为什么失败你必须要往下挖容器上次终止时的Last State是什么退出码是多少退出前最后一段日志说了什么这三样东西才是破案的关键。别盯着状态发愁直接去看证据。2. 先用这五步拿证据Pod 状态、Last State、退出码与崩溃前日志排错最忌讳上来就猜。我先给你一套固定的取证序列按顺序执行绝大多数情况下能直接锁定问题方向。2.1 第一步看 Pod 状态和副本分布先确认是不是所有副本都崩了kubectl -n kube-system get pods -o wide | grep coredns如果只有一个副本处于CrashLoopBackOff另一个是Running集群 DNS 不会完全中断但容量已经下降一半高峰期可能出现解析超时如果所有副本都崩了那影响面就是全集群。这一步还顺便告诉你崩溃 Pod 所在节点后面登录节点查 resolv.conf 会用到。2.2 第二步describe 里的 Last State 和退出码拿到 Pod 后立刻 describekubectl -n kube-system describe pod coredns-pod-name重点看容器状态部分尤其是Last State这一行。这是容器上一次退出时的快照比当前状态更有价值。退出码本身就是线索。我给一个快速对照表现象优先怀疑方向下一步怎么做Last State: Terminated, Reason: Error, Exit Code: 1CoreDNS 主动退出多半是配置或插件检测问题翻崩溃前日志查 loop / parse errorLast State: Terminated, Reason: OOMKilled, Exit Code: 137内存超限被杀调 resources查上游 DNS 超时Last State: Terminated, Reason: Error, Exit Code: 2Corefile 解析失败检查 ConfigMap 里的 Corefile 语法事件里有exec format error镜像架构不匹配换多架构镜像事件里有Failed to pull image镜像拉取失败这不是 CrashLoopBackOff是镜像源问题另外要留意 Events 区域。如果事件里既有Back-off restarting failed container又有别的异常把事件按时间顺序串起来看往往能还原崩溃链条。2.3 第三步logs --previous 拿到崩溃前的日志这一步非常关键但经常被忽略。容器重启后当前日志往往是空的因为你看到的是新进程的输出真正有价值的崩溃现场在上一份日志里kubectl -n kube-system logs coredns-pod-name --previous如果日志里有Loop ... detected问题基本就是 DNS 环路如果有parse error就是 Corefile 语法问题如果日志完全空白那大概率是进程被杀尤其是 OOM因为内存被杀不会留下应用级别的日志。2.4 第四步和第五步从节点视角看环境四、五两步经常要交叉进行如果日志指向 loop 问题登到 Pod 所在节点看/etc/resolv.conf以及本地 DNS 缓存服务比如 dnsmasq、systemd-resolved的配置如果日志指向权限问题看节点的 SELinux 状态、容器 securityContext 以及挂载的只读文件系统如果日志为空且退出码 137去节点上执行dmesg | grep -i oom确认内核 OOM 记录。这套顺序跑完后大部分问题已经能归类了。下面逐一展开最常见的几大类根因。3. loop 插件自检退出DNS 环路是怎么让 CoreDNS 反复自杀的loop插件的日志可能是最吓人的一行也是新手最容易理解错的地方。3.1 loop 插件的原理CoreDNS 为什么要主动自杀先看一段典型的崩溃日志plugin/loop: Loop (127.0.0.1:55923 - :53) detected for zone .看到这行说明 CoreDNS 的 loop 插件在启动阶段主动终止了进程。很多人的第一反应是这个插件有问题把它删掉但恰恰相反——loop 插件是保护机制不是故障源。它的工作逻辑是CoreDNS 启动时在每个配置了 loop 插件的 zone 内构造一个随机域名查询发送给自己。如果这个查询经过上游转发链路后又回到了 CoreDNS 自己loop 插件就判断存在 DNS 解析环路直接 Fatal 退出。这么做的意义在于如果上游链路真的形成了环路CoreDNS 继续运行下去会导致所有经过它的查询无限循环最终变成超时风暴。与其让整个系统的解析全部卡死不如启动时迅速退出逼你立刻修链路。你可以把它理解成一个接线员发现自己的电话线和隔壁房间的出线口又接了回来于是直接把总机断电免得整个楼里的电话都打不出去。3.2 最常见的环路构造节点 resolv.conf、本地缓存与 ClusterIP 的三方合唱要理解环路怎么来的先看默认配置。标准 Kubernetes 部署里CoreDNS 的 Corefile 通常长这样.:53 { errors health ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 forward . /etc/resolv.conf cache 30 loop reload loadbalance }注意forward . /etc/resolv.confCoreDNS 会把非集群内部域名的查询转发到容器内/etc/resolv.conf里写的上游 DNS。而 CoreDNS 容器内的/etc/resolv.conf是从所在节点继承下来的所以它的上游链路天然绑定在节点 DNS 配置上。一旦节点上的 DNS 配置被改造过环路就可能出现。我见过最典型的链路是这样Pod 查询 - CoreDNSClusterIP 10.96.0.10:53 - forward 到容器内 /etc/resolv.conf - 节点配置 nameserver 127.0.0.1 - 本机 dnsmasq / systemd-resolved - 上游又指回 10.96.0.10:53 - 又回到 CoreDNS 自己促成这种环路的常见原因有三个节点/etc/resolv.conf被写成nameserver 127.0.0.1本机恰好有 dnsmasq 在 53 端口监听节点使用 systemd-resolvednameserver指向127.0.0.53而 systemd-resolved 的代理配置里又指向了 CoreDNS 的 ClusterIP集群里装了 NodeLocal DNSCache但配置指回 CoreDNS ClusterIPCoreDNS 又指回本机缓存两边互相指形成一个闭环。3.3 处置方案给 forward 一个干净的上游遇到 loop 日志不要删 loop 插件而是拆环路。最快的解法是改 CoreDNS 的 forward直接指向一个逻辑上不会绕回 CoreDNS 的可靠上游。kubectl -n kube-system edit configmap coredns把 forward 那行改成明确的上游地址例如内网环境里网络管理员提供的可用 DNS 服务器forward . 10.0.0.2 10.0.0.3保存后重启 CoreDNSkubectl -n kube-system rollout restart deployment/coredns随后确认 Pod 进入 Running 状态日志里不再出现 Loop 字样。如果环境硬性要求 CoreDNS 必须走本机 DNS 缓存那你得保证缓存的转发目标不能又指回 CoreDNS。具体来说本机缓存可以负责把cluster.local这类内部域名转发给 CoreDNS把外部域名转发给真实上游而 CoreDNS 的 forward 指向真实上游两者不要交叉回指。但说实话这种双向转发配置非常容易踩坑基础集群里我不推荐一开始就这么搞先把一个干净的上游路径跑通再说。这里还想强调一点loop插件保留着比删掉好得多。删掉它之后环路不会消失只会变成查询超时故障反而更难发现。保留 loop 插件等于让事故在启动阶段就主动暴露这是好事。4. OOMKilled这种无声死亡怎么定位又该怎么调内存第二大类高频原因是内存问题。4.1 OOMKilled 和 exit code 137 的取证如果你在 describe 里看到Last State: Terminated Reason: OOMKilled Exit Code: 137那就是 CoreDNS 进程被 cgroup OOM killer 杀掉了。137 是 128 9 的结果也就是收到了 SIGKILL。这时候去节点上跑一下dmesg | grep -i oom大概率能看到内核记录里出现了 coredns 进程名以及被杀时进程占用的内存页数。这个证据很硬比任何日志都可靠。有个容易误导的点CoreDNS 的崩溃日志可能是空的。因为 OOM 是内核直接杀进程进程根本来不及写错误日志。所以如果你发现logs --previous什么都没有但退出码是 137基本可以断定是内存问题。4.2 为什么 CoreDNS 会吃内存缓存、并发与上游超时堆积CoreDNS 本身不算重内存应用正常负载下单个副本的内存占用也就几十 MB。但有几个条件叠加内存就很容易失控cache 插件默认会缓存 TTL 内的解析记录缓存条目数有上限正常不会爆上游 DNS 超时转发的外部解析请求如果迟迟得不到响应CoreDNS 会为每个查询维持等待中的 goroutine上游越慢堆积越多内存涨得越快业务 QPS 突增尤其是部署了大量业务 Pod 后集群内部域名解析频率大幅上升而默认的 limit 设置可能偏低。很多发行版的默认 Deployment 里CoreDNS 的内存 limit 就设在 128Mi 或 256Mi 附近。对于一个小集群够用但一旦业务规模上来或者上游链路有延迟这个值就可能成为瓶颈。4.3 内存调整与更合理的资源规划遇到 OOM最直接的办法是调大内存 limit。但无脑堆内存也不是好习惯我的建议是分两步走第一步先适度调大 limit比如从 128Mi 提到 256Mi 或 512Mi观察 Pod 是否稳定。同时在 Prometheus 里采集 CoreDNS 暴露的指标重点看process_resident_memory_bytes以及coredns_dns_request_duration_seconds这类延迟指标搞清楚是纯粹资源给得太小还是上游 DNS 太慢导致请求堆积。第二步如果发现是上游超时导致的堆积就去修 forward 链路不要光靠加内存硬扛。否则今天加到 512Mi明天业务翻倍又会 OOM。调整资源时requests 和 limits 可以拉开一点差距。比如 requests 设 64Mi 或 128Milimits 设 256Mi 或 512Mi。requests 过高会导致节点调度困难limits 过小又容易触发 OOM拉得太开也不是好事因为 Kubernetes 的 QoS 分类会被影响。一般 CoreDNS 这种基础组件我习惯给一个稳妥的中间值再用监控逐步校准。另一个常见操作是加副本。把 CoreDNS 副本数从 2 提到 3并且尽量让副本分布在不同节点既分散了单点压力也降低了节点故障时的 DNS 中断风险。如果集群已经装了 Prometheus记得给 CoreDNS 的可用副本数和内存使用量配上告警免得下次 OOM 又是半夜才被发现。5. Corefile 配置错误一个括号或一次缩进引发的 CrashLoopBackOff第三大类是配置错误。这类问题定位起来非常快只要你瞄一眼崩溃日志。如果日志里有parse error、unknown plugin、Caddyfile之类的字样基本就是 Corefile 出了问题。5.1 配置解析失败的特征CoreDNS 脱胎于 Caddy它读取的 Corefile 是一种带缩进和括号的 DSL。一个典型的解析错误日志长这样[FATAL] plugin/errors: read: unexpected end of Caddyfile看到这行你的第一反应应该是Corefile 里的某个区块没有正确闭合或者缩进层级不对。另一种情况是拼写错误比如插件名写错、插件参数格式过时导致 CoreDNS 完全不认识这段配置。5.2 常见 Corefile 语法坑我整理几个实际环境中经常见到的坑括号不闭合Corefile 的 zone 区块用一对大括号包住少写一个右括号整个配置解析就会报错。Tab 和空格混用配置文件最好全用空格缩进不要混入 Tab。Corefile 对缩进敏感Tab 在某些版本里会导致解析异常。ConfigMap 的 YAML 缩进灾难Corefile 是 ConfigMap 里data.Corefile字段的值。在 YAML 中它通常是一个块标量apiVersion: v1 kind: ConfigMap metadata: name: coredns namespace: kube-system data: Corefile: | .:53 { errors health ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } forward . /etc/resolv.conf cache 30 loop reload loadbalance }注意块标量|后面的内容必须保持 Corefile 原有的缩进层级。如果在kubectl edit时不小心把前面的空格也改了Corefile 的 zone 名可能变成.:53或者解析层级整个错乱。插件版本不匹配升级 CoreDNS 镜像版本后某些插件参数的可选值会有变化。比如旧版本里forward的某些写法到了新版本可能不再被容忍。所以不熟悉某个插件时不要随意在 Corefile 里追加参数。5.3 验证配置的两种安全姿势改 Corefile 前强烈建议先验证语法。最简单的方式是本地用容器跑一遍docker run --rm -v $PWD/Corefile:/etc/coredns/Corefile coredns/coredns:1.11.1 -conf /etc/coredns/Corefile如果它能正常起一个监听 53 端口的进程并且不退出说明语法没问题。如果报错正好可以在本地迭代修正而不是把坏配置直接打到集群里。没有本地容器环境时也可以在集群中用一个一次性 Pod 挂载同一个 Corefile 试运行。注意 CoreDNS 的启动参数里明确指定了配置文件路径-conf /etc/coredns/Corefile如果修改 Deployment 时把 volumeMounts 改坏了CoreDNS 会直接找不到配置文件表现同样是 CrashLoopBackOff。还有一个实操细节修改 ConfigMap 后CoreDNS 的 reload 插件可能自动感知并重新加载配置。但如果新配置有错reload 失败后 Pod 进入 CrashLoop回滚前还得等退避周期。所以我的习惯是改之前先把当前 ConfigMap 备份一份改完再手动 rollout restart这样在任何时候都能快速回滚。6. 节点环境在背后使绊子权限、SELinux、架构不匹配有时候 Corefile 没问题、内存也没爆崩溃现场却出现在环境层面。这类问题占比不高但一出现就容易让人摸不着头脑。6.1 别忽略 Permission deniedSELinux 与只读根文件系统CoreDNS 容器启动时需要读取 ConfigMap 挂载进来的 Corefile。如果节点上的 SELinux 处于 enforcing 模式而策略没有正确允许容器访问这个挂载文件容器进程就会报权限类错误比如open /etc/coredns/Corefile: permission denied这种时候可以先临时把节点 SELinux 切到 Permissive 模式做个对照测试。如果问题消失说明确实是 SELinux 策略的问题测试完再把模式恢复 enforcing。然后根据日志里的 AVC 审计记录调整文件标签或者放行策略。另一个类似的坑是容器 securityContext 里设置了readOnlyRootFilesystem: true而 CoreDNS 的某些插件需要写临时文件或访问可写目录。这种情况可以通过单独挂载一个 emptydir 目录来满足写需求或者区分清楚哪个目录必须可写不要把整个根文件系统设成只读。官方默认的 CoreDNS Deployment 一般没有这个限制倒是在一些加固过的集群里会遇到。6.2 镜像架构不对也能伪装成 CrashLoopBackOff如果你在一个混合架构的集群里部署 CoreDNS比如一部分节点是 arm64、一部分是 amd64拉取镜像时如果没有通过镜像仓库的 manifest 列表自动选择正确架构容器启动就会出现exec format error。故障表现是Events 里能看到standard_init_linux.go:... exec format error容器很快退出Pod 同样进入 CrashLoopBackOff。这跟配置完全无关纯粹是镜像与节点 CPU 架构不匹配。解决办法是使用支持多架构的镜像或者按节点架构指定对应 tag。排查时如果发现日志里什么都没有、退出码也很奇怪记得去 Events 里看一眼是不是架构问题。6.3 什么时候不是 CoreDNS 的错还有一种情况很容易被误拿来和 CrashLoopBackOff 混为一谈CoreDNS 状态是Running但集群里的域名解析还是失败。这个时候就别再盯着 CoreDNS 崩溃原因了问题大概率在链路上看 CoreDNS Service 的 Endpoints 是否正常看 kubelet 的--cluster-dns是否和 CoreDNS Service 的 ClusterIP 一致看节点上的 kube-proxy 是否正常工作iptables/ipvs 规则有没有到 CoreDNS Pod 的通路看网络插件是否健康Pod 与 Service 之间的连通性是否正常。这类问题表现为解析超时而不是容器崩溃和 CrashLoopBackOff 属于完全不同的分叉路。先确认方向再动手能省掉大量无效排查。7. 恢复确认与防复发验证清单和运维纪律问题修完之后不能只看 Pod 变成 Running 就收工。CrashLoop 恢复后的验证以及后续的防复发措施才是整个排障流程的收尾。7.1 解除 CrashLoop 后的完整验证清单建议按顺序跑一遍kubectl -n kube-system rollout status deployment/coredns kubectl -n kube-system get pods -o wide | grep coredns确认 Deployment 达到期望副本数后再实际解析几个代表性域名。可以用一个临时 Pod 做协议级验证kubectl run dns-check --imagebusybox:1.28 --restartNever --rm -it -- sh进去之后nslookup kubernetes.default.svc.cluster.local如果你的 busybox 版本没有 nslookup也可以改用getent hosts kubernetes.default.svc.cluster.local同时测试一个外部域名的解析前提是集群网络能访问该域名对应的上游 DNS。内部和外部两类查询都通过才算真正把链路验证完整。如果要检查 CoreDNS 健康端点可以直接从节点访问 Pod IP 的 8080 健康端口或者在 CoreDNS Pod 里用 wget 请求/health。注意默认健康端口是 8080readiness 探针走的是 8181 的/ready这两个别搞混。7.2 防复发把这条路固化进日常运维恢复只是第一步我更想说的是后续纪律。第一节点 DNS 配置要有统一规范。不要让/etc/resolv.conf随意出现127.0.0.1或回指集群 DNS 的配置尤其是自动化脚本改完节点 DNS 后最好自动检查一遍是否成环。第二Corefile 变更必须有验证流程。最简单的做法是把本地容器验证配置变成一个固定步骤哪怕只是手动跑一下也能拦住九成语法问题。第三监控要补齐。CoreDNS 的 metrics 被暴露在 9153 端口通过 Prometheus 采集后盯住三个关键指标可用副本数是否低于预期进程实际内存 RSS 是否接近 limitP99 解析延迟是否异常上升。一旦这三个指标出现任何一项异常都值得尽早介入。第四副本数至少两个最好反亲和到不同节点。大集群里如果外部域名查询比例特别高可以考虑 NodeLocal DNSCache但要记住配置纪律让本机缓存的内部域名回指 CoreDNS外部域名走真实上游不要搞成双向回环。我自己排查这类问题的固定顺序是先logs --previous看有没有 Loop再看 describe 里的 Last State 是不是 OOMKilled然后检查 Corefile 语法最后才往权限和架构方向看。这个顺序基本能覆盖百分之九十以上的 CrashLoopBackOff 场景。如果这次遇到的问题不在上面任何一种大概率就属于网络链路层面的旁路问题了。把这套排查路径放进自己的运维手册里下次再见到CrashLoopBackOff心态会稳很多。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/20 8:31:37
OpenClaw:大模型去云端化与低资源本地部署实践
2026/10/20 8:31:37
基于滑模观测器的无传感永磁同步电机SVPWM矢量控制仿真
2026/10/20 8:31:37
基于协同过滤与Django的音乐推荐系统:从算法到ECharts可视化
2026/10/20 9:12:04
JEB安卓逆向实战:从APK解析到脚本化批量分析
2026/10/20 9:12:04
CMake集成MuJoCo:C++动力学计算的最小工程实践
2026/10/20 9:12:04
多模态Agent如何规划UI测试路径:从感知到执行的工程实践
2026/10/20 9:12:04
免费WAF绕过测试实战:找出雷池的检测盲区并完成加固
2026/10/20 9:12:04
化工防爆气象站实战解析:选型、安装与运维
2026/10/20 9:07:00
零等待流水车间调度NWFSP求解:自适应双种群鸡群算法与Matlab实现
2026/10/20 1:05:26
WSL下STM32开发板烧录指南:USB透传与OpenOCD实操
2026/10/20 1:05:26
ROS2工业级开发:实时性、资源适配与可交付验证体系
2026/10/20 1:05:26
STM32+华为云IoT+Qt构建智能家居远程控制系统全解析
2026/10/19 14:00:16
OpenResearch实战:Claude Code、Codex、OpenCode、Cursor 工作流搭建与故障排查
2026/10/20 7:57:25
知识管理核弹:用Obsidian构建第二大脑的工程化方案(TaoToken 统一 Key 接入版)
2026/10/19 15:41:50
金融AI落地指南:从调研报告到技术选型与工程实践
2026/10/20 2:24:03
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/20 2:23:57
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/20 2:23:53
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)