云主机Kubernetes部署失败如何排查?
- 来源:纵横数据
- 作者:中横科技
- 时间:2026/5/22 17:12:14
- 类别:新闻资讯
说实话,Kubernetes这东西,用起来确实爽,但部署起来也真能让人掉层皮。尤其是在云主机上从头搭建一套K8s集群,失败的概率高得惊人。我前前后后在各种云平台上部署过几十次Kubernetes,有成功的,但更多的时候是卡在某个步骤上,然后开始漫长的排查。
这篇文章我想把这些年踩过的坑,尤其是部署失败后的排查思路,系统地整理一下。希望能帮你少走一些弯路。
部署前的准备工作:基础环境检查
讲具体案例之前,我先说一个最重要的经验:Kubernetes部署失败,至少有七成的原因是基础环境没准备好。很多人一上来就急着装kubeadm、初始化集群,结果卡住了才开始检查主机名、网络、时间这些基础的东西。
有一次我帮一个朋友排查集群部署失败的问题。他按照官方文档一步步操作,执行kubeadm init之后,集群初始化到一半就报错了。错误信息很长,核心意思是kubelet无法启动,apiServer健康检查失败。折腾了两个小时,最后发现是两台云主机的主机名都叫localhost。Kubernetes依赖主机名来区分节点,两个相同的主机名导致etcd选举的时候出现了混乱。改了主机名,重新初始化,不到十分钟集群就起来了。
基础环境检查至少要过这几关。主机名不能重复,而且不能包含下划线或者大写字母,Kubernetes对主机名有严格的格式要求。时间要同步,集群里所有节点的时间误差不能超过几秒钟,否则证书验证会失败。云主机上可以先安装ntp或者chrony做时间同步。关闭swap分区,kubelet要求swap必须关闭,否则启动的时候会报错。检查网络,确保节点之间可以互相访问,端口要开放,尤其是6443、2379、2380、10250这些关键端口。
还有一个容易被忽略的点是内核参数的调整。Kubernetes对网络和存储的要求比较高,默认的内核参数不一定够用。比如net.bridge.bridge-nf-call-iptables这个参数必须设置为1,否则网络包在网桥上的转发会有问题。另外,overlay2存储驱动需要内核模块支持,用lsmod检查一下。
kubeadm init阶段失败:从日志里找线索
kubeadm init是整个部署过程中最容易出问题的环节。这个命令干了太多事情,预检、生成证书、创建静态Pod、启动控制平面组件,任何一个子步骤失败,整个初始化过程就会中断。
我遇到过的最让人头疼的一次,是在一台配置较低的云主机上执行kubeadm init。命令执行之后,屏幕上打印了Preflight检查通过的提示,然后就开始卡住了,过了很久才报超时错误。查看了kubelet的日志,journalctl -u kubelet -n 100,发现kubelet一直在尝试拉取几个控制平面组件的镜像,但每次都超时。
原因是这台云主机在国内,镜像仓库默认指向的是Google的gcr.io,这个域名在国内访问极慢甚至直接被封。解决方案是使用国内的镜像仓库,或者配置代理。kubeadm init的时候可以用--image-repository参数指定阿里云或者其它可访问的镜像仓库地址。加上这个参数之后,镜像拉取就快多了,初始化也就顺利完成了。
还有一个常见的情况是cgroup driver不一致。Docker使用的cgroup driver是systemd还是cgroupfs,kubelet使用的cgroup driver必须跟Docker保持一致。如果不一致,kubelet虽然能启动,但是Pod运行会有问题。初始化失败的时候错误信息可能不太明显,需要仔细看kubelet日志才能发现。可以在kubeadm init的配置文件中显式指定kubelet的cgroup driver,或者在初始化之后修改kubelet的配置文件再重启。
容器运行时的问题:Docker以外的选择
虽然Kubernetes在1.20版本之后宣布逐步弃用Docker,但在很多云主机的部署场景里,大家还是在用Docker作为容器运行时。Docker本身的问题会导致Kubernetes部署失败。
有一个案例是这样的。一个团队在云主机上部署Kubernetes,Docker已经安装好了,也能正常运行容器。但是执行kubeadm init的时候,预检阶段报了一个错误,说容器运行时没有正常运行或者没有配置。检查了半天,发现是因为Docker服务虽然启动了,但是cgroup驱动没有设置正确,导致kubelet无法跟Docker正常通信。
解决方法是修改Docker的daemon.json文件,明确指定cgroup驱动为systemd。修改完成之后重启Docker,再重新执行kubeadm init,预检就通过了。后来这个经验被写进了团队的部署文档,每次部署新集群都会先检查这个配置。
如果你用的是containerd而不是Docker,那问题可能会更多一些。containerd的配置文件比较复杂,尤其是CRI插件的配置,很容易写错。我个人的建议是,如果不是特别熟悉containerd,部署Kubernetes的时候先用Docker,等集群跑起来了再考虑迁移到containerd。
网络插件安装失败:没有网络的集群像个孤岛
Kubernetes集群初始化成功之后,节点状态通常是NotReady。这是因为还没有安装网络插件。网络插件是Kubernetes最复杂的组件之一,安装失败的概率很高。
我经历过的一次比较大的故障是这样的。集群初始化成功了,节点状态也变成Ready了,但是部署一个简单的Nginx Pod之后,Pod一直处于ContainerCreating状态,过了一段时间报错说网络超时。检查了网络插件的Pod日志,发现Calico的Pod一直在报错,说找不到主机的网卡接口。
原因是这台云主机使用了虚拟网卡,网卡名字不是传统的eth0而是别的命名方式,Calico默认的配置检测不到正确的网卡。解决方案是在Calico的配置文件中指定网卡检测的正则表达式,匹配云主机上实际的网卡名称。改了这个配置之后,重新应用网络插件的YAML文件,网络就通了,Pod也能正常运行了。
网络插件种类很多,Flannel、Calico、Weave、Cilium各有各的特点。每种插件在云主机上部署的时候都有一些特殊的配置要求。Flannel相对简单,但性能和功能不如Calico。Calico功能强大,但配置复杂,尤其是在云环境里,经常需要手动指定IP池和网卡接口。我的建议是,第一次部署的时候先用Flannel,等熟悉了整个流程再换成更复杂的网络插件。
另外要注意的是,网络插件的YAML文件里的镜像地址。很多网络插件的默认镜像也在gcr.io或者quay.io上,国内云主机拉取可能失败。可以在部署之前先把镜像手动拉下来,或者修改YAML文件里的镜像地址为国内可访问的镜像仓库。
节点加入失败:证书和token的问题
控制平面初始化之后,要把工作节点加入集群。这一步看似简单,但也是容易出问题的地方。最常见的错误是token过期或者证书不匹配。
默认情况下,kubeadm init生成的token有效期是24小时。如果你在初始化集群之后没有马上加入节点,而是过了几天才做,token就已经过期了。这时候需要用kubeadm token create重新生成一个新的token。另外还有一个发现CA证书的哈希值,如果加入节点的时候没有指定--discovery-token-ca-cert-hash,或者指定的哈希值不对,加入也会失败。
我处理过一个比较棘手的问题。一个工作节点加入集群的时候,kubeadm join命令执行成功了,但是在控制平面上用kubectl get nodes看不到这个节点,或者看到的节点状态一直是NotReady。登录到工作节点上看kubelet日志,发现kubelet一直在尝试连接apiServer,但证书验证失败了。原因是控制平面上的CA证书在某个时候被重新生成了,工作节点上的旧证书没有被更新。
解决办法是清空工作节点上的/etc/kubernetes目录,重新执行kubeadm join。如果还是不行,可以考虑用kubeadm reset把工作节点彻底清理干净,然后再重新加入。
还有一个容易被忽略的点是云主机的内网IP和外网IP问题。云主机通常有两个IP,一个内网一个公网。Kubernetes默认使用主机名解析到的IP地址作为节点的通信地址。如果解析出来的是公网IP,而云主机之间的内网通信用的是内网IP,就会出现网络不通的情况。可以在kubeadm init的时候用--apiserver-advertise-address指定apiServer监听的内网IP,这样节点加入的时候就会用内网IP跟apiServer通信。
Pod启动失败:镜像拉取和资源限制
集群部署好了,节点也Ready了,终于可以部署应用了。但这时候又可能出现新问题,Pod启动失败。虽然这不算是严格意义上的集群部署失败,但如果你连一个测试Pod都跑不起来,那这个集群其实跟没部署也差不多。
镜像拉取失败是最常见的原因。云主机访问不了镜像仓库,或者镜像仓库需要认证,或者镜像标签写错了。解决方案跟之前提到的类似,配置镜像加速器,或者把镜像提前拉到本地,或者使用私有镜像仓库的时候配置imagePullSecrets。
另一个常见的原因是资源不足。Pod申请的CPU或者内存超过了节点上可分配的资源量,调度器会一直等待,Pod就会处于Pending状态。用kubectl describe pod可以看到具体的调度失败原因。要么降低Pod的资源请求,要么给集群增加新的节点,要么释放节点上已有的资源。
高可用部署的额外挑战
如果你的目标是部署一套高可用的Kubernetes集群,那复杂度会成倍增加。高可用部署需要多个控制平面节点,还需要一个负载均衡器来分发apiServer的流量。任何一个组件出问题,整个控制平面都可能不可用。
我参与过一个高可用集群的部署,用的是kubeadm的stacked etcd拓扑,也就是每个控制平面节点上同时运行etcd。部署过程本身还算顺利,但是当一个控制平面节点宕机之后,剩下的控制平面无法正常工作,etcd选举失败。
排查了很久才发现,问题出在负载均衡器的健康检查配置上。负载均衡器把流量分发到所有控制平面节点,但其中一个节点因为某些原因没有响应健康检查,负载均衡器就把这个节点从后端摘掉了。剩下的两个节点虽然还在,但是etcd集群要求多数节点存活才能正常工作,两个节点中的任何一个挂掉,集群就失去了多数优势。解决方案是使用三个控制平面节点,并且确保负载均衡器的健康检查配置合理,不会轻易把节点踢出。
监控和日志:排查的左右手
说了这么多具体的案例和方法,其实我最想强调的一点是,排查Kubernetes部署失败,最有力的工具不是经验也不是直觉,而是日志和监控。
kubectl describe可以查看资源对象的详细状态和事件。kubectl logs可以查看Pod里容器的输出日志。journalctl -u kubelet可以查看kubelet的日志。各个控制平面组件的日志可以通过kubectl logs查看静态Pod,或者直接到/etc/kubernetes/manifests目录下的日志文件里找。
把这些日志串起来看,大多数问题的根因都能找到。比如Pod起不来,先看Pod的状态,再describe看事件,然后看Pod里容器的日志,最后看节点上kubelet的日志。层层递进,总能找到线索。
总结
云主机上部署Kubernetes失败,本质上是因为Kubernetes对底层环境的要求太多了。主机名、时间、网络、内核参数、容器运行时、镜像仓库、证书、端口、资源配额,任何一个地方没准备好,部署就会卡住或者失败。
我的经验是,部署之前先把基础环境检查清单过一遍。部署过程中遇到失败,不要急着重新开始,先看日志。kubeadm init失败了看kubelet日志,kubeadm join失败了看工作节点上的kubelet日志,Pod起不来了用kubectl describe和kubectl logs看详情。
还有一点很重要,不要贪图一次性部署成功。复杂的东西拆解开来做,先搭一个单节点的测试集群,把流程跑通,然后再扩展到多节点。先选最简单的网络插件,等集群稳定了再考虑换更高级的。
Kubernetes的学习曲线确实很陡,但每一次部署失败其实都是一次学习的机会。把失败的原因记下来,把排查的过程整理成文档,慢慢地你就会发现,那些曾经让你头疼的问题,其实都有规律可循。




使用微信扫一扫
扫一扫关注官方微信 

