使用 Kubeadm 搭建 Kubernetes 集群:单控制面与高可用

This article is extracted from the chat log with AI. Please identify it with caution.

本文把单控制面入门路径与多控制面高可用(HA)路径放在一起说明。先根据目标选择一种拓扑,再执行对应章节的命令;不要把多台机器都执行一次 kubeadm init

kubeadm 负责引导控制平面和节点加入,不会替你选择或安装 CNI、存储、Ingress、监控和备份方案。Kubernetes 的安装方式、软件包仓库和 CNI 清单会随版本变化,执行前请以 kubeadm 官方安装指南 与本文末尾的官方文档为准。文中的 IP、域名、网段、token 和证书密钥均为占位符,不能直接用于生产环境。

一、先选择部署模式#

模式适用场景控制平面入口etcd 拓扑关键限制
单控制面学习、测试、小规模非关键环境单个控制平面节点的地址或稳定 DNS本地(stacked)etcd该节点故障会使控制面不可用
高可用控制平面生产或需要控制面容灾的环境面向全部控制平面节点的稳定 DNS 或 VIP堆叠式(stacked)或外置(external)etcd至少准备多个控制平面节点、负载均衡和 etcd 多数派

单控制面不能通过在初始化时加 –upload-certs 自动变成 HA;要扩展为多控制面,初始化阶段就应规划稳定的 controlPlaneEndpoint、负载均衡、API Server 证书 SAN 和 etcd 拓扑。

二、所有节点的共同准备#

本节适用于控制平面和工作节点。高可用集群还需要完成后文的负载均衡与 etcd 前置条件。

1. 资源、网络与版本规划#

  • 每台节点使用唯一且稳定的主机名、MAC 地址、product_uuid 与节点 IP;节点之间应能解析名称并保持时间同步。
  • 确保所有节点可互通,并按 Kubernetes 端口与协议要求 配置防火墙、安全组和网络策略。HA 场景特别注意 API Server 的 6443、堆叠式 etcd 的 2379/2380,以及 kubelet 的 10250。
  • 规划不与宿主机、数据中心或 VPN 网络冲突的 Pod CIDR 和 Service CIDR。Pod CIDR 必须与后续选用的 CNI 配置一致。
  • 选择相互兼容的 kubeadmkubeletkubectl 与 Kubernetes 版本;升级时遵守 版本偏差策略
  • 受限网络或离线环境应提前准备当前版本所需镜像,并确认节点能从 registry.k8s.io 或组织认可的镜像源获取镜像。

2. 主机基础配置#

禁用 swap、启用转发与桥接流量处理是常见前置条件。不同发行版的持久化方式可能不同,以下是 Linux 上的典型做法:

# 立即关闭 swap;同时应在 /etc/fstab 中移除或注释相应条目,避免重启后恢复。
sudo swapoff -a

# 加载网络模块。
cat <<'EOF' | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 配置内核网络参数。
cat <<'EOF' | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

sudo sysctl --system

安装可工作的 CRI(通常为 containerd),并确保 kubelet 与运行时的 cgroup 驱动一致;生产环境通常使用 systemd。多个 CRI 共存或 socket 不在默认位置时,应在 kubeadm 配置中明确指定 criSocket,不要依赖自动探测。具体配置以 容器运行时指南 为准。

随后按 官方安装步骤 安装并按需锁定 kubeletkubeadmkubectl;不要从本文复制固定版本号或已废弃的软件包仓库 URL。安装完成后可以启用 kubelet,它会在 initjoin 完成前持续重试是正常现象:

sudo systemctl enable --now kubelet

三、单控制面路径:从初始化到工作节点加入#

这条路径保留最直接的入门流程:准备一台控制平面节点,初始化、安装 CNI,再让工作节点加入。它不提供控制平面故障转移能力。

1. 初始化唯一的控制平面节点#

在控制平面节点执行。只有 CNI 需要指定 Pod CIDR 时才传入该参数,并替换为与你的 CNI 相符的网段:

POD_CIDR="10.244.0.0/16" # 按所选 CNI 的要求替换
sudo kubeadm init --pod-network-cidr="$POD_CIDR"

如果要固定更多集群参数,使用经版本控制和评审的 kubeadm 配置文件;配置 API 与字段以本机 kubeadm 的当前文档为准。初始化成功后,配置管理员 kubeconfig:

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,控制平面节点通常仍为 NotReady,直到 CNI 就绪。

2. 安装 CNI#

kubeadm 不会自动安装 Pod 网络。选择符合网络模型和运维要求的 CNI,并按其当前官方安装文档部署:

不要固定复制某个历史版本的 Calico manifest URL。确认 CNI 的 Pod CIDR 与初始化参数一致,然后观察系统组件和节点:

kubectl get pods -n kube-system -w
kubectl get nodes -o wide

3. 让工作节点加入#

kubeadm init 的输出会生成一条普通 worker join 命令。通过受控的秘密管理或安全通道保存它,而不是提交到 Git、聊天记录或工单正文。

sudo kubeadm join <api-server-address>:6443 \
  --token <bootstrap-token> \
  --discovery-token-ca-cert-hash sha256:<ca-public-key-hash>

当原 token 已过期时,在健康的控制平面节点重新生成 worker join 命令:

sudo kubeadm token create --print-join-command

4. 验证入门集群#

kubectl get nodes -o wide
kubectl get pods -n kube-system -o wide

# 可选:部署一个简单工作负载验证调度与网络。
kubectl create deployment nginx --image=nginx
kubectl get pods -o wide

如仅用一台机器做实验,控制平面默认的 NoSchedule 污点会阻止普通 Pod 调度。只有在明确接受这一取舍时才移除它:

kubectl taint nodes --all node-role.kubernetes.io/control-plane-

四、高可用路径:多控制平面、稳定入口与 etcd 多数派#

高可用不是“多执行几次 init”。第一台控制平面节点仅执行一次 kubeadm init;其余控制平面节点和工作节点都必须通过 kubeadm join 加入已有集群。

flowchart TB
    Client["kubectl、kubelet 与待加入节点"] --> LB["稳定入口:controlPlaneEndpoint<br/>api.k8s.example.com:6443"]

    subgraph CP["控制平面节点"]
        CP1["cp-1"]
        CP2["cp-2"]
        CP3["cp-3"]
    end

    LB --> CP1
    LB --> CP2
    LB --> CP3

    CP1 -. "仅外置 etcd" .-> ETCD["独立 etcd 集群<br/>通常为 3 或 5 个成员"]
    CP2 -. "仅外置 etcd" .-> ETCD
    CP3 -. "仅外置 etcd" .-> ETCD

1. HA 架构前置条件#

controlPlaneEndpoint 是稳定的 DNS 名称或 VIP,通常指向四层 TCP 负载均衡器的 6443 端口。它必须始终指向负载均衡器,而不是某一台控制平面节点;否则首台节点故障后,kubelet、管理员 kubeconfig 和待加入节点仍会访问失效地址。

负载均衡器应把所有控制平面节点作为后端,对 kube-apiserver 监听端口做 TCP 健康检查。初始化前后端连接被拒绝是可能的,因为 API Server 尚未启动;但从负载均衡器到后端的连接超时通常意味着 DNS、路由、安全组或负载均衡器配置错误。

还应确保:

  • 所有控制平面和工作节点都能访问 controlPlaneEndpoint:6443
  • API Server 证书的 SAN 包含该 DNS 名称和需要由客户端访问的 VIP/IP。
  • 生产环境通常使用奇数个控制平面节点。三成员 etcd 可容忍一个成员不可用,五成员可容忍两个;能否维持多数派决定 etcd 是否可写。
  • 不要把入口负载均衡器与 Ingress 业务流量混为一谈:前者用于 Kubernetes API Server,后者是应用流量入口。

2. 选择 etcd 拓扑#

拓扑etcd 位置控制平面 join 时发生什么取舍
堆叠式(stacked etcd)每台控制平面节点一个本地 etcd 静态 Pod新节点创建本地 etcd 静态 Pod,并加入 etcd 集群机器少、部署简单;控制平面与 etcd 共用故障域
外置(external etcd)先准备好的独立 etcd 集群join –control-plane 配置 API Server 连接既有 etcd;不会新增外置 etcd 成员故障域隔离较好;机器、证书与运维复杂度更高

外置 etcd 必须先完成部署、TLS、成员健康检查和备份策略。不要期待 kubeadm join –control-plane 管理独立 etcd 的成员;请参照 使用 kubeadm 搭建外置 HA etcd

3. 为第一台控制平面准备配置#

多节点场景建议把集群级参数写入经过审查的配置文件。以下为堆叠式 etcd 的最小示例;替换全部占位值,并根据正在使用的 kubeadm 版本核对配置 API:

# kubeadm-ha.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: <cp-1-ip>
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///run/containerd/containerd.sock
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
controlPlaneEndpoint: "api.k8s.example.com:6443"
networking:
  podSubnet: <pod-cidr>
  serviceSubnet: <service-cidr>
apiServer:
  certSANs:
    - api.k8s.example.com
    - <load-balancer-vip>

外置 etcd 时,在同一份 ClusterConfiguration 中加入 etcd.external。在每台控制平面节点上安全放置对应的 etcd CA、API Server 客户端证书和私钥:

etcd:
  external:
    endpoints:
      - https://<etcd-1>:2379
      - https://<etcd-2>:2379
      - https://<etcd-3>:2379
    caFile: /etc/kubernetes/pki/etcd/ca.crt
    certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
    keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key

不要在外置 etcd 拓扑中同时配置本地 etcd 成员;外置 etcd 的证书和私钥属于高敏感凭据。

4. 在 cp-1 执行 kubeadm init#

sudo kubeadm init --config kubeadm-ha.yaml --upload-certs

–upload-certs 会将后续控制平面节点所需的共享证书加密后放入集群的 kubeadm-certs Secret,并输出控制平面 join 命令和 certificate key。该 Secret 与用于解密它的 key 默认两小时后失效;它们都应当短时、按最小范围通过安全通道分发。

初始化后按单控制面路径的方式配置 admin.conf,并先依据 CNI 的官方安装说明部署网络插件。未部署 CNI 时,节点通常不会变为 Ready,CoreDNS 也可能无法正常调度。

init 的分阶段行为#

kubeadm init 实际上是一组按顺序执行的 phase。可用 kubeadm init –help 查看本机版本的完整顺序,并用 kubeadm init phase <phase> –help 检查各阶段的参数。关键过程如下:

阶段kubeadm 的动作结果或落盘位置
preflight检查 root 权限、端口、swap、CRI、内核条件、镜像和残留状态错误应修复根因,不建议以 –ignore-preflight-errors=all 绕过
certs生成或复用 CA、API Server、front-proxy、ServiceAccount 与 etcd PKI 材料默认在 /etc/kubernetes/pki;外置 etcd 不生成本地 etcd 服务端成员证书
kubeconfig为管理员、kubelet、controller-manager、scheduler 等生成不同身份的 kubeconfig/etc/kubernetes/admin.confcontroller-manager.confscheduler.conf
etcdcontrol-plane写入堆叠式 etcd(仅该拓扑)和 API Server、controller-manager、scheduler 的静态 Pod manifest/etc/kubernetes/manifests
kubelet-startwait-control-plane写入 kubelet 配置并启动 kubelet,等待控制面启动kubelet 监视 manifest 目录并经由 CRI 启动静态 Pod
upload-configmark-control-plane上传 kubeadm/kubelet 配置,给首台节点打控制平面标签与污点集群内 ConfigMap 和 Node 对象
bootstrap-tokenkubelet-finalize建立节点发现、TLS Bootstrap、CSR 自动审批与 kubelet 证书轮换所需配置后续节点可以安全申请 kubelet 身份
addon通过 API Server 安装 CoreDNS 与 kube-proxyCNI 未就绪时 CoreDNS 可能保持 Pending

控制平面组件不是普通 Deployment:kubeadm 写入静态 Pod manifest 后,本机 kubelet 直接监视该目录并运行它们。因此即使 API Server 尚不可用,首个 etcd(堆叠式)和控制平面组件仍能从本地启动。

5. 其余控制平面:带 –control-plane 的 join#

cp-2cp-3 上依次执行 init 输出的控制平面 join 命令:

sudo kubeadm join api.k8s.example.com:6443 \
  --token <bootstrap-token> \
  --discovery-token-ca-cert-hash sha256:<ca-public-key-hash> \
  --control-plane \
  --certificate-key <certificate-key>

–control-plane 表示创建新的控制平面,而不是普通 Worker;–certificate-key 允许节点下载并解密共享证书。建议一次只加入一台节点,确认 API、etcd 和 kubelet 健康后再继续。

控制平面 join 的关键步骤是:

  1. 对新节点执行 preflight,并通过负载均衡器获取集群发现信息。
  2. 以 bootstrap token 与 CA 公钥哈希验证控制面身份,避免信任伪造的 API Server。
  3. 下载共享证书,并生成该节点所需的本地控制平面证书和 kubeconfig。
  4. 写入 API Server、controller-manager、scheduler 静态 Pod manifest,配置并启动 kubelet。
  5. 堆叠式 etcd 会把本机 etcd 加入已有集群;外置 etcd 不会借此新增外置成员。
  6. kubelet 以 TLS Bootstrap 获得节点身份,控制平面组件加入 leader election。

多个 controller-manager、scheduler 和 API Server 副本并不表示控制循环同时写入;controller-manager 与 scheduler 会用 leader election 选出活跃实例。第二台控制平面节点加入后,若 CoreDNS 副本此前都落在首台节点,可执行以下命令触发重新调度:

kubectl -n kube-system rollout restart deployment coredns

6. 工作节点:普通 join#

工作节点只使用普通 join 命令,携带 –control-plane–certificate-key

sudo kubeadm join api.k8s.example.com:6443 \
  --token <bootstrap-token> \
  --discovery-token-ca-cert-hash sha256:<ca-public-key-hash>

两种 join 的边界如下:

节点角色额外参数获得的本地内容etcd 影响
控制平面–control-plane –certificate-key控制平面 PKI、kubeconfig、静态 Pod manifest仅堆叠式拓扑新增本地 etcd 成员
Workerkubelet 身份、kubelet 配置与节点对象不会成为 etcd 成员

Worker 会通过稳定 API 入口验证集群信息,使用 bootstrap token 提交 kubelet CSR;获批后以自己的正式身份注册 Node 对象。CNI DaemonSet 配置好该节点网络后,节点才会成为 Ready 并接收普通 Pod。

7. HA 验证与演练#

# 所有控制平面和工作节点均应为 Ready。
kubectl get nodes -o wide

# 观察控制平面、CoreDNS、kube-proxy 与 CNI。
kubectl get pods -n kube-system -o wide

# 验证 API Server 健康;客户端应通过稳定入口访问。
kubectl get --raw='/readyz?verbose'

# 仅堆叠式 etcd:每个控制平面节点通常应有一个 etcd 静态 Pod。
kubectl get pods -n kube-system -l component=etcd -o wide

在维护窗口中,可从负载均衡器摘除一台控制平面后端并验证 API 可用性;不要同时下线超过 etcd 容错上限的成员。对外置 etcd,应独立验证 etcd 集群成员状态和备份恢复流程。

五、令牌、证书与常见故障#

现象原因与处理
bootstrap token 过期初始化生成的 token 默认有效期为 24 小时。在健康控制平面执行 sudo kubeadm token create –print-join-command,为 Worker 生成新的基础命令。
控制平面 join 无法下载证书kubeadm-certs Secret 或其 certificate key 默认两小时后失效。在已加入的控制平面执行 sudo kubeadm init phase upload-certs –upload-certs,获得新的 key 后尽快执行控制平面 join。必要时再生成新的基础 join 命令,并附加 –control-plane –certificate-key
join 无法连接 API检查待加入节点到 controlPlaneEndpoint:6443 的 DNS、路由、安全组和负载均衡器健康检查;不要为“临时绕过”而把 join 目标改成某台控制平面 IP。
x509 证书名称不匹配确认负载均衡器 DNS/VIP 在 API Server 证书 SAN 中;在 apiServer.certSANs 中显式声明后,按官方证书管理流程修复。
Node 长期 NotReady 或 CoreDNS Pending通常是 CNI 未安装、Pod CIDR 与 CNI 配置不一致,或节点间 Pod 网络不可达。优先检查 CNI DaemonSet、节点路由和 kubelet 日志。
堆叠式 etcd 无法形成多数派检查各 etcd 静态 Pod、2379/2380 连通性与成员状态;不要在健康 etcd 成员上随意执行 kubeadm reset。外置拓扑则排查独立 etcd 集群。

不要在生产环境使用 –discovery-token-unsafe-skip-ca-verification。它跳过 CA 公钥哈希校验,会削弱待加入节点识别真实控制面的能力。bootstrap token、certificate key、admin.conf 和 etcd 客户端私钥都应视为敏感凭据:最小范围分发、短时保存、使用后轮换或失效。

六、后续运维与扩展#

  • 升级前运行 sudo kubeadm upgrade plan,再按 kubeadm 升级指南 执行;不要把历史文档中的固定版本命令直接用于当前集群。
  • 定期检查证书:sudo kubeadm certs check-expiration。续期、外部 CA 和证书分发请参照 kubeadm 证书管理
  • 对堆叠式或外置 etcd 都要定期验证备份与恢复,而不只是创建备份文件。
  • 部署存储、Ingress、Metrics Server、Helm 等组件时,使用各项目当前官方安装文档,并在测试环境先验证与 Kubernetes 版本、CNI 和安全策略的兼容性。
  • 删除节点前先 drain,再在目标节点执行 reset;kubeadm reset 是尽力清理,不会自动清除所有网络规则。

参考资料#

本文共 6025 字,创建于 Dec 31, 2025

相关标签: Kubernetes, ByAI