kubeadm搭建Kubernetes一主两从集群:从初始化到排错全记录

发布时间:2026/10/23 1:12:24
kubeadm搭建Kubernetes一主两从集群:从初始化到排错全记录
折腾过 Kubernetes 集群安装的人应该都清楚手动用二进制把 kube-apiserver、etcd、controller-manager 一个个弄起来有多痛苦。幸好有 kubeadm 这种官方引导工具它能帮你把证书生成、控制面组件启动、节点间信任关系这些重复劳动自动化让 Kubernetes 集群安装从地狱级手工活变成 init 和 join 两条主线。这篇文章就是我配合 kubeadm 从零搭建一套一主两从集群的完整记录覆盖环境初始化、容器运行时配置、控制面初始化、工作节点加入以及排错复盘适合刚入门容器编排、想亲手搭一套 K8s 环境的新手也适合被各种残缺教程坑过、想系统梳理一遍的运维朋友。1. 为什么用 kubeadm方案对比与背后原理1.1 三条自建路线为什么我选 kubeadm自建 Kubernetes 集群主流无非三条路纯手工二进制部署、kubeadm 部署、以及各类封装好的发行版工具。二进制部署最贴近原理每个组件都要手动下载、生成证书、写 systemd 服务一套流程走下来能让你把 K8s 内部结构吃透但对团队和个人的时间成本实在太高出一次错排查半天新手很容易在半路放弃。kubeadm 是官方为“引导集群”设计的工具它自动完成证书签发、kubeconfig 下发、etcd 初始化、token 签发这些一次性工作。另一类封装工具比如 minikube、kind更适合本机开发测试它们一般是在 Docker 里起单节点环境跟真实服务端集群的差距比较大不适合作为服务器部署的主方案。所以我的建议很直接想学原理看二进制部署的架构文档就够了别真去手搓想在几台服务器上搭一套能长期使用、能按官方流程升级的集群优先用 kubeadm。1.2 kubeadm 到底做了什么静态 Pod 是核心机制很多人第一次跑完 kubeadm init 会疑惑控制面组件到底装到哪去了答案是 /etc/kubernetes/manifests 这个目录。init 之后你会看到 kube-apiserver.yaml、kube-controller-manager.yaml、kube-scheduler.yaml、etcd.yaml 等文件这是 kubelet 认可的“静态 Pod”清单目录。kubelet 会定时扫描这个目录把其中的 Pod 定义拉起来组件挂了还能自动重启。这种设计的好处非常实际控制面组件本身也被 K8s 托管升级时只要替换 manifests 里的镜像版本组件就完成滚动更替。理解了这条主线排查问题会顺很多——比如 apiserver 起不来先去 manifests 目录看 yaml 内容再去看对应静态 Pod 的日志而不是盲目重装集群。kubeadm 在 init 阶段做的是“引导”而不是“常驻”引导结束之后日常运维还是 kubelet 和 API Server 说了算。2. 环境准备硬件规划与基础系统配置2.1 节点规划与硬件建议本次演示用三台机器一台控制面节点两台工作节点。IP 地址我用的是私有网段示例你实际部署时换成自己的段就行。主机名IP角色建议规格k8s-master01192.168.10.10控制平面2C4Gk8s-node01192.168.10.11工作节点2C4Gk8s-node02192.168.10.12工作节点2C8G学习环境 2C4G 起步其中控制面至少要 2 个核因为 etcd 和 kube-apiserver 都是 CPU 敏感型组件核数不够会频繁超时。工作节点如果跑真实应用规格按业务定内存建议 8G 起磁盘至少 40G系统盘和数据盘尽量分开防止容器镜像把根分区撑爆。2.2 每台机器都要做的四件套基础配置不管控制面还是工作节点下面四件事缺一不可第一设置主机名和 hosts 解析。控制面执行hostnamectl set-hostname k8s-master01worker 同理。然后在所有机器的 /etc/hosts 里都写上三行解析记录哪怕集群内部有 DNS 也要写这是避免 kubelet 因为解析不到 master 而反复报错最简单的办法。第二关闭 swap。执行swapoff -a再编辑 /etc/fstab 把 swap 行注释掉保证重启后不恢复。原因在于 kubelet 在资源管理上是“严格模式”一旦节点上报的内存里算上 swapPod 的 request 和 limit 就会失真调度器也会按错误的内存数据规划。K8s 从早期版本开始就要求关闭 swap开着直接会报错启动失败。第三加载内核模块并调整网络参数。执行cat EOF | tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter cat EOF | tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sysctl --systemoverlay 是容器镜像分层存储需要的文件系统驱动br_netfilter 负责让经过网桥的流量也能被 iptables 处理K8s 的 Service 转发规则依赖它。ip_forward 是 Pod 与 Pod、Pod 与外部网络通信的前提。这三个参数漏掉任何一个后面都会出现“网络通一半”的诡异现象。第四时间同步。apt install -y chrony systemctl enable --now chrony。Kubernetes 集群对时间一致性非常敏感证书校验和 token 过期都依赖时间节点间时间差太大会直接导致 kubelet 与 apiserver 的 TLS 握手失败。这类报错不仔细看根本想不到是时间问题。2.3 端口放行清单别让防火墙把集群拦在门外安装前先把端口规划好。实验环境我建议直接关闭防火墙省心生产环境按表放行端口服务说明6443kube-apiserver所有客户端和节点访问 API 的入口2379-2380etcd server client控制面内部通信10250kubelet管理面和各节点健康检查10257kube-controller-manager控制面组件10259kube-scheduler控制面组件30000-32767NodePort 服务端口对外暴露应用用8472flannel VXLAN节点间 Pod 网络隧道UDP如果用 Calico还需要放行 179 端口的 BGP。防火墙规则务必在所有节点保持一致不然会出现“从一个节点访问服务没问题从另一个节点访问就超时”的经典网络事故。3. 容器运行时与 kubeadm 全家桶安装3.1 运行时选 containerd 而不是 DockerK8s 从 1.24 版本开始移除了 dockershimDocker 不再是默认容器运行时。虽然可以单独装 cri-dockerd 做适配但主流方向是直接用 containerd这也是当前云原生生态的事实标准。本篇直接使用 containerd。安装很简单apt install -y containerd。装完必须做两步配置生成默认配置并修改命令是containerd config default | tee /etc/containerd/config.toml配置文件里需要重点改两个地方。第一个是SystemdCgroup true。原因是 kubelet 默认的 cgroup 驱动是 systemdcontainerd 默认是 cgroupfs两边不一致会导致 kubelet 无法正确管理和统计 Pod 资源典型症状就是节点反复 NotReady。统一成 systemd 是官方推荐做法。第二个是 sandbox_image 字段。pause 镜像是每个 Pod 都有的“占位容器”负责为 Pod 提供网络和命名空间基础。默认值指向的镜像仓库很多环境访问不稳定拉不到它所有 Pod 都会无限期卡在 ContainerCreating。把这个字段改成你能稳定访问的 pause 镜像地址是新手最容易漏的一步。改完执行systemctl restart containerd。注意 containerd 和 Docker 如果同时存在要确认 kubelet 配置指向的是 containerd 的 socket否则两个运行时端口冲突会直接拉垮节点。3.2 安装 kubelet、kubeadm、kubectl 并锁定版本三个二进制的作用分别说一下kubelet 是每个节点上的核心代理负责管理本节点的 Podkubeadm 是集群引导工具kubectl 是给管理员用的命令行客户端。三件套必须在所有节点都装版本必须一致。执行curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo deb [signed-by/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ / | tee /etc/apt/sources.list.d/kubernetes.list apt-get update apt-cache madison kubeadm查到具体版本号之后指定版本安装并锁定apt-get install -y kubelet1.28.2-1.1 kubeadm1.28.2-1.1 kubectl1.28.2-1.1 apt-mark hold kubelet kubeadm kubectl为什么要锁定版本因为 Kubernetes 有个硬性约定集群内各组件的版本差异不能超过一个 y 版本。apt 的 update 一旦把某台机器的组件升到新版本其他节点跟不上整个控制面就会进入半瘫痪状态。升级要走 kubeadm upgrade 的专项流程不能交给系统包管理器乱来。这个动作不写进很多教程里但实际踩坑概率非常高。3.3 初始化前先预拉镜像正式 init 之前强烈建议先跑两条命令kubeadm config images list kubeadm config images pull第一条列出控制面需要的全部镜像第二条实际拉取。如果默认镜像仓库拉不动可以在 init 时用--image-repository参数指定一个你可访问的镜像仓库前缀例如--image-repository某公共镜像仓库/google_containers。预拉镜像的意义在于真正 init 时不会因为网络问题卡在拉镜像阶段问题能提前暴露。实测下来这一步能帮你把整个安装时间缩短一大半。4. 控制平面初始化kubeadm init 与 CNI 网络4.1 参数逐个讲清楚再执行在 master 节点上执行初始化命令如下kubeadm init \ --apiserver-advertise-address192.168.10.10 \ --control-plane-endpoint192.168.10.10 \ --pod-network-cidr10.244.0.0/16 \ --kubernetes-versionv1.28.2四个参数的含义必须清楚。--apiserver-advertise-address是 API Server 对外绑定的地址写 master 的内网 IP 就行。--control-plane-endpoint是所有节点包括 worker访问管理面的地址也是 kubeconfig 里 server 字段的地址高可用场景这里要填负载均衡的虚拟 IP。--pod-network-cidr是 Pod 网段必须和你选的 CNI 插件默认网段对齐——Flannel 默认 10.244.0.0/16Calico 默认 192.168.0.0/16选错会导致网络插件完全失效。--kubernetes-version写实际部署的版本避免默认取值和已安装的 kubeadm 不一致。init 跑完终端会输出三块关键信息kubectl 的 kubeconfig 配置命令、worker 加入集群的 join 命令、token 过期时间。这些输出别关太快join 命令后面要用。如果丢了在 master 上用kubeadm token create --print-join-command重新生成。4.2 配置 kubectl 并验证节点状态init 完成之后需要把管理员的 kubeconfig 配置到当前用户下。非 root 用户执行mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/configadmin.conf 是 init 阶段生成的集群管理员凭证包含 apiserver 地址和 admin 证书。root 用户直接export KUBECONFIG/etc/kubernetes/admin.conf也行。配好之后跑kubectl get nodes能看到 master 处于 NotReady 状态这是正常的因为此时还没有网络插件节点的 Pod 网络还没建立。4.3 部署 Flannel 网络插件网络插件这一步最关键执行kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.ymlFlannel 通过 VXLAN 或 host-gw 模式把各节点的 Pod 网络打通。CNI 插件必须在任何业务负载运行之前装好因为 Kubernetes 本身不实现 Pod 网络没有 CNI 的节点永远无法进入 Ready 状态。装完之后用kubectl get pods -A -o wide观察等 flannel 的 Pod 都进入 Running再kubectl get nodes就会看到 master 变成 Ready。关于选型多说一句我演示用 Flannel 是因为它配置最简单适合学习和中小规模集群。如果生产环境有多租户隔离、网络策略、高性能转发需求建议上 Calico 或 Cilium它们功能更完整但也更吃资源。5. 工作节点加入与首个应用验证5.1 用 kubeadm join 接入 workerinit 输出里给的 join 命令长这样kubeadm join 192.168.10.10:6443 --token token \ --discovery-token-ca-cert-hash sha256:hash在每台 worker 节点上分别执行。token 是节点加入集群的临时通行证默认有效期 24 小时。过期之后在 master 上跑kubeadm token create --print-join-command它会直接输出一条完整的 join 命令复制到 worker 执行即可。加完节点回 master 上验证kubectl get nodes几台机器全部变为 Ready集群的基本骨架就算搭完了。从 init 到三节点 Ready正常情况下十分钟之内能完成。5.2 部署一个 Nginx 验证功能闭环集群搭完别急着收工跑一个应用验证链路是必要的。执行kubectl create deployment nginx --imagenginx kubectl expose deployment nginx --port80 --typeNodePort kubectl get svc nginxNodePort 类型的 Service 会在所有节点上开放一个 30000-32767 范围内的随机端口。用浏览器访问任意节点 IP 加这个端口能打开 nginx 首页说明 Pod 调度、kube-proxy 转发、节点间网络隧道的全链路都是通的。这一步排查的是“集群能建起来不代表能跑业务”的最后一环很多人建完集群卡在业务不通才开始怀疑网络不如一开始就做验证。6. 常见问题与排查技巧实录6.1 一切问题先看 kubelet 日志kubelet 是节点上最核心也最爱出问题的组件。遇到节点异常第一件事永远是journalctl -u kubelet -f -n 50不要靠猜。kubelet 的报错一般很明确比如 swap 未关闭、cgroup 驱动不匹配、证书路径不正确日志里都会直接写明原因。我见过太多人绕开日志去重装集群最后发现只是某台机器配置文件写错一个字母。6.2 节点 NotReady 的三种典型原因CNI 网络插件未安装查看kubectl describe node 节点名事件里会显示 KubeletNotReady并且提示没有找到网络插件。解决办法就是补装 Flannel 或对应 CNI 并等待 Pod 就绪。cgroup 驱动不一致典型报错是 kubelet 的 cgroupfs 和 containerd 的 systemd 不一致。去 containerd 配置里把SystemdCgroup改成 true 并重启或者给 kubelet 加--cgroup-drivercgroupfs参数但前一种才是推荐方向。pause 镜像拉不下来Pod 全部卡在 ContainerCreatingkubectl describe pod里会指明是 sandbox 镜像拉取失败。改 containerd 的 sandbox_image 配置即可。6.3 证书和 token 相关坑位token 过期是最常遇到的症状是 join 时提示 token 无效。重新用kubeadm token create --print-join-command生成就行新版 kubeadm 还会提示实际的有效期。证书过期则是另一个大坑kubeadm certs check-expiration可以一键查看所有证书的过期时间。控制面证书默认有效期是一年快到期时执行kubeadm certs renew all再用新的 kubeconfig 替代旧的。另外 join 命令里的--discovery-token-ca-cert-hash是控制面 CA 证书的指纹填错了会直接报 hash 校验失败务必用 init 输出或kubeadm token create --print-join-command里给的原值。6.4 端口和网络转发问题快查表症状可能原因解决办法节点间通信超时防火墙未放行 8472/4790 或 179 端口检查所有节点防火墙规则并保持统一NodePort 从外部访问不通安全组或本地防火墙拦截 30000-32767放行端口段Pod 间 ping 不通Flannel 隧道接口未建立或 ip_forward 未开启确认 sysctl 配置和 flannel.1 接口存在Service 访问不通kube-proxy 异常或 iptables 规则被覆盖kubectl get pods -n kube-system检查 kube-proxy 状态6.5 我踩过最隐蔽的一个坑这里单独说一下时间同步。有次我把集群搭好两个 worker 都 Ready但过了一天后 kubelet 开始频繁报证书校验失败排查半天才发现是非控制面节点的时钟漂移了将近三分钟。Kubernetes 的证书和 token 机制对时间偏差零容忍早早在 fstab、swap、sysctl 里都写了结果忘了时间同步这步。现在只要有新机器要加进集群我第一件事就是先把 chrony 装好确认时间源正常再谈其他。7. 集群搭完之后我会接着做这几件事这套一主两从的集群跑通之后常用扩展方向有这么几个。想把它变成一台学习机把控制面节点的污点去掉就允许普通业务 Pod 调度到 master 上命令是kubectl taint nodes --all node-role.kubernetes.io/control-plane-单机集群也能跑完整功能。想升级成高可用把控制面扩容到三台前面挂负载均衡control-plane-endpoint 指向虚拟 IP这是官方推荐的多管理面方案。不管哪种玩法都建议从第一天开始给 etcd 做定期快照备份哪天误删 namespace 或者升级翻车etcd 快照就是最后的救命稻草。一路踩坑搭下来我的体会是kubeadm 把安装 K8s 的门槛压得很低了真正考验人的反而是基础环境——内核参数、时间同步、cgroup 驱动、镜像拉取这些“看起来不重要的细节”。把这四样做扎实集群基本都是十分钟内正常起来做不扎实你会在 NotReady 和三联排错里浪费整整一个晚上。希望这篇记录能帮你少走几段我走过的弯路。