Skip to content

第三部分 大规模集群运维体系

"千台规模看架构,万台规模看运维。"——当 RKE2 集群跨越千节点门槛后,决定平台稳定性的不再是某一项调优参数,而是体系化的运维能力:SLO 驱动的目标管理、分级灰度的变更流水线、可演练的应急预案、以及把每一次故障转化为组织记忆的复盘文化。本部分以真实生产经验为蓝本,覆盖升级、节点生命周期、etcd、证书安全、资源治理、发布变更与应急演练七大运维域,并给出可直接落地的 Runbook 与命令。

适用版本:RKE2 v1.28+(示例命令在 v1.28 ~ v1.35 上验证通过) 读者对象:Kubernetes/RKE2 平台 SRE、平台架构师、运维团队负责人


目录


第 1 章 大规模运维的挑战与 SRE 方法论

1.1 数千节点运维 vs 百节点运维的本质差异

很多团队把大规模运维理解为"把一百节点的流程重复三十遍",这是最常见的认知误区。规模带来的不是线性工作量增长,而是质变

维度百节点集群数千节点集群
故障常态化节点故障是"事件"每天硬件故障是"常态",3000 节点按年故障率 3% 计,平均每天 0.25 次磁盘/内存故障
手工运维kubectl drain 手动执行可行必须有批量工具链 + 审批流,单人 SSH 逐台操作 = 自杀式运维
控制平面压力apiserver QPS < 500,etcd DB < 2GiBLIST/WATCH 风暴、etcd DB 逼近 8GiB 上限、kubelet 心跳洪流
变更风险升级失败回滚代价小一次错误变更影响数千节点,回滚窗口以小时计
网络效应局部故障影响有限雪崩级联:DNS 抖动 → 全集群健康检查失败 → 大规模驱逐
组织形态1~2 人兼职专职 SRE 团队 + oncall 轮换 + 分级响应
观测数据量GB/天TB/天,观测系统本身需要容量规划

本质差异总结为三句话

  1. 概率问题变成确定性问题:小概率故障 × 大基数 = 必然发生。不能靠"小心操作",要靠"假设故障随时发生"的容错设计。
  2. 人成为瓶颈:任何依赖"某人会做"的操作都是单点,必须工具化、Runbook 化、可交接。
  3. 爆炸半径控制成为第一设计目标:所有变更的第一问不是"能不能做",而是"最坏情况下炸多大"。
百节点思维模式                          数千节点思维模式
┌─────────────────┐                  ┌─────────────────────────┐
│ 故障 → 人去修    │                  │ 故障 → 系统自愈 → 人复盘  │
│ 变更 → 直接做    │      ───►        │ 变更 → 分级 → 灰度 → 回滚 │
│ 容量 → 不够再加  │                  │ 容量 → 预测 → 缓冲水位    │
│ 稳定性 = 不出事  │                  │ 稳定性 = SLO 达标率       │
└─────────────────┘                  └─────────────────────────┘

1.2 SLO 驱动运维

大规模运维的核心抓手是 SLO(Service Level Objective)。没有 SLO 的运维团队只会在"过度维稳"(不敢做任何变更)和"裸奔"(什么都敢动)之间摇摆。

1.2.1 RKE2 平台层 SLO 参考定义

指标(SLI)SLO 目标测量方式
kube-apiserver 可用性99.95%(月度)LB 层探测 /readyz,剔除维护窗口
apiserver 请求时延 p99(非 LIST)< 1sapiserver_request_duration_seconds
Pod 创建到 Running 时延 p99< 60s金丝雀 Pod 定时创建探测
节点 Ready 比例≥ 99.5%kube_node_status_condition{condition="Ready"}
集群 DNS 解析成功率≥ 99.99%黑盒探测 kubernetes.default.svc
etcd leader 稳定性每日 leader 变更 ≤ 2 次etcd_server_leader_changes_seen_total
平台组件升级成功率≥ 99%(单批次)升级流水线统计

1.2.2 错误预算(Error Budget)

以 apiserver 可用性 99.95% 为例,月度错误预算 = 30 天 × 24h × 0.05% ≈ 21.6 分钟

┌──────────────────────────────────────────────────────┐
│  错误预算消耗规则(团队共识,写入运维章程)              │
├──────────────────────────────────────────────────────┤
│  预算剩余 > 50%  → 正常节奏推进变更(含升级窗口)       │
│  预算剩余 20~50% → 冻结高风险变更,只做紧急修复         │
│  预算剩余 < 20%  → 全面冻结,进入稳定性专项治理         │
└──────────────────────────────────────────────────────┘

最佳实践:错误预算是"用来花的",不是"用来守的"。长期预算消耗为 0 说明团队过于保守、迭代太慢;连续两月透支说明风险管控失效。

1.3 变更管理体系

1.3.1 变更分级

级别定义示例审批执行方式
C0 紧急止血类操作证书过期紧急轮换、etcd 磁盘写满抢救oncall 自行决策 + 事后补审立即执行
C1 标准低风险已验证、可自动回滚节点下线、业务 namespace 配额调整自动化系统免检流水线自动执行
C2 常规影响单批次节点agent 节点滚动升级(单批 ≤ 5%)值班 TL 审批灰度 + 观察期
C3 高风险影响控制平面/全局网络RKE2 版本升级、CNI 升级、etcd 参数变更变更评审会(CAB)+ SRE Lead 双签维护窗口 + 金丝雀 + 完整回滚预案
C4 架构级不可逆或大范围集群拆分、CIDR 调整、证书体系重构架构委员会专项项目组

1.3.2 灰度环(Ring)模型

数千节点集群的通用灰度模型,任何节点级变更都遵循:

Ring0  金丝雀池(1%~2%,常驻低优先级业务) ──观察 24h──┐

Ring1  早鸟池(5%,配合度高的业务团队)    ──观察 12h──┐

Ring2  常规池(每批 10%,自动推进)        ──观察 4h───┐

Ring3  尾批(含敏感业务,人工确认后放行)

每个 Ring 之间设置自动健康门禁(Health Gate),任一指标恶化即自动暂停:

yaml
# 灰度门禁判定指标(示例)
health_gates:
  - name: node_ready_ratio
    expr: sum(kube_node_status_condition{condition="Ready",status="true"}) / count(kube_node_info) >= 0.995
  - name: pod_restart_burst
    expr: increase(kube_pod_container_status_restarts_total[10m]) <= 100
  - name: apiserver_p99
    expr: histogram_quantile(0.99, rate(apiserver_request_duration_seconds_bucket[5m])) <= 1.0
  - name: custom_business_slo
    expr: up{job="canary-probe"} == 1

1.3.3 回滚设计三原则

  1. 回滚路径必须在变更前验证,而不是变更失败后现想。
  2. 回滚决策人 ≠ 变更执行人,避免沉没成本心理导致硬撑。
  3. 设定明确的"回滚触发线":例如"单批次失败率 > 10% 或门禁连续 2 次不通过 → 无条件回滚"。

1.4 运维组织架构与 oncall

1.4.1 参考组织形态(3000 节点 / 3~5 集群规模)

                    ┌──────────────┐
                    │  平台负责人   │
                    └──────┬───────┘
        ┌──────────────────┼──────────────────┐
        ▼                  ▼                  ▼
 ┌────────────┐    ┌────────────┐     ┌────────────┐
 │ SRE 运行组  │    │ 平台工程组  │     │ 架构/容量组 │
 │ (oncall×N) │    │ (工具/CI)  │     │ (规划/评审) │
 └────────────┘    └────────────┘     └────────────┘
  值班/应急/巡检      自动化/发布平台      容量/标准/预算
  • SRE 运行组:一线 oncall,Follow-the-sun 或 7×24 轮换,建议单人连续值班不超过 1 周。
  • 平台工程组:维护升级流水线、节点生命周期工具、Runbook 代码化,不承担日常值班。
  • 架构/容量组:版本节奏规划、容量预测、变更评审会组织。

1.4.2 oncall 制度要点

项目建议做法
告警分级P1(电话)≤ 3 条/周,否则告警噪音会摧毁 oncall;P2(IM)当日处理;P3 进工单池
值班交接周一晨会口头交接 + 书面值班日志(进行中事件、近期变更、遗留风险)
升级机制P1 15 分钟未响应自动升级到二线,30 分钟到 TL
值班津贴与补休制度化,否则人员流失率会反噬稳定性
复盘文化Blameless Postmortem,48h 内出报告,action item 必须进跟踪系统

注意事项:oncall 的痛苦指数与告警数量成正比,与告警"质量"成反比。大规模集群第一要务是告警治理——所有不能导出"需要人立刻做什么"的告警,都应该降级为仪表盘指标。


第 2 章 大规模升级体系

2.1 RKE2 升级基本约束

动手之前必须刻进脑子里的三条铁律:

  1. 不可跨 minor 版本升级:v1.27 → v1.29 不允许,必须 v1.27 → v1.28 → v1.29。
  2. 升级顺序不可颠倒:先控制平面(etcd + server),后 agent。控制平面版本 ≥ agent 版本,apiserver 不能比 kubelet 老超过 3 个 minor(Kubernetes 版本偏差策略)。
  3. RKE2 没有官方降级路径:升级前不打快照 = 裸奔。

2.2 system-upgrade-controller 深度实战

RKE2 官方推荐的升级方式是 rancher/system-upgrade-controller(SUC),它通过 Plan CRD 驱动节点滚动升级。

2.2.1 部署 SUC

bash
kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/download/v0.14.2/system-upgrade-controller.yaml
# 生产建议:镜像先同步进私有 registry,再修改 Deployment 镜像地址
kubectl -n system-upgrade set image deploy/system-upgrade-controller \
  system-upgrade-controller=registry.internal/rancher/system-upgrade-controller:v0.14.2

注意:SUC 命名空间 system-upgrade 在 RKE2 中默认豁免 PSA(pod-security.kubernetes.io/enforce=privileged),迁移命名空间时别丢了 label。

2.2.2 Plan CRD 解剖

yaml
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: rke2-server-upgrade
  namespace: system-upgrade
spec:
  concurrency: 1                    # server 必须串行(见 2.2.4)
  nodeSelector:
    matchExpressions:
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
  serviceAccountName: system-upgrade
  cordon: true                      # 升级前 cordon
  drain:
    force: true
    deleteEmptyDirData: true        # v1.28+ 字段名,旧版为 delete-local-data
    ignoreDaemonSets: true
    gracePeriod: 60                 # 秒;-1 表示使用 Pod 自身 terminationGracePeriodSeconds
  upgrade:
    image: registry.internal/rancher/system-upgrade-controller
  version: v1.28.15+rke2r1
  secrets:
    - name: system-upgrade-registries
      path: /etc/rancher/rke2/registries.yaml   # 私有镜像仓库配置注入
  prepare:
    image: registry.internal/rancher/system-upgrade-controller
    args: ["prepare", "rke2-agent-upgrade"]

关键字段说明:

字段作用大规模注意事项
concurrency同时升级的节点数server=1 或 ≤ (etcd quorum 容忍数-1);agent 按池设 5%~10%
cordon升级时先 cordon防止升级期间新 Pod 调度上来
drain升级前驱逐业务必须配合 PDB,否则有状态业务可能被打穿
channel / version二选一,指定目标版本生产只用 version 显式指定,禁用 channel(避免意外升到新版本)
prepareserver 升级前预拉镜像大幅减少 server 升级窗口内的拉镜像耗时

2.2.3 按节点池分批:多 Plan 策略

3000 节点集群不可能用一个 Plan 一梭子打完。推荐按节点池(label)拆分 Plan,用 concurrency + 分批 label 控制节奏:

bash
# 打批次标签(示例:每批 150 台)
kubectl get nodes -l node-role.kubernetes.io/worker=,pool=general -o name \
  | shuf --random-source=<(yes 42) > /tmp/workers.txt
split -n l/20 -d /tmp/workers.txt /tmp/batch-
for i in $(seq -w 0 19); do
  for n in $(cat /tmp/batch-$i); do
    kubectl label ${n#node/} upgrade-batch=ring2-b$((10#$i))
  done
done
yaml
# 每批一个 Plan,只在通过上一批门禁后 apply
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: rke2-agent-ring2-b00
  namespace: system-upgrade
spec:
  concurrency: 30                  # 批内 30 并发
  nodeSelector:
    matchLabels:
      upgrade-batch: ring2-b00
  serviceAccountName: system-upgrade
  cordon: true
  drain:
    force: true
    deleteEmptyDirData: true
    ignoreDaemonSets: true
    gracePeriod: 120
  version: v1.28.15+rke2r1
  upgrade:
    image: registry.internal/rancher/system-upgrade-controller

流水线伪代码:

bash
for batch in ring0 ring1 ring2-b00 ring2-b01 ... ring3; do
  kubectl apply -f plans/agent-${batch}.yaml
  wait_plan_complete "rke2-agent-${batch}"          # 轮询 Plan status
  run_health_gates || { pause_and_page_oncall; break; }
  sleep ${OBSERVE_WINDOW}                            # ring0: 24h, ring2: 4h
done

2.2.4 控制平面与 etcd 升级顺序

对于 3(或 5)节点控制平面 + 内嵌 etcd 的 RKE2:

┌─────────────────────────────────────────────────────────┐
│ 控制平面升级顺序(concurrency: 1,SUC 串行执行)           │
├─────────────────────────────────────────────────────────┤
│ 1. 升级前:etcd 快照 + 校验 leader 分布                   │
│ 2. 先升级 follower server 节点(非首节点)                 │
│ 3. 首节点(cluster-init 那个)放最后                       │
│ 4. 每台升级后验证:                                        │
│    - rke2-server Active                                   │
│    - etcd endpoint health 全 OK                           │
│    - kubectl get --raw=/readyz?verbose 全 [+]ok           │
│    通过后再放下一台                                        │
└─────────────────────────────────────────────────────────┘

验证命令:

bash
# etcd 健康(RKE2 内嵌 etcdctl)
export ETCDCTL_API=3
export ETCDCTL_CACERT=/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt
export ETCDCTL_CERT=/var/lib/rancher/rke2/server/tls/etcd/server-client.crt
export ETCDCTL_KEY=/var/lib/rancher/rke2/server/tls/etcd/server-client.key
etcdctl --endpoints=https://127.0.0.1:2379 endpoint status --cluster -w table
etcdctl endpoint health --cluster -w table

注意事项:etcd 集群版本(etcdctl endpoint statusDB VERSION)在升级后可能停留在旧版本,需要所有成员升级完成并滚动重启后才会推进 cluster version。这期间不要慌,属于正常现象。

2.2.5 drain 策略在大规模下的调优

参数小集群常用值大规模建议值原因
gracePeriod30120~300大集群 Pod 迁移慢、镜像拉取排队
disableEvictionfalse视业务而定业务强依赖 PDB 时设 true(走 eviction API,尊重 PDB)
PodDisruptionBudget每个关键负载必配见第 3 章
预拉镜像Plan prepare + DaemonSet 预拉3000 节点同时拉镜像会打爆 registry

2.3 版本追赶策略

Kubernetes 每 ~4 个月发布一个 minor 版本,社区支持最近 3 个 minor。落后太多会陷入"跨版本追赶地狱"。

时间线(推荐节奏:每 4 个月升级一次,始终落后社区 1~2 个 minor 求稳)

K8s 发布   v1.28    v1.29    v1.30    v1.31    v1.32
           │        │        │        │        │
RKE2 稳定   ├──+2月──┤        │        │        │
我们升级          v1.28.x──►v1.29.x──►v1.30.x──►...
           踩坑期观望   灰度一个月    灰度一个月

要点:

  • 永远选 rke2r1 之后的 patch:例如 v1.28.15+rke2r1,等社区出到 +rke2r1 后 2~4 周、看完 GitHub issue 无重大回归再进生产。
  • patch 升级走快速通道:同 minor 内 patch 升级(v1.28.14→v1.28.15)可压缩为 Ring0 观察 4h 后全量。
  • 追赶多版本时的纪律:v1.27→v1.29 追赶,每个中间 minor 在生产至少停留 2 周(一个完整业务周期),不要一天连跳两级。
  • 年度版本日历写入运维章程,提前 6 周启动兼容性评估(CNI/Ingress/CSI 版本矩阵)。

2.4 升级前检查清单(Checklist)

text
[ ] 1. 目标版本 RKE2 release notes / known issues 通读完毕
[ ] 2. 组件兼容矩阵确认(CNI/Ingress/CSI/cert-manager/监控 agent)
[ ] 3. 全量 etcd 快照完成且已验证可恢复(最近一次恢复演练 ≤ 30 天)
[ ] 4. 快照已异地备份(S3)
[ ] 5. 所有节点磁盘空闲 ≥ 20%,/var/lib/rancher 无 inode 告警
[ ] 6. 节点无 NotReady、无 Pending 升级任务残留
[ ] 7. etcd DB size < 6GiB,fragmentation < 40%(否则先 compact+defrag)
[ ] 8. 私有 registry 容量与带宽确认(预拉镜像流量估算)
[ ] 9. 所有关键负载 PDB 配置审计通过
[ ] 10. 证书剩余有效期 > 升级周期 + 90 天
[ ] 11. 维护窗口已公告,业务方 oncall 已知会
[ ] 12. 回滚预案演练过(含快照恢复路径验证)
[ ] 13. 升级期间告警静默规则与白名单已配置(只静默预期噪音,不静默核心 SLO)
[ ] 14. 升级批次表与责任人表已签字

其中第 7 项检查命令:

bash
etcdctl --endpoints=https://127.0.0.1:2379 endpoint status --cluster -w table
# 关注 DB SIZE 与 DB SIZE IN USE;碎片率 = 1 - IN_USE/SIZE

2.5 升级失败回滚:快照兜底方案

RKE2 官方不支持降级rke2 server --cluster-reset 仅用于 etcd 灾难恢复,不是常规回滚手段。因此回滚体系 = 预防(快照) + 恢复(重置) 两层。

2.5.1 升级前强制快照

bash
# 控制平面每台(或至少首节点)执行
rke2 etcd-snapshot save --name pre-upgrade-v1.28.15
rke2 etcd-snapshot list

确认 S3 异地备份(server config):

yaml
# /etc/rancher/rke2/config.yaml(升级窗口期间临时加密保留策略)
etcd-snapshot-schedule-cron: "0 */1 * * *"   # 窗口期加密为每小时
etcd-snapshot-retention: 48
etcd-s3: true
etcd-s3-endpoint: s3.internal.example.com
etcd-s3-bucket: rke2-etcd-backup
etcd-s3-folder: prod-cluster-01
etcd-s3-access-key: xxx
etcd-s3-secret-key: xxx

2.5.2 失败场景与处置矩阵

失败场景处置
单台 agent 升级后 NotReady回退该节点二进制:/var/lib/rancher/rke2/bin 中由 SUC 生成的旧版本软链恢复,或重装旧版本;不行则下线该节点、重建
单台 server 升级失败rke2-killall.sh → 重装旧版本 → 若 etcd 成员异常则 member remove 后重新 join
控制平面大面积异常cluster-reset:选一台健康节点 rke2 server --cluster-reset --cluster-reset-restore-path=<快照>,其余 server 清理数据目录重新 join(流程同第 4 章 quorum 恢复)
业务层面不兼容(API 废弃)回滚业务应用,而非回滚集群;升级前的兼容性扫描(pluto/kubent)就是为了堵住这个

注意事项--cluster-reset 会丢失快照之后的所有写操作。它是"灾难恢复"不是"回滚",决策必须升级到 C0 级别事件指挥官。

2.6 真实案例:3000 节点 RKE2 升级 Runbook(v1.27.16 → v1.28.15)

背景:单集群 5 控制平面 + 2987 worker,3 个节点池(general / gpu / bigmem),全程 3 周。

text
W-6周  版本评估:通读 v1.28 changelog,pluto 扫描废弃 API(发现 2 个业务仍用
       flowcontrol.apiserver.k8s.io/v1beta1,下发整改工单)
W-2周  兼容性矩阵验证:calico v3.27 / ingress-nginx v1.10 / longhorn v1.6 测试环境验证
W-1周  Ring0 池(30 台)升级 + 7×24h 观察;etcd 快照恢复演练(借测试集群验证第 4 章流程)
W0-D1  控制平面 5 台串行升级(SUC server Plan, concurrency=1),耗时 3.5h
       每台间隔 20 分钟观察:etcd leader_changes、apiserver p99、业务探测
W0-D2  Ring1:gpu 池 50 台(低峰期),concurrency=10,耗时 4h
W0-D3  门禁复盘会:确认 Ring0/Ring1 无回归,错误预算消耗 0
W1     Ring2:general 池 20 批 × 120 台,每日 4 批(09:00/14:00/19:00/23:00)
       每批 concurrency=30,批间健康门禁自动判定
W2-D1  Ring3:bigmem 敏感池 8 批,业务方逐批确认后放行
W2-D3  全量完成;清理 upgrade-batch 标签;升级报告归档;etcd 快照策略恢复常态

踩坑记录(真实经验):

  1. D2 gpu 池某批 drain 卡住 40 分钟——某训练任务 PDB 配成 minAvailable: 100%,等于禁止驱逐。教训:升级前审计"绝对 PDB"(minAvailable == replicas)。
  2. W1 第 7 批门禁触发暂停:registry 带宽打满导致镜像拉取 p99 飙升。教训:预拉镜像 DaemonSet 必须先行。
  3. 控制平面第 3 台升级后 etcd 出现一次 leader 切换——正常,但触发了告警风暴。教训:升级窗口的预期告警要进静默白名单,且白名单要带过期时间。

第 3 章 节点生命周期管理

节点生命周期 = 上线 → 服役(维护)→ 下线 → 善后。3000 节点集群中,每周都有节点进出,必须把每一步做成幂等、可审计的流水线。

3.1 节点上线 SOP

装机/交付 ──► 基线检查 ──► 安装 RKE2 agent ──► 冒烟验证 ──► 打标签/污点 ──► 放行调度
              (硬件/内核)    (安装脚本)          (金丝雀Pod)   (CMDB同步)

3.1.1 上线前基线检查脚本(节选)

bash
#!/bin/bash
# precheck.sh —— 节点上线基线检查,全部通过才允许 join
set -e
fail() { echo "[FAIL] $1"; exit 1; }

# 1. OS 与内核版本
. /etc/os-release
[[ "$ID" == "rocky" && "$VERSION_ID" == 9.* ]] || fail "OS 非 Rocky 9"
kernel=$(uname -r); echo "kernel: $kernel"

# 2. 时钟同步(etcd 对时钟敏感)
chronyc tracking | grep -q "System time" || fail "chrony 未同步"
timedatectl | grep -q "synchronized: yes" || fail "NTP 未同步"

# 3. 防火墙/iptables(参考 k8s-issues/rke2-master-join-fails-iptables-firewall-blocking)
systemctl is-active firewalld | grep -q inactive || \
  echo "[WARN] firewalld 运行中,确认已按 RKE2 端口矩阵放行或禁用"

# 4. 关闭 swap
[[ $(swapon --show | wc -l) -eq 0 ]] || fail "swap 未关闭"

# 5. 内核参数与模块
for m in br_netfilter overlay; do lsmod | grep -q $m || modprobe $m; done
sysctl net.bridge.bridge-nf-call-iptables | grep -q "= 1" || fail "bridge-nf-call-iptables"

# 6. 磁盘:/var/lib/rancher 独立挂载、inode 充足
df -h /var/lib/rancher | tail -1
df -i /var/lib/rancher | awk 'NR==2 {gsub(/%/,"",$5); if($5>70) exit 1}' || fail "inode 使用率过高"

# 7. 到 server:9345 与 registry 连通性
curl -sk --connect-timeout 5 https://192.168.122.190:9345/ping || fail "server:9345 不可达"
curl -s --connect-timeout 5 http://192.168.122.156:30000/v2/ || fail "registry 不可达"

echo "[OK] 基线检查通过"

3.1.2 安装与 join

bash
# 参考 rke2/install-agent.sh 的生产化改造
curl -sfL https://get.rke2.io | INSTALL_RKE2_VERSION="v1.28.15+rke2r1" \
  INSTALL_RKE2_TYPE="agent" sh -
# 或使用离线安装包 + 私有 registry(大规模必选,见 rke2/install-source.md)

mkdir -p /etc/rancher/rke2
cat > /etc/rancher/rke2/config.yaml <<EOF
server: https://192.168.122.190:9345
token: ${NODE_TOKEN}
node-ip: ${NODE_IP}
node-name: ${NODE_NAME}
kube-proxy-arg:
  - "proxy-mode=ipvs"
system-default-registry: 192.168.122.156:30000
node-label:
  - "pool=general"
  - "zone=az1"
  - "onboard-batch=2026w29"
EOF

systemctl enable --now rke2-agent

3.1.3 上线后冒烟与放行

bash
# 节点 Ready 后先打上维护污点,冒烟通过再去除
kubectl taint nodes ${NODE_NAME} node.cilium.io/agent-not-ready=true:NoExecute --overwrite 2>/dev/null || true
kubectl taint nodes ${NODE_NAME} onboarding=true:NoSchedule

# 冒烟:DaemonSet 金丝雀 Pod(网络/DNS/挂载/镜像拉取)
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata: {name: onboard-smoke, namespace: platform-smoke}
spec:
  selector: {matchLabels: {app: onboard-smoke}}
  template:
    metadata: {labels: {app: onboard-smoke}}
    spec:
      tolerations: [{key: onboarding, operator: Exists, effect: NoSchedule}]
      containers:
      - name: smoke
        image: registry.internal/base/smoke:1.0
        command: ["/bin/sh","-c","nslookup kubernetes.default && curl -s https://kubernetes.default.svc -k && sleep 3600"]
EOF

# 冒烟通过后放行
kubectl wait --for=condition=ready pod -l app=onboard-smoke --field-selector spec.nodeName=${NODE_NAME} -n platform-smoke --timeout=300s \
  && kubectl taint nodes ${NODE_NAME} onboarding=true:NoSchedule-

最佳实践:上线批次的节点统一打 onboard-batch=YYYYwWW 标签。某批次集中出问题(如同一批次网卡固件 bug)时可一键圈定影响面。

3.2 节点维护:批量 cordon/drain 工具化

3.2.1 为什么手动 kubectl drain 不可行

3000 节点集群每周维护窗口可能涉及上百节点,手工操作的问题:无并发控制、无视 PDB 熔断、无进度跟踪、不可审计。必须工具化。

3.2.2 批量维护工具设计(内部平台 drainctl 逻辑)

python
#!/usr/bin/env python3
# drainctl —— 批量节点维护工具核心逻辑(伪代码,Python + kubernetes client)
MAX_CONCURRENT = 20          # 全局并发 drain 数
MAX_UNAVAILABLE_RATIO = 0.01 # 集群不可用节点比例熔断线

def drain_batch(nodes):
    for node in nodes:
        wait_for_capacity()                      # 并发信号量
        if cluster_unready_ratio() > MAX_UNAVAILABLE_RATIO:
            pause_and_alert("熔断:集群 NotReady 比例超阈值"); return
        if not pdb_precheck(node):               # 驱逐预演算
            skip_and_ticket(node); continue
        cordon(node)
        drain(node, grace_period=120, timeout=1800, ignore_daemonsets=True)
        mark_maintenance(node)                   # 打上 maintenance=true 标签 + 注释
        notify_owner(node)

def pdb_precheck(node):
    """预演算:该节点上的 Pod 驱逐后,是否所有 PDB 仍满足 minAvailable"""
    for pod in pods_on_node(node):
        pdb = find_pdb(pod)
        if pdb and pdb.status.disruptionsAllowed < 1:
            return False
    return True

3.2.3 PodDisruptionBudget 体系

大规模集群下,PDB 是"防止运维操作打死业务"与"防止业务卡死运维操作"的双向契约:

yaml
# 标准 PDB 模板(无状态服务)
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: {name: web-pdb, namespace: app-a}
spec:
  minAvailable: 85%          # 允许同时驱逐 15%,权衡运维效率与业务容量
  selector:
    matchLabels: {app: web}
---
# 有状态服务(如 Kafka、Elasticsearch)
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: {name: kafka-pdb, namespace: middleware}
spec:
  maxUnavailable: 1          # 有状态一次只允许动 1 个副本
  selector:
    matchLabels: {app: kafka}
反模式后果治理手段
minAvailable: 100% 或 == replicasdrain 永远卡死准入校验(OPA/Kyverno)直接拒绝
不配 PDB批量维护可能打穿业务CI 卡点:Deployment/StatefulSet 必须带 PDB
单副本业务配 PDB自我矛盾,drain 卡死准入校验提示改为 maxUnavailable: 1 且接受中断
yaml
# Kyverno 策略示例:禁止"绝对 PDB"
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata: {name: pdb-sanity}
spec:
  validationFailureAction: Enforce
  rules:
  - name: no-absolute-pdb
    match: {any: [{resources: {kinds: ["PodDisruptionBudget"]}}]}
    validate:
      message: "minAvailable 不允许为 100% 或等于副本数"
      pattern:
        spec:
          X(minAvailable): "100%"

3.3 节点下线 SOP

下线申请 ──► 长灰度 cordon(24h) ──► drain ──► 应用迁移验证 ──► 清理 ──► 删除 Node ──► CMDB 销户
bash
# 1. cordon 观察 24h(防止 drain 后发现遗漏)
kubectl cordon ${NODE}

# 2. drain
kubectl drain ${NODE} --ignore-daemonsets --delete-emptydir-data \
  --grace-period=120 --timeout=30m

# 3. 停止 agent 并清理
ssh ${NODE} "systemctl stop rke2-agent && rke2-killall.sh && systemctl disable rke2-agent"
# 彻底清理(重用该机器前必须执行)
ssh ${NODE} "rm -rf /var/lib/rancher /etc/rancher /var/lib/kubelet /var/lib/cni"

# 4. 从集群移除(删除 Node 对象前先确认无残留引用)
kubectl delete node ${NODE}

# 5. 善后检查:证书/静态 Pod/etcd 成员(若为 server 节点,必须先 member remove!)
etcdctl member list -w table    # server 节点下线后此处不应再有它

注意事项:下线 control-plane/etcd 节点时顺序相反——先 etcdctl member remove,再停服务,最后删 Node。直接关机走人会导致 etcd quorum 长期"带病运行"(期望成员数没变,实际少一个)。

3.4 硬件故障自愈:node-problem-detector + draino + descheduler

3000 节点 = 硬件故障常态化。自愈体系三层:

┌──────────────────────────────────────────────────────────┐
│ L1 检测:node-problem-problem-detector (NPD) DaemonSet      │
│    内核日志/硬件事件 → NodeCondition + Event               │
│ L2 决策:draino(按 condition 自动 cordon+drain)           │
│    只处理"明确不可修复"的 condition,其余进工单             │
│ L3 治理:descheduler(处理非故障类失衡,见第 6 章)          │
└──────────────────────────────────────────────────────────┘

3.4.1 NPD 关键配置

yaml
# node-problem-detector config(节选):监控磁盘、内存、网卡
{
  "plugin": "kmsg",
  "logPath": "/dev/kmsg",
  "conditions": [
    {"type": "KernelDeadlock", "reason": "KernelHasNoDeadlock", "message": "kernel has no deadlock"},
    {"type": "ReadonlyFilesystem", "reason": "FilesystemIsNotReadOnly", "message": "Filesystem is not read-only"},
    {"type": "MemoryPressureHW", "reason": "NoMCE", "message": "no machine check exception"},
    {"type": "NicLinkFlap", "reason": "NicStable", "message": "nic link stable"}
  ],
  "rules": [
    {"type": "permanent", "condition": "ReadonlyFilesystem",
     "reason": "FilesystemIsReadOnly", "pattern": "Remounting filesystem read-only"},
    {"type": "permanent", "condition": "MemoryPressureHW",
     "reason": "MCEHardwareError", "pattern": ".*Hardware Error.*memory.*"},
    {"type": "temporary", "condition": "NicLinkFlap",
     "reason": "NicFlapping", "pattern": ".*link is down.*"}
  ]
}

3.4.2 draino 配置与护栏

bash
# draino:检测到指定 condition 后自动 cordon+drain(部署在控制面)
draino \
  --node-condition="ReadonlyFilesystem,MemoryPressureHW" \
  --node-condition-expr="DiskPressure" \
  --drain-buffer=30m \
  --max-grace-period=8m \
  --eviction-drain-grace-period=120s \
  --eviction-headroom=30s

护栏设计(缺一不可)

护栏说明
condition 白名单只自动处置"确定性硬件故障"(只读盘、MCE),网络抖动类只告警
drain 速率限制draino 层限流 + PDB 兜底,防止误判批量驱逐
故障 node 保留尸检窗口cordon 后保留 24h 再 drain,供硬件团队抓取 dmesg
与升级流水线互斥升级窗口内 draino 降级为只告警(全局开关 label)

3.4.3 典型故障处置矩阵

故障检测信号自动动作人工动作
磁盘只读/坏道NPD ReadonlyFilesystem + smartctldraino cordon+drain工单换盘,机器下线
内存 MCE/EDAC 报错NPD MemoryPressureHW报错速率 > 阈值 → cordon换内存条;单条偶发可清零观察
网卡 flap/降速NPD + node_exporter node_network_speed_bytes连续 5 分钟降速 → cordon排查光模块/交换机端口
GPU 掉卡(Xid 错误)dcgm-exportergpu-fault 污点重置/返修
整机失联Node Ready→Unknown不自动驱逐(防脑裂!)确认电源/网络后再 force delete

注意事项节点失联(NotReady/Unknown)绝不自动 force delete + 驱逐。你不知道它是真死了还是只是网络分区——后者自动驱逐会造成双写脑裂。只有硬件团队确认"该机已断电/不可恢复"后,才允许执行:

bash
kubectl delete node <node> --force
kubectl delete pod <pod> -n <ns> --force --grace-period=0   # 逐 Pod,带审计

3.5 僵尸节点清理

僵尸节点 = Node 对象或底层资源处于"半死"状态的节点:NotReady 超期、kubelet 证书过期静默脱离、重复注册(同名不同机)、云实例已释放但 Node 对象残留。

bash
# 1. 找出 NotReady 超过 7 天的节点
kubectl get nodes -o json | jq -r '
  .items[] | select(.status.conditions[] | select(.type=="Ready" and .status!="True")) |
  .metadata.name'

# 2. 找出 Ready 但长时间未更新心跳(僵尸嫌疑)的节点
kubectl get --raw '/api/v1/nodes' | jq -r '
  .items[] | .metadata.name as $n |
  (.status.conditions[] | select(.type=="Ready")) |
  [$n, .lastHeartbeatTime] | @tsv' | \
  awk -v cutoff="$(date -d '3 days ago' -Is)" '$2 < cutoff'

# 3. 孤儿 lease 检查(kubelet 心跳 lease 与 node 不一致)
kubectl -n kube-node-lease get leases -o wide

# 4. 清理流水线:确认底层实例不存在 → 审计记录 → 删 Node → 删 lease → 回收 IP/标签

最佳实践:僵尸节点清理做成每周定时任务 + 报表,而不是出事再捞。同时把"节点 Ready 比例"和"僵尸节点数"纳入周报 SLO 看板。


第 4 章 etcd 运维

etcd 是 RKE2 集群的心脏。3000 节点集群的 etcd 写入 QPS 可达数千,DB size 常年在 4~7GiB 区间游走,etcd 运维是整个运维体系中 SLA 要求最高的一环

4.1 日常巡检(建议自动化为每日巡检脚本)

bash
#!/bin/bash
# etcd-daily-check.sh —— 在任一 control-plane 节点执行
export ETCDCTL_API=3
export ETCDCTL_CACERT=/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt
export ETCDCTL_CERT=/var/lib/rancher/rke2/server/tls/etcd/server-client.crt
export ETCDCTL_KEY=/var/lib/rancher/rke2/server/tls/etcd/server-client.key
EP="https://127.0.0.1:2379"

echo "=== 1. 成员与健康 ==="
etcdctl --endpoints=$EP member list -w table
etcdctl --endpoints=$EP endpoint health --cluster -w table

echo "=== 2. 状态(DB size / leader / raft index) ==="
etcdctl --endpoints=$EP endpoint status --cluster -w table

echo "=== 3. leader 变更频率(应 ≤ 2次/天) ==="
curl -sk --cert $ETCDCTL_CERT --key $ETCDCTL_KEY --cacert $ETCDCTL_CACERT \
  https://127.0.0.1:2381/metrics | grep -E 'etcd_server_leader_changes_seen_total|etcd_server_has_leader'

echo "=== 4. 慢请求与磁盘 WAL fsync 时延 ==="
curl -sk --cert $ETCDCTL_CERT --key $ETCDCTL_KEY --cacert $ETCDCTL_CACERT \
  https://127.0.0.1:2381/metrics | grep -E 'etcd_disk_wal_fsync_duration_seconds_(sum|count)|etcd_server_slow_apply_total'

echo "=== 5. 告警(etcd 自身 alarm,如 NOSPACE) ==="
etcdctl --endpoints=$EP alarm list

巡检指标红线

指标健康警告危险
DB SIZE< 4GiB4~6GiB> 6GiB(逼近 8GiB quota)
碎片率< 20%20~40%> 40%
leader 变更≤ 1 次/天2~5 次/天> 5 次/天(查时钟/网络/磁盘)
WAL fsync p99< 10ms10~25ms> 25ms(磁盘 IO 瓶颈)
alarm listNOSPACE 出现即 C0 事件

4.2 compaction 与 defrag 例行操作

RKE2 内嵌 etcd 由 kube-apiserver 做自动 compaction(默认每 5 分钟,压缩窗口可通过 kube-apiserver-arg: ["etcd-compaction-interval=..."] 调整),但 defrag 必须人工/脚本触发,且碎片整理有讲究:

bash
# 1. 查看碎片情况
etcdctl --endpoints=$EP endpoint status --cluster -w table
#   碎片率 = 1 - (DB SIZE IN USE / DB SIZE)

# 2. defrag:先 defrag 所有 follower,最后 defrag leader
#    (defrag 会锁定该成员,阻塞请求;逐个来,集群始终有可用 leader)
LEADER=$(etcdctl --endpoints=$EP endpoint status --cluster -w json | \
  jq -r '.[] | select(.Status.header.member_id == .Status.leader) | .Endpoint')
for ep in $(etcdctl --endpoints=$EP member list -w json | jq -r '.members[].clientURLs[0]'); do
  [[ "$ep" == "$LEADER" ]] && continue
  etcdctl --endpoints=$ep defrag
done
etcdctl --endpoints=$LEADER defrag

# 3. 整理后验证
etcdctl --endpoints=$EP endpoint status --cluster -w table

注意事项

  1. defrag 先 follower 后 leader,顺序反了等于主动制造 leader 切换。
  2. 出现 NOSPACE alarm 时,先 compact + defrag 释放空间,然后必须 etcdctl alarm disarm 解除告警,否则 etcd 仍拒绝写入。
  3. defrag 纳入月度维护窗口,与升级操作错开(不要在升级当天 defrag,出问题时不好归因)。

4.3 备份策略

本地快照(rke2 定时) ──┐
                        ├──► S3 异地(跨机房/跨 region)
按需快照(变更前)  ────┘

生产配置(RKE2 server config,参考 rke2/server-config.yaml 增强版):

yaml
etcd-snapshot-schedule-cron: "0 */6 * * *"   # 每 6 小时
etcd-snapshot-retention: 20                   # 本地保留 20 份(5 天)
etcd-s3: true
etcd-s3-endpoint: s3.internal.example.com
etcd-s3-bucket: rke2-etcd-backup
etcd-s3-folder: prod-cluster-01
etcd-s3-region: cn-north-1
etcd-s3-access-key: ${S3_AK}
etcd-s3-secret-key: ${S3_SK}
etcd-s3-timeout: 5m
备份层频率保留用途
本地快照6h5 天快速恢复(误删资源、短窗口回滚)
S3 异地每次快照同步90 天机房级灾难恢复
变更前按需快照手动7 天升级/重大变更兜底
月度归档每月 1 号快照打标1 年合规审计
bash
# 备份有效性自动校验(每日 CI 任务):随机抽取快照校验完整性
rke2 etcd-snapshot list
etcdctl --write-out=table snapshot status /var/lib/rancher/rke2/server/db/snapshots/<snapshot-name>

4.4 恢复演练制度

没有演练过的备份等于没有备份。制度要求:

  • 每月一次:在隔离测试集群完整走一遍"快照 → 全新集群恢复"流程,记录 RTO(目标 < 2h)与 RPO(≤ 6h)。
  • 每季度一次:包含 control-plane 重建 + 业务抽样验证的完整演练。
  • 每次重大升级前:最近一次演练记录不得超过 30 天。

演练检查单:

text
[ ] 从 S3 下载任意历史快照成功
[ ] cluster-reset 恢复流程执行成功(见 4.5)
[ ] apiserver /readyz 通过
[ ] 抽样 10 个 namespace 资源完整(deployment/pvc/secret/configmap)
[ ] 抽样业务 Pod 重建成功
[ ] 全程耗时记录并归档

4.5 quorum 丢失恢复流程

完整真实案例见仓库文档:k8s-issues/rke2-disaster-recovery-control-plane-quorum-loss.md(3 control-plane 全部宕机后的灾后恢复实录),本节提炼为标准流程。

场景:3(或多数)个 etcd 成员同时丢失,quorum 无法达成,所有 rke2-server 起不来,报错 etcdserver: request timed out

多数 etcd 成员丢失


  选择一台数据最新的节点(比较 raft index)作为恢复种子


  rke2-killall.sh 清理所有 server 节点残留进程


  ┌─── 路径 A:有可用的 etcd 数据目录 ───────────────────────┐
  │  1. 备份 /var/lib/rancher/rke2/server/db/etcd             │
  │  2. 编辑 db/etcd/config 追加 force-new-cluster: true      │
  │  3. 启动 rke2-server(强制单节点集群)                     │
  │  4. etcdctl member remove 移除所有失效成员                 │
  └──────────────────────────────────────────────────────────┘
  ┌─── 路径 B:数据目录也损坏,用快照恢复 ────────────────────┐
  │  rke2 server \                                           │
  │    --cluster-reset \                                     │
  │    --cluster-reset-restore-path=/path/to/snapshot        │
  └──────────────────────────────────────────────────────────┘


  验证单节点集群:kubectl get nodes / etcdctl endpoint health


  其余 server 节点:清理数据目录 → 以 join 模式重新加入
  (参考 k8s-issues/rke2-master-join-fails-critical-configuration-mismatch:
    重新 join 的节点配置必须与集群现存配置一致,否则 join 失败)


  恢复业务验证 + 复盘(为什么多数成员同时丢?电源?存储?误操作?)

关键命令摘录(与真实案例一致):

bash
# 所有 server 节点
rke2-killall.sh

# 种子节点:备份后强制单节点
mkdir -p /var/lib/rancher/rke2/server/db/etcd.bak.$(date +%Y%m%d%H%M%S)
cp -a /var/lib/rancher/rke2/server/db/etcd/* /var/lib/rancher/rke2/server/db/etcd.bak.*/
echo "force-new-cluster: true" >> /var/lib/rancher/rke2/server/db/etcd/config
systemctl restart rke2-server

# 移除失效成员
etcdctl --endpoints https://127.0.0.1:2379 member list -w table
etcdctl --endpoints https://127.0.0.1:2379 member remove <旧成员ID>

# 恢复后记得把 force-new-cluster 从 config 中删掉!(否则下次重启又是单节点重置)
sed -i '/force-new-cluster/d' /var/lib/rancher/rke2/server/db/etcd/config

# 其余节点重新 join:清空数据目录 + 保持 config.yaml 与集群一致
rm -rf /var/lib/rancher/rke2/server/db /var/lib/rancher/rke2/server/tls/etcd
systemctl start rke2-server

注意事项(血泪教训):

  1. force-new-cluster 是临时配置,恢复后必须删除,这是最常见的二次事故源。
  2. 重新 join 的节点,/etc/rancher/rke2/config.yamlcluster-cidr/service-cidr 等关键参数必须与集群一致,否则触发 "critical configuration mismatch" 拒绝加入(详见 k8s-issues/rke2-master-join-fails-critical-configuration-mismatch.md)。
  3. join 后节点长期 NotReady 先查镜像导入(参考 rke2-master-join-notready-image-import-delay.md)与 iptables/防火墙(rke2-master-join-fails-iptables-firewall-blocking.md)。

第 5 章 证书与安全运维

5.1 RKE2 证书轮换计划

RKE2 集群内部证书(apiserver、etcd、kubelet client 等 CA 签发的证书)默认有效期 1 年,CA 本身 10 年。3000 节点集群绝不能等到证书过期才处理——证书过期 = 全集群瘫痪(见第 8 章应急预案)。

5.1.1 证书台账与监控

bash
# 查看各证书到期时间(控制平面节点)
for crt in /var/lib/rancher/rke2/server/tls/*/server-*.crt \
           /var/lib/rancher/rke2/server/tls/client-*.crt; do
  echo "== $crt"
  openssl x509 -in "$crt" -noout -subject -enddate
done

# agent 节点 kubelet 证书
openssl x509 -in /var/lib/rancher/rke2/agent/client-kubelet.crt -noout -enddate

Prometheus 告警规则(提前 90/30/7 天三级预警):

yaml
- alert: CertExpiringSoon
  expr: (probe_ssl_earliest_cert_expiry{job="rke2-certs"} - time()) / 86400 < 30
  labels: {severity: P2}
  annotations: {summary: "证书 {{ $labels.instance }} 30 天内到期"}

5.1.2 轮换操作

bash
# 控制平面证书轮换(每台 server 滚动执行,C3 变更)
rke2 certificate rotate
systemctl restart rke2-server

# agent 证书:删除本地证书文件后重启 agent 即可重新签发
rm -rf /var/lib/rancher/rke2/agent/*.crt /var/lib/rancher/rke2/agent/*.key
systemctl restart rke2-agent
计划项安排
轮换节奏每 9 个月例行轮换(留 3 个月缓冲),与版本升级窗口错开 2 周
顺序follower server → 首节点 server → agent 分批(每批 ≤ 5%)
回滚证书轮换失败:恢复 /var/lib/rancher/rke2/server/tls 备份(操作前必须整目录备份)
特殊证书自定义 CA / tls-san 变更需要重建证书体系,属 C4 变更,须专项评估

5.2 kubeconfig 权限治理与 RBAC 体系

3000 节点集群通常对应数百个用户与几十个自动化系统。kubeconfig 满天飞是安全事故的头号温床。

5.2.1 治理原则

  1. 禁止分发 admin kubeconfig/etc/rancher/rke2/rke2.yaml 只允许留在控制平面节点 + 堡垒机应急柜(双人授权取用)。
  2. 人走 SSO,机器走 ServiceAccount:用户统一经 OIDC(dex/Keycloak)接入 apiserver;CI/CD 等机器身份用带 namespace 限定的 SA + 短期 token。
  3. RBAC 分层模板化
┌─────────────────────────────────────────────────────────┐
│ Cluster 层(极少数人)                                    │
│   cluster-admin          → 平台 SRE Lead(≤3 人,双人复核)│
│   cluster-viewer         → 架构/审计(全集群只读)          │
│ Namespace 层(大多数)                                    │
│   ns-admin  → 业务负责人(本 ns 全权,不可动 RBAC 本身)    │
│   ns-dev    → 开发(读写工作负载,不可 secret/不可 exec)   │
│   ns-viewer → 只读                                       │
│ 特殊约束                                                  │
│   平台保留 ns(kube-system/cattle-* 等)仅 cluster-admin   │
└─────────────────────────────────────────────────────────┘
yaml
# 模板示例:namespace 级开发角色(禁止读 secret、禁止 exec)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: {name: ns-dev, namespace: app-a}
rules:
- apiGroups: ["", "apps", "batch"]
  resources: ["deployments","statefulsets","jobs","pods","pods/log","services","configmaps"]
  verbs: ["get","list","watch","create","update","patch","delete"]
# 注意:刻意不包含 secrets 和 pods/exec
  1. 季度权限审计:列出所有 ClusterRoleBinding,清理离转人员;kubectl auth can-i --list 抽查高危权限。
bash
# 审计:所有拥有 cluster-admin 的主体
kubectl get clusterrolebindings -o json | jq -r '
  .items[] | select(.roleRef.name=="cluster-admin") |
  .subjects[] | "\(.kind)\t\(.name)"'

# 审计:能读 secret 的非系统主体
kubectl get clusterroles -o json | jq -r '
  .items[] | select(.metadata.name | startswith("system:") | not) |
  select(.rules[]? | .resources[]? == "secrets") | .metadata.name'

5.3 API 审计日志:采集与成本权衡

3000 节点集群 apiserver 审计日志可达 每小时数十 GB。全量采集 = 存储成本爆炸 + 淹没有效信号。大规模下必须裁剪策略:

yaml
# /etc/rancher/rke2/audit-policy.yaml —— 大规模裁剪版
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  # 1. 高频只读请求:直接丢弃(kubelet/list/watch 是流量大头)
  - level: None
    verbs: ["get", "list", "watch"]
  # 2. 系统组件的心跳类写入:丢弃
  - level: None
    userGroups: ["system:nodes"]
    resources: [{group: "", resources: ["nodes/status", "pods/status"]}]
  - level: None
    users: ["system:kube-scheduler", "system:kube-controller-manager"]
    verbs: ["update", "patch"]
    resources: [{group: "coordination.k8s.io", resources: ["leases"]}]
  # 3. 敏感操作:Metadata 级(谁、何时、对什么资源)
  - level: Metadata
    verbs: ["create", "update", "patch", "delete"]
  # 4. 高危操作:Request 级(含请求体,不带响应体)
  - level: Request
    resources:
      - {group: "", resources: ["secrets"]}
      - {group: "rbac.authorization.k8s.io"}
  # 5. exec/portforward/attach:RequestResponse 级(完整取证)
  - level: RequestResponse
    resources: [{group: "", resources: ["pods/exec","pods/portforward","pods/attach"]}]
yaml
# /etc/rancher/rke2/config.yaml
kube-apiserver-arg:
  - "audit-policy-file=/etc/rancher/rke2/audit-policy.yaml"
  - "audit-log-path=/var/lib/rancher/rke2/server/logs/audit.log"
  - "audit-log-maxage=7"
  - "audit-log-maxbackup=30"
  - "audit-log-maxsize=200"
  # 超大集群可选 webhook 模式直送 Kafka,避免本地落盘 IO
  # - "audit-webhook-config-file=/etc/rancher/rke2/audit-webhook.yaml"
  # - "audit-webhook-batch-buffer-size=10000"

最佳实践:裁剪后的审计日志经 filebeat/vector → Kafka → 热存储 7 天(ES/Loki 用于实时调查)+ 冷存储 1 年(S3 用于合规)。安全告警规则基于流式消费(Falco/自研),而不是事后查库。

5.4 漏洞响应流程

CVE 披露/情报订阅 ──► 影响评估 ──► 分级 ──► 修复路径选择 ──► 灰度修复 ──► 验证关闭
 (NVD/GHSA/厂商通告)   (是否命中?)            (patch版本/镜像/缓解配置)
严重度判定参考响应时限典型动作
Critical(如可远程 RCE、容器逃逸)CVSS ≥ 9 + 集群暴露面命中24h 内缓解,72h 内修复先缓解(准入策略/网络策略/临时补丁),再升级
HighCVSS 7~9 且需认证/特定条件7 天内纳入最近维护窗口 patch 升级
Medium/Low其余30 天随季度版本节奏

修复路径优先级(侵入性从小到大):

  1. 缓解配置:NetworkPolicy 隔离、PSA 加固、禁用受影响 feature-gate。
  2. 镜像修复:业务/组件镜像重打(基础镜像 CVE),走正常发布流水线。
  3. patch 版本升级:RKE2 同 minor 内 patch 升级(快速通道,见 2.3)。
  4. 临时自编译/hotfix:仅限 Critical 且无官方修复,事后必须回归官方版本。
bash
# 镜像漏洞扫描流水线(trivy 示例,CI 门禁)
trivy image --severity CRITICAL,HIGH --exit-code 1 registry.internal/app/web:v2.3.1

# 集群存量镜像扫描(starboard/trivy-operator 持续扫描报告)
kubectl get vulnerabilityreports -A --sort-by=.report.summary.criticalCount | head -20

5.5 CIS 基线持续合规

RKE2 默认部署即较接近 CIS 基线(profile: cis 可进一步强化),但漂移检测必须持续化:

bash
# 使用 rancher 的 kube-bench 适配版
docker run --rm --pid=host -v /etc:/etc:ro -v /var:/var:ro \
  registry.internal/aquasec/kube-bench:latest run --targets master,node,etcd,policies \
  --config-dir /etc/kube-bench/cfg --benchmark rke2-1.28
持续合规手段说明
kube-bench 月度扫描纳入巡检流水线,diff 报告进工单
准入控制(OPA/Kyverno)防止"运行中漂移":privileged 容器、hostPath、latest 镜像等在入口拦截
配置版本化/etc/rancher/rke2/config.yaml 全部进 Git,节点 Agent 定期 diff
例外管理不 compliant 项必须有书面例外单 + 到期复审日期

第 6 章 资源治理

无治理的 3000 节点集群会在半年内退化为"谁也说不清资源去哪了"的黑洞。资源治理四件套:配额、默认值、优先级、周期性整理

6.1 Namespace/租户配额体系

6.1.1 租户模型

平台 ──► 业务线(BU)──► 环境(prod/staging)──► namespace
            │                                     │
       BU 级配额预算(CMDB)              ResourceQuota + LimitRange

6.1.2 ResourceQuota 基线模板

yaml
apiVersion: v1
kind: ResourceQuota
metadata: {name: tenant-quota, namespace: app-a}
spec:
  hard:
    requests.cpu: "100"
    requests.memory: 400Gi
    limits.cpu: "200"
    limits.memory: 800Gi
    persistentvolumeclaims: "50"
    count/services.loadbalancers: "5"     # 防止滥用 LB
    count/services.nodeports: "10"        # NodePort 是稀缺资源,必须限制
    count/ingresses.networking.k8s.io: "20"

注意事项

  1. 一旦 namespace 设了 cpu/memory 配额,所有 Pod 必须带 requests/limits 否则创建被拒——所以 LimitRange(下节)必须先于 Quota 落地。
  2. 大规模集群慎用 count/pods 类对象数量配额,控制器重试风暴可能瞬间打满配额并伴随大量事件,压垮 etcd。优先用资源量配额。
  3. 配额调整走审批流 + 变更系统记录,杜绝"口头加配额"。

6.2 LimitRange 基线

yaml
apiVersion: v1
kind: LimitRange
metadata: {name: tenant-defaults, namespace: app-a}
spec:
  limits:
  - type: Container
    default:               # 未写 limits 时的默认值
      cpu: "1"
      memory: 2Gi
    defaultRequest:        # 未写 requests 时的默认值
      cpu: 100m
      memory: 256Mi
    max:                   # 单容器上限:防止单 Pod 吞掉半个节点
      cpu: "16"
      memory: 64Gi
    min:
      cpu: 10m
      memory: 64Mi
    maxLimitRequestRatio:  # limit/request 比值上限:防止过度超卖放大节点压力
      cpu: "4"
  - type: PersistentVolumeClaim
    max: {storage: 2Ti}
    min: {storage: 1Gi}

最佳实践maxLimitRequestRatio 是大规模集群稳定性的隐形护栏。超卖比失控时,节点负载尖峰会把 kubelet 打进 NotReady 旋涡。配合节点级超卖水位监控(sum(requests)/allocatable ≤ 1.2)一起用。

6.3 PriorityClass 体系设计

大规模混部集群必须回答"资源不够时谁先死"。设计原则:系统组件永不饿死,业务分级可预期驱逐

PriorityClass使用者说明
system-node-critical2000001000kubelet/网络/CNI DaemonSetK8s 内置,勿动
system-cluster-critical2000000000DNS、metrics-server 等K8s 内置
platform-critical1000000监控/日志 agent、ingress controller平台自建
biz-critical900000核心交易类业务需审批
biz-normal500000普通业务(default)默认
biz-batch100000离线任务/CI Job可被抢占
biz-besteffort1000探索性/测试随时牺牲
yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata: {name: biz-batch}
value: 100000
preemptionPolicy: PreemptLowerPriority   # 离线任务允许抢占 besteffort
globalDefault: false
description: "离线批处理任务,高峰可被抢占"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata: {name: biz-normal}
value: 500000
preemptionPolicy: Never                  # 普通业务之间互不抢占,避免连环驱逐
globalDefault: true

注意事项:除离线池外,preemptionPolicy 尽量设 Never。大规模集群中抢占级联(A 抢 B、B 抢 C)一次可能驱逐几百个 Pod,引发业务雪崩。

6.4 descheduler 治理

调度器只看"调度那一刻",长期运行后必然失衡(节点碎片、负载不均、亲和性漂移)。descheduler 周期性纠偏:

yaml
# descheduler policy(v0.30+,大规模生产参考)
apiVersion: descheduler/v1alpha2
kind: DeschedulerPolicy
profiles:
- name: default
  pluginConfig:
  - name: RemovePodsViolatingNodeTaints   # 清理落在错误污点节点上的 Pod
    args: {}
  - name: RemovePodsViolatingInterPodAntiAffinity
    args: {}
  - name: LowNodeUtilization              # 利用率均衡:压高填低,削峰
    args:
      thresholds: {cpu: 20, memory: 20}   # 低于 20% 的节点视为"欠载"
      targetThresholds: {cpu: 70, memory: 70}  # 高于 70% 的节点开始迁出
  - name: RemovePodsHavingTooManyRestarts # 异常重启 Pod 集中清理
    args:
      podRestartThreshold: 100
      includingInitContainers: true
  plugins:
    balance: {enabled: [LowNodeUtilization]}
    deschedule:
      enabled: [RemovePodsViolatingNodeTaints, RemovePodsViolatingInterPodAntiAffinity, RemovePodsHavingTooManyRestarts]

大规模使用红线:

  1. 只对无状态业务开启(label 过滤 governance.io/deschedule=true 白名单制),有状态中间件禁用。
  2. descheduler 尊重 PDB,但批量整理仍可能短时间造成大量迁移——运行窗口限低峰期,并与 draino/升级流水线互斥。
  3. 每次运行驱逐量设上限(--max-pods-to-evict-per-node / 命名空间级限制),防止整理本身变成故障源。

6.5 节点标签与污点体系规范

标签和污点是大规模调度的"交通规则",必须成文规范,否则三个月后无人敢动任何节点。

类别命名示例谁负责
硬件拓扑topology.kubernetes.io/zonenode.kubernetes.io/instance-typeaz1 / m5.16xlarge云平台/CMDB 自动打
节点池poolgeneral/gpu/bigmem/edge平台组
硬件特性hardware.io/gpu-modelnetwork.io/nic-speeda100-80g / 25g上线 SOP
运维状态onboard-batchmaintenance2026w29 / true运维流水线
调度约束污点dedicated=gpu:NoScheduleGPU 池专用平台组
故障隔离污点gpu-faulthardware-fault由 NPD/draino 自动打自愈系统
上线防护污点onboarding:NoSchedule冒烟通过即摘除上线 SOP
bash
# 规范审计:找出没有任何 pool 标签的"野节点"
kubectl get nodes -l '!pool' -o wide

# 规范审计:污点使用是否符合白名单
kubectl get nodes -o json | jq -r '
  .items[] | .metadata.name as $n | (.spec.taints // [])[] |
  "\($n)\t\(.key)=\(.value):\(.effect)"' | sort | uniq -c | sort -rn

第 7 章 变更与发布管理

7.1 集群组件变更灰度方案(CNI / Ingress 等)

平台组件(CNI、Ingress Controller、CSI、CoreDNS)的变更影响面是全局的,比业务发布危险一个数量级,必须按 C3 级变更管理。

7.1.1 CNI 升级(以 Calico 为例)

text
1. 版本兼容矩阵确认(Calico ↔ K8s 版本 ↔ RKE2 内置组件是否冲突)
2. 金丝雀:升级控制平面相关组件(calico-kube-controllers 等单点组件)
3. 节点金丝雀:先通过 Helm/Addon 配置仅对 ring0 节点池生效
   (RKE2 中通过 HelmChartConfig 自定义 rancher 管理的 calico chart)
4. DaemonSet 滚动策略调参:maxUnavailable 从默认 1 调整为节点池的 2%
5. 每环观察:BGP session 状态 / 跨节点连通性探测 / conntrack 表压力
6. 回滚预案:chart 版本 pin 住旧版 + 滚动回退 DaemonSet
yaml
# RKE2 中定制 CNI 的标准姿势:HelmChartConfig(勿直接改 chart)
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: rke2-calico
  namespace: kube-system
spec:
  valuesContent: |-
    installation:
      calicoNetwork:
        mtu: 1450
    felixConfiguration:
      prometheusMetricsEnabled: true
    typha:
      replicas: 9        # 3000 节点规模 typha 副本数 ≈ 每 300~400 节点 1 个

注意事项:RKE2 管理的组件(calico/coredns/ingress-nginx/metrics-server)由内置 helm-controller 维护,直接 kubectl edit 会被调谐回滚。所有定制必须通过 HelmChartConfig,且文件入 Git。

7.1.2 Ingress Controller 升级

text
滚动策略:DaemonSet 模式 → maxUnavailable 调小(如 2%)+ surge 不可用时加临时节点
金丝雀方案:
  A. 部署新版 ingress-nginx 为独立 Deployment(不同 IngressClass: nginx-canary)
  B. 通过 DNS/LB 权重将 1% 流量切到金丝雀实例
  C. 观察错误率/延迟后逐步放量
  D. 旧实例保留下线窗口 7 天,随时可切回
bash
# 切流验证
dig +short app.example.com
curl -H "Host: app.example.com" http://<canary-lb-ip>/healthz -v

参考本仓库真实案例 k8s-issues/rke2-ingress-nginx-controller-ContainerCreating.md:ingress controller Pod 长期 ContainerCreating 时,排查顺序为 镜像拉取(registry 连通)→ 挂载卷 → CNI 分配 IP → 资源配额

7.2 业务发布平台对接

SRE 与业务发布的边界约定(大规模下防止互相踩踏):

关注点平台侧职责业务发布平台职责
节点维护窗口发布节点维护日历 API(GET /maintenance/nodes发布前查询,避开节点正在 drain 的时间窗
容量提供集群水位 API(allocatable/requests 水位)大促扩容提前 2 周提容量工单
配额Quota 审批流配额内自助发布
优先级PriorityClass 定义与审计按规范选择业务等级
熔断集群级故障时冻结业务发布(全局开关)尊重冻结信号,发布流水线集成检查
bash
# 发布平台对接示例:发布前置检查
cluster_freeze=$(curl -s https://platform-api/v1/freeze-status | jq -r .frozen)
[[ "$cluster_freeze" == "true" ]] && { echo "集群处于冻结窗口,禁止发布"; exit 1; }

7.3 维护窗口制度

窗口类型时间允许的变更
日常低风险窗口工作日 10:00–17:00C1/C2 级,自动化执行
夜间维护窗口周二/周四 00:00–06:00C3 级组件升级、节点批量维护
月度大窗口每月第三个周六 00:00–08:00版本升级、etcd 碎片整理、证书轮换
冻结窗口大促前 7 天 ~ 后 3 天、重大节假日仅 C0 紧急修复

制度要点:

  1. 变更日历统一入口:所有窗口登记在共享日历,冲突由变更管理员仲裁。
  2. 窗口内必配"观察员":执行人之外必须有第二人盯大盘告警,执行人无权自行宣布"没问题"。
  3. 窗口结束即复盘:窗口内所有动作、异常、耗时自动记录进变更系统(流水线落库),周会过一遍。

第 8 章 应急预案与演练

8.1 应急预案库

预案的写法要求:判断条件 → 影响评估 → 处置步骤(带命令)→ 验证 → 升级路径,任何人拿到都能执行。

预案 1:控制平面全挂(API 不可用)

text
判断:LB 探测 apiserver /readyz 全部失败
第一步:确认是 apiserver 问题还是 etcd 问题
  - ssh 任一 server 节点:systemctl status rke2-server; journalctl -u rke2-server -n 200
  - etcdctl endpoint health --cluster
分支 A(etcd quorum 丢失):走第 4.5 节恢复流程(参考
  k8s-issues/rke2-disaster-recovery-control-plane-quorum-loss.md)
分支 B(apiserver 进程崩溃但 etcd 健康):
  - 检查磁盘(/var/lib/rancher 写满?audit 日志打爆?)
  - 检查 OOM:dmesg | grep -i oom
  - 修复后 systemctl restart rke2-server,逐台恢复
验证:kubectl get --raw=/readyz?verbose 全部 [+]ok;业务探测恢复

预案 2:etcd 数据损坏 / NOSPACE

text
判断:etcdctl alarm list 出现 NOSPACE,或日志报 mvcc: database space exceeded
处置:
  1. 获取当前 revision:etcdctl endpoint status -w json | jq '.[].Status.header.revision'
  2. compact:etcdctl compact <revision>
  3. defrag(先 follower 后 leader):见 4.2
  4. 解除告警:etcdctl alarm disarm
  5. 根因排查:是什么对象暴涨?(常见:事件风暴、ConfigMap 大数据、CRD 失控)
     kubectl get --raw /api/v1/events | wc -c
预防:etcd quota 监控告警(>70% 即介入);事件保留收敛;CRD 写入限流

预案 3:网络分区(机房/可用区级)

text
判断:某 zone 节点批量 NotReady,但节点本身健康(可达)
处置原则:
  - 不驱逐!不驱逐!不驱逐!(分区≠死亡,自动驱逐=脑裂)
  - 确认分区范围:zone 拓扑标签统计 NotReady 分布
  - 若控制平面 quorum 仍在线:保持现状,等网络恢复,Pod 自动重连
  - 若分区导致 etcd quorum 风险:评估是否将分区侧 server 节点主动关机
    (保 quorum 侧),再按 member remove 流程收缩
  - 网络恢复后:关注 Pod 大规模 reschedule 风暴,必要时分批 uncordon

预案 4:证书过期

text
判断:kubectl 报 x509: certificate has expired;kubelet 日志 TLS handshake error
处置:
  1. 确认过期范围:openssl x509 -enddate 逐个检查(5.1.1)
  2. 若 admin kubeconfig 过期但集群内部证书未过期:
     控制平面节点上 /etc/rancher/rke2/rke2.yaml 仍可用(supervisor 端口)
  3. 证书轮换:rke2 certificate rotate && systemctl restart rke2-server(逐台)
  4. agent:删除 /var/lib/rancher/rke2/agent/*.crt *.key 后重启 rke2-agent
  5. 全部恢复后:复盘为什么 90/30/7 天三级告警全部失效

预案 5:私有镜像仓库宕机

text
判断:大面积 Pod ImagePullBackOff;registry /v2/ 探测失败
影响评估:存量 Pod 运行不受影响;新调度/驱逐重建受影响
处置:
  1. 切换备用 registry:registries.yaml 中配置多 endpoint(参考 rke2/registries.yaml)
     mirrors:
       "192.168.122.156:30000":
         endpoint:
           - "http://192.168.122.156:30000"
           - "http://192.168.122.157:30000"   # 备用
     修改后逐台 systemctl restart rke2-agent(纳入节点池分批)
  2. 立即冻结节点维护操作(drain 会触发大量镜像拉取)
  3. registry 恢复后解冻
预防:registry 双活/主备 + 存储层共享;关键基础镜像(pause/cni)节点本地预置

预案 6:集群 DNS 故障

text
判断:业务大面积解析失败;coredns Pod CrashLoop 或被打满
处置:
  1. 定位:kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide
  2. 若 CoreDNS 资源不足被打爆:紧急扩容 replicas + 提 requests
     (3000 节点集群 CoreDNS 建议 ≥ 10 副本 + HPA + cluster-proportional-autoscaler)
  3. 若上游 DNS 异常:检查 Corefile forward 配置,切备用上游
  4. 临时缓解:确认 nodelocaldns(NodeLocal DNSCache)是否已部署;
     未部署则作为事后改进项(大规模集群强烈建议部署,
     参考 metaLB/coredns-nodelocaldncache-from-beginner-to-master.md)

8.2 混沌工程演练(大规模下的谨慎使用)

混沌工程的价值毋庸置疑,但在 3000 节点生产集群上"随机搞破坏"是危险的。原则:先测试环境练熟,生产只打"已知可控"的故障

工具适用大规模注意事项
Chaos MeshPod/网络/IO 故障注入,有 Dashboard 和审批流生产必选 mode: fixed-number 限定影响 Pod 数;全部实验走 C2 审批
kube-monkey随机杀 Pod 验证业务自愈只在预发/影子集群跑;生产禁用或限定"自愿参加"的 namespace
节点级混沌(关机/断网)验证自愈与预案只打 ring0 维护池节点;必须在维护窗口内
yaml
# Chaos Mesh 示例:限定范围的 Pod 故障注入(演练"业务对单 Pod 死亡的容忍")
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata: {name: drill-kill-web, namespace: chaos-testing}
spec:
  action: pod-kill
  mode: fixed-number
  value: "1"                      # 每次只杀 1 个,绝不按比例
  selector:
    namespaces: [app-a]
    labelSelectors: {app: web}
  scheduler:
    cron: "0 3 * * 3"             # 每周三凌晨低峰

8.3 GameDay 制度

┌─────────────────────────────────────────────────────────┐
│ GameDay = 有导演、有剧本、有观众的"计划内故障日"            │
├─────────────────────────────────────────────────────────┤
│ 频率:每季度 1 次,半天                                    │
│ 角色:导演(注入故障)、防守方(oncall 轮值团队)、观察员    │
│ 剧本:从预案库抽 2~3 个场景(如 etcd 快照恢复 + DNS 故障)  │
│ 环境:首选 1:1 影子集群;生产只做"读路径"类演练             │
│ 产出:                                                    │
│   - 每个预案的实测 RTO vs 目标 RTO                        │
│   - 预案文档的偏差修订(命令过期、步骤缺失)                 │
│   - 新发现的监控盲区 → 告警补建工单                        │
└─────────────────────────────────────────────────────────┘

最佳实践:GameDay 的 KPI 不是"故障恢复多快",而是"预案文档有多少地方是错的"。第一次 GameDay 预案命中率低于 50% 是正常的,这正是它的价值。


第 9 章 常见面试题

1. 问:RKE2 集群升级为什么不能跨 minor 版本?多版本落后时如何追赶?

答:Kubernetes 官方只保证相邻 minor 版本间的兼容性(组件版本偏差策略),跨版本升级可能遇到 API 废弃、存储版本迁移跳步、etcd 数据格式不兼容等未验证路径。追赶策略:逐级升级(v1.27→v1.28→v1.29),每个中间版本在生产停留至少一个业务周期(2 周+),先控制平面后 agent,agent 按节点池分批灰度,每批间设健康门禁。

2. 问:system-upgrade-controller 的 Plan 中 concurrency 对 server 和 agent 分别应该怎么设?为什么?

答:server(控制平面/etcd)必须设 1(或小于 etcd quorum 容忍数),因为同时升级多个 etcd 成员可能丢失 quorum,且 apiserver 版本跳变需要逐个验证。agent 可按节点池大小设 5%~10% 并发,配合 drain + PDB 与健康门禁,兼顾效率与爆炸半径控制。

3. 问:RKE2 没有官方降级方案,升级失败怎么回滚?

答:核心思路是"快照兜底":升级前对 etcd 打全量快照(rke2 etcd-snapshot save 并同步 S3)。单节点失败回退二进制或重建;控制平面大面积失败走 --cluster-reset --cluster-reset-restore-path=<快照> 恢复单节点后其余节点重新 join。代价是快照之后的写操作丢失,所以决策级别是 C0。真正的防线是升级前的灰度验证和废弃 API 扫描,让回滚永远用不上。

4. 问:节点失联(NotReady)后能不能自动驱逐上面的 Pod?为什么?

答:不能。失联≠死亡,可能是网络分区,此时 Pod 其实还在正常运行。自动 force delete + 重建会造成新旧 Pod 双写(脑裂),对有状态服务是灾难。正确做法:失联只告警,由人工/硬件侧确认节点彻底不可恢复(断电、已下电)后,再带审计地 force delete。能自动化的只有"确定性硬件故障"信号(如磁盘只读、内存 MCE),走 NPD + draino。

5. 问:etcd 出现 NOSPACE 告警怎么处理?如何预防?

答:处理四步:etcdctl compact <current-revision> → 按 follower 优先、leader 最后的顺序 defragetcdctl alarm disarm 解除告警 → 排查根因(事件风暴、大对象、CRD 失控写入)。预防:监控 DB SIZE(红线 6GiB)与碎片率(>40% 即整理)、月度例行 defrag、收敛事件保留、apiserver 审计策略裁剪减少 etcd 无关写入。

6. 问:大规模集群的 API 审计日志为什么要裁剪?怎么裁?

答:3000 节点集群审计日志可达每小时数十 GB,全量采集存储成本失控且淹没有效信号。裁剪原则:kubelet 心跳、list/watch 等高频只读流量置 None;系统组件的 lease/status 更新置 None;写操作记 Metadata;secret/RBAC 变更记 Request;exec/portforward 记 RequestResponse 用于取证。落地为 audit-policy.yaml 分级规则 + Kafka 流式消费 + 冷热分层存储。

7. 问:etcd quorum 丢失的恢复流程是什么?有哪些坑?

答:选数据最新的节点为种子,rke2-killall.sh 清理 → 备份数据目录 → db/etcd/configforce-new-cluster: true 强制单节点启动(或用快照 --cluster-reset 恢复)→ etcdctl member remove 移除失效成员 → 其余节点清数据目录重新 join。三大坑:①恢复后忘删 force-new-cluster 导致重启再次重置;②重新 join 节点配置与集群不一致触发 critical configuration mismatch;③join 后 NotReady 未排查镜像导入/防火墙问题。

8. 问:PriorityClass 体系怎么设计?preemptionPolicy 有什么讲究?

答:分层:系统组件用内置 system-node/cluster-critical;平台组件(监控/ingress)自建 platform-critical;业务分 critical/normal/batch/besteffort 四级。关键讲究是 preemptionPolicy:除离线批处理池外一律设 Never,否则大规模集群中抢占级联一次可驱逐数百 Pod 引发雪崩;同时配 PDB 与节点超卖水位监控(sum(requests)/allocatable ≤ 1.2)共同兜底。

9. 问:drain 被 PDB 卡死怎么处理?如何从根本上避免?

答:临时处理:定位卡住的 Pod 所属 PDB(kubectl describe pod 看 eviction 报错),与业务确认后临时调低 minAvailable 或人工终止 Pod。根本避免:①准入控制禁止"绝对 PDB"(minAvailable=100% 或等于副本数);②CI 卡点要求无状态服务必须配 PDB 但留有 disruption 余量;③批量维护工具 drain 前做 PDB 预演算(disruptionsAllowed ≥ 1 才驱逐)。

10. 问:设计一套 3000 节点集群的变更灰度体系,关键要素有哪些?

答:五要素:①变更分级(C0~C4,决定审批层级与窗口);②灰度环模型(ring0 金丝雀 1~2% → ring1 5% → ring2 每批 10% → ring3 尾批人工确认),环间设观察期(24h/12h/4h 递减);③自动健康门禁(节点 Ready 率、Pod 重启突增、apiserver p99、业务探测,任一恶化自动暂停);④回滚预案前置(回滚路径变更前验证、回滚决策人与执行人分离、明确触发线如批次失败率 >10%);⑤错误预算联动(预算剩余 <20% 时冻结非紧急变更)。


附录 A:本部分引用的仓库资料索引

资料位置用途
etcd quorum 丢失灾后恢复实录k8s-issues/rke2-disaster-recovery-control-plane-quorum-loss.md第 4.5 节、预案 1
master join 配置不匹配故障k8s-issues/rke2-master-join-fails-critical-configuration-mismatch.md第 4.5 节注意事项
master join 被防火墙阻断故障k8s-issues/rke2-master-join-fails-iptables-firewall-blocking.md第 3.1 节基线检查
master join 镜像导入延迟 NotReadyk8s-issues/rke2-master-join-notready-image-import-delay.md第 4.5 节注意事项
节点添加操作手册k8s-issues/rke2-node-addition-operation-manual.md第 3.1 节上线 SOP
ingress-nginx ContainerCreating 排查k8s-issues/rke2-ingress-nginx-controller-ContainerCreating.md第 7.1 节
RKE2 server/agent 配置样例rke2/server-config.yamlrke2/agent-config.yamlrke2/registries.yaml第 2、3、4、8 章
NodeLocal DNSCache 教材metaLB/coredns-nodelocaldncache-from-beginner-to-master.md预案 6 延伸阅读

附录 B:运维节奏一页纸(建议打印贴墙)

频率动作
每日巡检报表(etcd/SLO/僵尸节点/证书倒计时);oncall 交接
每周僵尸节点清理;配额与 RBAC 变更复核;低峰节点维护窗口
每两周patch 版本评估与灰度
每月etcd defrag + 快照恢复演练;kube-bench 扫描;月度大维护窗口
每季度权限审计;GameDay;descheduler/准入策略回顾;配额水位评审
每 4 个月minor 版本升级(跟随社区节奏,落后 1~2 个 minor)
每 9 个月证书轮换
每年容量规划评审;预案库全面刷新;灾备架构演练

下一部分预告:第四部分《大规模集群观测体系》将深入 metrics/logs/traces 三层架构、Prometheus 联邦与Thanos 长存储、万级告警治理与 SLO 看板工程化。