☰
Kubernetes 1.33.7集群部署实战:kubeadm踩坑与网络插件配置指南
2026/10/2 9:13:35 网站建设 项目流程

上周帮项目组交付一套新的容器云底座,目标版本就锁在 Kubernetes 1.33.7。整个部署从裸机配置、容器运行时安装,到控制平面初始化、工作节点接入,前后大约花了半天时间。网上关于 Kubernetes 安装部署的资料一直不少,但版本一换,参数和坑也跟着变。尤其 1.33.x 这套,很多人还拿着 1.26 甚至更早的老教程硬套,结果在 kubeadm init 阶段被各种 preflight 报错磨掉耐心。这篇文章不绕弯子,直接按我这次实际部署的流程来写,把每个关键参数为什么这么设、每个坑怎么踩的都讲明白,适合刚上手 K8s、以及被 kubeadm 错误日志劝退的运维和开发同学参考。文章用的环境是三台 Linux 服务器,一控两员,生产小规模或实验环境都够用。

1. 部署方案与版本选型思路

1.1 为什么锁定 1.33.7 这个版本号

先说选型。Kubernetes 的版本迭代节奏是三个月一个大版本,1.33 属于 2025 年上半年发布的功能版本。但真正适合拿来落地建集群的,一般不会是 .0,因为首发的 .0 往往会有一些内核适配、组件兼容性上的打磨问题。1.33.7 是 v1.33 系列的第 7 个补丁版本,意味着它已经吸收了 6 轮安全修复和稳定性回改,像 kubelet 在节点状态上报、kube-proxy 在 ipvs 模式下的规则收敛、调度器在特定配额场景下的性能回退这类问题,通常都会在补丁版本里得到处理。所以如果不想追新也不愿意用太旧的版本,选一个维护窗口内的稳定补丁版是性价比最高的选择。

这里要特别说明一点:Kubernetes 官方对 minor 版本的支持窗口是最近三个版本,1.33.x 在很长一段时间内都在安全更新覆盖范围内,软件供应链角度也说得过去。如果你所在的公司有合规要求,一般还会要求在锁定的版本区间内把镜像全部同步到内部仓库,补丁版本恰好适合做这种资产盘点。

1.2 kubeadm、二进制、发行版工具,怎么选不后悔

安装 K8s 集群的主流方式有几种:官方 kubeadm、纯二进制手动部署、RKE2、以及云厂商托管。我的建议很直接:除非你有强烈的离线交付需求或想彻底掌控每一个组件,否则用纯二进制方式部署的成本远高于收益。它需要你自己处理 etcd 证书签发、apiserver 的 service account 密钥、controller-manager 与 scheduler 的配置拆分,这套操作对排障能力要求非常高,新手很容易在证书和启动参数上绕不出来。

kubeadm 的优势在于它是官方维护的一等公民工具。证书、kubeconfig、静态 Pod manifest、引导 token 全部由它自动化生成,而且生成的产物路径高度统一,便于审计和后续升级。RKE2 这类发行版更偏向“开箱即用”,但它把很多细节封装掉了,出了问题反而更难定位。我的经验是:先用 kubeadm 把官方原生集群跑明白,你才能真正理解 K8s 控制平面的各个零件长什么样,之后再看其他发行版会容易得多。

1.3 三台机器的拓扑与端口规划

这次部署用一个最小但不过度简化的拓扑:一台 control-plane 节点,两台 worker 节点。如果生产环境要求高可用,把控制面扩展成三台并前置一个负载均衡器即可,kubeadm 对多控制面的支持已经相当成熟,原理和单控制面差别不大。

机器规模方面,控制面节点建议至少 2 核 4G,worker 节点建议 2 核 4G 起步,磁盘控制在 50G 以上。做实验可以更小,但 etcd 对磁盘 IO 比较敏感,机械盘会拖慢 apiserver 的响应。

安装前还需要把端口清单理清楚。控制面节点要放行 6443(apiserver)、2379/2380(etcd 通信)、10250(kubelet)、10259(scheduler)、10257(controller-manager);所有节点都要放行 10250、10256(kube-proxy),如果走 Calico 的 VXLAN 还要放行 4789/udp,走 BGP 则要放行 179/tcp。不要一上来就关掉防火墙图省事,理解端口比关防火墙更有用。

2. 环境准备:把裸机调成一个合格的 K8s 节点

2.1 系统选型与 cgroup v2 问题

我看到很多老教程还在用 CentOS 7.9 演示。CentOS 7.9 本身已经 EOL,更麻烦的是它默认使用 cgroup v1,内核停留在 3.10,这在较新的 Kubernetes 版本上会带来不少兼容性问题。Kubernetes 在 v1.31 之后进一步收紧了 cgroup v1 的支持,而像 kubelet 的内存管理、CPU 管理器这些功能在 v1.33 上基本都是以 cgroup v2 为基准设计和验证的。所以这次系统我选了 Rocky Linux 9.4,内核 5.14 以上,cgroup v2 直接就是默认状态,省去很多折腾。

装系统前先确认一下 cgroup 版本,一条命令足够:stat -fc %T /sys/fs/cgroup,输出cgroup2fs就说明是 v2。如果输出的是 tmpfs,那说明跑在 cgroup v1 下,要么换系统,要么在内核启动参数里加systemd.unified_cgroup_hierarchy=1再重启。另外,内核模块也要保证能加载 overlay 和 br_netfilter,这是容器网络和镜像分层的基础。

2.2 主机名、swap 与内核参数,最容易被忽略的环节

正式操作前,我习惯先把主机名和 hosts 写好。三台机器分别命名为 k8s-m01、k8s-w01、k8s-w02,然后在 /etc/hosts 里加上彼此的解析。K8s 集群的组件之间通过主机名互相通信,如果只靠 IP,后面 kubeadm 生成的证书里 SAN 可能对不上,你会看到莫名其妙的证书校验失败。

swap 必须关掉。swapoff -a之后,还要把 /etc/fstab 里对应的 swap 行注释掉,否则重启之后 swap 又回来了。kubelet 的资源管理基于 cgroup 和 Linux OOM 语义,开着 swap 会让 Pod 的内存回收和 QoS 判断变得不可预期,这是 K8s 的硬性要求,不是建议。接着写入内核参数:

cat > /etc/sysctl.d/k8s.conf <<EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sysctl --system

第一条参数解决的是容器流量经过 bridge 时也要被 iptables/netfilter 处理的问题,否则 NodePort 和 ClusterIP 的 DNAT 规则可能对同一节点的 Pod 流量不生效;第三条 ip_forward 则是保证容器网络包能在节点之间转发。这些参数两分钟内能配好,但漏掉任何一个,后续排障都够喝一壶。时间同步也可以顺手配上,chrony 或者 systemd-timesyncd 都行,控制面证书对时间偏移很敏感。

2.3 containerd 配置里的两个坑

Kubernetes 从 1.24 之后就完全移除了 dockershim,所以现在的主流做法是直接使用 containerd 作为容器运行时,没必要再套一层 docker。安装 containerd 建议直接装 1.7.x 或更新的稳定版,因为老版本对 CRI 的兼容性和新内核的适配都会差一些。

装好 containerd 后,第一件要做的事是生成默认配置:

containerd config default | tee /etc/containerd/config.toml

然后必须改两个地方。第一个是SystemdCgroup,这个值默认是 false,需要改成 true:

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

不同 containerd 版本里配置块的细节略有出入,总的原则是:让 runc 使用 systemd cgroup 驱动,和 kubelet 保持一致。第二个是sandbox_image,默认的 pause 镜像地址在某些受网络限制的节点上拉取会很慢甚至失败。如果公司内部有镜像仓库,建议把sandbox_image改成仓库里的 pause 镜像,并提前同步过去。这个值不配好,kubelet 启动时会一直报 failed to pull image,节点迟迟变不成 Ready。

配置改完后systemctl restart containerd。可以拉一个 busybox 镜像并用crictl pull验证 CRI 通路是否正常,这一步虽然简单,但能提前暴露仓库连通性和证书问题。

2.4 安装 kubeadm、kubelet、kubectl 并锁定版本

三件套的安装不算难,关键是版本要和集群一致。这里有三点容易被忽略:kubeadm、kubelet、kubectl 三个二进制必须锁同一个版本;软件源要使用官方提供的 Kubernetes 仓库,而不是发行版自带的过期版本;安装时最好显式指定版本号,不要直接yum install kubelet kubeadm kubectl装到最新,否则初始化的时候版本不匹配就尴尬了。

以 RPM 系为例:

cat > /etc/yum.repos.d/kubernetes.repo <<EOF [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.33/rpm/ gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.33/rpm/repodata/repomd.xml.key enabled=1 EOF yum install -y kubelet-1.33.7 kubeadm-1.33.7 kubectl-1.33.7

如果提示找不到 1.33.7 这个精确版本,先yum list kubeadm --showduplicates看一看可用版本列表,不同发行版的包版本号后面会带 build 后缀,写法类似1.33.7-150500.0。Debian 系的同学把 yum 换成 apt,仓库路径写在 sources.list 里,原理一样。

装完后执行systemctl enable --now kubelet。注意,这时候 kubelet 还没拿到配置和 static pod 清单,它会反复重启,日志里全是等待和报错,这是正常现象,不用慌,等 kubeadm init 完成后它自然就稳定了。很多新手在这一步被吓住,以为装坏了。

3. 控制平面初始化与集群装配全流程

3.1 用 kubeadm 配置文件替代一堆命令行参数

kubeadm init 支持大量命令行参数,但生产环境我更推荐写一个配置文件,把参数固化下来,这样团队复查和后续扩容都有据可依。先跑一遍kubeadm config print init-defaults,它会按当前版本打印一份标准模板,你只需要在模板基础上改。

apiVersion: kubeadm.k8s.io/v1beta4 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.10.41 bindPort: 6443 nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock --- apiVersion: kubeadm.k8s.io/v1beta4 kind: ClusterConfiguration kubernetesVersion: v1.33.7 controlPlaneEndpoint: 192.168.10.41:6443 imageRepository: registry.k8s.io networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: systemd

如果kubeadm config print init-defaults生成的模板里 apiVersion 和上面不同,以你本机生成的为准,把对应字段改掉即可,不要硬抄。advertiseAddress必须填当前节点在集群网络里可路由的 IP;controlPlaneEndpoint也一样。podSubnet的选择要和后面的 CNI 插件对应,我用的是 10.244.0.0/16,这是 Flannel 默认网段;如果准备用 Calico,建议统一规划成 172.16.0.0/16 或者 192.168.0.0/16,避免和现有办公网段冲突。

然后直接在控制面节点上执行:

kubeadm init --config kubeadm-config.yaml

这一步会持续一两分钟。看到类似[init] Using Kubernetes version: v1.33.7的输出就意味着正式进入安装流程。

3.2 初始化输出背后到底发生了什么

很多教程只贴命令,不讲输出,导致用户一看到[wait-control-plane]卡住就手足无措。其实 kubeadm init 的输出就是把控制面组件的每一步展开给你看:先是 preflight 检查,确认内核参数、端口、容器运行时、swap 都没问题;然后是证书生成和 kubeconfig 生成;接着把 etcd、kube-apiserver、kube-controller-manager、kube-scheduler 的 static Pod manifest 写到 /etc/kubernetes/manifests;再之后是等待控制面健康检查通过,最后生成 join token 并输出 kubeconfig 位置。

如果 init 卡在 preflight 检查,前面基础环境一定有遗漏;如果卡在[wait-control-plane],多半是 kubelet 起不来,重点去看 kubelet 日志;如果顺利走完,最后它会打印一串清晰的操作提示,包括三件套:kubectl 配置、join 命令、以及后续加入节点的 token 哈希。这部分内容建议存到临时文件里,别关掉终端就找不到了。

有一个经验:初始化前先把/var/lib/kubelet、/etc/kubernetes和/var/lib/etcd这些目录备份或清空,否则在同一个节点上重复 init 时,会因为这些残留目录报 directory not empty。重装环境最常见的坑就是这一条。

3.3 配好 kubectl 再装网络插件

控制面初始化完成后,用普通用户执行 kubectl 需要配置权限。官方输出里给的是 root 命令,实际使用中我建议按普通用户来做:

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

第一次kubectl get nodes会看到控制面节点处于 NotReady 状态,这是完全正常的,因为此时还没有安装 CNI 网络插件。这种状态下的控制面核心组件其实都在运行,etcd、apiserver、controller-manager、scheduler 都应该是 Running。可以用kubectl get pods -A确认一下 kube-system 命名空间里的状态,只要 coredns 等组件不是一直 Pending,就不必太担心。

3.4 Calico 还是 Flannel?网络插件安装要点

装网络插件这一步是很多同学最困惑的。Flannel 的好处是简单、轻量,默认用 10.244.0.0/16,适合快速跑通环境;Calico 的好处是完整支持 NetworkPolicy,性能在多层叠加场景下也更可控。我的建议是:如果只是学 K8s 或者内部实验,Flannel 足够;如果有网络策略需求、或者集群规模要往上走,直接上 Calico,省得以后迁移。

以 Calico 为例,通常做法是下载官方 manifest 后创建。由于 manifest 里默认的 pod CIDR 是 192.168.0.0/16,如果你的 podSubnet 不是这个值,需要修改 ConfigMap 里的CALICO_IPV4POOL_CIDR,或者下载后直接改环境变量:

curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.28.2/manifests/calico.yaml kubectl apply -f calico.yaml

创建后观察:

kubectl get pods -n kube-system -w

等 calico-node 和 calico-kube-controllers 都变成 Running,coredns 也会从 Pending 落到正常状态。节点从 NotReady 变成 Ready 通常需要一分钟左右。如果等了五分钟还是 NotReady,优先看 calico-node 的日志,多半是网卡自动选择错误、MTU 不对、或者是防火墙没有放行 VXLAN 端口。

3.5 工作节点加入与第一次跨节点验证

现在去两台 worker 节点,把之前保存的 join 命令粘进去执行。如果超过 24 小时 token 过期了,就在控制面节点上重新生成:

kubeadm token create --print-join-command

把生成的命令拿到 worker 节点执行即可。执行完回到控制面:

kubectl get nodes -o wide

三台节点都变成 Ready 后,我习惯第一时间跑一个真实的跨节点验证:创建一个三副本的 nginx deployment,然后用 NodePort 方式暴露,从集群外访问任意一个节点 IP 对应的端口。如果三次请求都能成功,说明 Service、kube-proxy、CNI 这条链路是通的,这个集群才算是真正能交付的底座。

4. 常见问题与排障技巧实录

4.1 preflight 检查和 init 阶段翻车合集

先整理一下最经典的 preflight 报错。第一种:[preflight] running pre-flight checks之后提示[ERROR Swap]: running with swap on is not supported。原因和解决办法都很直接,swapoff -a并把 fstab 里那行注释掉,重启后依然生效才算根治。第二种:提示 CRI 版本与 kubelet 不兼容,通常是 containerd 版本太老,升级到 1.7.x 以上基本能解决。第三种:端口被占用,常见于上一套集群没删干净,可以用ss -lntp | grep 6443确认占用进程。

如果 init 卡在[wait-control-plane]很长一段时间不动,不要再盯着终端发呆,直接打开另一个终端查 kubelet:

journalctl -u kubelet -f

日志里如果持续报 CRI 连接失败,先确认 containerd 是否在运行;如果报/var/run/containerd/containerd.sock不存在,检查 criSocket 路径是否填对;如果报 cgroup 驱动不一致,去改 containerd 的 SystemdCgroup。这个阶段九成问题都集中在容器运行时和 kubelet 的衔接上,跟 apiserver 关系不大。

4.2 节点 NotReady 和 CoreDNS 悬而不决

前面提到初始化完成后节点 NotReady 是正常的,但如果装完 CNI 插件好几分钟还不变,就要按顺序排查了。先kubectl get pods -A看 calico 相关 pod 是否正常,再kubectl describe pod <calico-node-xxx> -n kube-system看事件。最常见的情况是 calico-node 一直报 Error getting MTU 或者 auto-detected IPv4 address is not suitable,这是多网卡环境下的自动选择问题。解决方式是给 calico 配置网卡自动检测范围:

- name: IP_AUTODETECTION_METHOD value: "cidr=192.168.10.0/24"

CoreDNS 一直 Pending 还有一种典型情况:没有安装 CNI,它拿不到 Pod 网段的地址。有些教程把 flannel 安装步骤省略了,新手就会看到 coredns 永远 Pending。记住一个规律:只要 Pod 处在 ContainerCreating 或 Pending 状态超过几分钟,第一反应不是去调 Pod 配置,而是回头看 CNI 和容器运行时的日志。

4.3 镜像、证书、token 问题速查表

我把这次部署中遇到以及周围同事高频踩过的坑整理成一张速查表:

故障现象可能原因处理方式
节点一直 NotReady,kubelet 报 failed to pull image sandboxsandbox_image 指向的 pause 镜像拉不动修改 containerd 的 sandbox_image 为内部仓库地址,重启 containerd
worker 节点 join 时报 unknown CAtoken 过期或 ca cert hash 不匹配重新用kubeadm token create --print-join-command生成最新命令
集群运行一段后 kubectl 报 x509 证书过期控制面证书到期kubeadm certs renew all后重启相关组件进程
同一节点重复 init 报 directory not empty上个集群残留目录清空 /etc/kubernetes、/var/lib/kubelet、/var/lib/etcd 后重试
CoreDNS CrashLoopBackOff缺少 CNI 或 Pod CIDR 与插件不一致安装对应 CNI,核对 podSubnet 是否匹配

另外补充一条容易忽略的细节:kubeadm 生成的证书默认有效期是一年,如果你只是搭个测试环境,一年后证书过期会导致整个集群不可用。虽然kubeadm certs renew all可以续期,但更好的是在部署时就考虑证书的审计和备份,把 /etc/kubernetes 目录里的 CA 证书和私钥单独离线存一份。

4.4 一套能救命的排障顺序

被各种报错反复折磨之后,我总结出一个排障顺序:先从下往上查,容器运行时 -> kubelet -> 控制面组件 -> 网络插件 -> 应用。具体操作是,先确认systemctl status containerd kubelet都没问题;然后journalctl -u kubelet -f和crictl ps看容器运行时层面是否有错误;接着kubectl get pods -A和kubectl get events -A看资源层;最后才看应用自身的日志。

这套顺序看起来笨,但能避免很多无效排查。比如 kubelet 起不来的时候,去调网络插件配置就没有意义;反过来,如果网络插件正常而 Service 不通,再去折腾 kubelet 也会白白浪费时间。排障时不要被某一个报错带偏,先确认依赖链路的每一项基础组件是否健康,再去追上层的问题。

最后说点个人体会。每次装集群,我都会在全部节点 Ready 之后,先跑一个跨节点的 Nginx 访问测试,确认 Service 和 kube-proxy 是通的,再决定是否交付。Kubernetes 安装部署翻车的案例里,十有八九不是 kubeadm 本身有多复杂,而是基础环境和网络细节没有对齐。建议把这套流程沉淀成一条命令能跑完的脚本或者 Ansible 编排,下次从零到可用能压缩在半小时内。遇到版本号对不上也不要慌,先用官方默认模板当参照,再逐项对照实际环境改,比硬背命令行参数靠谱得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询