ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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_API3 etcdctl --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ --endpointshttps://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:yourpasswordstats 页面在排查“某个 apiserver 明明活着但请求失败”的场景时能一眼看出端倪。不过 stats 端口千万不要暴露到公网。3. 部署一套 nginx 业务验证容器调度与故障自愈3.1 Deployment 加 Service 完整下发集群可用的最终标志是业务容器能跑起来并且对外提供服务。这里我以部署 nginx 为例把 “Kubernetes 部署 nginx” 的完整流程走一遍顺带把 Deployment、Service 的关键字段拆开讲透。创建一个nginx-demo.yamlapiVersion: 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-datacordon让节点标记为不可调度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-dockerdpreflight 会一直提示找不到 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-errorsall来硬跳除非你已经明确知道某个报错不会影响当前部署场景。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-driversystemd要和它保持一致。这两个地方不一致轻则节点反复 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_API3 etcdctl --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ --endpointshttps://master01:2379 \ snapshot save /backup/etcd-snapshot-$(date %Y%m%d).db恢复时用etcdctl snapshot restore将快照恢复到指定目录然后重启 etcd。这里我最想强调的个人经验是备份文件要异地存放不能只放在 master 节点本地磁盘。否则整机故障时备份和源数据一起丢失就等于没有备份。至少推送到独立的存储服务器或对象存储这点不花多少成本却能在灾难演练时救你一命。最后再讲一句心里话从 v1.26 到 v1.32Kubernetes 的部署细节一直在变化但高可用集群的核心思想没有变——控制面要冗余、故障要自动转移、数据要能恢复。这套验收和演练思路不管以后版本怎么迭代都是适用的。
返回列表