Calico Node Init:CrashLoopBackOff 排查实战:从日志到修复
先交代一下背景。这篇不是讲什么高深原理而是我自己在维护 Kubernetes 集群时真实遇到并完整解决的一类问题calico-node 这个 DaemonSet Pod 起不来卡在 Init 阶段状态反复变成Init:CrashLoopBackOff。这类问题在 K8s 网络组件里面算是高频故障尤其是新装集群、扩容节点、或者升级 Calico 版本之后特别容易出现。先说结论CrashLoopBackOff本身不是一种具体错误它只是 Kubernetes 对“容器反复启动又反复崩溃”这一现象的描述。真正的问题藏在日志里。只要你搞清楚是哪个容器在崩、崩之前打印了什么剩下的事情就是按图索骥。如果你也被kubectl get pods里的这行红字折磨过或者正准备排查一个网络插件起不来的集群这篇内容应该能帮你省下不少时间。1. 先把现象看清楚Init 容器崩溃背后的机制1.1 CrashLoopBackOff 不是故障本身是故障的“症状”在 Kubernetes 里一个 Pod 由多个容器组成时Init 容器会先于业务容器执行只有所有 Init 容器都成功退出主容器才会被拉起。CrashLoopBackOff这个状态意味着某个容器启动后立刻崩溃kubelet 捕获到非零退出码按照退避策略反复重启它而且每次重启的间隔会指数增加10秒、20秒、40秒……直到 5 分钟封顶。这个机制本身是为了避免故障容器无限消耗节点资源。但从排障角度看它有个麻烦你看到的状态永远是“正在崩溃-正在重启”的中间态如果不加参数直接看日志很可能看到的只是最近一次启动时的输出真正的错误信息已经被下一次启动覆盖了。你可以把 Init 容器理解为“飞机起飞前的安全检查”。Kubernetes 不会因为安全检查失败就直接把飞机扔掉而是反复尝试重新检查直到成功或者一直卡在这。所以处理这类问题的第一步永远不是盲改配置而是先把“哪一步检查失败了”弄清楚。1.2 calico-node 的 Init 容器到底在做什么Calico 作为 Kubernetes 最常用的 CNI 网络插件之一它以 DaemonSet 方式在每个节点上运行一个calico-nodePod。这个 Pod 里通常包含两类容器install-cniInit 容器负责把 Calico 的 CNI 插件二进制文件复制到宿主机的/opt/cni/bin目录并把网络配置写入/etc/cni/net.d/比如生成10-calico.conflist。calico-node主容器负责运行 Felixiptables/路由管理和 BIRDBGP 路由组件真正处理 Pod 网络的连通性和网络策略。很多新手不知道 Init 容器和主容器是两个独立容器日志也是分开的。排查时必须指定-c参数才能看到对应容器的日志。install-cni崩溃和calico-node主容器崩溃指向的故障方向完全不同前者通常跟宿主机目录权限、CNI 二进制文件、挂载有关后者通常跟网卡探测、内核模块、数据存储连接有关。我把这两个容器的职责先写清楚是因为后续所有排查动作都建立在这个区分之上。你连哪个容器在崩都没搞明白就去看一堆日志只会越看越乱。2. 标准排查路径先看状态再看日志最后动配置2.1 看 Pod 状态与事件describe 是第一步遇到Init:CrashLoopBackOff我习惯先执行一条命令kubectl get pods -n kube-system -o wide | grep calico这一步能看到所有 calico-node Pod 的分布情况是单个节点出问题还是所有节点都起不来。如果只有单个节点异常优先怀疑节点本身的环境问题如果全集群都异常大概率是部署配置、RBAC 或网络插件公共依赖出了问题。接着对异常的 Pod 执行describekubectl describe pod -n kube-system calico-node-xxxxx重点关注 Events 部分的输出。我见过几种典型报错Failed to create pod sandbox: ...说明容器运行时层面有问题比如 containerd 或 docker 异常。MountVolume.SetUp failed ...说明 Pod 声明的 volume 挂载失败通常指向宿主机路径权限或 kubelet 的 volume 插件问题。Error: failed to generate container ...说明容器创建被安全策略或运行时拦截。describe的输出能帮你快速判断问题是否发生在容器启动之前。如果 Events 里干干净净没有明显报错那问题大概率出在容器内部的启动逻辑上此时必须看日志。2.2 按容器逐个抓日志--previous 才是关键看日志是定位根因的核心动作。针对「Init:CrashLoopBackOff」需要分别查看 Init 容器和主容器的日志。先看 Init 容器kubectl logs -n kube-system calico-node-xxxxx -c install-cni如果输出为空或只有少量正常日志再用--previous参数拿上一次崩溃退出前的完整日志kubectl logs -n kube-system calico-node-xxxxx -c install-cni --previous这个--previous是我反复强调的点。容器 CrashLoopBackOff 时你直接看的是“最近一次启动”的日志如果它还没跑到报错点就被 kill 了日志里反而看不到错误。而--previous返回的是上一次运行周期里进程退出前写入的所有内容往往真正的报错堆栈就在那里。再看主容器kubectl logs -n kube-system calico-node-xxxxx -c calico-node kubectl logs -n kube-system calico-node-xxxxx -c calico-node --previous抓日志时记住一个原则看“最后一屏”不要从头翻。CrashLoopBackOff 的容器日志通常很短真正的错误信息一般出现在最后几行。我习惯先tail一下比如kubectl logs -n kube-system calico-node-xxxxx -c install-cni --previous | tail -n 50这样避免被大量无关的启动信息干扰。2.3 再检查宿主机的 CNI 文件和内核模块如果日志信息不足以定位问题或者想验证容器内检测到的宿主机环境是否符合预期可以登到节点上检查几个关键位置。CNI 二进制目录ls -l /opt/cni/bin/正常情况下应该能看到calico、loopback、portmap等二进制文件。如果目录为空说明install-cni从未成功写入或者之前被误清理过。CNI 配置目录cat /etc/cni/net.d/10-calico.conflist如果存在多个配置文件而且不是 calico 开头的kubelet 可能会选择错误的 CNI 配置导致 sandbox 创建失败。内核模块方面如果使用 IPIP 模式Calico 默认的跨节点封装方式之一需要确认ipip模块是否加载lsmod | grep ipip modprobe ipip某些最小化安装的 Linux 系统默认没加载这个模块calico-node 主容器启动时会因为创建tunl0接口失败而退出。这类问题在云服务器和容器化部署的操作系统镜像里尤其常见。3. 高频根因与对应解决方案3.1 根因一install-cni 容器因网卡探测失败而退出install-cni容器先崩溃的情况里最典型的是日志里出现Unable to detect the network interface to use for pod traffic或者failed to find system default route: open /proc/net/route: no such file or directory这类问题的根源是Calico 需要探测节点用于 Pod 流量的网卡 IP默认使用first-found策略也就是从路由表中挑第一个可用的 IPv4 地址。当节点上有多个网卡、或者默认路由缺失、或者容器内路由表异常时探测就会失败。解决思路是显式指定网卡探测方式让 Calico 别猜。比较稳妥的做法是给 DaemonSet 里的容器设置环境变量IP_AUTODETECTION_METHODkubectl set env daemonset/calico-node -n kube-system IP_AUTODETECTION_METHODinterfaceeth.*如果你的节点网卡是ens、enp之类就把正则改成interfaceens.*或interfaceen.*。这个改动会触发 DaemonSet 滚动重启执行后观察 Pod 状态kubectl rollout status daemonset/calico-node -n kube-system之所以推荐正则而不是直接写死一个网卡名是因为集群里不同节点可能网卡命名不完全一致用正则能兼容一批节点。如果确实所有节点都是同一张网卡也可以写成can-reach192.168.1.1这种形式让 Calico 通过探测能否到达某个网关地址来选网卡这种方式更贴近实际网络拓扑。3.2 根因二内核模块缺失导致主容器反复重启install-cni正常退出但calico-node主容器反复崩溃这种情况里日志经常出现这类内容Error: failed to create IPIP device: open /sys/class/net/tunl0: no such file or directory或Failed to create ipip tunnel: failed to ensure tunnel device: operation not permitted原因基本就是内核没加载ipip模块。Kubernetes 节点上跑 Calico如果用 IPIP 模式做跨节点封装没有这个模块就建立不了tunl0隧道接口主容器只能退出。在节点上执行modprobe ipip lsmod | grep ipip临时解决后为了保证重启不丢还需要把模块写入系统配置echo ipip /etc/modules-load.d/calico-ipip.conf systemctl restart systemd-modules-load这里有一个必须提醒的坑不是所有环境都支持 IPIP 模式比如某些云厂商的 VPC 网络限制了 L3 隧道协议或者出于安全考虑禁用了相关内核模块。遇到这种情况就别硬刚 IPIP 了把 Calico 切换到 VXLAN 模式更省心。VXLAN 走 UDP 封装兼容性普遍更好。改法是在 Calico 的 installation 或 felix 配置里把 IPIP 禁用、启用 VXLAN。判断当前集群用没用 IPIP可以看节点上有没有tunl0接口ip addr show tunl0如果接口存在且状态正常说明 IPIP 隧道是通着的如果接口不存在就要考虑是不是模式配置有问题。3.3 根因三RBAC 权限不足或 API 访问异常Calico 要用 Kubernetes 作为数据存储KDD 模式时需要访问 API Server 获取 Node、Pod、NetworkPolicy 等资源。如果 RBAC 权限没配好或者 ServiceAccount 缺失calico-node 启动后会立刻报权限类错误。典型日志关键词包括error getting node spec: nodes node-01 is forbidden: User system:serviceaccount:kube-system:calico-node cannot get resource nodes也可能是connection refused: dial tcp 10.96.0.1:443: connect: connection refused前者是 RBAC 权限不够后者是 API Server 访问地址不通。处理方式如果是权限问题最简单也最可靠的办法是用官方提供的 YAML 重新应用一遍把 RBAC 相关的资源重建kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml如果只想查问题先确认 ServiceAccount 存在kubectl get sa -n kube-system | grep calico kubectl get clusterrolebinding | grep calico如果是 API Server 连接问题先确认 kube-apiserver 的 Service 是否正常以及节点的 kubelet 配置能否正常接入集群。我遇到过一次比较隐蔽的情况calico.yaml 是从旧版本集群拷贝来的RBAC 资源和当前 K8s 版本不完全兼容导致部分权限校验失败。所以我会建议优先使用和集群版本匹配的官方清单重新部署而不是手动去补一条 ClusterRoleBinding。3.4 根因四数据存储连接异常etcd 或 KDD 模式Calico 支持两种数据存储模式etcd 模式和 Kubernetes API 数据存储KDD模式。如果你不是用官方默认清单部署而是用了基于 etcd 的旧配置那么 etcd 连接失败也是 calico-node 崩溃的常见原因。看环境变量可以确认模式kubectl set env daemonset/calico-node -n kube-system --list | grep DATASTORE如果输出里有DATASTORE_TYPEetcdv3说明走的是 etcd 模式。此时需要检查 etcd 地址、证书路径配置ETCD_ENDPOINTS ETCD_CA_CERT_FILE ETCD_CERT_FILE ETCD_KEY_FILE常见错误日志Error listing network policies: client: etcd cluster is unavailable or misconfigured或connection refused: dial tcp 192.168.x.x:2379: connect: connection refused现在新版本官方清单默认用 KDD 模式DATASTORE_TYPEkubernetes。这种模式下 Calico 直接读写 Kubernetes API 的资源不依赖独立 etcd。如果你不是必须使用 etcd 模式比如要对接非 K8s 的 Calico 网络建议迁移到 KDD能少维护一个组件排障时也少一层复杂度。3.5 根因五CNI 配置残留或目录权限问题这类问题多在已有集群里“升级”或“重新部署” Calico 时出现。install-cni容器需要往宿主机/etc/cni/net.d/写配置文件往/opt/cni/bin写二进制。如果目录权限不对或者目录里已有其他 CNI 的配置文件安装过程可能失败。日志里可能看到failed to write config file: open /etc/cni/net.d/10-calico.conflist: permission denied或The directory /etc/cni/net.d exists and is not empty解决思路清理宿主机上旧的 CNI 配置只保留一份 calico 配置文件。确保宿主机目录有写权限ls -ld /etc/cni/net.d /opt/cni/bin查看属主和权限。如果目录不存在先创建并赋权mkdir -p /opt/cni/bin /etc/cni/net.d chmod 755 /opt/cni/bin /etc/cni/net.d还有一种情况是 SELinux 开启导致容器写宿主目录受限。如果节点开启了 SELinux需要检查对应的策略或暂时用chcon调整目录上下文。这个在 CentOS/RHEL 系列系统上出现概率比较高Ubuntu 默认不开 SELinux反倒少遇。4. 一次完整排障实录从报错到恢复4.1 现场状态与日志摘录讲一个我实际处理过的案例。某次给一个测试集群扩容加入三台新节点后kube-system下的 calico-node Pod 有几个一直起不来状态如下calico-node-abcde 0/1 Init:CrashLoopBackOff 5 4m12s calico-node-fghij 0/1 Init:CrashLoopBackOff 3 2m30sdescribe没有报挂载或沙箱错误Events 里只有容器重启的记录。于是直接抓 Init 容器日志kubectl logs -n kube-system calico-node-abcde -c install-cni --previous | tail -n 40关键的输出是2025-xx-xx 10:22:31.123 [INFO][1] install_cni.go: 第 1 次尝试安装 CNI 2025-xx-xx 10:22:31.130 [ERROR][1] install_cni.go: 无法检测到默认路由对应的网卡: no default route found看到“no default route found”就明白了新节点上没有配置默认路由或者路由表里没有可用的默认网关导致 Calico 不知道往哪个网卡上分配 IP。4.2 定位与修复过程先上节点看一眼路由表ip route show果然三台新节点里有路由表只有子网路由没有默认路由。这种情况多半是初始化节点时网络配置遗漏了。临时处理可以先加一条默认路由比如ip route add default via 192.168.10.1 dev eth0不过临时加路由重启就没了根本解决办法是修正节点的网络配置让它能自动获取默认路由。这里不再展开网络配置细节重点是路由恢复后让 Calico 自己重新探测。考虑到以后可能还会遇到节点网卡命名差异我把IP_AUTODETECTION_METHOD先显式指定成interfaceeth.*kubectl set env daemonset/calico-node -n kube-system IP_AUTODETECTION_METHODinterfaceeth.*然后滚动重启 DaemonSetkubectl rollout restart daemonset/calico-node -n kube-system kubectl rollout status daemonset/calico-node -n kube-system几分钟后看 Podkubectl get pods -n kube-system | grep calico全部变成Running。4.3 修复后的验证清单容器起来了不代表网络真的通了一定要做连通性验证。我习惯按下面的清单逐项检查Pod 状态确认kubectl get pods -n kube-system | grep calico全部 Running 且READY为 1/1。节点路由检查在节点上执行ip route能看到cali开头的接口路由以及到其他节点 Pod 网段的路由。跨节点互通测试创建一个测试 Pod比如kubectl run test --imagebusybox -- sleep 3600然后kubectl exec进去 ping 另一个节点上的 Pod IP。能 ping 通说明跨节点网络路径是通的。网络策略检查如果业务依赖 NetworkPolicy再验证一下策略是否生效这个容易被忽略。如果步骤 2 或 3 失败说明虽然 calico-node 进程起来了但路由或 iptables 规则没收敛这时候需要进一步看 Felix 日志和 BIRD 日志。5. 常见错误日志速查表与避坑经验5.1 速查表我把日常排障里最常遇到的 calico-nodeInit:CrashLoopBackOff现象整理成一张表方便你对照排查日志关键词可能原因处理手段no default route found/Unable to detect the network interface节点网卡探测失败、默认路由缺失、多网卡选错检查路由表显式设置IP_AUTODETECTION_METHODfailed to create IPIP device/open /sys/class/net/tunl0: no such file内核缺少 ipip 模块modprobe ipip并写入/etc/modules-load.d/或切换 VXLAN 模式is forbidden: User ... cannot get resourceRBAC 权限不足重新应用官方 calico.yaml检查 ClusterRoleBindingconnection refused: dial tcp 10.96.0.1:443无法连接 Kubernetes API Server检查 API Server Service、kubelet 状态、网络连通性etcd cluster is unavailable or misconfiguredetcd 模式配置异常或 etcd 不可达检查ETCD_ENDPOINTS和证书配置必要时迁移到 KDDpermission denied写入 CNI 配置目录宿主机目录权限、SELinux修复目录权限调整 SELinux 上下文The directory /etc/cni/net.d exists and is not empty存在旧 CNI 配置文件清理旧配置保留一份 calico 配置这张表不能覆盖所有情况但覆盖了绝大多数我见过的生产报障。核心思路还是那句先确定是 Init 容器崩还是主容器崩再根据日志关键词找根因。5.2 避坑清单排障多了有几个坑特别值得记下来。第一个坑删除或覆盖 CNI 配置过于粗暴。有些教程会让你直接删掉/etc/cni/net.d下的文件然后重启 kubelet但如果没有把 Calico 的 DaemonSet 一起处理干净kubelet 会因为找不到 CNI 配置而拒绝创建新的 Pod。正确做法是先用官方清单清理kubectl delete -f calico.yaml再处理残留文件避免“一边创建一边清理”的混乱状态。第二个坑改完 DaemonSet 环境变量后没有检查 rollout 是否成功。kubectl set env是异步的新配置发布过程中可能有一段时间部分节点旧的 calico-node 还在运行。如果直接在同一时刻去验证网络很可能误判。我习惯先执行kubectl rollout status等滚动完成再看结果。第三个坑在 IPIP 和 VXLAN 模式之间切换时没有清理旧 tunnel 接口。如果之前用的是 IPIP节点上已经有tunl0切换到 VXLAN 后可能会有残留路由或 iptables 规则干扰。稳妥起见大版本切换模式后直接重启节点或者至少手动清一遍 Calico 相关的 iptables 链再让 Felix 重建。第四个坑忽略 kubelet 自身的 CNI 配置参数。有些发行版在 kubelet 启动参数里指定了--cni-conf-dir和--cni-bin-dir为非默认路径。如果 Calico 的install-cni容器默认写入/etc/cni/net.d而 kubelet 读的是/var/lib/cni/net.d那即使 calico-node 看起来正常Pod 也建不起来。检查 kubelet 参数是排查 CNI 问题永远不能跳过的一步。最后分享一点小经验排过这么多次 calico-node 的故障我发现一个问题很多人看到CrashLoopBackOff就觉得是天大的事急着去网上搜命令反而忽略了最基础的日志排查。其实 calico 的设计已经帮你把问题分类好了——Init 容器负责“装环境”主容器负责“跑服务”你只需要先分清失败发生在哪一步然后去看那一步的日志大部分问题不会超过半小时就能定位。我个人的习惯是凡是动网络插件哪怕只是改一个环境变量也要先做好回滚预案。DaemonSet 的好处是配置可以快速回滚kubectl rollout undo daemonset/calico-node -n kube-system一下就回去了。真正需要谨慎的是手动去节点上删文件、清 iptables——这些操作没有版本管理搞错了只能靠重启节点救场。线上环境如果条件允许先在一台测试节点上验证修复方案再推向全集群。稳永远是第一位的。