news 2026/10/3 3:42:10

Kubernetes高可用集群部署验收与故障演练实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kubernetes高可用集群部署验收与故障演练实战

这是Kubernetes高可用集群部署系列的第十篇。前面九篇,我们把etcd集群、负载均衡层、master节点、worker节点全部跑通,这一篇不再聊“怎么装”,而是聊“装完之后怎么验收”。我可以直接说结论:一个高可用集群即使部署时零报错,也不代表生产可用。我见过太多“kubectl get nodes 全 Ready,但一拔网线就全崩”的案例,问题基本都出在只验证了“装好了”,没验证“坏了能不能顶住”。所以这篇会带你把部署结果逐项验收,再把核心链路真的破坏一遍,最后补上日常维护最关键的那几件事。不论你是照着本系列从零搭的,还是刚接手一个别人搭好的Kubernetes集群,这一篇里的检查项和验证思路都能直接用。

1. 集群部署验收清单:把“部署完成”四个字做实

1.1 节点与核心组件复核不要只看 Ready

部署完成后的第一件事,不是急着跑业务,而是把集群的基础状态完整确认一遍。我习惯按下面这个顺序来,每一条都有明确目的,不搞“看起来没问题”这种自欺欺人的操作。

执行kubectl get nodes -o wide查看节点状态和版本信息,这是最基础的检查。我需要确认三件事:所有节点都处于 Ready、节点内核版本和容器运行时版本差异不大、每个节点的角色标识符合预期。这里最容易出现的问题是:某个节点虽然 Ready,但 kubelet 启动参数里的--node-labels没配好,角色标签缺失,导致后面调度策略不生效。用kubectl get nodes --show-labels复核一次标签,比后面发现问题再回头排查省事得多。

紧接着检查核心组件是否齐全且健康:

kubectl get pods -A -o wide

重点关注 kube-system 命名空间里的几个固定角色:coredns、etcd、kube-apiserver、kube-controller-manager、kube-scheduler。这些组件在 master 节点上全是以 static pod 方式由 kubelet 直接拉起,如果某个 apiserver 的 pod 反复重启,问题通常不在 pod 本身,而在证书、etcd 连接或资源分配。我的经验是:不要只看 STATUS 是 Running,还要看RESTARTS列。静态 pod 出现单次重启可以容忍,比如证书临时抖动;但如果半小时内连续重启三次以上,就必须查 kubelet 日志和容器运行时日志,这不是正常现象。

还有一个很多人会漏的细节——kubectl cluster-info的输出。正常情况会显示 Kubernetes control plane 的地址和 CoreDNS 的地址。这里的地址如果是内网 VIP 或域名,说明 kubeconfig 配置正确;如果显示 127.0.0.1 或者某个外部 IP,说明 kubeconfig 里的 server 地址写死了,后续管理机一换网络环境就没法访问。这个地址尽量写成负载均衡的 VIP 或域名,而不是某个单点 master 的 IP。

1.2 证书有效期与 etcd 健康不能跳过

证书过期这个问题,隐蔽性极强,但破坏力极大。Kubernetes 各组件之间的通信大量依赖 TLS 证书,尤其是 kubelet 证书、apiserver 证书和 service account 相关证书。部署时如果没注意时间同步,或者证书签发时间异常,会出现“上午还能用,下午突然全部鉴权失败”的诡异现象。

用下面命令检查所有证书的有效期:

kubeadm certs check-expiration

输出会列出每个证书的剩余时间,我重点看两个:admin.conf和kubelet证书。如果剩余时间少于 90 天,就应该规划续期,而不是等到过期当周再处理。很多团队的教训是:证书续期的操作并不难,难的是证书过期三个月后没人记得这件事,最终在凌晨被报警炸醒。

etcd 健康检查同样是必选项。etcd 是整个集群的存储底座,它的文件损坏或性能劣化会直接影响 apiserver。用客户端工具直接探测:

ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ --endpoints=https://master01:2379,https://master02:2379,https://master03:2379 \ endpoint health

三个节点都应返回 healthy。如果某个节点反复在 healthy 和 unhealthy 之间跳动,先检查节点时间同步(chrony/systemd-timesyncd),再看 etcd 日志有没有磁盘读写超时。在 haproxy 层把请求转给 apiserver 时,etcd 健康状态直接影响 apiserver 的/readyz,所以这一步检查越早,越省事。

2. 高可用能力专项验证:VIP切换与流量分发

2.1 keepalived 主备切换实测

高可用集群和普通集群的根本区别在于:单点故障时,服务不中断。这个能力不是搭好 keepalived 就自动具备的,必须做一次真实的故障注入。我通常选择在业务低峰期做这套验证,但仍要提前告知相关人,避免误报。

先看 VIP 当前落在哪台节点:

ip addr show | grep 10.0.0.100

假设 VIP 在 lb01 上,手动停掉 lb01 上的 keepalived 服务:

systemctl stop keepalived

正常情况下,lb02 会在 3 到 5 秒内接管 VIP。验证方法很简单,在 lb02 上执行ip addr show,能看到 VIP 已经绑定到 eth0;同时从客户端再次访问 apiserver 的 VIP 地址,请求仍然正常返回。keepalived 的切换速度取决于 VRRP 通告间隔,我这边配置的是advert_int 1,配合nopreempt模式,避免主节点恢复时反复切换造成抖动。

这里有个容易踩的坑:VRRP 协议使用组播或单播通信,如果服务器上有防火墙策略,需要放行 VRRP 协议(IP 协议号 112),否则两个 keepalived 节点会互相认为对方挂了,同时绑定 VIP,造成 IP 冲突。检查方式是在两个 LB 节点上分别执行tcpdump -i eth0 vrrp,如果没有任何 VRRP 报文,优先查防火墙和网卡组播配置,而不是查 keepalived 配置。

2.2 apiserver 负载均衡与健康检查

keepalived 只负责 VIP 漂移,真正的流量分发靠 haproxy。在 lb01 和 lb02 上都部署 haproxy,后端指向三个 master 节点的 6443 端口。

我的 haproxy 配置核心段如下:

frontend k8s-api bind *:6443 default_backend k8s-masters backend k8s-masters mode tcp balance roundrobin option tcp-check server master01 10.0.0.11:6443 check fall 3 rise 2 server master02 10.0.0.12:6443 check fall 3 rise 2 server master03 10.0.0.13:6443 check fall 3 rise 2

这里的关键不只是“转发 TCP”,而是 haproxy 会定期对后端做健康检查。fall 3表示连续三次失败才标记节点不可用,rise 2表示连续两次成功就恢复。这两个参数决定了后端 apiserver 故障时的摘除和恢复速度,不要随意改成fall 1 rise 1,否则一次网络抖动就可能导致后端节点被反复上下线,apiserver 连接被频繁中断。

验证流量分发是否生效,可以在管理机上反复执行 kubectl 命令,比如kubectl get nodes连续执行十几次,然后在 haproxy 的 stats 页面观察各 master 的会话数分布。如果所有请求都打在同一台上,检查是不是 kubeconfig 里 server 地址写死在某个 master IP 上,或者 haproxy 没开启balance roundrobin。

这里还有个小技巧:把 haproxy 的 stats 页面打开,配合访问控制可以快速定位后端节点是否正常。配置如下:

listen stats bind *:8080 mode http stats enable stats uri /stats stats auth admin:yourpassword

stats 页面在排查“某个 apiserver 明明活着但请求失败”的场景时,能一眼看出端倪。不过 stats 端口千万不要暴露到公网。

3. 部署一套 nginx 业务,验证容器调度与故障自愈

3.1 Deployment 加 Service 完整下发

集群可用的最终标志,是业务容器能跑起来,并且对外提供服务。这里我以部署 nginx 为例,把 “Kubernetes 部署 nginx” 的完整流程走一遍,顺带把 Deployment、Service 的关键字段拆开讲透。

创建一个nginx-demo.yaml:

apiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo namespace: default spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.26 ports: - containerPort: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 3 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: nginx-demo-svc spec: type: NodePort selector: app: nginx ports: - port: 80 targetPort: 80 nodePort: 32080

执行kubectl apply -f nginx-demo.yaml,然后kubectl get pods -o wide观察 Pod 分布。如果三个 Pod 分别落在三个不同节点上,说明调度器工作正常;如果挤在同一个节点上,就需要检查节点标签或调度策略,但多数默认部署下,replicas: 3会尝试打散到不同节点。

Service 的selector必须和 Pod 的labels完全匹配。这里我用的是app: nginx,两者一一对应。nodePort: 32080是从 30000-32767 端口范围内选的一个固定值,生产环境建议显式指定,避免每次重建 Service 端口漂移。

在任意 worker 节点执行:

curl http://<node-ip>:32080

如果能返回 nginx 欢迎页,说明容器网络、kube-proxy 转发、Service 后端选择都正常。这个验证非常重要,因为很多集群在控制面正常的情况下,节点上的 kube-proxy 或 CNI 插件有问题,导致 Service 访问失败。

3.2 故障演练:节点宕机与 Pod 漂移

高可用集群真正的大考,是节点故障时工作负载能否自动恢复。这里我不建议直接拔电源,先用更温和的手段:

kubectl cordon worker01 kubectl drain worker01 --ignore-daemonsets --delete-emptydir-data

cordon让节点标记为不可调度,drain则把该节点上已有的 Pod 驱逐到其他节点。执行 drain 后,kubectl get pods -o wide会看到原来在 worker01 上的 Pod 在其他节点重新创建。如果某类 Pod 一直处于 Pending 状态,优先检查资源是否充足、是否有节点亲和性限制。

更接近真实故障的演练是把节点直接关机。节点宕机后,Kubernetes 不会立即重建 Pod,而是等待一段时间。这个时间受两个参数控制:kube-controller-manager 的--node-monitor-grace-period(默认 40 秒)决定节点状态从 NotReady 到标记为故障的时间;而 Pod 的默认驱逐容忍时间是 300 秒(5 分钟),即节点不可用超过 5 分钟后,该节点上的 Pod 才会被强制迁移。

实际操作中要理解这个机制,不要看到节点关机后 Pod 五分钟内没重建就觉得集群有问题。我通常在演练时开着 watch 观察状态变化:

watch -n 2 kubectl get pods -o wide

第一次看第三节 Pending 状态,第五分钟开始会看到 Terminating 和重新创建的过程。这套机制验证通过,说明高可用集群的故障自愈链路是完整的。

有一件事必须提醒:如果业务是 StatefulSet,节点宕机后 Pod 重建可能因为数据卷的节点绑定而卡住,这不是控制面能解决的问题。所以故障演练一定要区分无状态应用和有状态应用,不能拿一条 nginx Deployment 的验证结果去推断数据库集群的容灾能力。

4. kubeadm preflight 与版本演进排查实录

4.1 preflight 到底在检查什么

我在部署 v1.26 版本集群时遇到过这样一段日志:

[init] using kubernetes version: v1.26.0 [preflight] running pre-flight checks

后面紧跟着一串检查结果,大多数都通过了,但最后报出了一个 ERROR。这不是罕见情况,遇到过 preflight 检查失败的读者一定对那段红字印象深刻。preflight 不是走形式,它把集群初始化的关键前置条件一次性验完,主要有这么几类:

  • 系统环境:是否使用 root 执行、操作系统版本、swap 是否关闭
  • 端口占用:6443、2379-2380、10250、10259 等端口是否被占用
  • 容器运行时:是否能够连接到 containerd 或 cri-dockerd 的 socket
  • 内核参数:net.bridge.bridge-nf-call-iptables是否开启
  • 镜像可用性:kubeadm 需要的控制面镜像能否从配置的镜像仓库拉取

其中最容易出问题的就是 CRI 连接。Kubernetes 1.24 之后彻底移除了 dockershim,如果还沿用 Docker 作为运行时且没有装 cri-dockerd,preflight 会一直提示找不到 CRI socket。处理方式不是绕开检查,而是装好适配的 CRI 插件,并在 kubeadm 配置文件的criSocket字段里显式指定路径。

常见 preflight 报错和处理方式,我整理成了一张表:

报错信息可能原因处理方式
[ERROR FileAvailable--etc-kubernetes-manifests]已有历史 kubeadm 痕迹备份并清空 /etc/kubernetes/manifests
[ERROR Port-6443]端口被现有服务占用停掉占用进程或换节点
[ERROR Swap]swap 未关闭swapoff -a并注释 /etc/fstab 中的 swap 行
[ERROR CRI]: unable to check CRI容器运行时 socket 未配置或不可连安装/配置 containerd,打通 /run/containerd/containerd.sock
[ERROR ImagePull]镜像仓库不可达或认证失败提前拉镜像到本地,或配置imageRepository为内网镜像仓库

preflight 的宝贵之处在于:它把问题前置到 init 之前暴露,而不是等集群初始化到一半再失败。所以我们排查时不要用--ignore-preflight-errors=all来硬跳,除非你已经明确知道某个报错不会影响当前部署场景。

4.2 从 v1.26 到 v1.32:部署细节与升级注意

热词里出现 v1.26.0 不是偶然,很多生产集群至今仍跑在 v1.26 上。而本系列标题是 v1.32,这中间跨了多个大版本,部署细节有不少变化。如果你手里已经有一个旧版本集群,想平滑升级到 v1.32,有几个点必须提前留意。

容器运行时的 cgroup driver 要求更严。从 v1.28 开始,kubelet 对 cgroup driver 的检查越发严格,v1.32 部署时推荐直接使用 systemd driver。这意味着 containerd 的配置/etc/containerd/config.toml需要把SystemdCgroup设置为true,并且 kubelet 的启动参数--cgroup-driver=systemd要和它保持一致。这两个地方不一致,轻则节点反复 NotReady,重则容器内进程因为 cgroup 配置错误出现资源统计异常。

镜像仓库地址也在演进。v1.32 的 kubeadm 默认镜像仓库是registry.k8s.io,如果部署机器无法直接访问公网仓库,需要提前把所需的 control-plane 镜像拉到本地,或通过参数修改imageRepository为内网镜像。这里要特别小心,不要只看 kubeadm 默认值就一键部署,生产环境离开内网镜像仓库是走不远的。

API 资源的演进也值得关注。从 v1.26 到 v1.32,一部分 flowcontrol 和 policy API 从 v1beta1 升到 v1,旧的 CRD 如果使用已废弃的 apiVersion,在升级时需要同步修改。升级路径上,kubeadm 不支持跨越多个 minor 版本直接升级,必须逐版本升级,例如 v1.26 -> v1.27 -> ... -> v1.32。这也是我反复强调要规划版本窗口的原因,毕竟每跳一个小版本都涉及镜像更新、组件重启和 API 兼容性确认。

5. 生产接手后必须养成的几个习惯

5.1 一套顺手的健康巡检命令

集群不是搭完就一劳永逸的,日常巡检能帮你尽早发现隐患。我给自己定了一套固定巡检流程,每个工作日早上执行一次,耗时不到三分钟。

kubectl get nodes -o wide kubectl get pods -A | grep -v Running | grep -v Completed kubeadm certs check-expiration

第一条看节点状态,第二条看异常 Pod,第三条看证书剩余时间。如果节点里有 NotReady,立刻journalctl -u kubelet -n 100看日志;如果 Pod 有 CrashLoopBackOff,先看kubectl describe pod里的 Events,再进容器查业务日志。这套组合拳能覆盖绝大部分日常问题。

我还会额外加一条 etcd 健康检查,频率不需要每天,但每两天看一次比较合理。etcd 的存储空间增长能反映集群元数据变化速度,如果某个命名空间下有海量 Pod 频繁创建删除,etcd 的磁盘占用会快速上升,提前发现可以避免存储打满的“血案”。

5.2 证书续期与 etcd 备份不能靠脑子记

证书续期这件事,最大的敌人是“忘了”。kubeadm 提供了续期命令:

kubeadm certs renew all

执行完成后需要重启 kubelet 和 apiserver 等静态 Pod 组件,让新证书生效。这台机器上的/etc/kubernetes/admin.conf也要同步更新,否则管理端 kubeconfig 会失效。我在团队里推行的是每半年执行一次的续期计划,并且在日历上设置提前提醒,比等监控告警要温和得多。

etcd 备份同样要有固定节奏。数据是集群的“账本”,一旦损坏,重建集群比恢复备份代价高得多。备份命令很简单:

ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ --endpoints=https://master01:2379 \ snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db

恢复时用etcdctl snapshot restore将快照恢复到指定目录,然后重启 etcd。这里我最想强调的个人经验是:备份文件要异地存放,不能只放在 master 节点本地磁盘。否则整机故障时,备份和源数据一起丢失,就等于没有备份。至少推送到独立的存储服务器或对象存储,这点不花多少成本,却能在灾难演练时救你一命。

最后再讲一句心里话:从 v1.26 到 v1.32,Kubernetes 的部署细节一直在变化,但高可用集群的核心思想没有变——控制面要冗余、故障要自动转移、数据要能恢复。这套验收和演练思路,不管以后版本怎么迭代,都是适用的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 3:42:04

Cloudflare D1上的ORM选型:Prisma vs Drizzle的实战权衡

2. 先搞清楚边界&#xff1a;D1不是"普通数据库"D1号称是跑在Cloudflare全球边缘网络上的SQLite数据库。但你要是把D1当成普通的PostgreSQL或者本地SQLite来用&#xff0c;拿MySQL那套思路往上套&#xff0c;很快就会被现实教育。D1的底层确实是SQLite&#xff0c;但…

作者头像 李华
网站建设 2026/10/3 3:41:49

MySQL索引实战:B+树、最左前缀与失效排查

在MySQL这条进阶路上&#xff0c;索引就是那个"一懂全懂、一卡全卡"的知识节点。前期写SQL可能没太大感觉&#xff0c;等数据量一上来、线上查询变慢&#xff0c;你回头看执行计划时才发现&#xff0c;当初建表时随手写的几个索引到底有多重要。这篇文章想系统性地把…

作者头像 李华
网站建设 2026/10/3 3:41:43

先进封装核心技术:RDL重布线层的原理、工艺与应用解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 3:40:37

OpenShell:从零构建高性能跨平台Shell工作台的组件化实践

1. 从零零散散的Shell配置&#xff0c;到如今这个开源小项目先把话说明白&#xff1a;OpenShell是我花了大半年维护的一套开源Shell环境方案&#xff0c;准确说&#xff0c;它不是某个单一软件&#xff0c;而是一整套基于组件化思路搭建的Shell工作台&#xff0c;目标很直接——…

作者头像 李华
网站建设 2026/10/3 3:40:04

Flutter三方库executable鸿蒙化适配全攻略

1. executable 到底是什么&#xff0c;为什么鸿蒙化时所有人都盯着它先花点时间把这个概念聊透。Flutter 三方库里的executable&#xff0c;不是可执行二进制文件本身&#xff0c;而是pubspec.yaml里的一个顶级配置字段。它定义的是&#xff1a;当这个包作为依赖被安装后&#…

作者头像 李华
网站建设 2026/10/3 3:40:03

汽车电池异常检测实战:从zip数据集到模型部署全解析

简介&#xff1a;面向汽车电池异常检测模型构建与验证&#xff0c;这套压缩包整合了竞赛级建模全流程&#xff0c;适合科研人员、算法工程师及新能源汽车相关方向学习者。包体小巧&#xff0c;共19个文件&#xff0c;总大小2.59MB&#xff0c;以6个ipynb分析/训练脚本、3个pkl保…

作者头像 李华