TT Lab
开始
学习 学习路径 课程

ICA — Istio 认证助理

新的控制平面已经上线,却没有一个 Pod 迁移过去

在 TT Lab 中继续学习

目标

把 Istio 控制平面以 revision 的方式并排装两套,通过命名空间标签、default tag 把工作负载从旧 revision(stable)迁移到新 revision(canary),然后在保住共享资源的同时,撤掉旧 revision。 安装定制(IstioOperator)也通过渲染结果来确认。

为什么重要

原地升级会一次性替换控制平面。出了问题,整个网格会一起动荡。金丝雀升级是在旁边建立新的控制平面,按命名空间迁移,所以回退容易,但代价是必须准确知道“这个命名空间现在由哪个控制平面注入”。现场常出的事故有三种。

本实验环境中哪些是真的、哪些不是

这个 Pod 所在的集群是 kwok。API 服务器、Webhook 选择和 CRD 是真的,但没有 istiod 进程和容器。 所以用 --dry-run=server 请求创建 Pod 时,API 服务器会按命名空间标签真正选出并调用注入 Webhook, 由于没有 istiod,调用会失败,并把哪个 Webhook 想去哪个 istiod Service 留在错误消息中。 本实验以此作为判定依据。反过来,sidecar 是否真的连上新的 istiod,在这个环境中看不到 (那在 VM 实验“亲眼看网格真的强制了什么”中讲)。两个 revision 都用同一个 istioctl 1.24.2 生成。

步骤

  1. 用 IstioOperator 编写定制安装配置,并渲染清单。
  2. 应用 revision 为 stable 的控制平面。
  3. 确认仅安装 revision 时不会注入 istio-injection=enabled,并用 default tag 解决。
  4. 用同样的定制部署 canary revision,并确认仅靠安装不会迁移任何命名空间。
  5. 把 payments 命名空间迁移到 canary(旧标签陷阱)。
  6. 用 canary 的注入配置做离线注入,查看 sidecar 会被构造成连接哪个控制平面。
  7. 把 default tag 改指向 canary,一次性迁移 legacy 命名空间。
  8. 保留共享资源,只撤掉 stable。

参考

安装之前,先把要装的东西渲染出来

在 /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 是否没有变化。