上周有个同事抱着文档手动部署 Kubernetes,折腾了两天,最后装出来的版本还是老旧的,节点证书各种对不上。我干脆把手头的一批新机器全部切到 kubeadm 流程,基于 Kubernetes 1.26.4 从裸机到集群可用的完整过程只花了一个下午。这篇文章就把这次从环境准备、容器运行时配置、控制平面初始化到工作节点加入的全程记录下来,包括每个关键参数的用途、踩过的坑和排查思路。适合两类人参考:一类是第一次搭集群、想要一份不绕弯路的操作指南,另一类是已经习惯了二进制或脚本部署、想切到 kubeadm 标准方案的开发者。
1. 整体设计:kubeadm 部署 1.26.4 的思路拆解
1.1 为什么用 kubeadm,而不是手撕二进制
部署 Kubernetes 的路线大致有三条:二进制手工部署、kubeadm、以及各类发行版套件和云托管方案。二进制部署的好处是能看清每个组件的启动参数,适合学习原理,但坏处也很明显:证书、kubeconfig、systemd unit、etcd 集群全部要自己维护,升级一个 patch 版本就要重复做一遍,人肉操作极易出错。
kubeadm 是 Kubernetes 社区官方的集群构建工具,它把证书签发、kubeconfig 生成、etcd 启动、控制面组件静态 Pod 编排这些事都封装成了标准流程。这不是说它是黑盒——恰恰相反,它生成的配置都放在 /etc/kubernetes 目录下,每一份文件都可以打开看,也可以按需覆盖。
对比其他方案:一些发行版套件和云托管集群开箱即用,但往往多了一层自己的抽象,遇到深层问题还是要回到底层理解 kubelet 和容器运行时;如果你在公司内网、离线环境,或者对版本有强管控要求,最后还是得自己动手。kubeadm 是社区标准,文档全、社区踩坑记录多,从单控制面到高可用都有官方支持路径,这是我选它的核心原因。
1.2 集群拓扑规划:控制面和工作节点怎么分
先定规划再动手。这次我用了三台机器,一台控制面、两台工作节点,规模最小但流程完整:
| 主机名 | 角色 | 内网 IP | 建议配置 |
|---|---|---|---|
| k8s-cp1 | control-plane | 192.168.10.10 | 4C8G |
| k8s-w1 | worker | 192.168.10.11 | 4C8G |
| k8s-w2 | worker | 192.168.10.12 | 4C8G |
控制面节点承载 apiserver、etcd、scheduler、controller-manager 这些核心组件,etcd 对磁盘 IO 和内存尤其敏感,所以生产环境控制面至少给到 4C8G,有条件就上 SSD。工作节点按业务负载估算,这里的 4C8G 纯粹是演示用。
系统我选的是 Ubuntu 22.04 LTS。Kubernetes 对操作系统没有硬性绑定,但 22.04 的内核较新,对 overlay 和 iptables 的支持更好,软件源里也能直接装 containerd,省掉不少麻烦。其他发行版的部署流程类似,配置参数基本通用。
1.3 版本选型的想法
为什么是 1.26.4?Kubernetes 的 minor 版本选择一般遵循两个原则:一是用偶数版本,因为社区长期观察下来奇数版本出问题的概率略高;二是选一个发布了一段时间、已经经过下载量验证的 patch 版本。1.26.4 是 1.26 这个系列里比较成熟的补丁,安全修复和稳定性修复都累积得比较全。
更关键的是,kubelet、kubeadm、kubectl 这三个组件的 minor 版本必须保持一致,节点上的 kubelet 版本不能比 apiserver 高太多,这是 Kubernetes 的版本偏差策略。所以后面安装时会用=精确指定版本号,而不是装 latest。
2. 环境准备:操作系统层面一口气配到位
2.1 内核参数与必要模块
Kubernetes 对节点的内核有一组基本要求,这些不做,后面 kubeadm init 的 preflight 检查会直接报错。先加载 overlay 和 br_netfilter 两个内核模块:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilteroverlay 是容器镜像分层文件系统的基础,containerd 默认用 OverlayFS 作为 snapshotter;br_netfilter 让 Linux 网桥的流量也能经过 iptables/nftables 处理。Kubernetes 的网络模型要求在网桥转发场景下 iptables 规则依然生效,这个模块不加载,集群内 Pod 通信和 Service 转发都会出问题。
接着写入 sysctl 参数:
cat <<EOF | sudo 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 sudo sysctl --systemnet.ipv4.ip_forward 控制 IP 转发,Pod 跨节点通信和 Service 转发都依赖它。执行完sysctl --system后,最好验证一下:sysctl net.ipv4.ip_forward,输出 1 就是生效了。
然后是 swap。Kubernetes 在 1.26 里对 swap 的支持仍是 alpha,默认情况下 kubeadm 的 preflight 检测到 swap 开启会直接拒绝安装。更本质的原因是 kubelet 的节点资源管理和 Pod 的 QoS 机制依赖一个相对固定的内存视图,swap 一开,节点可用内存和容器实际内存会发生偏差,OOM 判定也变得不可预期。生产环境老老实实关掉:
sudo swapoff -a sudo sed -i '/ swap / s/^/#/' /etc/fstabswapoff -a是临时关闭,/etc/fstab里注释掉才能保证重启后不生效。这一步三台机器都要做,不要只做控制面节点。
2.2 主机名、hosts、时间同步与防火墙
主机名最好在搭集群前就定好,不要装完再改,后面证书和节点名称都会用到:
sudo hostnamectl set-hostname k8s-cp1三台机器分别设置成 k8s-cp1、k8s-w1、k8s-w2,然后在每台机器的 /etc/hosts 里加上集群内解析:
192.168.10.10 k8s-cp1 192.168.10.11 k8s-w1 192.168.10.12 k8s-w2加 hosts 记录是为了让 kubelet 和各组件之间不依赖外部 DNS,特别是当你后面要用 control-plane-endpoint 做高可用时,所有节点对这个地址的解析必须一致。
时间同步用 chrony 就够了。etcd 对时钟偏移非常敏感,成员之间时钟差超过阈值,raft 选举和数据一致性都会出问题。安装后启动服务:
sudo apt-get install -y chrony sudo systemctl enable --now chrony防火墙(ufw/firewalld)是不少同学容易忽略的点。如果按最小权限收敛,需要放行一组端口,我整理成了表格:
| 端口 | 组件 | 说明 |
|---|---|---|
| 6443 | kube-apiserver | 所有客户端和节点的 HTTPS 接入 |
| 2379、2380 | etcd | client 和 peer 通信(控制面节点之间) |
| 10250 | kubelet | apiserver 访问 kubelet 做日志/exec |
| 10257 | controller-manager | 健康检查与 metrics |
| 10259 | scheduler | 健康检查与 metrics |
| 30000-32767 | NodePort | Service 对外映射的端口段 |
| 179 | Calico BGP | 如果用 BGP 模式进行跨节点通信 |
本地实验环境直接关闭防火墙能省很多排查时间,但生产环境一定要按表放行。如果用的是 CentOS 手工环境,顺手getenforce看一下 SELinux 状态,建议改成 permissive 或 disabled,否则端口放行了也可能被 SELinux 拦。
2.3 containerd 安装与两个关键配置
容器运行时这里选 containerd。Kubernetes 从 1.24 移除 dockershim 之后,官方最推荐的就是 containerd,1.6+ 版本的性能和稳定性都验证得不错。Ubuntu 上直接装:
sudo apt-get update && sudo apt-get install -y containerd cri-toolscri-tools 会带上 crictl,后面排查容器和镜像全靠它。CentOS 系的同学要注意,业务源里可能没有提供 containerd.io,需要先从软件提供方的仓库加上 containerd 的源,装完记得 enable。
装完后先让 containerd 生成默认配置,再改两个关键位置:
sudo mkdir -p /etc/containerd sudo containerd config default | sudo tee /etc/containerd/config.toml第一处是SystemdCgroup,默认是 false,改成 true:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true原因要讲清楚:宿主机的 init 是 systemd,cgroup 由 systemd 统一管理。containerd 默认用 cgroupfs 直接写 cgroup 文件,两套管理同时操作同一个子系统会出问题,典型症状是 kubelet 报 cgroup driver 不一致,或者容器内的 CPU/内存统计漂移。把 containerd 的 runc 切到 systemd cgroup driver,kubelet 这边也默认是 systemd,两边就对上了。
第二处是 sandbox_image,也就是 pause 镜像。CRI 每启动一个 Pod 都会先拉一个 pause 容器作为 sandbox 来持有网络命名空间。默认值指向官方仓库,网络环境不通的话每个 Pod 都会卡在 ContainerCreating。改成你实际能拉到的地址:
sandbox_image = "registry.k8s.io/pause:3.9"如果你后面统一用了某个镜像仓库地址,这里也要同步替换。改完重启 containerd 并确认:
sudo systemctl enable --now containerd sudo systemctl restart containerd sudo ctr version顺手配一下 crictl 的 endpoint:
cat <<EOF | sudo tee /etc/crictl.yaml runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 EOF2.4 安装 kubeadm、kubelet、kubectl 并锁定版本
三个工具的作用各不相同:kubeadm 只在初始化、join、upgrade 时用到;kubelet 是每个节点上常驻的节点代理;kubectl 是你作为管理员操作集群的客户端。三者的 minor 版本必须一致,装的时候把版本钉死在 1.26.4。
先配置软件源。不同地区的网络情况不一样,官方源访问慢就换成本地镜像站,把下面的<MIRROR>换成你实际可用的地址:
sudo apt-get install -y apt-transport-https ca-certificates curl gpg curl -fsSL https://<MIRROR>/core:/stable:/v1.26/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://<MIRROR>/core:/stable:/v1.26/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt-get update sudo apt-get install -y kubelet=1.26.4-00 kubeadm=1.26.4-00 kubectl=1.26.4-00装完立刻 hold 住版本,防止以后apt-get upgrade把三个组件升到别的 minor 版本:
sudo apt-mark hold kubelet kubeadm kubectl有同学问过,能不能 kubelet 用新的、kubeadm 用老的?不建议。kubeadm 的版本决定了它生成的静态 Pod 清单格式,kubelet 的版本决定了它能否正确解析这些清单,两者差太远,后面的 upgrade 流程也会断言失败。保持同步是最省事的原则。
3. 控制平面初始化:kubeadm init 全流程
3.1 先看清要拉哪些镜像
初始化前先列一遍镜像清单,这一步能帮你提前发现网络或仓库问题,而不是等 init 卡住:
kubeadm config images list --kubernetes-version v1.26.4输出大概是这些(具体镜像以当时输出为准):
registry.k8s.io/kube-apiserver:v1.26.4 registry.k8s.io/kube-controller-manager:v1.26.4 registry.k8s.io/kube-scheduler:v1.26.4 registry.k8s.io/kube-proxy:v1.26.4 registry.k8s.io/pause:3.9 registry.k8s.io/etcd:3.5.6 registry.k8s.io/coredns/coredns:v1.9.3如果直接拉官方仓库超时或失败,可以先手动把镜像拉下来:
kubeadm config images pull --kubernetes-version v1.26.4 --image-repository <MIRROR>拉完检查一下:crictl images。这里有个细节:用--image-repository拉镜像,后面kubeadm init也必须带同样的参数,否则 kubeadm 会按照默认仓库生成静态 Pod 清单,又从原始仓库去拉镜像,多此一举还容易失败。
3.2 init 命令的参数逐个讲
我这次用的初始化命令如下,IP 换成你控制面节点的内网地址,镜像仓库地址按实际替换:
sudo kubeadm init \ --apiserver-advertise-address=192.168.10.10 \ --control-plane-endpoint=192.168.10.10:6443 \ --kubernetes-version=v1.26.4 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --image-repository=<MIRROR> \ --cri-socket=unix:///run/containerd/containerd.sock逐个解释这些参数:
--apiserver-advertise-address:apiserver 对外广播的地址。多网卡机器如果不指定,kubeadm 可能挑到错误的网卡(比如管理口和业务口都有 IP),导致其他节点连不上 6443。单网卡环境可以不写,但写上更明确。--control-plane-endpoint:高可用的关键参数。单控制面时它和 apiserver-advertise-address 一致就行;将来扩容高可用时,这里应该是一个负载均衡地址(VIP 或 DNS 名),所有节点统一通过它访问 apiserver,kubeadm 生成的证书也会包含这个地址。现在就把参数带上,以后升级不用重建集群。--kubernetes-version:指定版本。不加这个参数,kubeadm 会去查最新的稳定版,跟你装的组件版本可能对不上,所以一定要显式给。--pod-network-cidr:Pod 网段。这个网段必须和后面 CNI 插件的默认值兼容,Flannel 默认 10.244.0.0/16,Calico 默认 192.168.0.0/16。选择一个和物理网络不冲突的私网段,想好了就别随便改,改的话要连 CNI 的配置一起改。--service-cidr:Service 虚拟 IP 段,默认是 10.96.0.0/12。这个网段是集群内的虚拟地址,同样不能和实际网络重叠,如果你的物理机正好用了 10.96.x.x,就要改掉。--image-repository:镜像仓库前缀。第 3.1 步是用哪个地址拉镜像,这里保持一致。--cri-socket:告诉 kubeadm 用哪个容器运行时 socket。现在 containerd 是默认,但如果机器上还有别的 runtime,显式指定能避免预检选错。
更规范的做法是写成 kubeadm 配置文件,提交到 git,任何人 clone 下来就能复现:
apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.10.10 bindPort: 6443 --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.26.4 controlPlaneEndpoint: 192.168.10.10:6443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 imageRepository: <MIRROR>执行sudo kubeadm init --config kubeadm-config.yaml效果相同,配置文件方式在多人协作和版本管理上明显更优。
3.3 初始化后的 kubectl 配置
init 成功后的输出里有一段关键信息,是配置 kubectl 的命令,复制执行:
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/configadmin.conf 是集群管理员凭据,里面包含 apiserver 地址、CA 证书和 client 证书。复制到用户目录并设置属主,kubectl 默认读取$HOME/.kube/config。如果一直用 root 操作,直接export KUBECONFIG=/etc/kubernetes/admin.conf也行,但测试集群一般按官方流程配置到普通用户目录。
之后先看节点状态:
kubectl get nodes此时控制面节点大概率是 NotReady,这是正常的——CNI 还没装,等第 3.4 步做完才会 Ready。如果长时间 NotReady 或者核心 pod 起不来,就要进入第 5 节的排查流程了。
3.4 装 CNI:以 Calico 为例
CNI(Container Network Interface)是 Pod 网络的底座,没有它,Pod 只有 loopback 网络,跨节点通信无从谈起。CNI 选型上,Calico 和 Flannel 是两大主流。Flannel 简单、资源占用小,但只有二层覆盖网络,没有网络策略能力;Calico 基于 BGP/IPIP/VXLAN,支持 NetworkPolicy,功能完整,企业内部常用。
安装方式:到项目官方文档的快速开始页面,复制当前推荐版本对应的 manifest 地址,然后用 kubectl 应用:
kubectl create -f <calico-manifest-url>默认清单把 Pod 网段设定为 192.168.0.0/16。如果你 init 时用的是 10.244.0.0/16 或者其他小网段,就必须改 Calico 的配置,否则 Calico 从 192.168.0.0/16 段分配 Pod IP,而 kubelet 仍认为 Pod 网段是 10.244.0.0/16,两边立刻对不上。改法是在 calico.yaml 里把 CALICO_IPV4POOL_CIDR 环境变量的值改成和 init 参数一致。
多网卡环境下,Calico 还要注意 IP 自动探测。默认它会找第一个可用网卡,但服务器经常有多块网卡,容易选错出口网卡,典型表现是集群内 ping 得通、跨节点业务不通。这时给 Calico 的 DaemonSet 加上环境变量:
- name: IP_AUTODETECTION_METHOD value: "interface=eth.*"正则限定网卡,按你机器实际网卡名调整。
等待 Calico 相关 Pod 全部 Running:
kubectl get pods -n calico-system kubectl get nodes当所有节点的状态变成 Ready,控制面初始化才算真正完成。
4. 工作节点加入与集群验收
4.1 生成 join 命令与 token 管理
工作节点的加入方式非常标准。把 init 输出里的 join 命令拿到工作节点上执行即可,形如:
kubeadm join 192.168.10.10:6443 \ --token <TOKEN> \ --discovery-token-ca-cert-hash sha256:<HASH>两个关键参数:token 是节点加入集群的通行证,默认有效期 24 小时;discovery-token-ca-cert-hash 是 apiserver CA 证书的 sha256 指纹,作用是让工作节点在信任证书之前先确认对方身份。两个信息 init 成功时都会打印,如果当时没保存,可以随时在控制面节点上重新生成:
kubeadm token create --print-join-command这个命令非常实用,它会把 token 和 hash 拼好,直接复制就能用。临时验证环境可以加--ttl 0让 token 永不过期,但生产环境建议保留较短 TTL,要用的时候再生成。
工作节点上要提前完成第 2 部分的全部环境准备——内核模块、sysctl、swap、主机名、containerd、kubeadm/kubelet——这些准备步骤和机器角色无关,每台机器都一样。工作节点上不需要执行 init,只需要 join。
注意 join 命令里的 apiserver 地址必须能通。工作节点和 k8s-cp1 之间的 6443 端口可达,防火墙、安全组、hosts 三个地方按顺序检查一遍。
执行 join 后,在控制面节点上再看:
kubectl get nodes两个工作节点从 NotReady 到 Ready 一般需要几十秒,因为要完成 kubelet 注册、CNI 接入、Pod 调度初始化。如果一直 NotReady,看第 5.2 节。
4.2 跑一个 Demo 应用验证集群
集群建完先别急着粘业务,用一个小应用做全链路验证是最快的方式。跑一个 Deployment,暴露成 NodePort,从外部访问:
kubectl create deployment nginx-demo --image=nginx:1.25 kubectl expose deployment nginx-demo --type=NodePort --port=80 kubectl get svc nginx-demoNodePort 端口在 30000-32767 之间随机分配,输出里能看到80:<随机端口>/TCP。然后在集群外访问任意节点的<节点IP>:<随机端口>,能打开欢迎页就说明 Pod 创建、调度、容器运行时、CNI、kube-proxy 的 Service 转发全部正常。
kubectl cluster-info会列出核心组件的访问地址,kubectl get pods -A -o wide能一次性看到所有命名空间下 Pod 分布在哪些节点、IP 是什么、镜像版本是什么,这两个命令建议养成习惯。
还有一个可选但值得了解的操作:默认 kube-proxy 用 iptables 做 Service 转发,集群规模大了以后规则会越来越长,性能衰减明显。编辑 kube-system 下的 kube-proxy ConfigMap,把 mode 改为ipvs,再滚动重启 kube-proxy DaemonSet,就能切换到 ipvs 模式。这个调整可以后续有空再做,不影响当前集群可用性。
5. 常见问题与排查技巧实录
5.1 镜像拉不下来怎么处理
初始化阶段最容易遇到的问题就是镜像失败。执行kubeadm init时,apiserver、etcd、coredns 这些组件的镜像都要先拉到本地,任何一个拉失败,init 就会在这一步卡住或者直接报错,常见输出是:
[kubelet-check] It seems like the Kubernetes API server isn't running...这时候不要慌,先分清楚是镜像问题还是组件启动问题。用crictl images看看缺哪几个镜像,用crictl ps -a看有没有容器一直在拉取或退出。如果确实是镜像拉取失败,报错一般是failed to pull and unpack image ... failed to resolve reference。
处理思路就一个:换一个能用的仓库,或者提前把镜像拉到本地再初始化。最省事的路径是统一用--image-repository指向可用的镜像站,然后初始化。已经 init 失败一半的机器,先sudo kubeadm reset -f清理,再重新 init。
还有一个容易踩的点:有些同学用 docker 把镜像拉到本地就以为完事了,但 Kubernetes 用的是 containerd 的镜像存储,和 docker 的镜像是隔离的。docker 拉下来的镜像 containerd 根本看不到。要么全程用crictl pull,要么确保--image-repository参数前后一致。
5.2 kubelet 与节点状态问题的排查
节点 NotReady 是集群搭建时出现频率最高的问题,原因五花八门。我的排查顺序一般是固定的。
第一步看 kubelet 状态和日志:
journalctl -u kubelet -fkubelet 反复重启、日志刷屏,多半是配置冲突或证书问题。典型错误是 cgroup driver 不一致,会以 misconfiguration 开头,原因是 containerd 的 SystemdCgroup 没改成 true,或者机器上其他 runtime 和 kubelet 配置冲突。回到第 2.3 节把 containerd 配好,重置后重新 init 即可。
第二步看容器运行时:
systemctl status containerd crictl ps -a确认 containerd 在跑、pause 容器能正常创建。如果 pause 容器拉起失败,回到 sandbox_image 配置。之前遇到过 pause:3.9 拉不下来导致所有 Pod 都 ContainerCreating 的情况,问题表面上看像网络插件不工作,实际源头是 sandbox 起不来。
第三步看 CNI:
kubectl get pods -A -o wide如果 calico-system 里的 Pod 一直是 Pending,一般是节点资源不足或者没有可调度节点;如果是 CrashLoopBackOff,看日志kubectl logs -n calico-system <pod> -f。Calico 最常见的启动问题是网卡探测异常和端口被占,按第 3.4 节把 IP_AUTODETECTION_METHOD 配好基本能解决。
排查完如果确定要重置,记得kubeadm reset之后把 /etc/cni/net.d 和 /var/lib/cni 下的残留配置也清掉,不然二次初始化时 CNI 残留会让 kubelet 的 pod CIDR 判断错乱。这是很多人重置后依然报错的核心原因。
5.3 CoreDNS 与网络相关故障
CoreDNS 是集群内部 DNS,它的故障直接影响 Service 域名解析。常见两种情况。
第一种,CoreDNS 一直 Pending。原因通常是 CNI 没装好,Pod 无法分配 IP,自然调度不到 Running。先解决 CNI,再回来看它。
第二种,CoreDNS 反复 CrashLoopBackOff,日志里看到 loop detected 是典型信号。检查节点的 /etc/resolv.conf,如果里面的 nameserver 指向了集群自身的 DNS 地址,或者指向了一个会把查询转发回节点的 DNS,CoreDNS 就会在自身和上游之间形成循环。解决方法是给 kubelet 指定一个干净的 resolv.conf,去掉集群内 DNS 地址,或者在节点上调整 /etc/resolv.conf,让上游指向真正的外部 DNS。
另外,如果你曾经往 /etc/hosts 里写过集群相关域名,并把这个文件塞给了容器,也可能出现类似的解析问题。总之,DNS 出问题先从 resolv.conf 入手查,别一上来就翻应用代码。
5.4 高频问题速查表
| 现象 | 原因 | 排查/解决办法 |
|---|---|---|
kubeadm init 报[ERROR Swap] | swap 未关闭 | swapoff -a,并注释 /etc/fstab 里的 swap 行 |
| 报 br_netfilter 相关文件不存在 | br_netfilter 模块未加载 | modprobe br_netfilter,写入 /etc/modules-load.d 后重启生效 |
报[ERROR Port-6443]或 etcd 目录已存在 | 端口占用或上次初始化残留 | 清理占用进程,或kubeadm reset -f后重来 |
| kubelet 连接 containerd 失败 | runtime 没启动或版本不匹配 | systemctl status containerd,重启后crictl info确认 |
| 24 小时后 join 失败 | token 过期 | kubeadm token create --print-join-command重新生成 |
| join 提示 discovery hash 不匹配 | hash 复制错了 | 用上面命令重新获取,不要手抄 hash |
| Pod 一直 ContainerCreating | CNI 未装或 sandbox 镜像拉不下来 | 检查 CNI Pod,检查 pause 镜像,确认 sandbox_image 可访问 |
| 节点长时间 NotReady | kubelet 或 CNI 未就绪 | journalctl -u kubelet -f,查 CNI Pod 日志 |
| 节点之间 ping 不通业务 Pod | Calico 网卡探测错误或网段冲突 | 配置 IP_AUTODETECTION_METHOD,核对 CALICO_IPV4POOL_CIDR |
6. 实操心得与后续扩展建议
6.1 让我省了很多事的小习惯
第一条,所有机器无论控制面还是工作节点,第一步永远先跑一遍统一的基础脚本:内核模块、sysctl、swap off、hosts、时间同步、containerd 配置。这样后面不管是 init 还是 join,都不会因为环境差异出现“这台行、那台不行”的玄学问题。
第二条,写 kubeadm 配置文件而不是一长串命令行参数。命令行适合一次性演示,配置文件配合 git,每次变更都有记录,出问题能直接 diff。
第三条,装完立刻做资源预留。kubelet 的 system-reserved 和 kube-reserved 参数建议在部署时就规划好,否则业务一多,节点上的系统组件先被挤垮。
第四条,维护一份镜像清单。kubeadm config images list的输出保存成文件,和集群版本一起归档,离线环境重建和后续升级都比对着报错现拉强。
6.2 从单控制面到高可用还需要补什么
单控制面集群不适合承载核心业务。扩展高可用的方向大致有三块:控制面组件做多副本,etcd 用 stacked 模式跟着三个控制面节点走,前置一个负载均衡地址作为 control-plane-endpoint,这也是为什么第 3.2 节建议 init 时就把 control-plane-endpoint 带上;后期给已有单控制面集群追加控制面节点也能做,但限制比较多,提前规划比后期迁移省事。
证书生命周期也值得关注。kubeadm 签发的集群证书默认一年有效期,到期前要执行kubeadm certs renew all并重启相关组件,建议把证书过期检查直接写进监控。
监控方面,metric-server 是kubectl top的基础,想要完整看板再引入完整的监控栈。这些都属于集群建完后的配套设施,第一次搭集群建议先把工作节点全部跑通,再加戏。
最后聊点个人体会。我最早也是从二进制部署入门的,刚接触 kubeadm 时总觉得它把原理藏起来了,心里不踏实。真正用久了才明白,kubeadm 的价值不是隐藏原理,而是把“每次都要手工重复且容易出错”的那部分流程固定下来,剩下的配置文件全都可以打开检查。这次部署 1.26.4 的全程,最让我感慨的是:只要环境准备干净、版本对齐、镜像仓库事先确认,初始化基本一次就能过。大部分翻车都栽在前面 30 分钟的基础配置上,希望这篇记录能让你少踩几个我已经踩过的坑。