1. 拆开标题看本质:KubeKey 到底帮我们省掉了哪几层活
聊 KubeKey 部署高可用 Kubernetes 集群这件事,我先说结论:它不是什么"一键魔法",而是把 kubeadm 那套需要手工串起来的流程,收敛成了一份声明式配置加一个二进制。真正值钱的地方不在于它敲了几条命令,而在于它把证书分发、VIP 漂移、etcd 集群组建、容器运行时安装、CNI 部署这些"顺序敏感"的动作编排好了。你要是自己用 kubeadm 手搓过三节点高可用,就明白我说的顺序敏感是什么意思——证书得先放对位置,haproxy 得先起来,kubeadm 的 controlPlaneEndpoint 得指到 VIP 上,任何一步顺序错了,后面就得回滚重来。
我用 KubeKey 落过不少套集群,小到三台 4C8G 的测试环境,大到几十节点的生产环境,中间踩过的坑基本都集中在"配置文件写错一行"和"环境基线没对齐"这两类上。这篇内容适合三类人看:一是第一次要从零搭一套能扛住单节点宕机的集群,二是已经用 kubeadm 玩过单机、想补齐高可用这块拼图,三是手里有离线环境需求、内网机器不能出公网,需要一套能打包带走的部署方案。完全不接触过 Kubernetes 的读者也能看,但前置知识最好有一点,至少知道 Pod、kubelet、API Server 分别干什么。
先说清楚 KubeKey 的定位。它是一个 Go 编译的静态二进制,跑在部署机上,通过 SSH 连到目标节点上执行动作。它有三种典型用法:kk create cluster从零装集群、kk add nodes往已有集群加机器、kk upgrade做版本升级。配置文件用 YAML,主流版本是apiVersion: kubekey.kubesphere.io/v1alpha2,早期的 v1alpha1 和后来的一些版本字段有差异,这一点很容易被忽略——你从网上抄来的配置粘进去报"字段不认识",八成就是 apiVersion 和当前 kk 版本对不上。
1.1 高可用的三个层面,别只盯着控制面
很多人说"高可用集群",脑子里只有一件事:master 挂一台还能不能调度。这只是一层。完整拆开至少三层。
第一层是API Server 的可用性。客户端(kubectl、kubelet、controller-manager)连的不是某一台 master 的 IP,而是一个虚拟 IP 或域名,背后挂三台 master 的 6443。这一层靠 keepalived 维护 VIP 漂移、靠 haproxy 做四层转发,或者你直接上一个外部负载均衡设备。
第二层是etcd 的仲裁能力。etcd 是 Raft 协议,三节点容忍挂一台,五节点容忍挂两台。注意这里说的是"容忍",不是"性能无损"——挂一台之后剩下的两台仍然能选主、能写入,但压力全压在两台机器上,如果这时再来一次磁盘抖动,集群就可能进入无主状态。所以 etcd 节点数的选择不是越多越好,三节点是最常见的甜点,五节点用于规模大、写入压力高的场景。
第三层是工作负载的可用性。这一层跟 KubeKey 关系不大,取决于你有没有配 Pod 的副本数、反亲和、PDB、就绪探针。我见过太多人把 master 做成三节点高可用,然后业务 Deployment 副本数写的 1,节点一重启业务就断——控制面高可用救不了单副本应用。
把这三层分清楚,后面配置里的每一项才知道为什么要填。
1.2 为什么选 KubeKey,而不是纯 kubeadm 或托管方案
纯 kubeadm 手搓的好处是完全透明,每一步干了什么你都清楚。坏处是重复劳动太多:三台机器都要装容器运行时、都要配内核参数、都要拉镜像、都要对齐时间;证书要在第一台生成再 scp 到另外两台;haproxy 和 keepalived 的配置要自己写,还得保证在 kubeadm init 之前就绪。做一遍是学习,做十遍就是浪费生命,而且手工操作的一致性没法保证——三个人装三套环境,细节一定会有出入。
托管方案省心,但离线环境、内网环境、某些对数据落盘位置有要求的场景用不了,而且节点级别的调优空间被收窄了。
KubeKey 的取舍我认为是合理的:它保留了 kubeadm 作为底层的初始化引擎,所以你出了问题还能用kubeadm相关命令去排查;同时把环境准备、组件分发、高可用层搭建都编排进去了。它的离线能力也做得比较扎实,kk artifact export能把镜像和二进制打成包,带到没有外网的环境里。
注意:KubeKey 本质上是一个"编排器",不是"运维平台"。集群建起来之后,节点的日常巡检、etcd 的定期备份、证书的续期监控,这些仍然要你自己建立流程。指望装完就一劳永逸,后面一定出问题。
2. 拓扑与资源规划:动手前先把账算明白
我见过最典型的翻车场景是:机器都申请好了,配置也写好了,跑起来发现 etcd 频繁告警,一查是系统盘用的机械硬盘,fdatasync 延迟 40ms 往上。所以规划这步不能跳。
2.1 节点角色怎么划分
最小的高可用形态是三个节点同时承担 etcd、control-plane、worker 三个角色。这种"三合一"配置在测试环境和中小规模生产里非常常见,资源利用率高,机器数量少。
| 形态 | 节点数 | etcd | control-plane | worker | 适用场景 |
|---|---|---|---|---|---|
| 三合一 | 3 | 3 节点 | 3 节点 | 3 节点 | 测试、中小规模内部系统 |
| 控制面分离 | 3 + N | 3 节点 | 3 节点 | N 节点独立 | 中等规模,业务资源可独立扩缩 |
| etcd 独立 | 3 + 3 + N | 3 节点独占 | 3 节点 | N 节点 | 规模较大、写入压力高 |
三合一的好处是省机器,坏处是业务 Pod 和 etcd 抢 CPU、抢磁盘 IO。etcd 对磁盘延迟极其敏感,业务里但凡有个日志写爆磁盘的容器,etcd 的 WAL 写入就可能被拖慢,进而触发 leader 选举。所以我的建议是:只要条件允许,etcd 至少要和业务节点分开。如果暂时只能三合一,那就把业务 Pod 的资源限制(requests/limits)配严格,并且给 etcd 数据目录单独挂一块盘。
关于节点数,再强调一次 etcd 的算术:Raft 需要多数派。3 节点容忍 1 台故障,5 节点容忍 2 台。4 节点和 3 节点的容错能力一样(都是容忍 1 台),但成本更高、选举开销更大,纯属浪费。所以 etcd 节点数应该是 3、5、7 这样的奇数。
2.2 VIP 放哪、谁来漂
KubeKey 默认方案是在 control-plane 节点上同时跑 haproxy 和 keepalived,VIP 在这几台之间漂移。配置里对应两个字段:
controlPlaneEndpoint: domain: lb.kubesphere.local address: "192.168.10.100" port: 6443address就是那个 VIP,domain是一个内部解析名,会写进各节点的 hosts。所有 kubelet、kubectl 都连这个地址。
这里有个容易被忽略的点:VIP 必须是一个当前没被占用的空闲 IP,且和节点在同一个二层网段。keepalived 靠 VRRP 协议(IP 协议号 112)做心跳,VRRP 默认用组播,如果交换机或安全组把组播拦了,VIP 就漂不起来。遇到这种情况要改成单播模式,在 keepalived 配置里显式指定unicast_peer列表。
另外,某些环境的网络策略禁止自定义协议号,那就干脆放弃 keepalived,改用外部负载均衡器指向三台 master 的 6443,配置里address填负载均衡器的 VIP 就行。KubeKey 检测到 VIP 不在本机时会跳过 haproxy/keepalived 的安装,这条路径是通的。
2.3 资源与磁盘的量化估算
我按实际跑过的环境给一组参考值,都是能跑起来且不会天天告警的底线:
- 三合一节点:4 核 8G 起步,系统盘 SSD 不少于 100G。低于 4 核,etcd 和 apiserver 在高并发写入时会互相拖累。
- 纯 worker 节点:2 核 4G 能跑,但建议 4 核 8G,具体看业务。
- etcd 独立节点:2 核 4G 够用,但磁盘必须是 SSD 或 NVMe,且数据目录独占。iops 建议不低于 500。
etcd 的存储配额默认是 2GiB(--quota-backend-bytes),到 90% 会告警,到 100% 集群直接变只读,只能靠 defrag 或者调大配额救。规模上去了要把它调到 8GiB。按经验,每 1000 个 Pod 大约产生几十 MB 的 etcd 数据,再叠加 Event、Lease、ConfigMap,实际增长速度比直觉快。上线前把监控里的etcd_db_total_size_in_bytes拉个面板出来,比事后救火强。
网络规划也要提前定:
network: plugin: calico kubePodsCIDR: 10.233.64.0/18 kubeServiceCIDR: 10.233.0.0/18kubePodsCIDR用 /18,能分配约 16000 个 Pod IP,Calico 默认每节点切一个 /26 的 block(64 个地址),够 250 个节点用。kubeServiceCIDR用 /18 能提供 16000 多个 Service。这两个网段绝对不能和主机网段、办公网段、VPC 路由重叠,重叠了就是灾难级问题——Pod 能起来,但访问内网服务时会路由错乱,排查起来极其痛苦。改这两个 CIDR 只能重建集群,没有后悔药。
3. 环境基线:装集群之前必须对齐的几件事
KubeKey 会自动做一部分系统初始化(关 swap、加载内核模块、配 sysctl),但它做的是"通用基线",很多和环境强相关的东西它管不了。下面这几条我每次都要手工核一遍。
3.1 操作系统、内核与容器运行时
操作系统选主流发行版都行,内核版本建议 4.19 以上,5.x 更好。低版本内核在 cgroup v2、conntrack、overlayfs 上会有各种小毛病。所有节点必须版本一致,混装不同发行版(一台 CentOS 一台 Ubuntu)在证书、SELinux、firewalld 行为上会有差异,出问题很难定位。
容器运行时方面,现在的版本默认用 containerd,比 docker 时代少一层 dockershim,路径也更清晰:数据落在/var/lib/containerd,配置在/etc/containerd/config.toml。配置里通过containerManager: containerd指定。如果你的环境里已经在用 docker,KubeKey 也支持,但要确认 docker 的 cgroup driver 是 systemd,和 kubelet 保持一致,否则 kubelet 起不来,日志里会明晃晃写着misconfiguration: kubelet cgroup driver: "cgroupfs" is different from docker cgroup driver: "systemd"。
磁盘分区上,我最推荐的做法是给/var/lib/containerd(或/var/lib/docker)单独挂一块盘。原因很直白:镜像和容器日志会持续膨胀,跟着根分区一起涨,涨到 100% 的时候 kubelet 会进入 DiskPressure,开始驱逐 Pod,而且是随机驱逐,业务侧根本看不懂发生了什么。
3.2 网络、端口与时间同步
防火墙这块,我的建议是内网机器直接关掉(systemctl disable --now firewalld),用安全组在网络边界做控制。如果必须开,下面这些端口一个都不能少:
| 端口/协议 | 用途 | 谁访问谁 |
|---|---|---|
| 6443/TCP | API Server | 所有节点、运维终端 → master |
| 2379-2380/TCP | etcd 客户端与对等通信 | etcd 节点互相 |
| 10250/TCP | kubelet API | apiserver → 所有节点 |
| 10257、10259/TCP | controller-manager、scheduler | 本机回环与健康检查 |
| 179/TCP | BGP(Calico BGP 模式) | 节点之间 |
| 4789/UDP | VXLAN(Calico VXLAN 模式) | 节点之间 |
| 30000-32767/TCP | NodePort 服务 | 外部 → 所有节点 |
| 112/VRRP | keepalived 心跳 | control-plane 之间 |
时间同步是最容易被忽视、后果最严重的一项。Kubernetes 的证书校验、ServiceAccount token、etcd 的 lease 全都依赖时间。节点之间差超过几秒,就会出现证书"尚未生效"或者 token 立即过期这种玄学故障。所有节点统一用 chrony 指向同一个内网时间源,装完先chronyc sources确认一下状态是^*。
主机名也有讲究:每台机器的主机名必须唯一,且要能互相解析。KubeKey 会往/etc/hosts里写,但如果你的环境里 DNS 优先于 hosts,那就要在 DNS 里也配好。主机名重复的后果是 kubelet 注册时互相覆盖,节点列表里永远只有一台。
3.3 KubeKey 二进制与离线制品的准备
在线环境很简单,从发布页下对应架构的二进制,chmod +x kk就能用。生产环境我更倾向离线包,一是可控,二是版本锁定,三是装的时候不受网络抖动影响。
离线包的制作分两步。找一台能出网的机器,先把集群配置文件生成出来:
./kk create config --with-kubernetes v1.26.5 -f cluster.yaml然后按目标环境改好配置,导出制品:
./kk artifact export -m manifest-sample.yaml -o kubekey-artifact.tar.gzmanifest-sample.yaml用./kk create manifest生成,里面声明要打包的镜像列表。把打好的包和 kk 二进制一起拷到内网部署机,解压到指定目录(比如/var/kubekey/artifact),配置文件里加上:
registry: privateRegistry: "" # 或者在执行时指定执行时用-a参数指向制品目录:
./kk create cluster -f cluster.yaml -a /var/kubekey/artifact提示:离线包里最容易被漏掉的是"pause 镜像"和"kube-proxy 镜像"。这两个是所有 Pod 的基础,缺了之后现象是节点一直 NotReady、沙箱创建失败,日志里提示
failed to pull image ... pause。导出制品前用./kk artifact export的--images参数显式列出全部组件,别图省事只打业务镜像。
4. 配置文件逐段过一遍,然后开跑
配置文件是整件事的核心,写错一行就要重来。我按段落拆。
4.1 hosts 与 roleGroups 的写法
apiVersion: kubekey.kubesphere.io/v1alpha2 kind: Cluster metadata: name: demo-ha spec: hosts: - {name: k8s-m1, address: 192.168.10.11, internalAddress: 192.168.10.11, user: root, password: "YourPass"} - {name: k8s-m2, address: 192.168.10.12, internalAddress: 192.168.10.12, user: root, password: "YourPass"} - {name: k8s-m3, address: 192.168.10.13, internalAddress: 192.168.10.13, user: root, password: "YourPass"} roleGroups: etcd: - k8s-m1 - k8s-m2 - k8s-m3 control-plane: - k8s-m1 - k8s-m2 - k8s-m3 worker: - k8s-m1 - k8s-m2 - k8s-m3address是部署机连过去的地址,internalAddress是节点之间通信用的地址。单网卡环境两个填一样;双网卡(一张管理网一张业务网)就要分开填,内部通信走业务网能显著降低 etcd 同步延迟。
name必须和节点实际主机名一致,或者至少唯一且能解析。我踩过一次坑:节点主机名是localhost,配置文件里写的是k8s-m1,结果 kubelet 注册上去的名字和预期不符,后面加节点时因为"节点已存在"直接失败。
roleGroups里的角色名和hosts里的name是引用关系,写错一个字母就是空角色,集群会因为"etcd 节点数为 0"直接拒绝创建。这种错误很好查,报错信息很明确。
密码认证只适合内网受控环境。生产环境用 SSH 私钥更合适:
- {name: k8s-m1, address: 192.168.10.11, internalAddress: 192.168.10.11, user: root, privateKeyPath: "/root/.ssh/id_rsa"}4.2 网络、etcd 与 Kubernetes 版本段
controlPlaneEndpoint: domain: lb.demo.local address: "192.168.10.100" port: 6443 kubernetes: version: v1.26.5 clusterName: cluster.local autoRenewCerts: true containerManager: containerd kubeletArgs: [] kubeProxyConfiguration: mode: ipvs etcd: type: kubekey dataDir: /var/lib/etcd network: plugin: calico kubePodsCIDR: 10.233.64.0/18 kubeServiceCIDR: 10.233.0.0/18 calico: ipipMode: Never vxlanMode: Always几个关键决策点解释一下。
type: kubekey表示用 KubeKey 自己管理的 etcd(本质是 kubeadm 起的静态 Pod 形式的 etcd)。如果你有外部 etcd 集群,改成type: external然后填endpoints列表。规模较大时外部 etcd 更可控,因为它是独立进程,不受 kubelet 重启影响。
autoRenewCerts: true这个必须开。Kubernetes 证书默认一年到期,到期后 API Server 直接拒绝所有连接,集群等于宕机。开了自动续期,kubelet 和 kubeadm 会在证书快过期时自动换新。即便开了,也建议在监控里盯一下证书剩余天数,因为自动续期依赖 kubelet 正常运行,kubelet 挂了就不会续。查证书还有多久过期:
kubeadm certs check-expirationmode: ipvs是 kube-proxy 的工作模式。iptables 模式在 Service 数量上千之后规则匹配会明显变慢,ipvs 用哈希表查找,几千个 Service 也不会有压力。用 ipvs 的前提是节点内核加载了ip_vs系列模块,KubeKey 会自动加载,但老内核上可能缺ip_vs_rr、ip_vs_wrr这些,装完用lsmod | grep ip_vs核一下。
Calico 的封装模式:vxlanMode: Always表示全走 VXLAN 封装,跨网段、跨可用区都能通,代价是有约 50 字节的封装开销,MTU 要相应降到 1450。如果所有节点在同一个二层网段,用ipipMode: Never加 BGP 模式性能更好,没有封装开销,但要求网络设备允许 179 端口。这个选择错了不影响集群起来,只影响性能和最大包长,很多人装完发现大包丢、连接卡住,就是 MTU 没对齐。
4.3 执行创建与过程观察
配置写好后,先做一次 dry-run 式的检查:
./kk create cluster -f cluster.yaml --dry-run确认无误,正式执行。第一次跑我建议开-v或者直接开--debug,日志会详细到每条远程命令,出问题好定位:
./kk create cluster -f cluster.yaml -a /var/kubekey/artifact --debug这个过程大约 15 到 30 分钟,取决于网络和磁盘速度。整个流程大致这么几步:连上所有节点做环境初始化 → 分发二进制和镜像 → 拉起 etcd 集群(第一台起得慢,要等它选主)→ 配置 haproxy 和 keepalived → kubeadm init 第一台 control-plane → 其余 control-plane 节点 join → worker 节点 join → 部署 CNI → 部署附加组件。
卡住的地方基本集中在三段:etcd 起不来(磁盘慢或时间不同步)、VIP 不通(VRRP 被拦)、CNI 拉不到镜像(离线包不全)。这三处只要过了,后面基本一马平川。
4.4 装完之后的验收清单
装完屏幕上打出Installation is complete不代表真好了。我每次必做这套验收:
# 1. 节点状态,必须全是 Ready kubectl get nodes -o wide # 2. 组件健康,三个 URL 都要 ok kubectl get --raw='/readyz?verbose' # 3. etcd 成员与健康 kubectl -n kube-system exec etcd-k8s-m1 -- etcdctl \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ endpoint health --cluster # 4. 组件 Pod 全绿 kubectl get pods -n kube-system # 5. 起个双副本测试业务,验证跨节点调度和 Service 访问 kubectl create deployment nginx-test --image=nginx:alpine --replicas=2 kubectl expose deployment nginx-test --port=80 --type=NodePort重点看 etcd 的endpoint health --cluster,三台必须全返回healthy,而且 leader 只有一个。如果某台返回context deadline exceeded,说明那台的 2379/2380 没通,或者证书没同步过去。
再补一条容易被漏的验证:模拟 VIP 漂移。在持有 VIP 的那台机器上重启 keepalived:
systemctl restart keepalived然后立刻kubectl get nodes,看能不能继续正常返回。VIP 漂移期间会有 2 到 3 秒的短暂不可用,之后应该自动恢复。如果漂移后彻底连不上,大概率是 haproxy 配置没跟着走,或者新持有 VIP 的节点上 haproxy 没监听 6443。
5. 问题排查实录:那些报错信息背后的真话
这一段我挑几个最常见的场景讲,都是实际碰过的。
5.1 创建阶段的三道坎
卡在Waiting for the kube-apiserver to be ready超过 5 分钟。这句话翻译过来是:kubeadm init 已经跑了,但通过 VIP 访问 6443 不通。按这个顺序查:先在 master 上curl -k https://127.0.0.1:6443/healthz,本地通说明 apiserver 没问题;再curl -k https://<VIP>:6443/healthz,VIP 不通就去看 haproxy 有没有起、ss -lntp | grep 6443看谁在监听;最后ip a | grep <VIP>,VIP 不在本机说明 keepalived 没跑起来或 VRRP 被拦。
镜像拉取失败,报ImagePullBackOff或者sandbox image not found。离线环境的头号问题。检查顺序:先看节点上有没有对应镜像crictl images | grep <name>;没有的话确认 artifact 包里的镜像清单是不是全;如果用了私有仓库,确认registry.privateRegistry配置正确且节点能访问;如果是自签证书仓库,要加到insecureRegistries里,否则 containerd 会因为证书校验失败拒绝拉取。
节点一直 NotReady,describe 显示failed to get sandbox image。这个八成是 pause 镜像的问题。解决方式很直接:在/etc/containerd/config.toml里找到sandbox_image,确认它指向的镜像在本地存在,然后systemctl restart containerd。
5.2 运行期的三类故障
etcd 频繁 leader 选举,日志里大量election timeout。别急着怀疑 etcd 本身,先看磁盘。用fio或者etcdctl check perf测一下 fdatasync 延迟,超过 10ms 就要警惕,超过 25ms 基本必然出问题。另一个常见原因是网络抖动,尤其是 etcd 跨机房部署时,RTT 超过 10ms 就不建议跨机房组 etcd 集群了。还有一种是 CPU 被业务打满,etcd 进程抢不到时间片,同样会导致心跳超时——给 etcd 进程绑核或者用 CPU 管理策略隔离,效果明显。
节点突然 NotReady,日志里no space left on device。容器日志写爆了。默认 containerd 的日志是 json-file 格式,落盘在/var/log/pods,没有大小限制。必须在 kubelet 配置里加日志轮转:
kubeletArgs: - "container-log-max-size=100Mi" - "container-log-max-files=5"加完重启 kubelet 生效。这个是底线配置,新建集群时就应该写进配置文件,别等到磁盘满了才想起来。
Service 访问超时,Pod 之间正常。如果 kube-proxy 用的是 ipvs 模式,检查ip addr show kube-ipvs0上是不是挂了太多 IP,超过几千个时内核参数net.ipv4.conf.all.arp_ignore要调整(设成 1),否则 ARP 响应会有问题。另外nf_conntrack表满也会导致新连接直接超时,把net.netfilter.nf_conntrack_max调到 1048576 并写进 sysctl.conf 长期生效。
5.3 常见问题速查表
| 现象 | 最可能的原因 | 快速验证 | 处理 |
|---|---|---|---|
| VIP 不漂移 | VRRP 组播被拦 | tcpdump -i eth0 proto 112 | 改 keepalived 单播模式 |
| 证书过期集群全断 | 未开自动续期 | kubeadm certs check-expiration | 续期后重启 apiserver |
| Pod 跨节点不通 | MTU 不一致 | ping -M do -s 1472 <podIP> | 调小 CNI 的 MTU |
| 节点反复 NotReady | cgroup driver 不一致 | journalctl -u kubelet | 统一改 systemd |
| etcd 只读 | db 超过配额 | etcdctl endpoint status | defrag 或调大配额 |
| 拉镜像 401 | 私有仓库凭据缺失 | crictl pull手工试 | 配 imagePullSecret 或 registry 认证 |
6. 交付之后:扩容、升级与 etcd 备份
集群建起来只是开始,真正的运维工作在后头。这三件事没有捷径。
6.1 加节点与删节点
加节点的思路是:复制现有配置文件,把新节点加到hosts和roleGroups里,然后kk add nodes -f cluster.yaml。注意roleGroups的增量——只加 worker 就别往 etcd 和 control-plane 里写,否则它会尝试把新节点加入 etcd 集群,反而打乱现有的仲裁结构。
删节点的标准动作是三步:kubectl drain <node> --ignore-daemonsets --delete-emptydir-data驱逐业务,kubectl delete node <node>从集群移除,最后在节点上kk reset或者手工停掉 kubelet 和 containerd。如果是 etcd 节点,必须先确认剩余节点仍是多数派——三节点的集群只能一次删一台,删两台就永久失去仲裁,只能靠快照恢复。
6.2 版本升级的顺序
Kubernetes 跨版本升级不能跳,比如 1.24 到 1.26 必须走 1.24 → 1.25 → 1.26。KubeKey 的kk upgrade支持按目标版本自动规划,但底层还是依次执行。顺序上有硬性要求:先升控制面,再升 kubelet/kube-proxy,最后升业务节点,而且控制面的 etcd 要单独先升。
升级前必须做两件事:备份 etcd,以及确认 API 废弃情况。1.22 到 1.25 之间移除了大量 beta 版 API(Ingress、PodDisruptionBudget、CronJob 等),升级后老的 YAML 直接 apply 不上去。用kubectl api-resources和pluto这类工具提前扫一遍,比升级完发现业务起不来要省事得多。
6.3 etcd 快照:备份不做演练等于没做
etcd 是集群的唯一真相来源,它丢了等于集群没了——Pod 可以重建,但 Deployment、Service、ConfigMap、Secret、PVC 的绑定关系全在 etcd 里。备份命令:
kubectl -n kube-system exec etcd-k8s-m1 -- etcdctl \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ snapshot save /var/lib/etcd/backup-$(date +%Y%m%d%H%M).db然后把快照文件从容器里拷出来,落到对象存储或异地。用 CronJob 每小时跑一次,保留最近 24 份加最近 7 天的日备份。
关键的一步很多人省了:恢复演练。拿一份快照,在一套隔离的三节点环境里跑etcdctl snapshot restore,看能不能把集群拉起来。不演练的话,真出事的时候你会发现快照文件是空的、或者版本不匹配、或者你根本不知道该把恢复出来的数据目录挂到哪。我见过一次真实事故,备份文件每天都在生成,但从来没验证过,恢复时才发现备份的目标路径在一个 emptyDir 里,Pod 一重启就没了。
提示:恢复 etcd 时要停掉所有 control-plane 节点上的 etcd 和 apiserver,三台都用同一份快照恢复,其中一台用
--initial-cluster参数指定完整成员列表,不要三台各自独立恢复成三个单节点集群。这个细节做错,恢复出来的集群会分裂成三个互不相认的残缺集群。
最后提一句个人体会:高可用集群的可靠性,从来不是装出来的,是演练出来的。我见过配置堪称完美的三 master 集群,因为没人测过 VIP 漂移,真到主节点宕机那天才发现 keepalived 的网卡名写错了;也见过配置很朴素的集群,因为每季度做一次节点故障演练、每月做一次 etcd 恢复演练,跑了好几年没出过大事故。装完之后给自己定两个动作:拔掉一台 master 的网线,看集群能不能扛住;恢复一次 etcd 快照,看流程能不能走通。这两件事做完,你才算真正拥有了这套集群。