服务网格升级、迁移与弃用治理:用双轨验证完成可证明的退出
一次 Istio canary 升级中,新旧 istiod 都是 Ready,测试 namespace 也已改成新 revision。团队随即卸载旧控制面,几分钟后仍有一批 Pod 失去配置更新。原因很简单:namespace 标签只改变以后创建 Pod 的注入目标,现有 sidecar 没有被重建;一个手工安装的 gateway 又曾被原地升级,既不属于新 revision,也没有随旧 revision 回退。表面上的“控制面双跑”实际只覆盖了安装对象,没有覆盖正在承载请求的数据面。
另一支团队迁移 Gateway API 所有权时,先在旧 Helm release 中关闭 CRD 管理,再部署新的 CRD bundle。Helm 把原 CRD 视作应删除资源,所有依赖它的 Route 一并消失;API server 接受的新 CRD 并不能恢复已经删除的实例。与此同时,节点 CNI 仍保留旧重定向,部分 Pod 带旧代理,策略控制器却已切到新 API,形成“请求还被截获,但没有任何一套控制面完整负责”的半迁移状态。
升级单位不是控制面 Pod,而是一组有顺序的状态
服务网格的版本状态分散在 API server、控制器、节点和工作负载中。一个可回滚变更至少包含以下对象:
CRD 决定对象能否保存、以哪个版本保存和如何转换;控制面读取这些对象并产生配置;CNI、eBPF 或 iptables 决定请求是否进入数据面;代理执行路由、授权和证书验证;CA 与 issuer 决定新身份能否签发;遥测决定团队是否看得见迁移中的偏差。只替换其中一个组件,会产生版本偏差窗口,而不是完成升级。
把每次变更写成版本事务:固定源版本和目标版本,声明允许的 skew,定义每个对象的 owner,列出提交点和回滚点。事务不要求所有组件原子更新,但要求任何阶段都只有一套清晰的请求语义,并能停止继续推进。若 CRD/storage 已迁移到旧二进制无法读取的格式,回滚就不再是“换回旧镜像”,而是恢复状态备份或执行反向迁移。
先做资产账本,再安装候选版本
升级前从目标产品的支持页、upgrade notes 和安装入口确定可行路径。Istio canary 可跨官方允许的 minor 窗口,in-place 要逐 minor;Cilium 只测试相邻 minor 的升级与回滚;Kuma 一次最多跨两个 minor;Consul 需要按版本里程碑和逐版本 notes 计算路径;OpenShift Service Mesh 还受 Operator channel、OCP 和 downstream 支持矩阵约束。任何 latest、chart 浮动版本或未固定 digest 都会让回滚基线失去意义。
资产账本至少导出这些内容,并标明 owner 与恢复入口:
Helm release、values、Operator CR、镜像 digest、CLI 与 Kubernetes 版本;CRD 的 served/storage versions、conversion webhook、所有 CR 实例与 finalizer;Gateway API bundle、实现 conformance、Gateway/Route/ReferenceGrant 和 CRD owner;
namespace/Pod 注入标签与注解、revision tag、webhook selector;CNI 配置、DaemonSet、节点规则、eBPF/iptables、gateway/waypoint;sidecar、ztunnel、dataplane、Envoy 与扩展组件版本分布;
identity root/intermediate、issuer、trust bundle、证书有效期和轮换任务;授权、路由、超时重试、egress、多集群、遥测、dashboard 与 alert;remote secret、ServiceAccount、ACL token、LoadBalancer、DNS、PVC 和云费用资源。
以下命令只读取状态,适合建立基线;输出仍可能包含内部服务名、地址和身份,应存入受控证据库,不要直接贴到工单或公开日志:
kubectl get crd -o custom-columns=NAME:.metadata.name,STORAGE:.status.storedVersions
kubectl get mutatingwebhookconfigurations,validatingwebhookconfigurations
kubectl get ns --show-labels
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}'
helm list -A
istioctl proxy-status安装候选版本前验证权限边界。平台发布身份可以更新 CRD、webhook、ClusterRole、CNI 和控制面;应用发布身份只应迁移指定 namespace;PKI 身份负责 issuer 与根信任;观测身份只能读取必要状态。临时 kubeconfig、registry credential、remote token、CA 私钥和 Helm registry credential 不得进入 values、命令行历史或流水线明文日志。候选镜像应固定 digest 并保留签名、SBOM 与来源记录。
弃用发现要覆盖“仓库里有什么、集群存了什么、运行时还有谁在调用”三层。只搜索 YAML 会漏掉 Helm 渲染、Operator 生成对象、旧控制器和外部客户端;只看 API server 对象又会漏掉尚未部署的分支。先用与目标 release 对齐的 CLI 分析期望配置和现存配置,再结合 Kubernetes 的 Warning、审计事件与 API 指标识别真实调用者:
# 候选 istioctl 对 Git 中的完整渲染结果做离线分析
istioctl analyze --use-kube=false rendered-mesh/*.yaml
# 对现存所有 namespace 做只读分析,IST0002 表示依赖已弃用能力
istioctl analyze --all-namespaces
# CRD 的 served/storage 与历史存储版本必须分别观察
kubectl get crd \
-o custom-columns='NAME:.metadata.name,SERVED:.spec.versions[*].served,STORAGE:.spec.versions[*].storage,STORED:.status.storedVersions'Kubernetes 从 1.19 起会为弃用 API 请求返回 Warning、在审计事件标记 k8s.io/deprecated=true,并暴露 apiserver_requested_deprecated_apis。指标为 1 只说明至少有请求发生,还要用审计中的 user agent、身份和对象定位调用者;仓库扫描为零也不能覆盖集群外客户端。CRD 把新版本标为 storage: true 后,旧对象不会自动改写;只有完成存储迁移并确认旧版本从 status.storedVersions 消失,才能关闭旧 served 和 conversion webhook。Kubernetes 弃用策略 CRD 版本迁移
每一条弃用项都要有 owner、最后调用者、替代对象、语义差异、迁移批次和删除 release。istioctl analyze 能发现配置问题,但官方明确它只分析 Kubernetes 配置,不观察真实流量;因此 IST0002 清零只是进入请求实验的条件,不是删除旧能力的依据。Istio 配置分析
用 Istio revision 建立最小 canary
Istio 官方推荐 canary/revision 升级。先用目标版本 CLI 对源版本做 precheck,再升级共享 base/CRD,最后并行安装不可变 revision。版本和 revision 由发布流水线显式注入;参数未设置时让 shell 立即退出,避免尖括号占位符被误当成重定向:
: "${SOURCE_MINOR:?set the installed Istio minor, for example 1.x}"
: "${TARGET_VERSION:?set the pinned target chart version}"
: "${TARGET_REVISION:?set an immutable target revision}"
istioctl x precheck --from-version="${SOURCE_MINOR}"
helm upgrade istio-base istio/base \
--namespace istio-system \
--version "${TARGET_VERSION}" \
-f base-values.yaml
helm install istiod-canary istio/istiod \
--namespace istio-system \
--version "${TARGET_VERSION}" \
--set "revision=${TARGET_REVISION}" \
-f istiod-values.yamlbase/CRD 是共享资源,不属于某一个 revision;先升级它们会改变新旧控制面共同读取的 schema。revision 是不可变控制面实例,revision tag 是可变注入指针。移动 tag 或修改 namespace 标签只影响后续创建的 Pod,现有 sidecar 必须 rollout 后才连接新控制面。
Istio 的支持边界要求控制面最多领先数据面一个版本,数据面不能领先控制面;revision 虽能支持官方允许的跨 minor canary 路径,也不意味着任意跨度都可跳跃。升级前把 Kubernetes 支持范围、Istio/Envoy 发行线、扩展 ABI、Gateway API bundle 和数据面版本偏差写入矩阵,超出任一产品明示窗口就拆成中间跳板。compatibilityVersion 只临时保留官方列出的旧行为,且会随对应旧发行线结束支持而失效,不能代替中间版本或长期冻结迁移。Istio 支持发行线 兼容行为版本
用隔离的 mesh-canary namespace 部署 client、orders-v1、orders-v2,保留旧 revision 作为 stable cohort,再把 canary cohort 绑定目标 revision:
: "${TARGET_REVISION:?set the installed canary revision}"
kubectl label namespace mesh-canary istio-injection- "istio.io/rev=${TARGET_REVISION}" --overwrite
kubectl -n mesh-canary rollout restart deployment
kubectl -n mesh-canary rollout status deployment --timeout=5m
istioctl proxy-status
istioctl version预期证据是新建 Pod 的注入状态、代理镜像和控制面连接都指向目标 revision;旧 cohort 仍连接旧 revision;CR 与代理配置没有 NACK;新旧数据面均可建立身份、执行策略并访问正确 endpoint。rollout status 成功只证明 Pod 达到 Deployment 条件,不能替代请求链证据。
revision tag 适合把 prod-canary 和 prod-stable 指向不同 revision,再按 namespace 或 workload 批次重建。引入 default tag 时要特别检查默认 injection、validation webhook 和 singleton leader,避免 non-revisioned webhook 与新 webhook 同时注入。gateway 若由 Helm 手工管理,不会因为 tag 移动而自动升级;它必须列入独立 cohort。
配置字段决定升级的影响半径
| 对象或字段 | 它真正控制什么 | 迁移错误的后果 |
|---|---|---|
namespace istio.io/rev | 新 Pod 选择哪个注入 webhook/revision | 已运行 Pod 不变,出现标签已切但代理仍旧 |
| revision tag | 后续注入目标的可变指针 | 移动过快使新建 Pod跨批次漂移 |
CRD served/storage | API 可接受版本与持久化版本 | 旧控制器读不懂新状态,降级失败 |
| conversion webhook | 多 API 版本之间的读写转换 | webhook 删除后旧版本读取或写入失败 |
| Gateway API CRD owner | 谁安装、升级和删除共享 CRD | Helm ownership 切换导致 Route 级联删除 |
CNI excludeNamespaces/重定向 | 哪些 Pod 被捕获、节点何时就绪 | 出现明文绕过、启动阻塞或全节点中断 |
| proxy metadata / compatibility | 数据面连接、功能兼容和旧行为开关 | 误把局部兼容开关当成完整旧版本模拟 |
| trust bundle / issuer | 哪些新旧证书可互认与谁负责签发 | 旧连接存活、新连接批量 TLS 失败 |
policy selector/targetRefs | 策略由 sidecar、waypoint 或其他目标执行 | 新旧数据面各自加载不同授权语义 |
配置变更要从 desired object 追到 observed data plane。API server apply 成功只证明 schema/admission 通过;Route 还要看 Accepted、ResolvedRefs、observedGeneration,代理还要看实际 listener/route/cluster/endpoint 与策略。字段被目标版本忽略时,控制面可能 Ready、请求却悄悄回到默认行为。
正反实验覆盖四种版本方向
双轨窗口至少验证旧到旧、旧到新、新到旧、新到新四种调用方向;若有 gateway、egress 或跨集群,再给每种方向增加对应路径。每个请求带唯一 request ID,同时保存源/目标身份、双方代理版本、控制面 revision、命中 endpoint、策略 revision、应用响应、代理本地响应和上游响应。
正向集合包括:正常身份访问允许路径;错误身份被拒绝;L4 与 L7 policy 命中相同语义;版本路由、timeout/retry 总预算不变;egress 仍经批准路径;证书轮换后新连接成功;日志、指标和 Trace 标签能区分新旧 cohort。目标环境尚未执行时将结果标为“待测”;兼容矩阵只能决定能否进入实验,不能替代请求证据。
反向集合至少主动制造四类错误:
给 Route 引用不存在的 backend,预期 controller status 显示 ResolvedRefs=False 或产品对应 reason,代理不应悄悄采用旧路由。让 canary 使用错误 ServiceAccount,预期授权在目标数据面拒绝,应用无对应请求;若请求成功,说明身份或策略作用域发生变化。暂停候选控制面连接,预期既有代理使用最后配置,但新策略、endpoint 和证书续期不收敛;恢复后 observed version 应追上 desired generation。
在安全隔离环境缩短测试证书或制造不重叠 bundle,预期新连接出现明确 TLS 失败,旧连接行为单独记录;禁止通过关闭校验修复。
容量也要纳入升级判据。固定相同 RPS、连接、payload、policy、日志和采样,比较升级前后 p99、error/reset、proxy/control-plane CPU 与 RSS、配置收敛、active series、日志字节和 Trace ingest。示例停止条件可以是任一 SLO 越界、错误持续增长、配置长时间 NACK、遥测丢失或资源预算超限;生产阈值来自现网基线和容量预算,不使用脱离环境的万能百分比。
迁移波次应是一台可暂停的状态机,而不是“观察一会儿继续”。每一波只包含一个明确故障域,推荐顺序为离线渲染与空集群、非关键 namespace、单 zone、单 cluster、同区域其余集群、跨区域;共享 CNI、根信任和 CRD 不能伪装成 namespace 波次。开始下一波前同时检查以下信号:
| 信号 | 继续条件 | 立即停止并回切流量的条件 |
|---|---|---|
| 配置 | desired generation 已被目标代理 ACK,关键 Route/Policy condition 正常 | NACK、旧 generation 停滞、默认路由或默认授权接管 |
| 身份与权限 | 新旧四向请求的允许和拒绝语义一致,新连接证书链正确 | 错误身份获准、正确身份拒绝、旧根撤销后仍受信 |
| 流量 | error、reset、retry amplification 与 p99 在本项目预算内 | 任一用户 SLO 越界,或重试/连接风暴持续增长 |
| 容量 | 失去一个副本或节点后仍有已测余量 | proxy、gateway、控制面或遥测队列越过安全水位 |
| 观测 | request ID、revision、cluster、response flag 能闭环 | 日志/指标/Trace 中断,无法区分候选与稳定批次 |
| 状态 | CRD storedVersions、CNI、证书和数据库仍处于声明的回滚区间 | 已发生旧版本无法解释的写入或不可逆 schema 迁移 |
停止后先冻结 GitOps 自动推进和新建 Pod,再把业务权重、revision tag 或节点调度指回 stable;候选仍保留用于取证。只有请求链恢复并证明旧控制面重新接管,才清理候选。若停止条件来自 CRD/storage、CA 或数据存储的不可逆写入,不执行二进制降级,转入隔离恢复或前滚修复。
CRD 与 Gateway API 所有权迁移必须先接管,再放弃
CRD 是数据容器,不是普通无状态安装文件。删除 CRD 会级联删除所有 CR 实例;重新创建同名 CRD 不会恢复对象。迁移 owner 前先导出 CRD、CR、storedVersions、conversion 配置和 finalizer,并在副本环境验证恢复。若目标版本要求 storage migration,应完成迁移、确认所有对象可由新旧读者解释,再关闭旧 conversion webhook。
Linkerd 的 Gateway API ownership 迁移提供了典型教训:2.19 起 Linkerd 不再代装/拥有 Gateway API CRD,过渡必须按官方 ownership migration 让外部安装先接管,再移除旧 Helm ownership。顺序反转可能删除 CRD 和所有依赖 Route。Gateway API CRD 也可能被 Istio、Cilium、网关控制器和业务共同使用,任何单个 mesh release 都无权在卸载时擅自删除。
升级还要核对目标 Gateway API bundle、Mesh Profile 和实现 conformance。Service parentRef 已进入 Standard Channel,不代表任意产品版本、sidecar/ambient 模式、所有 filter 和 consumer route 都兼容。上游 Istio 的 conformance 报告也不能外推到 OpenShift Service Mesh downstream build。每个 Route 都要检查目标 parent 的 condition 与真实请求。
CNI、节点代理和共享数据面不能按 workload 灰度
sidecar 可以按 Pod rollout,节点 CNI、eBPF agent、ztunnel 这类共享组件却以节点为故障域。Istio ambient 升级要拆分 base/CRD、istiod、Istio CNI、ztunnel、waypoint/gateway;CNI 当前没有和 sidecar revision 等价的 per-workload canary,ztunnel 原地升级会影响节点上所有 ambient 流量。需要更小影响面时,使用 cordon/drain、专用 canary node pool 或 blue/green 节点池,并验证长连接重建。
Cilium 同时承担 CNI 时,升级 agent、operator、Envoy/Hubble 和 CRD 要遵守同一受支持组合。官方只测试相邻 minor;先升级当前线最新 patch,再逐跳阅读 Upgrade Guide。跨 chart 升级不要用 --reuse-values,否则新默认值可能被旧 values 静默遮蔽。经过 userspace proxy 的 L7 policy、Ingress/Gateway API 连接可能中断并需要重连,不能把 L3/L4 的最小中断承诺外推到所有路径。
节点层回滚前保存 CNI config、binary、BPF program/map、tc、route、link 与 IPAM 状态。新 CRD 或 datapath state 已被写入后,DaemonSet rollout undo 只回退容器模板,不保证旧 agent 能解释节点和 API 状态。替代 CNI 未就绪前卸载 Cilium会直接让新 Pod 网络失败。
策略与证书迁移需要重叠窗口,不需要永久双写
策略迁移先建立等价性表:旧对象的 selector、作用域、默认 allow/deny、执行点和冲突优先级,分别映射到新对象。迁移期间允许短暂双轨观察,但不能让两套控制器长期同时写同一 listener/route 或授权边界。shadow/dry-run 只能生成观察证据,不会阻断;切到真实 deny 前必须发送正确身份、错误身份、错误 path 和未纳管来源四组请求。
sidecar 迁移到 ambient 时,L4 policy 可由 ztunnel 执行,L7 policy、JWT、Wasm 或流量路由通常要迁到 waypoint 的 targetRefs/Gateway API 语义。官方 sidecar 到 ambient 迁移 允许按 namespace 渐进回退,但 L7 policy 没有原子 handoff;sidecar source 还可能绕过 waypoint。正确顺序是先建立 waypoint 与新策略,再启用 ambient、验证 HBONE,然后移除 injection 并重建 Pod。EnvoyFilter 不能直接迁到 waypoint,primary-remote、VM 和特定证书提供方也可能成为阻塞项。
证书事务要分 root、intermediate、issuer、workload certificate 和 trust bundle。先让验证方同时信任旧根与新根,再让签发方切到新链,确认所有新工作负载获得新证书并完成新连接,最后撤销旧根。旧长连接存活不能证明轮换成功。Linkerd 自动轮换 workload 证书,但 issuer/trust anchor 是另一项运维责任;Consul CA provider、Kuma MeshIdentity/SPIRE 与 Istio 外部 CA 也各有不同的签发和回退状态。
项目双轨接入要限制写入者和批次
GitOps 仓库可以同时保存 stable 与 candidate 的不可变 values。Kubernetes 对象可以有多个 field manager,但同一语义字段只能有一个明确 owner;多个 manager 仅在字段集合互不重叠且冲突能被审计时并存,不能让两套控制器争写 listener、route、selector 或授权规则。按 namespace、工作负载或节点池建立 cohort,清单中记录负责人、开始条件、停止条件、回滚动作和完成证据。自动化每次只推进一个批次,等待代理版本、配置、身份、请求和容量窗口全部稳定后再继续。
应用团队需要看到的不是 mesh 内部版本号,而是调用契约:目标身份是否变化、明文是否仍允许、路由/重试语义是否变化、观测标签是否改名、故障时由谁回切。若应用依赖 mesh 注入的 Header、TLS origination、重试或 DNS rewrite,必须先建立非 mesh 替代路径,不能等卸载后才发现隐式依赖。
共享环境禁止用集群级 --force、--purge 或批量 CRD 删除缩短窗口。所有破坏性动作应要求二人复核 context、owner 与对象清单;凭证使用短期身份,命令输出脱敏,诊断 artifact 设置保留期限。离职、供应商退出和集群退役时同步撤销 registry、Kubernetes、remote API、CA、ACL 与观测访问。
产品升级机制不能横向套模板
Linkerd 的顺序是 CLI、CRD/control plane、extensions、data plane,最后 prune 旧资源。控制面最多领先数据面一个完整 Linkerd version;edge release 不遵循 semver。linkerd check --proxy 和 linkerd version --proxy 用于找旧代理,不能代替业务请求。Upgrading Linkerd
Linkerd multicluster 从旧 service-mirror Deployment 迁到 GitOps controller 时,以 Lease 表示单写所有权。先确认新 controller 已取得目标 Link 的 Lease,再删除旧 controller;两个 controller 同时存在不等于可以并行写镜像 Service。Upgrading Multi-cluster Components
Kuma single-zone 先 control plane 后 dataplane;multi-zone 先 global CP、再 zone CP、最后 DPP,兼容窗口也约束共享 store。Universal transparent proxy 还要迁移宿主机规则,二进制回退不会自动恢复 iptables。Upgrade Kuma
Consul 先逐个 server 保持 quorum,再升级 client/dataplane、gateway 和 mesh workload。Kubernetes 从 client agent 转 Consul Dataplane 时要保留旧 client,重建所有 mesh workload 后确认新 dataplane 接管,再删除旧 DaemonSet。Upgrade Consul
OpenShift Service Mesh 同时有 OLM Operator channel 与 Istio.spec.version/updateStrategy 两个更新面。从 2.x 迁移时,源端必须先达到官方要求的 2.6.14,再依次进入 3.0、3.1 和目标 3.2 z-stream;过程还涉及 ServiceMeshControlPlane 到 Istio、独立 CNI、add-ons、成员发现和 gateway 所有权变化,不能照抄上游 istioctl 或跳过中间 minor。OpenShift Service Mesh 更新文档
Istio ambient 推荐的组件顺序和 sidecar revision 不同;compatibilityVersion 只保留 release note 明确列出的旧行为,不是完整旧版本模拟,也不能成为永久配置债务。
机制选型时优先问:是否能并存候选控制面、能否按 workload 或节点控制影响、CRD/storage 是否可逆、策略是否有 shadow、证书能否建立重叠信任、供应商是否给出支持窗口和降级说明。没有这些条件的产品,升级预算要包含更大的维护窗口和完整状态恢复。
归档项目触发迁移,不触发历史抹除
CNCF Open Service Mesh 项目页 已将 OSM 标为 Archived,官方 openservicemesh/osm 仓库也已归档并声明不再开发。它不适合作为新生产基线;已有集群则要先盘点 MeshConfig、SMI/OSM CRD、sidecar injection、证书、策略、ingress/egress 和 Prometheus 指标,再迁移等价能力。直接卸载会让旧代理、策略和证书依赖失去 owner。
Service Mesh Interface 官方仓库同样已归档,标示规范停在 v0.6.0。已有 TrafficSplit、TrafficTarget 等对象可以作为迁移输入,但不能继续把 SMI 描述为活跃演进、跨实现一致的标准。迁移时按目标实现重新验证默认值、冲突优先级和身份语义,不做机械 YAML 改名。
Traefik Mesh 的官方仓库没有显示 GitHub archived 标记,也没有明确 EOL/归档公告;只能把维护状态记为未确认。长期没有新 release 是风险信号,不是“官方已停止维护”的证据。现有用户应向维护方或供应商确认支持、漏洞响应和兼容计划,同时准备可演练退出路径;新选型在状态澄清前不应假设其生命周期承诺。
Linkerd Jaeger extension 从 2.19 起不再更新,是另一种更细粒度弃用:core mesh 仍可运行,扩展却应迁往独立 tracing infrastructure。弃用治理必须落到组件级,不能只看顶层项目是否活跃。
回滚先回流量,再回配置,最后决定是否回二进制
canary 出现问题时,第一动作通常是停止新批次并把流量/注入 tag 切回 stable,随后重建受影响 workload;旧 revision 仍在时,这比立刻卸载候选更可控。接着恢复策略和路由到已知版本,验证四种调用方向及身份正反例。只有确认 CRD、stored state、CNI 和证书仍在旧版本兼容范围内,才考虑回退二进制。
回滚证明包括:新创建 Pod 确实注入旧数据面;旧控制面收到代理连接;目标代理加载旧配置 generation;新连接使用预期证书;业务请求、拒绝请求、egress 和跨集群路径恢复;候选 gateway/waypoint 无剩余流量。只看到 Deployment Ready 或 Helm rollback 成功不算完成。
若 CRD/storage、数据库或 CA 已越过不可逆提交点,优先从验证过的状态备份恢复到隔离环境,或继续前滚到修复版本。把未知状态强行交给旧控制器会把可见故障变成静默配置误解。
网格退出按八个状态完成核销
盘点隐式依赖: 注入、CNI、DNS rewrite、identity、mTLS、授权、route、retry、egress、多集群、遥测和应用读取的 mesh Header。建立非网格路径: 恢复 Kubernetes Service/DNS/NetworkPolicy、客户端 TLS/认证、负载均衡、timeout/retry 和批准出口,并做正反请求。分批撤数据面: 移除新 Pod 的注入或 ambient 标签,rollout 后检查 Pod spec、节点路径和真实请求;旧 Pod 仍带代理时批次未完成。
迁移策略语义: 接受 NetworkPolicy 只能替代部分 L3/L4 语义;L7 authorization、outlier detection、TLS origination 和重试必须有明确替代或已审批的能力损失。拆多集群: 停止导出,清零 mirror/global service 流量,unlink 远端,撤销 remote Secret/ServiceAccount 和 trust bundle。
拆观测: 把 SLO/告警迁到应用、gateway、CNI 或独立 OTel pipeline,保留重叠观察窗口,再删除旧指标与 dashboard。删扩展与控制面: 确认无 injected Pod、无代理连接、无 CR 实例后,按 owner 删除 extension、gateway、control plane;CRD 最后处理。核销资产: webhook、RBAC、namespace 标签、Secret/PVC、CNI/BPF/iptables、DNS、LoadBalancer、证书、镜像仓库权限、日志索引和云费用全部归零。
Istio istioctl uninstall --purge 会删除共享 cluster-scoped 资源;Linkerd --force 会绕过 injected Pod 检查;Consul Kubernetes 卸载默认保留 Secret/PVC,而 wipe-data 是破坏性动作;Cilium 的 post-uninstall-cleanup 会删除节点 CNI、BPF、tc、route 与 link 状态。这些命令都不能作为普通清理捷径。先证明业务已经脱网,再由对应 owner 审批执行。
退出完成有五条硬证据:所有 workload spec 无旧代理/注入;旧控制面和 gateway 无连接、无流量;旧身份、remote credential 和 registry 权限已撤销;替代告警能够发现正反实验中的故障;PVC、LB、DNS、日志保留和商业订阅不再产生费用。项目从资产账本移除前还应保存最终配置、恢复期限、审计记录和数据删除证明。
长期治理把生命周期债务变成可见预算
每个网格组件指定 owner、支持发行线、升级频率、最大允许 skew、CRD owner、证书轮换责任、回滚等级和退出替代。每次例行巡检统计代理版本分布、过期 revision、未使用 CRD/策略、即将到期证书、远端凭据、遥测基数和孤儿 LoadBalancer;超过宽限期的旧 cohort 阻止继续发布。
容量成本要同时计算双轨控制面、重复 gateway、额外节点池、双写遥测和重叠保留。canary 不是免费保险:它降低变更影响半径,却增加一段时间内的 CPU、内存、镜像、指标和运维认知成本。若团队无法为双轨证据和清理负责,宁可选择更小批次和明确维护窗口,也不要让候选 revision 永久留在集群。
最后把退出演练纳入年度或重大版本前验证:从一个非关键 namespace 撤掉网格,证明直连身份、授权、超时、出口与告警都能接管,再恢复或完成删除。能安装、能升级、能回滚、能退出四项同时成立,服务网格才是受治理的平台能力,而不是只能继续续费和叠加版本的基础设施依赖。
