news 2026/10/21 2:50:05

kubeadm init 初始化报错排查:cgroup 驱动与镜像拉取实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
kubeadm init 初始化报错排查:cgroup 驱动与镜像拉取实战

1. 集群初始化卡壳的典型现场还原

kubeadm init这个命令,但凡自己动手搭过 Kubernetes 集群的人,大概率都跟它有过一段不太愉快的回忆。报错信息刷屏、卡在某个步骤不动、日志翻来覆去看不出所以然,这些都是家常便饭。我见过不少朋友在这一步反复重装系统、反复重置环境,折腾一整天最后发现只是一个很基础的问题。这篇内容就是围绕kubeadm init初始化报错这个核心场景展开的,重点放在第二类高频故障的排查与解决上,适合正在自建集群、准备考相关认证、或者工作中需要维护 K8S 环境的运维和开发人员参考。

先说清楚这个场景到底在干什么。kubeadm init是 Kubernetes 官方提供的集群初始化工具,它做的事情可以拆成几个阶段:环境预检、生成证书、写入 kubeconfig 文件、启动控制平面组件(etcd、kube-apiserver、kube-controller-manager、kube-scheduler)、安装网络插件前的准备、最后输出加入集群的 token 命令。任何一个阶段出问题,命令都会中断并抛出错误。所谓“报错02”,我把它理解为在预检通过之后、控制平面启动阶段出现的第二类典型故障,这类故障往往跟容器运行时、镜像拉取、端口占用、cgroup 驱动配置有关,比第一类“环境预检不通过”更隐蔽,也更让人头疼。

为什么这类问题值得单独拿出来讲?因为第一类预检错误通常提示很明确,比如“swap 没关”“防火墙没关”“CPU 核数不够”,照着提示改就行。但第二类错误经常表现为组件起不来、日志里一堆连接拒绝、超时、权限不足,新手很容易懵。我自己的经验是,这类问题里超过一半都跟容器运行时和 kubelet 的配置有关,尤其是 cgroup 驱动不一致这个坑,几乎每个从 Docker 切换到 containerd 的人都会踩一次。

下面我会按照“整体思路拆解、核心细节解析、实操过程、常见问题排查”四个大块来讲,每一块都会给出我实际踩过的坑和验证过的解法。你不需要从头到尾按顺序读,遇到问题可以直接跳到对应章节查。

2. 初始化流程的整体设计与故障定位思路

2.1 为什么 kubeadm init 要分阶段执行

很多人不理解为什么kubeadm init要设计成这么多阶段,直接一把梭启动所有组件不行吗?其实这个分阶段设计恰恰是为了方便排查。你可以用kubeadm init --dry-run先跑一遍看它会做什么,也可以用kubeadm init phase单独执行某一个阶段。这种设计的好处是,当初始化失败时,你能精确定位到是哪个阶段挂了,而不是面对一个黑盒。

从架构上看,kubeadm 本身不负责长期运行容器,它只是把配置写好、把静态 Pod 的 manifest 文件放到/etc/kubernetes/manifests/目录下,然后由 kubelet 去监听这个目录并启动对应的静态 Pod。所以真正让控制平面跑起来的是 kubelet,而 kubelet 又依赖容器运行时。这条链路是:kubeadm 写配置 → kubelet 读取配置 → kubelet 调用容器运行时 → 容器运行时拉镜像并启动容器。任何一环断了,kubeadm init都会卡住或报错。

理解这条链路非常关键。我见过有人反复重跑kubeadm init,但其实问题出在 kubelet 服务本身没起来,重跑一百遍也没用。正确的做法是先看systemctl status kubelet,再看journalctl -xeu kubelet,最后看crictl ps -a确认容器运行时层面容器有没有被创建。这个排查顺序能帮你省下大量时间。

2.2 第二类报错的典型特征与定位路径

所谓第二类报错,我总结下来有几个典型特征:预检阶段显示[preflight]全部通过,然后进入[certs]、[kubeconfig]、[control-plane]阶段,在某一步卡住几十秒到几分钟,最后抛出类似timed out waiting for the condition、connection refused、context deadline exceeded这样的错误。这类错误的核心矛盾通常不在 kubeadm 本身,而在它依赖的下层组件。

定位路径我建议按这个顺序走:第一步确认容器运行时是否正常,systemctl status containerd或systemctl status docker;第二步确认 kubelet 是否正常,systemctl status kubelet;第三步用crictl ps -a看容器创建情况;第四步看具体组件的日志,比如crictl logs或者直接看/var/log/pods/下的日志;第五步检查端口占用和网络配置。这个顺序是从底层往上层走,能最快锁定问题边界。

提示:不要一上来就去看 kube-apiserver 的日志,如果容器运行时本身有问题,apiserver 的容器根本没被创建,看它的日志是浪费时间。先确认底层,再往上看。

2.3 环境差异带来的方案取舍

不同的操作系统、不同的容器运行时、不同的 Kubernetes 版本,报错表现会不一样。比如 CentOS 和 Ubuntu 在防火墙、SELinux、cgroup 版本上就有差异;Docker 和 containerd 在 cgroup 驱动默认值上也不一样。所以网上搜到的解法不能照搬,得先确认自己的环境。

我一般会先跑几个命令把环境摸清楚:cat /etc/os-release看系统版本,containerd --version或docker version看运行时版本,kubelet --version看 kubelet 版本,kubeadm version看 kubeadm 版本,swapon --show确认 swap 状态,free -h看内存,nproc看 CPU 核数。这几个信息凑齐了,再去对照官方文档的兼容性矩阵,基本就能判断出问题方向。

这里有个经验:kubeadm、kubelet、kubectl 三个组件的版本要尽量保持一致,小版本差异一般没事,但大版本跨度过大容易出问题。我遇到过 kubeadm 是 1.28 而 kubelet 还是 1.26 的情况,初始化时各种诡异报错,统一版本后一次通过。

3. 核心细节解析与高频报错实操要点

3.1 cgroup 驱动不一致:最常见的隐形杀手

cgroup 驱动不一致是我遇到最多的问题,没有之一。简单说,Linux 系统上有两种 cgroup 驱动:cgroupfs和systemd。容器运行时和 kubelet 必须使用同一种驱动,否则就会出现容器启动失败、kubelet 报错的情况。Docker 默认用cgroupfs,而 kubelet 从某个版本开始默认用systemd,两者一冲突,控制平面容器就起不来。

怎么确认当前用的是什么驱动?对于 containerd,可以看配置文件/etc/containerd/config.toml里的SystemdCgroup字段;对于 Docker,可以看/etc/docker/daemon.json里有没有"exec-opts": ["native.cgroupdriver=systemd"]。kubelet 这边可以看/var/lib/kubelet/config.yaml里的cgroupDriver字段,或者看 kubelet 启动参数。

解决方式就是统一。我推荐统一成systemd,因为它是目前的主流方向,也更稳定。containerd 的配置里把SystemdCgroup设为true,然后重启 containerd;kubelet 的配置里把cgroupDriver设为systemd。改完之后一定要systemctl daemon-reload再重启服务,否则配置不生效。

注意:改完 cgroup 驱动后,最好把之前残留的容器清理干净,用crictl rmi --prune清掉无用镜像,用crictl rm删掉残留容器,避免旧容器带着旧配置干扰新初始化。

3.2 镜像拉取失败:网络与仓库配置的双重考验

Kubernetes 的控制平面组件都是以容器镜像形式运行的,kubeadm init过程中需要拉取kube-apiserver、kube-controller-manager、kube-scheduler、etcd、pause等镜像。如果镜像拉不下来,初始化就会卡在[control-plane]阶段,最后超时报错。

镜像拉取失败的原因通常有两个:一是默认镜像仓库访问不畅,二是容器运行时的镜像仓库配置有问题。对于第一个原因,可以在初始化时通过--image-repository参数指定一个可访问的镜像仓库地址。对于第二个原因,需要检查 containerd 或 Docker 的镜像仓库配置,确认 registry 地址、认证信息是否正确。

我一般会先用kubeadm config images list把需要的镜像列出来,然后手动用crictl pull或docker pull逐个拉取,确认哪个拉不下来。这样比等kubeadm init超时要快得多。如果手动能拉下来,但 kubeadm 拉不下来,那多半是 kubeadm 用的仓库地址跟你手动用的不一致,检查--image-repository参数即可。

3.3 端口占用与防火墙拦截:容易被忽视的细节

控制平面组件需要占用一系列端口,比如 kube-apiserver 的 6443、etcd 的 2379 和 2380、kubelet 的 10250 等。如果这些端口被其他进程占用,或者被防火墙拦截,组件之间就无法通信,初始化会失败。

检查端口占用可以用ss -tlnp | grep 6443这样的命令,看端口是不是被别的进程占了。如果被占用,要么停掉占用进程,要么改 Kubernetes 的端口配置。防火墙方面,需要放行这些端口,或者在内网环境下直接关闭防火墙(生产环境不推荐,但测试环境可以接受)。

我踩过的一个坑是:之前装过一版 Kubernetes 没清理干净,etcd 的残留进程还在跑,占着 2379 端口,导致新集群初始化时 etcd 起不来。后来用ps -ef | grep etcd找到进程杀掉,再清理/var/lib/etcd目录,才恢复正常。所以重装集群前一定要做彻底的清理,kubeadm reset之后还要手动清理残留目录和进程。

3.4 主机名与 hosts 解析:小问题引发大故障

kubelet 在启动时需要用主机名去解析节点地址,如果主机名解析有问题,kubelet 就会报错。常见的情况是/etc/hosts里没有把主机名映射到正确的 IP,或者主机名包含了大写字母、下划线等不合规字符。

检查方式很简单,hostname看当前主机名,ping $(hostname)看能不能解析到正确 IP。如果解析不到,就在/etc/hosts里加一行127.0.0.1 你的主机名或者本机IP 你的主机名。主机名建议用全小写字母、数字和连字符,不要用下划线和大写。

这个问题的隐蔽性在于,它不一定在初始化时立刻报错,有时候是初始化看起来成功了,但节点状态一直是NotReady,排查半天才发现是主机名解析的问题。所以初始化之前先把主机名和 hosts 确认好,能省掉很多后续麻烦。

4. 完整实操过程与关键环节实现

4.1 初始化前的环境清理与检查清单

在正式跑kubeadm init之前,我习惯先做一轮彻底的环境清理和检查。这一步看起来繁琐,但能避免大量重复劳动。清理的命令大致如下:

# 重置 kubeadm 配置 kubeadm reset -f # 停止并清理 kubelet systemctl stop kubelet systemctl stop containerd # 清理残留目录 rm -rf /etc/kubernetes/ rm -rf /var/lib/kubelet/ rm -rf /var/lib/etcd/ rm -rf /etc/cni/net.d/ rm -rf $HOME/.kube # 清理 iptables 规则 iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X # 重启容器运行时 systemctl start containerd

清理完之后,按清单逐项确认:swap 是否关闭(swapon --show输出为空)、防火墙是否放行或关闭、SELinux 是否设为 permissive 或 disabled、主机名和 hosts 是否正确、时间同步是否正常(timedatectl看 NTP 同步状态)、内核参数是否配置(br_netfilter和ip_forward)。这些检查项每一项都对应一类常见报错,提前确认能省下大量排查时间。

4.2 容器运行时与 kubelet 的配置对齐

以 containerd 为例,配置对齐的核心是让 containerd 和 kubelet 都用systemd作为 cgroup 驱动。containerd 的配置文件在/etc/containerd/config.toml,如果没有这个文件,可以用containerd config default > /etc/containerd/config.toml生成一份默认配置,然后修改其中的SystemdCgroup字段:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true

同时把sandbox_image改成与你的 Kubernetes 版本匹配的 pause 镜像地址。改完之后重启 containerd:systemctl restart containerd。

kubelet 这边,如果是用 kubeadm 初始化的,kubelet 的配置会在初始化时自动生成,默认就是systemd驱动。但如果你之前手动改过/var/lib/kubelet/config.yaml,就要确认里面的cgroupDriver字段是systemd。确认无误后,systemctl daemon-reload && systemctl restart kubelet。

提示:配置改完后,用containerd config dump | grep SystemdCgroup确认配置真的生效了,不要只看文件内容,因为有时候配置项位置写错了不会报错但也不生效。

4.3 执行初始化并实时观察组件状态

配置对齐之后,就可以执行初始化了。我一般会用这样的命令:

kubeadm init \ --apiserver-advertise-address=本机IP \ --image-repository=可访问的镜像仓库地址 \ --kubernetes-version=v1.28.0 \ --pod-network-cidr=10.244.0.0/16 \ --cri-socket=unix:///run/containerd/containerd.sock

参数说明一下:--apiserver-advertise-address指定 apiserver 对外通告的地址,多网卡机器上一定要指定,否则可能选错网卡;--image-repository指定镜像仓库,解决拉取问题;--pod-network-cidr指定 Pod 网段,后面装网络插件要用,必须和插件配置一致;--cri-socket指定容器运行时 socket,多运行时共存时必填。

初始化过程中,另开一个终端实时观察。先看 kubelet 日志:journalctl -xeu kubelet -f;再看容器状态:watch crictl ps -a。正常情况下,你会看到 etcd、kube-apiserver、kube-controller-manager、kube-scheduler 这几个容器依次被创建并进入 Running 状态。如果某个容器反复重启或一直处于 Created 状态,就说明有问题,这时候去看对应容器的日志。

4.4 初始化成功后的收尾与验证

初始化成功后,kubeadm 会输出一段配置 kubeconfig 的命令,照着执行:

mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config

然后验证节点状态:kubectl get nodes。这时候节点状态应该是NotReady,因为还没装网络插件。装完网络插件后,节点会变成Ready。网络插件的选择上,Flannel 配置简单适合入门,Calico 功能强适合生产,Cilium 性能好适合对网络要求高的场景。选哪个取决于你的需求,但要注意插件的 Pod CIDR 必须和初始化时的--pod-network-cidr一致。

验证集群健康状态可以用kubectl get pods -n kube-system看所有系统 Pod 是否 Running,用kubectl get componentstatuses看核心组件状态(这个命令在新版本里已废弃,可以用kubectl get --raw='/readyz?verbose'替代)。全部正常后,这个集群就算搭好了。

5. 常见问题与排查技巧实录

5.1 高频报错速查表

报错信息可能原因排查方向解决方式
timed out waiting for the condition组件容器未启动看 kubelet 日志、crictl ps -a检查 cgroup 驱动、镜像拉取
connection refused到 6443apiserver 未起来看 apiserver 容器日志检查端口占用、证书、etcd 状态
failed to pull image镜像仓库不可达手动 crictl pull 测试换镜像仓库、检查网络
kubelet not runningkubelet 服务异常systemctl status kubelet看日志、检查配置、重启服务
port 2379 already in useetcd 残留进程ss -tlnp 查端口杀进程、清理 etcd 目录
node NotReady网络插件未装kubectl get pods -n kube-system安装网络插件、检查 CIDR
cgroup driver mismatch驱动不一致看 containerd 和 kubelet 配置统一为 systemd

这张表是我自己整理出来备查的,基本覆盖了八成以上的初始化报错。遇到问题先对号入座,能快速缩小排查范围。

5.2 日志排查的正确姿势

很多人排查问题时不知道看哪个日志,这里我把常用的日志位置和查看方式列一下。kubelet 的日志用journalctl -xeu kubelet,这是最关键的日志,kubelet 起不来什么都白搭。容器运行时的日志,containerd 用journalctl -xeu containerd,Docker 用journalctl -xeu docker。控制平面组件的日志,可以用crictl logs 容器ID看,也可以直接看/var/log/pods/下对应目录的日志文件。

看日志有个技巧:先看时间戳,定位到报错发生的时间点,然后从那个时间点往前看几十行,通常能找到根因。不要从日志开头一行行看,效率太低。另外,kubeadm init本身的日志可以用kubeadm init --v=5提高日志级别,输出更详细的信息,排查疑难问题时很有用。

5.3 几个容易忽略的坑

第一个坑是时间不同步。Kubernetes 组件之间用证书认证,证书对时间敏感,如果节点时间偏差太大,认证会失败。用timedatectl确认 NTP 同步是 active 状态,没有的话装个 chrony 或 ntp 同步一下。

第二个坑是内核参数没配。br_netfilter和ip_forward这两个参数必须开启,否则 Pod 之间网络不通。配置方式是在/etc/modules-load.d/k8s.conf里加br_netfilter,在/etc/sysctl.d/k8s.conf里加net.bridge.bridge-nf-call-iptables=1和net.ipv4.ip_forward=1,然后sysctl --system生效。

第三个坑是磁盘空间不足。容器镜像和日志很占空间,/var分区如果太小,初始化到一半可能因为磁盘满而失败。初始化前用df -h确认/var至少有 20G 以上可用空间。

第四个坑是 SELinux 没关。某些系统默认开启 SELinux,会阻止容器访问某些目录。测试环境建议直接设为 permissive:setenforce 0,并修改/etc/selinux/config永久生效。

5.4 重置与重试的正确流程

初始化失败后,不要直接重跑kubeadm init,一定要先kubeadm reset。reset 会清理 kubeadm 写入的配置和证书,但不会清理容器运行时里的容器和镜像,也不会清理 etcd 数据目录。所以完整的重置流程是:kubeadm reset -f→ 清理残留目录 → 清理 iptables → 重启容器运行时和 kubelet → 再重新初始化。

我自己的习惯是写一个重置脚本,把清理步骤都串起来,每次重试前跑一遍,保证环境干净。这样虽然每次多花一两分钟,但能避免因为残留配置导致的诡异问题,总体上是省时间的。

6. 个人实操体会与后续扩展方向

折腾kubeadm init这些年,我最大的体会是:报错不可怕,可怕的是没有章法地乱试。绝大多数初始化失败都能归到有限的几类原因上,只要建立起“从底层往上层排查”的思路,配合日志和状态检查,基本都能定位到根因。cgroup 驱动、镜像拉取、端口占用、主机名解析这几个点,覆盖了我遇到问题的绝大部分场景,把这几个点吃透,初始化成功率会大幅提升。

另外,环境清理这件事值得反复强调。很多人失败后急于重试,结果旧配置和新配置混在一起,问题越来越复杂。养成“失败先重置、重置要彻底”的习惯,能帮你省下大量时间。我现在的做法是每次初始化前都跑一遍检查清单,确认环境干净、配置对齐,再执行初始化,一次通过的概率明显提高。

后续如果要把这套经验扩展,可以考虑几个方向:一是把初始化流程脚本化,把检查、清理、配置、初始化串成一个脚本,减少手动操作;二是研究高可用集群的初始化,多控制平面节点的场景下,负载均衡和证书分发是新的挑战;三是结合基础设施即代码工具,把集群搭建纳入自动化管理。这些方向我后续会陆续整理,有兴趣的可以自己先动手试试。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 2:49:38

开箱即用的EasyOCR:从环境搭建到调优避坑的完整实践

最近整理一批旧书扫描件时,我满脑子都在琢磨怎么把书页文字转成可搜索的文本。试了一圈之后,真正让我省心的是一款光学字符识别工具——EasyOCR。它给我的第一印象就是标题里的四个字:开箱即用。装好Python包、加载模型、调用接口&#xff0c…

作者头像 李华
网站建设 2026/10/21 2:49:28

大模型 API token 计费全拆解:成本估算、监控与优化策略

最近不少同学在群里吐槽:模型效果是挺好,但每天调接口像在烧钱,月底一看账单直接懵了。尤其是做 AI 应用、写自动化脚本、接客服机器人的朋友,对 token 的消耗速度非常敏感。很多时候,我们只关心“调用一次花多少钱”&…

作者头像 李华
网站建设 2026/10/21 2:49:18

Qt飞机大战C++源码解析:从游戏循环到碰撞检测

简介:这是一份基于Qt与C开发的飞机大战2D射击游戏项目,面向有C基础、希望系统学习Qt游戏编程的初学者和课程设计者。压缩包共包含1017个文件,总大小约42.88MB,以cpp、h源文件及pro工程为主干,附有大量png、jpg、bmp图片…

作者头像 李华
网站建设 2026/10/21 2:49:16

Foxmail Server v2.0 公测版Windows部署全指南

简介:本资源为Foxmail Server For Windows v2.0公测版安装包,面向中小型企业IT管理员、邮件系统初学者及国产化邮件服务实践者,提供轻量级、全中文、开箱即用的自建邮件服务器解决方案。资源包共2个文件(1个安装程序exe、1个说明h…

作者头像 李华
网站建设 2026/10/21 2:49:15

Python歌词文本分析实战:中文分词、词频统计、情感分析与词云可视化

最近在整理歌曲数据时,发现不少朋友对“歌名热度”“歌词高频词”“情感倾向”这类分析很感兴趣。正好《思念漫过海岸线》这首歌在网络上讨论不断,相关话题也比较热,我便以它为例,完整跑通了一套“从零搭建歌曲歌词文本分析与可视…

作者头像 李华
网站建设 2026/10/21 2:48:32

目前短视频账号互动率下跌严重------预计每天总播放量>5000

预计可能会从每日2500互动减少到1000互动,最不利的情况可能会下跌到500互动/天。所以怕个屁啊,那不是还有500各客户吗?只要文案搞得好,每天转化几个我都愿意。--------一个好消息是:抖音粉丝数量>2000,那么每天视频…

作者头像 李华