☰
kubeadm部署Kubernetes 1.28集群:架构拆解与实战避坑
2026/10/16 4:09:33 网站建设 项目流程

我印象最深的一次集群搭建,不是在云上点几个按钮,而是模拟项目X里拿到三台裸机,要求用 kubeadm 部署一套 Kubernetes 1.28 集群。当时最让我迷惑的不是命令记不住,而是网上教程一大把,照着敲完节点却一直 NotReady。后来我才想明白,问题不在命令,而在我没有理解 kubeadm 部署背后的整套架构逻辑。

本文我会把 Kubernetes 1.28 基于 kubeadm 部署的架构彻底拆开,讲清楚控制平面和工作节点各自在做什么、kubeadm init 到底执行了哪些关键步骤、为什么网络插件必须等集群初始化完成之后再装,以及日常运维中最容易踩的几个坑。适合三类人看:第一次用 kubeadm 搭集群、搭完集群后遇到各种 NotReady、或者虽然能跑通部署但想弄明白“每个组件为什么存在”的开发者。

1. 为什么说 kubeadm 是理解 1.28 集群架构的最佳入口

1.1 三种搭建方式,为什么我推荐 kubeadm

部署 Kubernetes 集群的方式大致可以分成三类:二进制手动部署、kubeadm 官方引导工具、以及各种发行版或托管平台。二进制部署是把 kube-apiserver、kube-controller-manager、kube-scheduler、etcd、kubelet、kube-proxy 这些二进制文件全部下载到服务器上,然后手动生成证书、写 systemd 服务文件、配置 kubeconfig。这种方式的好处是可定制性强,坏处是太容易出现低级错误。证书签错名字、组件启动参数写错、某个服务没有按顺序启动,都会让你排查到怀疑人生。

kubeadm 是 Kubernetes 项目官方维护的引导工具,它的定位不是“一鍵部署神器”,而是把证书生成、组件静态 Pod 配置、kubeconfig 分发、节点引导凭证这些繁琐又容易出错的环节自动化。发行版或托管平台虽然更省事,但对理解架构几乎没有帮助。你只知道“点一下集群就起来了”,却不知道里面发生了什么。真出了问题,只能干瞪眼。

我推荐用 kubeadm 的另一个重要原因是:它生成的目录结构,就是 Kubernetes 架构的活地图。比如/etc/kubernetes/manifests下面是 kube-apiserver.yaml、kube-controller-manager.yaml、kube-scheduler.yaml、etcd.yaml 这四个文件,它们静态声明了控制平面组件的全部启动参数。你读一遍这些 YAML,比看十篇架构文章都管用。

1.2 kubeadm 到底替你做了哪些事

很多人把kubeadm init理解成“一条命令安装 Kubernetes”,这个理解不准确。它实际做的是:先做环境预检,然后生成 PKI 证书体系,生成各个组件需要的 kubeconfig 文件,再把控制平面组件以静态 Pod 的方式写入/etc/kubernetes/manifests,最后创建 bootstrap token 让工作节点可以安全加入。

具体来说,执行完kubeadm init之后,你会看到这几个产物:

  • /etc/kubernetes/pki:存放 CA、API Server 证书、etcd 证书、前端代理证书等。
  • /etc/kubernetes/manifests:存放控制平面四个静态 Pod 的定义文件。
  • /etc/kubernetes/admin.conf、kubelet.conf、controller-manager.conf、scheduler.conf:不同角色访问集群使用的 kubeconfig。
  • /var/lib/kubelet/config.yaml:kubelet 的完整配置。

理解了这些产物,你就知道 kubeadm 的价值不是“帮你把程序跑起来”,而是“把集群里最复杂、最容易出错的初始化部分固化成标准化流程”。它不需要你在每个节点手工去验签证书、不需要你手工创建 Service Account,也不需要你手工决定组件之间的信任关系。

1.3 部署前必须满足的运行边界

kubeadm 的预检环节叫 preflight checks,很多失败都源于环境不达标。1.28 版本对节点的基本要求并不高,但有一些硬性条件必须满足:操作系统是 64 位、CPU 至少 2 核、内存至少 2GB,内核版本尽量新一些。更重要的是以下几个配置:

  • 必须加载overlay和br_netfilter内核模块,因为容器网络和 iptables 过滤都依赖它们。
  • 必须设置net.bridge.bridge-nf-call-iptables=1和net.ipv4.ip_forward=1,否则 Pod 网络和流量转发都会出问题。
  • 必须关闭 swap,或者至少确保 kubelet 不会因为 swap 开启而报错。
  • 容器运行时建议使用 containerd,并且要把 cgroup driver 配置为 systemd,与 kubelet 保持一致。

这些条件听起来琐碎,但几乎每个部署失败的案例都能在这里找到源头。我见过有人明明配置了net.ipv4.ip_forward=1,却忘了sysctl -p让它立即生效,结果集群初始化成功,但节点网络始终起不来。

提示:执行kubeadm init --dry-run可以在真正初始化之前跑一遍预检。它能帮你提前发现内核参数、端口占用、运行时版本等问题,强烈建议第一次部署时先用它兜底。

2. 控制平面拆解:1.28 集群里的四个关键组件如何协作

2.1 kube-apiserver:所有请求的唯一闸门

kube-apiserver 是整个集群的入口,也是一切控制流量的“唯一闸门”。无论是kubectl get pods、kubelet 上报节点状态,还是 controller-manager 想要修改副本数,所有请求都必须经过 kube-apiserver。它是唯一会同时和 etcd 通信的组件,所有对集群状态的读写都以它为准。

你可能会有疑问:这么多请求都从它过,它不会成为瓶颈吗?确实会。所以 kube-apiserver 在设计上做了两件事:一是支持水平扩展,多实例通过 leader 选举机制分担读压力;二是大量采用 watch 机制,让其他组件维持长连接,只要状态没有变化,就不需要反复全量拉取数据。这也是为什么你一执行kubectl get pods会觉得很快,因为很多数据其实来自客户端侧的缓存或 watch 结果。

在 1.28 版本里,apiserver 对请求并发和内存消耗的控制更加敏感。默认的--max-requests-inflight和--max-mutating-requests-inflight并不是无限大,一旦超过阈值,新请求会被拒绝并返回 429 错误。生产环境里如果你的 controller 突然高频重试,或者有某个异常客户端疯狂调 API,很容易把 apiserver 打到限流状态。架构层面记住一句话:apiserver 是集群的“神经系统”,它挂了,其他组件再正常也无法协同工作。

2.2 etcd:强一致性存储与 Raft 的胜负规则

etcd 是集群唯一的状态存储,负责保存所有资源对象的数据。Pod、Service、Deployment、ConfigMap、Secret,包括节点信息,最终都是以 key-value 形式存在 etcd 里。kubeadm 默认会部署一个单节点的 etcd,它和 apiserver 同机运行,数据目录是/var/lib/etcd。

etcd 之所以重要,是因为它采用 Raft 共识算法来保证数据一致性。简单说,在一个多节点 etcd 集群里,只有超过半数节点确认写入,这次写入才算成功。单节点 etcd 虽然因为“只有一个人说了算”而写性能最好,但也意味着它挂了整个集群都读不到数据。所以生产环境通常会部署三节点或者五节点 etcd 集群,kubeadm 也支持在初始化时通过配置文件自定义 etcd 节点。

在架构层面,不建议把业务 Pod 调度到 etcd 所在的控制平面节点上。原因很简单:etcd 对 IO 延迟非常敏感,而业务容器的磁盘、网络和 CPU 争抢都可能把 etcd 拖慢。分布式系统里,数据面的性能抖动最终都会反馈为控制面的不稳定。

2.3 scheduler 与 controller-manager:监督者和调度者的分工

kube-scheduler 和 kube-controller-manager 经常被混在一起,但职责完全不同。kube-scheduler 负责“调度”,也就是为新创建的 Pod 选择一个最合适的节点。它考虑的维度包括容器资源请求、节点标签、污点容忍、亲和性规则,以及 1.28 版本里更细粒度的拓扑分布约束。调度器本身不创建 Pod,它只会把一个待调度的 Pod 标记上nodeName。

kube-controller-manager 负责“调和”,它内部包含了一大堆控制器循环。DeploymentController 发现实际副本数和期望副本数不一致时,会创建或删除 Pod;NodeController 周期性地检查节点心跳,超过一定时间没收到就会把节点标记为 NotReady;EndpointsController 负责维护 Service 后面的一组 Pod 地址。

这两者之间的关系,可以理解成项目经理和监工。scheduler 决定“活派给谁”,controller-manager 决定“活有没有干完”。它们都不会直接操作容器,只通过 apiserver 修改资源状态。

2.4 静态 Pod:kubeadm 让控制平面自举的关键设计

控制平面组件本身跑在容器里,这带来一个先有鸡还是先有蛋的问题:apiserver 和 etcd 需要容器运行时来启动,但容器 Pod 又需要 apiserver 来调度管理。kubeadm 的解法是静态 Pod。

静态 Pod 是 kubelet 直接识别并启动的 Pod,不需要通过 apiserver 调度。只要 YAML 文件出现在/etc/kubernetes/manifests目录里,kubelet 就会盯着这个目录,文件一旦改变,它就会重建容器。所以 kubeadm init 其实只是在做一件事:先把 kubelet 这个“地基”装好,然后把控制平面组件的 YAML 放到指定目录,kubelet 再反过来把 apiserver、etcd 这些组件拉起来。

这也是为什么控制平面组件崩溃后,不需要你去systemctl restart docker之类的操作,kubelet 会自动感知并重启它们。理解静态 Pod,就理解了 kubeadm 整个自举过程的精髓。

3. 工作节点的运行闭环:kubelet、kube-proxy 与容器的协作

3.1 kubelet:节点上最忙的协调者

工作节点加入集群后,真正干活的是 kubelet。它运行在每个节点上,相当于节点自己的“小控制平面”。kubelet 会持续监听 apiserver,拿到分配给本节点的 Pod 列表,然后通过 CRI 协议告诉容器运行时:启动这些容器、挂载这些卷、执行这些健康检查。

kubelet 的很多问题都跟身份凭证有关。它通过/etc/kubernetes/kubelet.conf使用自己的客户端证书访问 apiserver。1.28 版本已经默认开启证书轮换机制,证书快过期时 kubelet 会自己请求新证书,不需要手工干预。这一点很重要,因为以前很多集群运行一年后突然“失联”,就是 kubelet 证书过期导致的。

3.2 Bootstrap Token:加入集群时的身份交接

工作节点执行kubeadm join时,靠的是两块信息:一个是 bootstrap token,另一个是 CA 证书的哈希值。token 相当于一次性的邀请码,默认有效期 24 小时;CA 证书哈希则用来确认你正在连接的确实是目标集群的 apiserver,防止中间人欺骗。

加入了初始握手之后,kubelet 会生成自己的私钥和证书签名请求,通过 bootstrap token 的权限提交 CSR,最后由 kube-controller-manager 里的 CSRApprovingController 自动批准。整个流程完成后,工作节点才真正获得稳定的身份凭证,之后就不再依赖 bootstrap token 了。

注意:如果执行kubeadm join时提示 token 过期,不需要重新初始化集群,直接在控制平面节点执行kubeadm token create --print-join-command重新生成即可。这是一个非常高频的运维操作。

3.3 kube-proxy 的转发逻辑与 1.28 的新后端

kube-proxy 解决的是 Service 访问问题。它会把 Service 的虚拟 IP(ClusterIP)转换为后端 Pod 的 IP。默认模式是 iptables,数据包进来之后通过规则链做 DNAT;IPVS 模式则使用内核的 IP 虚拟服务器模块,规则更高效,适合大规模 Service 场景。1.28 版本引入了新的 nftables 后端,作为 iptables 的现代化替代方向,但整体还处于逐步成熟阶段。

需要强调一点:kube-proxy 不负责 Pod 之间的网络连通,那部分由 CNI 插件完成。定位类似“入口转发代理”,而不是“网络工程师”。所以集群里网络插件和 kube-proxy 各管一段,判断网络故障时先分清是哪一层的职责。

3.4 从 sandbox 到容器:一次 Pod 启动的完整过程

当 kubelet 收到一个新 Pod 任务时,它会先让 containerd 创建一个 sandbox 容器,也就是你常看到的 pause 容器。pause 容器的唯一作用是把一组网络命名空间、IPC 命名空间等底座提前建好,业务容器启动时再共享这些命名空间。这样做的好处是,Pod 内容器具备一致的网络视角,所以访问 localhost 就能互相通信。

随后 kubelet 逐个启动业务容器。每个容器启动之前,都要先从镜像仓库拉取镜像、创建挂载卷、设置环境变量和启动命令。这些动作全部通过 CRI 接口发往 containerd,containerd 再根据 OCI 规范调用底层运行时去创建进程。整个链路里,kubelet 是决策者,containerd 是执行者,runC 是最底层真正拉起进程的“工匠”。

4. kubeadm 部署 1.28 的实操细节:命令、参数与验证

4.1 基础环境配置:内核模块、sysctl 与 containerd

在控制平面节点上,第一步是配置内核。推荐做法是手动创建/etc/modules-load.d/k8s.conf,内容写入overlay和br_netfilter两个模块,然后执行modprobe加载。接下来修改/etc/sysctl.d/k8s.conf,塞入网络转发相关参数,再执行sysctl --system生效。

containerd 的配置是另一个重点。安装完成后,先执行containerd config default | tee /etc/containerd/config.toml生成默认配置,然后确认SystemdCgroup被设置为true。这个参数没配好,kubelet 和 containerd 对进程 cgroup 的认知就会不一致,轻则无法正确识别资源使用量,重则容器启动失败。

4.2 一份可复用的 kubeadm 配置文件

推荐把初始化参数写进 YAML 文件,而不是在命令行堆一堆--pod-network-cidr之类的参数。下面这份是 1.28 常见组合:

apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.10.10 bindPort: 6443 --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.2 controlPlaneEndpoint: "192.168.10.10:6443" networking: podSubnet: "10.244.0.0/16" serviceSubnet: "10.96.0.0/12"

这里的advertiseAddress是本机对外提供 apiserver 服务的地址,controlPlaneEndpoint则是高可用场景下所有客户端连接 apiserver 的统一入口。如果只有一个控制平面节点,两者可以填同一个 IP;如果有负载均衡器,controlPlaneEndpoint就写负载均衡器的虚拟 IP 或域名。

podSubnet和serviceSubnet必须根据你后续选择的网络插件来定。比如 Flannel 常用10.244.0.0/16,Calico 默认则偏爱192.168.0.0/16,两者不能冲突。

4.3 kubeadm init 的六个阶段分别干什么

执行kubeadm init --config=kubeadm-config.yaml后,你会看到一系列阶段输出。归纳下来大致是:

  1. preflight:检查系统环境、端口占用、运行时是否可用。
  2. certs:生成和签发各种证书,写入/etc/kubernetes/pki。
  3. kubeconfig:生成给不同组件使用的 kubeconfig 文件。
  4. control-plane:把 apiserver、controller-manager、scheduler 的静态 Pod 清单写入/etc/kubernetes/manifests。
  5. etcd:同样以静态 Pod 方式启动 etcd。
  6. mark-control-plane:给控制平面节点打上标签和污点,并配置 admin.conf。

如果初始化失败,先看是哪个阶段报错。很多问题会集中在 preflight 和 control-plane。前者是环境检查没过,后者很可能是镜像拉取失败或静态 Pod 配置有问题。

4.4 安装网络插件与验收命令

控制平面初始化成功后,节点还处于 NotReady 状态。这是因为 CNI 网络插件尚未安装,容器间的网络无法建立,kubelet 的节点状态检查过不去。此时把网络插件的 YAML 文件下载下来,执行kubectl apply -f即可。

安装完成后,需要验证几个关键对象:

kubectl get nodes kubectl get pods -n kube-system kubectl get cs

第一行确认节点都是 Ready,第二行确认控制平面的 Pod 都处于 Running,第三行看组件健康状态。我遇到过有人只看第一项就认为集群成功了,结果发现 kube-system 里一堆 CrashLoopBackOff,后续所有操作都不稳定。所以验收时一定要同时检查这三项才算数。

5. 证书、升级与 etcd 备份:1.28 集群长期运维要点

5.1 PKI 目录与证书轮换

kubeadm 会在/etc/kubernetes/pki目录里建立一套完整的 PKI 体系。CA 证书的有效期默认是 10 年,但 apiserver、etcd、kubelet 等组件的证书只有 1 年。这意味着集群长期运行后,证书续期是每个运维人员都必须掌握的基本操作。

kubeadm certs check-expiration可以一次性列出所有证书的过期时间。手动续期用kubeadm certs renew all,执行完毕后需要重启相关组件才能加载新证书。如果只是 kubelet 证书过期,很多情况下 kubelet 已经支持自动轮换,但前提是证书签发流程正常、节点没有长期失联。

提示:证书过期不是一个“等到那天再说”的问题。1.28 集群尤其是生产环境,一定要把证书检查纳入周期巡检脚本,至少每个月跑一次kubeadm certs check-expiration。

5.2 版本升级路线与注意事项

Kubernetes 官方版本升级有一个基本原则:不能跨大版本跳跃。1.27 可以直接升级到 1.28,但如果你的集群还在 1.26,想升到 1.28,就必须先升级到 1.27,再升到 1.28。升级顺序也有讲究:先升级 kubeadm 工具本身,再在控制平面节点执行kubeadm upgrade plan查看可升级版本,然后执行kubeadm upgrade apply v1.28.x升级控制平面,最后逐个升级工作节点上的 kubelet 和 kubeadm。

升级前需要看当前版本到 1.28 之间的 API 变更。有些 API 在中间版本里被废弃或移除,如果业务还在使用旧版本 API 资源,升级后可能出现资源无法解析的问题。最稳妥的做法是先在测试集群走一遍升级,并把整个升级窗口安排在业务低峰期。

5.3 etcd 备份与恢复的基本方法

apiserver 是集群的“大脑”,etcd 就是“记忆”。丢了记忆,再聪明的大脑也白搭。所以在架构规划里,etcd 备份永远值得优先考虑。单节点 etcd 的备份很简单,通过 etcd 的 snapshot 接口把数据打成快照文件,保存到独立存储位置。

kubeadm 部署的 etcd 运行在静态 Pod 里,可以直接进入 etcd 容器执行etcdctl相关命令,也可以通过etcdctl snapshot save的方式导出快照。恢复时先把集群置为维护状态,再通过etcdctl snapshot restore把数据恢复到新数据目录,然后替换 etcd 的静态 Pod 配置。

如果不做备份,遇到硬盘损坏或者误删除资源,恢复难度会成倍上升。即使你的环境里已经有监控和日志,也不能替代等。这是我在模拟项目X里学到的最深刻教训。

6. 踩坑实录:最容易让新手反复折腾的几个问题

6.1 NotReady 的背后往往是 CIDR 不一致

很多人的集群第一次kubectl get nodes就看到 NotReady,第一个怀疑对象通常是网络插件没装好,于是反复卸载重装。其实最典型的根因是 kubeadm 的podSubnet和 CNI 插件里的 IP 池配置不一致。

比如 kubeadm 配置了10.244.0.0/16,Flannel 默认就能对上;但如果你换用 Calico,却没有修改 Calico 的CALICO_IPV4POOL_CIDR,Calico 默认可能用192.168.0.0/16,那集群内部网络就直接劈成两半。解决方法是确认网段配置一致后,再重启相关 Pod 或者重新 apply 一遍 CNI 清单。

6.2 containerd 的 cgroup driver 与 kubelet 不匹配

这个问题在 containerd 场景里特别常见。kubelet 默认推荐 systemd cgroup driver,而 containerd 默认配置往往还是cgroupfs。表现出来的现象比较多样:节点刚启动时正常,过一段时间 kubelet 报错,或者 Pod 创建后一直 ContainerCreating。

排查方向很明确:打开/etc/containerd/config.toml,找到SystemdCgroup字段,改成true,然后重启 containerd,再systemctl restart kubelet。很多所谓“集群不稳定”的诡异现象,其实都是这里没对齐。

6.3 swap 没有完全关闭,节点状态反复跳动

Kubernetes 对节点 swap 的要求是关闭。很多人只在当前的 shell 里执行了swapoff -a,没有删除/etc/fstab里的 swap 条目。结果一重启,swap 又回来了,节点状态开始反复横跳。

正确做法是两条命令一起做:swapoff -a处理当前会话,然后用编辑器注释掉/etc/fstab里的 swap 行。这样即使重启,swap 也不会再被自动挂载。

6.4 已经运行的集群不要随便改主机名和 IP

有次模拟项目X里的同事为了整理机器命名,把一个运行中的工作节点的主机名改了。结果 kubelet 在注册时用新主机名上报,和旧节点的记录冲突,最终导致节点被标记为 Unknown,之后又变成 NotReady。

在 Kubernetes 的架构里,节点身份与主机名、IP 强相关。如果确实要改名,标准流程是先把节点kubectl cordon并驱逐上面的 Pod,然后把节点从集群中删除,改完主机名后再重新kubeadm join。不要跟系统文件“硬刚”,那只会让问题更复杂。

最后再说一点

我个人的习惯是,每次 kubeadm 部署完成之后,会把/etc/kubernetes/manifests下的四个 YAML 文件从头到尾读一遍,再观察一遍 kube-system 里各 Pod 的启动顺序。这种“边读配置边看表现”的方式,比我当年啃一整本原理书都管用。

如果你还在准备第一次初始化,别忘了kubeadm init --dry-run这个选项,它能提前暴露大部分硬件环境问题。等集群真正跑起来之后,也不要放过任何一个看似不起眼的警告日志,很多架构层面的隐患,最初都只是日志里一行不起眼的 warning。

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

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

立即咨询