本文中的节点、地址、域名、环境名和容量数据均为脱敏示例。Secret、数据库备份、Token 和内部仓库信息不属于可发布内容。

系列导航:Kubernetes 重建迁移教程系列。本篇覆盖迁移日的执行、停止条件与回滚。

上一篇文章复盘了重建式迁移中遇到的问题。这一篇不再按事故时间线叙述,而是把经验沉淀成一份可以逐阶段执行的 Runbook。

“一步到位”并不等于一个命令启动所有节点和业务。它的真正含义是:迁移窗口内只执行经过验证的版本化资产,不再临时修改 YAML、Nacos、Service IP、节点污点或防火墙。

总原则:上一阶段失败就停止

flowchart LR
  A["冻结设计"] --> B["备份与恢复演练"]
  B --> C["操作系统基线"]
  C --> D["高可用控制平面"]
  D --> E["CNI 与基础设施"]
  E --> F["零副本预部署"]
  F --> G["数据恢复"]
  G --> H["Nacos 与平台依赖"]
  H --> I["业务分批启动"]
  I --> J["真实业务验收"]
  J --> K["入口切换"]

每个箭头都是硬性门槛。失败时只能:

  1. 回滚当前批次。
  2. 在 Git 中修正源配置。
  3. 重新 dry-run 和评审。
  4. 从失败阶段重新执行。

不要用 kubectl edit、临时 patch、固定 sleep 或重启 Pod 来掩盖配置错误。

阶段 0:冻结不可变基线

创建第一个控制平面之前,冻结:

  • Kubernetes、kubeadm、kubelet、kubectl 版本。
  • 操作系统和 containerd 版本。
  • 控制平面节点和 API VIP。
  • Pod CIDR、Service CIDR、集群 DNS 域。
  • CNI、Ingress、StorageClass 和 metrics-server 方案。
  • Nacos 版本与集群模式。
  • 镜像来源和 digest。
  • 节点池、标签、污点和 toleration。

三类网络必须有清晰边界:

网络用途是否写入应用配置
节点网段虚拟机、节点和少量外部依赖仅在确有必要时
Pod CIDRCNI 分配的 Pod IP禁止
Service CIDRClusterIP仅兼容白名单

应用内部默认使用:

<service>.<namespace>.svc.cluster.local:<port>

阶段 1:准备全部输入

执行前必须具备:

  • 服务器、虚拟机、磁盘和节点池清单。
  • 旧主机名到新主机名的映射。
  • etcd snapshot 和 Kubernetes 资源归档。
  • 数据库、消息系统和文件存储备份。
  • Nacos namespace、group、dataId、内容和校验值。
  • 镜像及 digest 清单。
  • Service DNS 替换表和固定 ClusterIP 白名单。
  • 域名、证书、Ingress 和外层入口映射。
  • 回滚负责人、停止条件和恢复命令。

只要任一输入缺失,就不应重装仍承载数据的旧节点。

阶段 2:完成恢复演练

备份矩阵可以按下面的方式建立:

服务备份验收
etcdsnapshot隔离 restore,API 对象可读
MySQL全量备份、用户授权、binlog关键表、行数、应用账号查询
PostgreSQLdump/base backup、角色、WALschema、扩展、应用查询
MongoDBdump/snapshot、认证库collection、索引、业务查询
RedisRDB/AOF、配置PING、关键 key、TTL 抽样
Kafkatopic、partition、offsetbroker、ISR、consumer group
RabbitMQdefinitions、vhost、用户exchange、queue、binding
NFS/hostPath文件或存储快照文件数、容量、checksum 抽样

备份文件存在本身不算完成。恢复演练才是门槛。

阶段 3:所有配置先在 Git 中改完

迁移前完成以下静态治理:

  • 内部 URL 改为 Service DNS。
  • Pod IP 引用归零。
  • 固定 ClusterIP 只保留白名单。
  • Kafka advertised listener 单独评审,不做字符串替换。
  • Deployment 的资源、探针、调度、DNS 和优雅停机统一。
  • 导出对象移除只读字段和旧管理平台元数据。
  • Deployment、StatefulSet 初始副本为 0。
  • CronJob 初始 suspend=true。

常用静态扫描:

rg -n '<pod-cidr-pattern>' config/ manifests/
rg -n '<service-cidr-pattern>' config/ manifests/
rg -n 'node[0-9]+' manifests/

扫描结果不能直接批量替换,每个命中都需要归类为 Service、节点、外部依赖或遗留垃圾配置。

阶段 4:统一节点基线

重装后每台节点执行同一脚本:

swapoff -a
modprobe overlay
modprobe br_netfilter
sysctl --system
systemctl enable --now containerd
systemctl enable kubelet

逐节点验收:

hostnamectl
ip address
ip route
timedatectl
swapon --show
systemctl is-active containerd
crictl info

firewalld 规则必须在 kubeadm 之前完成。所有节点需要允许 Pod CIDR 转发;hostNetwork 服务则按实际端口增加最小范围的放行规则。

阶段 5:创建控制平面

顺序固定为:

  1. 初始化第一个 control-plane。
  2. 部署 kube-vip 并验证 API VIP。
  3. 加入其余 control-plane。
  4. 检查 etcd 成员。
  5. 测试 VIP Leader 漂移。
  6. 生成并妥善保存 worker join 命令。

控制平面验收项:

  • API VIP 连续可达。
  • 三个控制平面节点 Ready。
  • etcd 三成员健康。
  • kubeconfig 指向 VIP,而非单台主机。
  • 证书 SAN 包含实际访问地址。

阶段 6:安装网络和基础设施

安装 CNI 后,先验证跨节点 Pod IP、CoreDNS、Service ClusterIP 和外部 DNS。

再依次安装:

  1. 默认存储类。
  2. metrics-server。
  3. Ingress Controller。
  4. 日志与监控。

每个节点池至少执行一次 PVC smoke test;kubectl top nodes 和 kubectl top pods -A 都返回数据后,指标系统才算可用。

阶段 7:标签、污点和零副本预部署

节点加入后立即设置目标标签和污点,避免未标记节点参与调度。

多副本状态服务使用 required Pod Anti-Affinity:

podAntiAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    - topologyKey: kubernetes.io/hostname
      labelSelector:
        matchLabels:
          app: zookeeper

资源预部署后的硬性检查:

  • PVC 已 Bound 或有明确恢复计划。
  • Service selector 与 Pod label 完全匹配。
  • 固定 ClusterIP 未冲突。
  • 所有镜像可拉取。
  • 不存在旧主机名。
  • 停用环境的副本保持为 0。

阶段 8:数据恢复

固定顺序:

存储
→ 基础数据库与消息组件
→ Kafka
→ PostgreSQL 与辅助服务
→ Nacos 数据库
→ Nacos

每个服务必须从同节点 Pod、跨节点 Pod、Service DNS 三个入口验证。只看 Pod 是否 Ready,无法发现 kube-proxy、firewalld 或 DNS 问题。

阶段 9:Nacos 集群验收

Nacos 三 Pod 需要两类 Service:

Service类型用途
nacos-hsHeadless成员稳定 DNS 和集群互联
nacos-csClusterIP客户端统一入口

三层验收:

  1. 三个成员均为健康状态。
  2. 三个节点的运行模式开关一致。
  3. 测试客户端注册后,三个节点都能查询到同一实例。

完成后再导入 Nacos 配置,逐个回读 namespace、group、dataId 和校验值。

阶段 10:平台依赖和业务分批启动

先启动鉴权、字典、文件、工作流等平台依赖,再启动 OAuth、Gateway 和 DevOps。首个鉴权服务应成为硬性门槛:

  • Deployment Ready。
  • Service 有 Endpoint。
  • 三个 Nacos 节点实例一致。
  • OAuth 能通过 Service DNS 调用。
  • 无效登录应返回明确的业务错误,而不是“无可用实例”。

业务一次只启动一个命名空间。该批次全部通过后再继续下一批。

阶段 11:Ingress 与真实业务验收

先绕过外部入口,直接请求 Ingress:

curl --resolve app.example.com:443:<ingress-address> \
  https://app.example.com/

集群内成功后,再配置路由器、NLB 或代理层的 Host/SNI 转发。当集群内返回 200 而公网访问失败时,不要修改 Kubernetes 业务资源。

真实验收至少覆盖:

  • 正常登录、失败登录和 Token 刷新。
  • 权限、文件、Session。
  • 数据库读写。
  • Kafka/RabbitMQ 生产消费。
  • WebSocket。
  • CI/CD 上传发布。

自动停止条件

遇到以下情况立即停止:

  • etcd 或数据库备份无法恢复。
  • 网络 CIDR 冲突。
  • API VIP 漂移失败。
  • CNI 或 kube-proxy 未全量就绪。
  • 数据校验不一致。
  • Nacos 三节点实例不一致。
  • 核心鉴权链路不可用。
  • 出现不可接受的数据写入错误。
  • 公网故障需要修改业务 Pod 才能绕过。

Go / No-Go 清单

  • 服务器与节点池清单无重复、无遗漏。
  • 版本、网络和镜像已冻结。
  • etcd 与数据库已恢复演练。
  • 文件存储已备份。
  • Nacos 配置已导出、DNS 化并回读。
  • 固定 ClusterIP 白名单已评审。
  • 标签、污点和 toleration 已成对校验。
  • 防火墙初始化脚本已验证。
  • Nacos 两类 Service 清单完整。
  • 域名、证书和入口映射已准备。
  • 所有资源可零副本预部署。
  • 真实验收请求和预期结果已准备。
  • 回滚入口、负责人和停止条件已确认。

完成定义

迁移完成必须同时满足:

全部节点 Ready
+ 基础组件健康
+ 数据恢复并通过应用查询
+ Nacos 三节点一致
+ Service DNS 与 Endpoint 正常
+ 活跃业务全部 Ready
+ 登录与核心业务成功
+ 公网入口成功
+ 配置和脚本已进入 Git
+ 旧集群经过观察窗口后下线

Runbook 的真正价值不是让操作更“机械”,而是把依赖个人记忆的临场决策,转化为可以审查、暂停、回滚和复现的工程流程。