新的控制平面已经上线,却没有一个 Pod 迁移过去
目标
把 Istio 控制平面以 revision 的方式并排装两套,通过命名空间标签、default tag 把工作负载从旧 revision(stable)迁移到新 revision(canary),然后在保住共享资源的同时,撤掉旧 revision。
安装定制(IstioOperator)也通过渲染结果来确认。
为什么重要
原地升级会一次性替换控制平面。出了问题,整个网格会一起动荡。金丝雀升级是在旁边建立新的控制平面,按命名空间迁移,所以回退容易,但代价是必须准确知道“这个命名空间现在由哪个控制平面注入”。现场常出的事故有三种。
- 只安装了 revision,结果
istio-injection=enabled命名空间没有挂上 sidecar(没有 default tag)。 - 加了
istio.io/rev=canary,但旧标签还在,所以仍由 stable 注入(istio-injection 优先)。 - 把旧 revision 的清单整个删掉,连共享 CRD 也被删掉,VirtualService 全部消失。
本实验环境中哪些是真的、哪些不是
这个 Pod 所在的集群是 kwok。API 服务器、Webhook 选择和 CRD 是真的,但没有 istiod 进程和容器。
所以用 --dry-run=server 请求创建 Pod 时,API 服务器会按命名空间标签真正选出并调用注入 Webhook,
由于没有 istiod,调用会失败,并把哪个 Webhook 想去哪个 istiod Service 留在错误消息中。
本实验以此作为判定依据。反过来,sidecar 是否真的连上新的 istiod,在这个环境中看不到
(那在 VM 实验“亲眼看网格真的强制了什么”中讲)。两个 revision 都用同一个 istioctl 1.24.2 生成。
步骤
- 用 IstioOperator 编写定制安装配置,并渲染清单。
- 应用 revision 为 stable 的控制平面。
- 确认仅安装 revision 时不会注入
istio-injection=enabled,并用 default tag 解决。 - 用同样的定制部署 canary revision,并确认仅靠安装不会迁移任何命名空间。
- 把 payments 命名空间迁移到 canary(旧标签陷阱)。
- 用 canary 的注入配置做离线注入,查看 sidecar 会被构造成连接哪个控制平面。
- 把 default tag 改指向 canary,一次性迁移 legacy 命名空间。
- 保留共享资源,只撤掉 stable。
参考
- 工作文件放在
/root/ica-upgrade/。注入 Webhook 的调用有 10 秒限制,所以每次 dry-run 大约要 10 秒。 - 读取 Webhook 配置:
kubectl get mutatingwebhookconfiguration <이름> -o yaml(占位符为名称)的namespaceSelector、clientConfig.service。 - tag 列表:
istioctl tag list。 - 官方文档: Canary Upgrades、 Customizing the installation configuration、 Installing the Sidecar、 istioctl 命令参考、 Dynamic Admission Control
安装之前,先把要装的东西渲染出来
在 /root/ica-upgrade/stable.yaml 中写入 IstioOperator:profile: minimal、revision: stable、meshConfig.accessLogFile: /dev/stdout,istiod(pilot)容器的请求量 cpu: 250m、memory: 512Mi。然后把用 istioctl manifest generate -f 渲染的结果保存到 /root/ica-upgrade/stable-manifest.yaml。暂时不要应用到集群。
IstioOperator 的 spec.revision 会成为附加在控制平面对象名称后面的后缀。网格整体配置放在 spec.meshConfig,组件的 Kubernetes 配置放在 spec.components.<구성요소>.k8s(占位符为组件名称)之下。请用 yq 在渲染结果中找到 Deployment 名称和请求量、ConfigMap 的 mesh 配置,确认想要的值是否已放进去。
部署 revision 为 stable 的控制平面
创建命名空间 istio-system,并把 stable-manifest.yaml 应用到集群。必须生成 Istio CRD、istiod-stable Deployment 和 Service,以及 istio-sidecar-injector-stable Webhook 配置。
这个集群是 kwok,所以 istiod Pod 看起来是 Running,但实际上没有进程。尽管如此,API 服务器、Webhook 配置和 CRD 都是真的。应用之后,请在 kubectl get mutatingwebhookconfiguration -o yaml 中读取 Webhook 调用哪个 Service(clientConfig.service)、选择哪些命名空间标签(namespaceSelector)——这是下一步的关键。
明明是 istio-injection=enabled,sidecar 却没有挂上
给命名空间 shop 添加标签 istio.io/rev=stable,给命名空间 legacy 添加标签 istio-injection=enabled。用 kubectl -n <ns> run probe --image=registry.example.invalid/app:1 --restart=Never --dry-run=server(占位符为命名空间)确认创建 Pod 会走向哪个注入 Webhook,并保存输出(包括错误)——legacy 的结果保存到 probe-legacy-before.txt。然后把 istioctl tag generate default --revision stable 的输出保存为 tag-default.yaml 并应用,再把 legacy 保存到 probe-legacy-after.txt,把 shop 保存到 probe-shop.txt(都在 /root/ica-upgrade/ 之下)。
带 revision 名称的安装,其注入 Webhook 只选择 istio.io/rev=<리비전>(占位符为 revision 名称)标签。旧方式的 istio-injection=enabled 表示默认(default)revision,而只装 revision 时,没有承担这个角色的 Webhook。default tag 会补上这个位置。dry-run=server 请求也会经过变更 Webhook,所以在没有 istiod 的这个集群中,如果 Webhook 被调用,失败消息里会出现 Webhook 名称和 istiod Service 地址;如果没有被调用,就只会显示 created。Webhook 调用有 10 秒限制,所以要稍等一会儿。
新的控制平面装好了,却没有任何命名空间迁过去
在 /root/ica-upgrade/canary.yaml 中,用与 stable 相同的定制(accessLogFile、请求量)写一个 revision: canary 的 IstioOperator,并应用渲染出的 canary-manifest.yaml。创建命名空间 payments(标签 istio-injection=enabled),再创建 VirtualService payments/api(hosts [api],destination host api)作为样本,用来查看升级过程中已有配置能否保留下来,并把它的 uid 写入 /root/ica-upgrade/sentinel-uid.txt。最后对 shop 重新做 dry-run,保存到 probe-shop-after-canary.txt——仅仅安装新的 revision,shop 不应该迁移过去。
两个 revision 因名称后缀不同而并存。命名空间走向哪一边,不取决于安装,而取决于标签和 tag。CRD 与 revision 无关,在集群中只有一份,是共享资源,这一点也请通过比较两份清单来确认。现在 default tag 的验证 Webhook 会在每次写入 Istio 资源时被调用(失败时忽略),所以创建 VirtualService 要花 10 秒左右。
标签改了,却仍然走向旧的控制平面
把命名空间 payments 迁移到 canary。最终 payments 中必须有 istio.io/rev=canary 并且没有 istio-injection 标签,dry-run 结果必须走向 canary 的 istiod。把迁移之后的 dry-run 输出保存到 /root/ica-upgrade/probe-payments.txt。
只添加 istio.io/rev=canary,然后做一次 dry-run。两个标签同时存在时哪个 Webhook 胜出——也就是 revision Webhook 的 namespaceSelector 中对 istio-injection 有什么条件——就是答案。官方金丝雀升级文档也是出于同样的原因,让你删除旧标签。在真实集群中,之后还要重启 Pod,新的 sidecar 才会被注入。
“要重启才会迁移”这句话写在哪里
在 /root/ica-upgrade/api.yaml 中写 Deployment api(命名空间 payments,副本 1,容器 api 镜像 nginx:1.27),并把用 canary revision 的注入配置执行 istioctl kube-inject 的结果保存到 /root/ica-upgrade/api-injected.yaml。注入配置要从集群的 ConfigMap istio-sidecar-injector-canary(键 config、values)和 istio-canary(键 mesh)中取出,以文件形式传入。结果不要应用到集群。
kube-inject 默认会连接 istiod 获取配置,而这个集群中没有 istiod 进程。所以把 --injectConfigFile、--valuesFile、--meshConfigFile 三个文件都提供给它,它就会离线渲染。请在结果中找出 istio-proxy 被构造成连接哪个地址(discoveryAddress)的控制平面。这个值是在创建 Pod 时写死的,所以已经运行的 Pod,即使修改了命名空间标签,仍然连着旧的 istiod。
把还在用旧标签的团队也一次迁走
把 default tag 改为指向 canary(保存为 /root/ica-upgrade/tag-default-canary.yaml 并应用)。在不动命名空间 legacy 的标签的前提下,legacy 的 dry-run 必须走向 canary 的 istiod。把输出保存到 /root/ica-upgrade/probe-legacy-canary.txt。
tag 是命名空间标签与 revision 之间的一个名称。与其在几十个命名空间里逐个改标签,不如只改 tag 所指的 revision,使用这个名称的地方就会一起迁移。重新创建已有的 tag 时,请阅读 istioctl 要求什么的错误消息。
删掉旧的控制平面,差点连配置都没了
撤掉 stable。先把仍在使用 stable 的命名空间(shop)迁移到 canary,再删除只存在于 stable 清单中的对象。两个 revision 共用的对象(Istio CRD、ServiceAccount istio-reader-service-account)不能删除——删掉 CRD,这个种类的所有资源(包括 VirtualService payments/api)都会一起消失。把已删除对象的列表(每行一个 종류/이름,占位符为种类与名称)留在 /root/ica-upgrade/retired.txt 中。
从两份清单中各提取 종류/이름(占位符为种类与名称)列表再比较,就能分出只在 stable 中的对象和共用的对象(用 yq 提取,用 comm 比较)。本实验是用 kubectl 应用的,所以用同一份清单来回退。官方文档中的 istioctl uninstall --revision 是为用 istioctl 安装的集群准备的命令,但在这个集群中实测时,它找不到要删除的对象。删除之后,请确认 shop 的 dry-run 是否走向 canary,以及样本 VirtualService 的 uid 是否没有变化。