1. 为什么现在还要手敲kubeadm部署?——不是过时,而是回归本质
“kubeadm部署安装”这个标题看起来像一份十年前的文档草稿,甚至有人会脱口而出:“都2024年了,谁还手动跑kubeadm?直接上托管K8s服务不香吗?”——这话没错,但错在把“要不要用”和“懂不懂底层”混为一谈。我去年参与某金融级中间件平台重构时,就遇到一个典型场景:云厂商提供的托管集群默认启用了IPv6双栈、强制启用PodSecurityPolicy(后被PSP替代)、且控制平面组件日志路径被深度封装,当某次etcd快照恢复失败导致API Server反复CrashLoopBackOff时,运维团队花了37小时才定位到问题根源——是kubeadm init生成的/etc/kubernetes/manifests/kube-apiserver.yaml中--audit-log-path参数指向了一个被SELinux策略拦截的挂载路径。而这个路径,恰恰是kubeadm根据--config文件中certificatesDir字段推导出的默认值。
这就是kubeadm不可替代的价值:它不是“部署工具”,而是Kubernetes控制平面的可审计、可复现、可调试的声明式蓝图生成器。它把原本需要手工编写12个静态Pod manifest、配置6类证书、校验5种RBAC绑定关系的复杂过程,压缩成一条命令+一个YAML配置文件。你不需要记住openssl req -x509 -newkey rsa:4096...的全部参数,但必须清楚kubeadm init --config kubeadm-config.yaml背后到底生成了什么、修改了哪些文件、依赖哪些系统级约束。这就像汽车修理工不必亲手冶炼钢铁,但必须知道活塞环间隙超差0.02mm会导致拉缸。
关键词里虽未明写,但本篇实际覆盖的核心技术点非常明确:kubeadm init / join 工作流、证书生命周期管理、静态Pod机制与kubelet交互、CNI插件集成边界、以及最关键的——kubeadm配置文件的字段语义与取舍逻辑。适合三类人:正在考CKA认证的备考者(考试环境强制使用kubeadm)、需要在离线环境交付K8s集群的交付工程师、以及负责SRE平台建设的技术负责人(他们需要把kubeadm封装进自动化流水线)。接下来的内容,不会教你“复制粘贴就能跑”,而是带你拆开kubeadm这个黑盒,看清每个齿轮如何咬合。
2. kubeadm init全流程解剖:从命令行参数到磁盘上的17个关键文件
很多人以为kubeadm init就是执行一条命令,等它输出Your Kubernetes control-plane has initialized successfully!就完事了。实则不然。这条命令背后是一套精密的、分阶段执行的状态机,共经历PreFlight → Certs → KubeConfig → ControlPlane → Etcd → WaitControlPlane → UploadConfig → MarkControlPlane → BootstrapToken → Housekeeping 10个阶段。每个阶段失败都会返回明确错误码,但多数人只看到最后一行红色报错,却不知该回溯到哪个阶段查日志。下面以一次标准的单节点init为例,逐层展开其在文件系统上留下的痕迹。
2.1 阶段0:PreFlight检查——那些被忽略的“系统体检报告”
kubeadm启动第一件事不是生成证书,而是执行23项预检(PreFlight Checks)。这些检查分散在/usr/libexec/kubeadm的Go代码中,但结果会实时输出到终端。例如:
[pre-flight] Running pre-flight checks [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 24.0.7. Recommended version: 20.10.17 [ERROR Port-10250]: Port 10250 is in use这里三个警告/错误分别对应不同层级的问题:
IsDockerSystemdCheck:Docker的cgroup驱动必须与kubelet一致。若Docker用cgroupfs而kubelet用systemd,会导致Pod资源限制失效。修复不是改Docker配置,而是统一为systemd——需编辑/etc/docker/daemon.json添加"exec-opts": ["native.cgroupdriver=systemd"],然后systemctl restart docker。SystemVerification:kubeadm内置了验证矩阵(validation matrix),只认可特定Docker版本。这不是“兼容性问题”,而是因旧版Docker存在已知的OOM Killer误杀容器进程的bug。绕过检查用--ignore-preflight-errors=SystemVerification,但生产环境严禁。Port-10250:kubelet的API端口被占用。常见于之前运行过minikube或k3s残留进程。用lsof -i :10250定位进程并kill -9。
提示:所有PreFlight检查均可通过
kubeadm init phase preflight单独触发,配合--v=5参数可输出详细调试日志。这是排查init卡住的第一步。
2.2 阶段1-2:证书与kubeconfig生成——理解PKI体系的最小闭环
kubeadm init最核心的产出是证书体系。它不调用OpenSSL命令,而是直接使用Go的crypto/x509库生成自签名CA及下游证书。整个PKI结构如下图所示(文字描述):
ca.crt (Root CA) ├── apiserver.crt (CN=kube-apiserver, O=system:masters) ├── front-proxy-ca.crt (Front Proxy CA) │ └── front-proxy-client.crt (CN=front-proxy-client) ├── etcd/ca.crt (Etcd Root CA) │ ├── etcd/server.crt (CN=etcd-server, DNS=master, IP=192.168.1.100) │ └── etcd/peer.crt (CN=etcd-peer, DNS=master) └── sa.key + sa.pub (Service Account密钥对)关键点在于:所有证书的Subject Alternative Name(SAN)必须精确匹配集群访问需求。例如,若要让外部kubectl通过https://k8s.example.com:6443访问,apiserver.crt的SAN必须包含DNS:k8s.example.com,IP:192.168.1.100。而kubeadm默认只填入localhost和节点IP,因此必须通过--apiserver-cert-extra-sans参数显式追加:
kubeadm init \ --apiserver-cert-extra-sans="k8s.example.com,10.0.0.10" \ --pod-network-cidr=10.244.0.0/16生成的证书全部存放在/etc/kubernetes/pki/目录下,共12个文件。其中ca.key是最高机密,一旦泄露,攻击者可签发任意合法证书。因此kubeadm init完成后,必须立即备份/etc/kubernetes/pki/ca.key并设置严格权限:
chmod 600 /etc/kubernetes/pki/ca.key chown root:root /etc/kubernetes/pki/ca.key同时生成的/etc/kubernetes/admin.conf是管理员kubeconfig文件,它包含client-certificate-data(即admin.crt的base64编码)和client-key-data(即admin.key)。这个文件本质是kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes的凭证。切记:admin.conf不能直接给开发人员使用,应通过kubeadm kubeconfig user --client-name dev-user > dev-user.conf生成受限用户配置。
2.3 阶段3-5:ControlPlane与Etcd——静态Pod如何接管集群生命
kubeadm init真正的魔法在于“静态Pod机制”。它不直接运行kube-apiserver进程,而是创建/etc/kubernetes/manifests/kube-apiserver.yaml等4个YAML文件。kubelet会持续监控该目录,发现新YAML就拉起对应容器。这种设计实现了“用K8s管理K8s自身”的递归哲学。
以kube-apiserver.yaml为例,其核心字段解析如下:
apiVersion: v1 kind: Pod metadata: creationTimestamp: null name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - --advertise-address=192.168.1.100 # 对外广播地址,必须能被Node访问 - --allow-privileged=true # 允许特权容器(CNI必需) - --authorization-mode=Node,RBAC # 授权模式,Node用于kubelet,RBAC用于用户 - --client-ca-file=/etc/kubernetes/pki/ca.crt # 客户端证书CA,验证kubectl请求 - --enable-admission-plugins=NodeRestriction # 强制Node只能修改自身Pod状态 - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt # Etcd通信CA - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt # APIServer连Etcd的证书 - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key # 对应私钥 - --secure-port=6443 # HTTPS端口 volumeMounts: - mountPath: /etc/kubernetes/pki name: k8s-certs - mountPath: /etc/ssl/certs name: ca-certs volumes: - hostPath: path: /etc/kubernetes/pki type: DirectoryOrCreate name: k8s-certs这里的关键陷阱是--advertise-address:它不是kube-apiserver监听的地址(那是--bind-address),而是告诉集群其他组件“请用这个IP联系我”。若填错,kube-controller-manager会无法连接APIServer,导致Node NotReady。实测中,我们曾将此值误设为Docker网桥IP(172.17.0.1),结果所有Node加入后状态始终为NotReady,因为controller-manager尝试用该IP连接,但防火墙拒绝了。
Etcd的manifest(/etc/kubernetes/manifests/etcd.yaml)同样重要。它指定了--initial-cluster=master=https://192.168.1.100:2380,这个URL必须与/etc/kubernetes/pki/etcd/peer.crt中的SAN完全一致。否则etcd进程启动后会报错failed to find member with given ID,因为TLS握手失败。
2.4 阶段6-10:从集群就绪到令牌分发——最后五步的隐藏逻辑
当WaitControlPlane阶段完成,意味着4个静态Pod全部Running,但此时集群仍不能工作。因为:
- Bootstrap Token未创建:
kubeadm join需要的token由kubeadm init在BootstrapToken阶段生成,并存入kube-system命名空间的bootstrap-token-xxxxxxSecret中。该Secret包含auth-extra-groups: system:bootstrappers:kubeadm:default-node-token,赋予新Node请求CSR的权限。 - CoreDNS未就绪:
kubeadm init默认部署CoreDNS,但它的Deployment位于/etc/kubernetes/manifests/kube-controller-manager.yaml中指定的--cluster-signing-cert-file证书签发的ServiceAccount下。若证书过期,CoreDNS Pod会卡在ContainerCreating,因为无法挂载/var/run/secrets/kubernetes.io/serviceaccount。 - kube-proxy未生效:虽然
kubeadm init会部署kube-proxy DaemonSet,但它依赖--cluster-cidr=10.244.0.0/16参数。若该CIDR与后续CNI插件的Pod网络冲突(如Flannel也用10.244.0.0/16),会导致Node间Pod无法通信。
最终,kubeadm init输出的kubeadm join命令形如:
kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234其中--token有效期24小时,--discovery-token-ca-cert-hash是/etc/kubernetes/pki/ca.crt的SHA256哈希。这个哈希值确保Node下载的CA证书与Master一致,防止中间人攻击。你可以用以下命令重新生成有效token:
kubeadm token create --print-join-command3. kubeadm join深度排错:为什么Node卡在“NotReady”?——一张表看懂12种状态原因
kubeadm join命令执行后,Node状态长期停留在NotReady是最高频问题。表面看是kubectl get nodes输出NotReady,但背后有至少12种根本原因。我整理了一张实战排查表,按发生概率从高到低排序:
| 状态现象 | 根本原因 | 快速验证命令 | 修复方案 |
|---|---|---|---|
kubectl get nodes显示NotReady,kubectl describe node <node>中Events无报错 | kubelet未启动或崩溃 | systemctl status kubelet | journalctl -u kubelet -n 100 --no-pager查看最近100行日志 |
Node Ready条件中NetworkReady=False | CNI插件未部署或配置错误 | ls /opt/cni/bin/(应有flannel、calico等二进制)cat /etc/cni/net.d/10-flannel.conflist | 手动部署CNI:kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml |
kubectl get pods -n kube-system中coredns-xxx状态为Pending | CoreDNS Deployment的nodeSelector不匹配Node标签 | kubectl get node <node> -o wide查看Labelskubectl get deploy -n kube-system coredns -o yaml | grep nodeSelector -A 5 | kubectl label node <node> node-role.kubernetes.io/control-plane=(若为Master)或删除Deployment的nodeSelector |
kubectl get cs输出etcd-0为Unknown | Node无法连接Etcd集群 | telnet 192.168.1.100 2379(Master Etcd端口) | 检查Node防火墙:iptables -L INPUT | grep 2379,开放端口 |
kubectl get pods -A中kube-proxy-xxx状态为CrashLoopBackOff | kube-proxy配置的clusterCIDR与CNI插件冲突 | kubectl get cm -n kube-system kube-proxy -o yaml | grep clusterCIDR | 编辑ConfigMap:kubectl edit cm -n kube-system kube-proxy,修改clusterCIDR为CNI实际网段 |
kubectl describe node <node>Events中出现Failed to pull image "k8s.gcr.io/coredns:v1.10.1" | 镜像仓库不可达(国内环境常见) | crictl pull k8s.gcr.io/coredns:v1.10.1 | 替换镜像:kubectl edit cm -n kube-system coredns,将k8s.gcr.io/coredns改为registry.cn-hangzhou.aliyuncs.com/google_containers/coredns |
kubectl get nodes显示NotReady,但kubectl get pods -n kube-system全部Running | Node的kubelet未向APIServer注册 | ps aux | grep kubelet查看启动参数`cat /var/lib/kubelet/config.yaml | grep -E "(server | address)"` |
kubectl describe node <node>中Conditions显示DiskPressure=True | Node磁盘空间不足(/var/lib/docker或/var/lib/kubelet占满) | df -h | 清理镜像:docker system prune -a -f;清理kubelet缓存:find /var/lib/kubelet/pods -name "volume-subpath*" -delete |
kubectl get nodes显示NotReady,journalctl -u kubelet报错failed to run Kubelet: unable to load bootstrap kubeconfig | /etc/kubernetes/bootstrap-kubelet.conf文件缺失或权限错误 | ls -l /etc/kubernetes/bootstrap-kubelet.conf | 重新生成:kubeadm init phase kubeconfig admin --config kubeadm-config.yaml |
kubectl get nodes显示NotReady,kubectl get cs正常,但kubectl get pods无响应 | APIServer的--service-cluster-ip-range与Node网段重叠 | ip route show查看Node路由表kubectl get svc kubernetes -n default -o yaml | grep clusterIP | 修改kubeadm config中networking.serviceSubnet为不冲突网段(如10.96.0.0/12) |
kubectl describe node <node>Events中出现Failed to communicate with cloud provider | 启用了--cloud-provider=aws但未配置AWS凭据 | kubectl get cm -n kube-system cloud-config -o yaml 2>/dev/null | 删除cloud-config ConfigMap,或正确配置云厂商凭据 |
kubectl get nodes显示NotReady,crictl ps显示大量Pause容器 | CRI运行时(containerd)未正确配置 | crictl versioncat /etc/containerd/config.toml | grep systemd | 确保containerd配置中[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]下SystemdCgroup = true |
这张表覆盖了95%的NotReady场景。但最致命的错误往往藏在细节里:比如某次我们发现Node卡在NotReady,journalctl -u kubelet显示failed to load client CA file "/etc/kubernetes/pki/ca.crt"。排查发现/etc/kubernetes/pki/目录下只有ca.pem,没有ca.crt——因为kubeadm版本升级后默认使用.pem后缀,而kubelet配置仍指向.crt。解决方案不是改kubeadm,而是软链接:ln -s /etc/kubernetes/pki/ca.pem /etc/kubernetes/pki/ca.crt。
注意:所有修复操作后,必须重启kubelet:
systemctl daemon-reload && systemctl restart kubelet。不要相信systemctl reload kubelet,它不会重载证书文件。
4. kubeadm配置文件实战:从空白YAML到生产级集群的11个必填字段
kubeadm init --config kubeadm-config.yaml是生产环境唯一推荐的方式。裸用kubeadm init命令只适用于学习,因为它无法精确控制证书有效期、组件镜像仓库、安全加固参数等。一个完整的kubeadm-config.yaml至少需定义11个关键字段,缺一不可。下面给出一个经过某金融客户POC验证的模板,并逐字段解释其生产意义。
4.1 ClusterConfiguration:集群元数据与安全基线
apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.2 imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers certificatesDir: /etc/kubernetes/pki clusterName: prod-cluster networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 dnsDomain: cluster.local apiServer: timeoutForControlPlane: 4m0s extraArgs: enable-admission-plugins: "NodeRestriction,PodSecurity,ServiceAccount" audit-log-path: "/var/log/kubernetes/audit.log" audit-log-maxage: "30" audit-log-maxbackup: "10" audit-log-maxsize: "100" certSANs: - "k8s-prod.example.com" - "10.0.0.10" - "127.0.0.1" controllerManager: extraArgs: "feature-gates": "RotateKubeletServerCertificate=true" "terminated-pod-gc-threshold": "1000" scheduler: extraArgs: "policy-config-file": "/etc/kubernetes/scheduler-policy.json"字段解析:
imageRepository:国内必须替换。registry.cn-hangzhou.aliyuncs.com/google_containers是阿里云镜像站,比k8s.gcr.io稳定10倍。注意:kubeadm config images list --config kubeadm-config.yaml可预览将拉取的镜像列表。certificatesDir:证书存储路径。生产环境建议设为/data/kubernetes/pki,避免与系统盘耦合。但需提前mkdir -p /data/kubernetes/pki && chown root:root /data/kubernetes/pki。podSubnet与serviceSubnet:必须与CNI插件严格一致。Flannel固定用10.244.0.0/16,Calico推荐192.168.0.0/16。serviceSubnet不能与物理网络重叠,否则Node无法访问ClusterIP。apiServer.extraArgs.enable-admission-plugins:PodSecurity是v1.25+强制启用的安全插件,替代旧版PodSecurityPolicy。它基于Pod标签(如pod-security.kubernetes.io/enforce: baseline)执行安全策略。apiServer.certSANs:必须包含所有可能访问APIServer的域名/IP。漏掉127.0.0.1会导致kubectl --insecure-skip-tls-verify get nodes失败,因为证书不匹配。
4.2 InitConfiguration:初始化阶段的硬性约束
--- apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration bootstrapTokens: - token: "abcdef.0123456789abcdef" ttl: "24h" usages: - signing - authentication nodeRegistration: criSocket: /run/containerd/containerd.sock taints: - key: "node-role.kubernetes.io/control-plane" effect: "NoSchedule" kubeletExtraArgs: cgroup-driver: "systemd" fail-swap-on: "false" rotate-server-certificates: "true" name: "master-01" localAPIEndpoint: advertiseAddress: "10.0.0.10" bindPort: 6443字段解析:
bootstrapTokens:生产环境必须预设token,避免kubeadm token create生成的临时token过期。ttl: "24h"可延长至720h(30天)。nodeRegistration.criSocket:明确指定containerd socket路径。若用Docker,应为/var/run/dockershim.sock(但Docker已弃用,强烈建议containerd)。nodeRegistration.taints:为Master节点打污点,防止普通Pod调度到控制平面。这是安全基线,不可删除。nodeRegistration.kubeletExtraArgs.cgroup-driver:必须与Docker/containerd的cgroup driver一致。systemctl status containerd查看--cgroup-manager=systemd,则此处必须为systemd。localAPIEndpoint.advertiseAddress:这是kubeadm join时Node连接的地址,必须是Node能路由到的IP。若Master有多个网卡,务必指定业务网卡IP,而非lo或docker0。
4.3 JoinConfiguration:Worker节点的标准化接入
--- apiVersion: kubeadm.k8s.io/v1beta3 kind: JoinConfiguration discovery: bootstrapToken: apiServerEndpoint: "10.0.0.10:6443" token: "abcdef.0123456789abcdef" caCertHashes: - "sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234" unsafeSkipCAVerification: false nodeRegistration: criSocket: /run/containerd/containerd.sock taints: [] kubeletExtraArgs: cgroup-driver: "systemd" fail-swap-on: "false" name: "worker-01"字段解析:
discovery.bootstrapToken.caCertHashes:必须与Master的/etc/kubernetes/pki/ca.crt哈希一致。用命令生成:openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 | sed 's/^.* //'。nodeRegistration.taints: []:Worker节点必须清空污点,否则Pod无法调度。若需保留污点(如GPU节点),应显式设置- key: "nvidia.com/gpu" effect: "NoSchedule"。nodeRegistration.name:建议用hostname -s动态获取,避免硬编码。可在脚本中:sed -i "s/name: \"worker-01\"/name: \"$(hostname -s)\"/" kubeadm-config.yaml。
4.4 生产环境必须禁用的3个危险选项
有些kubeadm字段看似方便,实则埋下重大隐患:
--skip-phases=upload-config:跳过上传配置到kubeadm-configConfigMap。后果:kubeadm upgrade时无法读取原始配置,升级失败。--ignore-preflight-errors=all:忽略所有预检。后果:Docker版本不兼容导致kubelet频繁OOM,集群稳定性归零。--dry-run:仅模拟不执行。看似安全,但kubeadm init --dry-run --config kubeadm-config.yaml不会验证证书SAN是否合法,直到真正init时才报错,浪费20分钟。
经验:每次修改kubeadm-config.yaml后,必须执行
kubeadm init phase certs all --config kubeadm-config.yaml验证证书生成是否成功。这是上线前的黄金检查点。
5. kubeadm进阶:证书轮换、集群升级与灾难恢复的三大生死线
kubeadm部署的集群不是“一次部署,永久运行”。生产环境中,有三件事决定集群生死:证书过期、K8s版本升级、控制平面崩溃。kubeadm提供了原生支持,但用法极其反直觉。下面用真实故障案例说明。
5.1 证书轮换:为什么kubeadm certs renew不能解决所有问题?
Kubernetes证书默认有效期1年。kubeadm certs renew all命令看似万能,但它只更新/etc/kubernetes/pki/下的证书文件,不重启任何静态Pod。这意味着kube-apiserver仍在用旧证书监听,新证书完全无效。正确流程是四步:
- 轮换证书:
kubeadm certs renew all - 重启静态Pod:
rm /etc/kubernetes/manifests/kube-apiserver.yaml(触发kubelet删除Pod)→ 等待kubectl get pods -n kube-system | grep apiserver消失 →touch /etc/kubernetes/manifests/kube-apiserver.yaml(触发重建) - 更新kubeconfig:
kubeadm init phase kubeconfig admin --config kubeadm-config.yaml生成新admin.conf - 分发新证书:将
/etc/kubernetes/pki/ca.crt和新admin.conf同步到所有管理员机器
但最大陷阱在于etcd证书。kubeadm certs renew etcd-*只更新/etc/kubernetes/pki/etcd/下的证书,而etcd.yaml静态Pod中引用的是/etc/kubernetes/pki/etcd/server.crt。若etcd证书过期,etcdctl endpoint health会报context deadline exceeded。此时必须:
- 先
kubeadm certs renew etcd-server etcd-peer etcd-healthcheck-client - 再手动编辑
/etc/kubernetes/manifests/etcd.yaml,确认volumeMounts路径与新证书位置一致 - 最后按上述步骤重启etcd Pod
提示:为防证书过期,建议在集群部署后立即设置告警。用
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates提取notAfter字段,当剩余天数<30时触发企业微信告警。
5.2 集群升级:kubeadm upgrade的原子性与回滚代价
kubeadm upgrade apply v1.28.2不是平滑升级,而是破坏性操作。它会:
- 停止所有静态Pod(APIServer、ControllerManager等)
- 替换
/etc/kubernetes/manifests/下的YAML文件 - 更新
/usr/bin/kube-apiserver等二进制文件 - 重启kubelet
整个过程约3-5分钟,期间集群完全不可用。因此生产环境必须:
- 先升级Master节点:
kubeadm upgrade plan→kubeadm upgrade apply v1.28.2 - 再逐个升级Worker节点:
kubeadm upgrade node(需先kubectl drain <node> --ignore-daemonsets) - 最后升级kubelet/kubectl:
apt-get install kubelet=1.28.2-00 kubectl=1.28.2-00
回滚几乎不可能。kubeadm upgrade会备份旧版YAML到/etc/kubernetes/tmp/,但二进制文件已被覆盖。唯一可靠回滚方式是:从快照恢复整个Master节点系统盘。因此,升级前必须做三件事:
etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).db(备份Etcd)cp -r /etc/kubernetes/ /backup/kubernetes-$(date +%Y%m%d)/(备份全部配置)kubectl get all --all-namespaces -o yaml > /backup/cluster-state-$(date +%Y%m%d).yaml(备份资源状态)
5.3 灾难恢复:当/etc/kubernetes/manifests/被误删后如何救活
最恐怖的故障:运维误执行rm -rf /etc/kubernetes/manifests/*,导致所有静态Pod消失,kubectl命令全部超时。此时集群“活着但失明”。恢复步骤如下:
- 确认Etcd是否存活:
etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key endpoint health。若健康,则数据完好。 - 重建manifests目录:从备份恢复
/etc/kubernetes/manifests/,或从kubeadm-config.yaml重新生成:kubeadm init phase control-plane all --config kubeadm-config.yaml kubeadm init phase etcd local --config kubeadm-config.yaml - 强制重启kubelet:
systemctl restart kubelet。kubelet会扫描/etc/kubernetes/manifests/并拉起Pod。 - 验证APIServer:
curl -k https://127.0.0.1:6443/healthz应返回ok。
若无备份且kubeadm-config.yaml丢失,则进入“考古模式”:从/etc/kubernetes/pki/证书反推配置。例如,openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text | grep "DNS:"可获知certSANs,etcdctl get /registry/minions --prefix可列出所有Node,从而重建网络配置。
最后分享一个血泪教训:某次我们为测试证书轮换,在非生产环境执行
kubeadm certs renew apiserver后忘记重启APIServer Pod。结果所有kubectl命令开始随机失败,错误信息是x509: certificate has expired or is not yet valid。排查耗时4小时,最终发现是kube-apiserver进程仍在用旧证书内存映射。结论:任何证书操作后,必须ps aux | grep kube-apiserver确认进程PID变化,否则一切皆空。