迁移工作负载
迁移的第 3 阶段。工作负载会逐个命名空间或逐个工作负载地从 Mesh v1 修订版本迁移到 Mesh v2 修订版本。每次迁移都会执行滚动重启,并且在 第 5 阶段停用 Mesh v1 之前,都可以撤销迁移。
开始之前,请完成安装 Mesh v2 控制平面的操作,并保留该页面以及准备迁移页面设置的 shell 变量。
一次迁移一个命名空间
本页面中的所有操作都针对 MESH_NAMESPACE。为一个命名空间完整执行本页面中的所有操作 — 生成其补丁、切换其修订版本标签、迁移其工作负载、检查结果 — 然后再将 MESH_NAMESPACE 设置为下一个命名空间并重新开始。
注入方式的决定因素
命名空间带有标签 istio.io/rev,其值决定为其中的 pod 注入 sidecar 的控制平面。Mesh v1 本身是一个采用修订版本的部署(1-22),因此将标签从 ${V1_REVISION} 更改为 ${V2_REVISION},即可将命名空间迁移到 Mesh v2。正在运行的 pod 不受影响;此更改仅适用于之后创建的 pod。
这里有两种 Mesh v1 行为需要注意:
- Mesh v1 控制器不会更改已有值的修订版本标签,但如果标签缺失,它们会将标签重新添加回来。Mesh v1 运行期间,切勿从命名空间中移除该标签。
- pod 级别的
istio.io/rev 标签不会覆盖命名空间标签。当命名空间带有修订版本标签时,该修订版本会为其中的每个 pod 注入 sidecar。
使用 autoInject: disabled — 第 0 阶段中记录的 Mesh v1 默认值 — 时,只有带有 sidecar.istio.io/inject: "true" 的 pod 才会被注入。Mesh v1 会将该注释添加到每个带有 sidecar 的微服务的 pod 中,同时添加该微服务的 sidecar 设置。Mesh v2 没有执行此操作的组件,下一节将介绍相关内容。
保持 Mesh v1 控制器运行,直到第 5 阶段
在第 5 阶段之前,不要缩减 asm-controller 的规模。其 pod 变更 webhook 使用 failurePolicy: Fail,因此在控制器停止运行期间,API server 会拒绝所有位于标记为 cpaas.io/serviceMesh=enabled 的命名空间中的新 pod,包括本页面滚动更新所创建的 pod。
持久化 Mesh v1 pod 设置
Mesh v1 会在每个 MicroService 创建 pod 时,将其设置添加到 pod 中:是否注入 sidecar、代理资源、代理日志级别,以及不参与捕获的端口或地址范围。这些设置不在工作负载模板中,因此在第 5 阶段移除 Mesh v1 webhook 后会立即消失 — 此后重启的 pod 将不再包含这些设置,并且会在完全没有 sidecar 的情况下运行 autoInject: disabled。
迁移工作负载时,将这些设置写入工作负载模板。两项更改会通过同一次滚动更新生效。
生成补丁
从 ${BACKUP_DIR}/v1-mesh-namespaces.txt 的某一行中获取命名空间;在第 0 阶段中,该命名空间记录为 <namespace> injected=<count> pods=<count>。处理 injected 不为零的行 — injected=0 所在的行带有修订版本标签,但没有网格工作负载,将留待第 5 阶段处理。将 <namespace> 替换为你自己的值。
export MESH_NAMESPACE=<namespace>
PATCH_DIR="${BACKUP_DIR}/metadata-patches/${MESH_NAMESPACE}"
mkdir -p "${PATCH_DIR}"
kubectl -n "${MESH_NAMESPACE}" get deploy -o json | jq -c '.items[]' | while read -r deploy; do
name=$(jq -r '.metadata.name' <<<"${deploy}")
if jq -e '((.metadata.labels["operator.istio.io/component"] // "") | test("Gateways$"))
or ((.spec.template.metadata.annotations["inject.istio.io/templates"] // "") | test("asm-gateway"))' \
<<<"${deploy}" >/dev/null; then
echo "${name}: gateway, skipped"
continue
fi
selector=$(jq -r '.spec.selector.matchLabels // {} | to_entries | map("\(.key)=\(.value)") | join(",")' <<<"${deploy}")
[ -z "${selector}" ] && continue
pod=$(kubectl -n "${MESH_NAMESPACE}" get pod -l "${selector}" -o json \
| jq -c '[.items[] | select(.metadata.annotations["sidecar.istio.io/status"] != null)][0] // empty')
[ -z "${pod}" ] && continue
jq -n --argjson d "${deploy}" --argjson p "${pod}" '
($d.spec.template.metadata.labels // {}) as $tl
| ($d.spec.template.metadata.annotations // {}) as $ta
| ($p.metadata.labels // {}) as $pl
| {spec: {template: {metadata: {
labels: (if ($tl.app == null and $pl.app != null) then {app: $pl.app} else {} end),
annotations: (($p.metadata.annotations // {}) | with_entries(select(
(.key | test("^(sidecar\\.istio\\.io/(inject|proxyCPU|proxyMemory|proxyCPULimit|proxyMemoryLimit|logLevel)|traffic\\.sidecar\\.istio\\.io/(excludeInboundPorts|excludeOutboundPorts|excludeOutboundIPRanges))$"))
and ($ta[.key] == null) and ($tl[.key] == null))))
}}}}' > "${PATCH_DIR}/${name}.json"
echo "${name}: $(jq -c '.spec.template.metadata' "${PATCH_DIR}/${name}.json")"
done
示例输出
ingress-1: gateway, skipped
frontend: {"labels":{},"annotations":{"sidecar.istio.io/inject":"true","sidecar.istio.io/logLevel":"warning","sidecar.istio.io/proxyCPU":"100m","sidecar.istio.io/proxyCPULimit":"500m","sidecar.istio.io/proxyMemory":"128Mi","sidecar.istio.io/proxyMemoryLimit":"512Mi"}}
backend: {"labels":{},"annotations":{"sidecar.istio.io/inject":"true","sidecar.istio.io/proxyCPU":"50m","traffic.sidecar.istio.io/excludeOutboundPorts":"12345"}}
该命令会将运行中的 pod 与其模板进行比较,并且只写入缺失的内容。没有 sidecar 的工作负载不会生成补丁,会保持不变。应用每个补丁之前,请先进行检查。
sidecar.istio.io/inject 会以注释形式写入,这与 Mesh v1 使用的形式相同。如果将其作为标签写入,则会更改 Mesh v1 控制器跟踪的 pod 模板哈希,随后控制器会再次重启该工作负载。
- 不要将
msname 和 asm.cpaas.io/msname 标签添加到模板中。同时带有 app、msname 和 asm.cpaas.io/msname 的模板会被 Mesh v1 控制器视为旧版工作负载,控制器随后会移除你刚写入的注释。
- Ingress 和 egress gateway 会被跳过,因此不会生成可能导致其重启的补丁。它们的 pod 带有与应用 pod 相同的 Mesh v1 注释,因此必须明确排除它们。它们不得在此阶段重启,参见迁移命名空间中的警告;它们将在第 4 阶段迁移。
本页面中的每条命令都作用于 deployment。Mesh v1 微服务始终是 Deployment,因此网格命名空间中不会包含其他类型的注入工作负载。
检查访问日志配置
Mesh v1 会为每个微服务使用一个 Telemetry 资源启用访问日志记录,这些资源通过 Mesh v1 webhook 添加的 msname 标签选择 pod。在第 5 阶段之后,该标签会消失,这些资源将不再匹配任何对象。
kubectl -n "${MESH_NAMESPACE}" get telemetries.telemetry.istio.io -o json | jq -r '.items[]
| "\(.metadata.name) owner=\(.metadata.ownerReferences[0].kind // "-") selector=\(.spec.selector.matchLabels // {} | tojson)"'
使用能够匹配工作负载模板中现有标签的资源重新创建配置。如果命名空间中的每个工作负载都启用了访问日志记录,则一个作用于整个命名空间的资源即可满足需求:
cat <<EOF | kubectl apply -f -
apiVersion: telemetry.istio.io/v1
kind: Telemetry
metadata:
name: access-logging
namespace: ${MESH_NAMESPACE}
spec:
accessLogging:
- providers:
- name: envoy
EOF
如果某个命名空间中只有部分工作负载启用了访问日志记录,请添加一个匹配这些工作负载标签的 selector,例如 app。
迁移命名空间
将命名空间切换到 Mesh v2 修订版本
kubectl label ns "${MESH_NAMESPACE}" istio.io/rev="${V2_REVISION}" --overwrite
正在运行的 pod 会保留其 Mesh v1 proxy。此后创建的 pod 才会由 Mesh v2 注入。
同时包含网关的命名空间
从命名空间指向 Mesh v2 修订版本的那一刻起,便无法再创建 Mesh v1 gateway pod,参见切换命名空间标签是单向操作。切换后重启的 gateway 会缩减到零个副本,而其 Gateway 和 VirtualService 资源仍会显示为正常。
已在运行的 gateway pod 会继续提供流量,因此在此阶段切勿重启 ingress 或 egress gateway——本页面中的命令会因此跳过 gateway deployment。请在其命名空间中的工作负载迁移完成后,直接在第 4 阶段迁移 gateway 本身;上面的标签切换就是该单向操作。
迁移工作负载
应用每个工作负载的补丁。该补丁还会设置重启注解,因此一次 rollout 即可同时持久化设置并将工作负载迁移到 Mesh v2。将 <workload> 替换为你自己的值。
export WORKLOAD=<workload>
jq -c --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = $now' \
"${BACKUP_DIR}/metadata-patches/${MESH_NAMESPACE}/${WORKLOAD}.json" > /tmp/workload-patch.json
kubectl -n "${MESH_NAMESPACE}" patch deploy "${WORKLOAD}" --type=merge -p "$(cat /tmp/workload-patch.json)"
kubectl -n "${MESH_NAMESPACE}" rollout status deploy "${WORKLOAD}" --timeout=300s
若要迁移命名空间中的每个工作负载,请循环应用这些补丁:
for f in "${BACKUP_DIR}/metadata-patches/${MESH_NAMESPACE}"/*.json; do
[ -e "${f}" ] || { echo "no patches in ${BACKUP_DIR}/metadata-patches/${MESH_NAMESPACE}"; break; }
workload=$(basename "${f}" .json)
jq -c --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = $now' "${f}" > /tmp/workload-patch.json
kubectl -n "${MESH_NAMESPACE}" patch deploy "${workload}" --type=merge -p "$(cat /tmp/workload-patch.json)"
kubectl -n "${MESH_NAMESPACE}" rollout status deploy "${workload}" --timeout=300s
done
检查哪个控制平面注入了 pod
kubectl -n "${MESH_NAMESPACE}" get pod -o json | jq -r '.items[]
| select(.metadata.annotations["sidecar.istio.io/status"] != null)
| "\(.metadata.name) \(.metadata.annotations["sidecar.istio.io/status"] | fromjson | .revision)"'
istioctl proxy-status --revision "${V2_REVISION}"
示例输出
NAME CLUSTER ISTIOD VERSION SUBSCRIBED TYPES
frontend-567fbdc959-6g8v4.app-all cluster-1 istiod-default-v1-30-4-6b9c94c84b-57qb2 1.30.4-asm-r0 5 (CDS,LDS,EDS,RDS,NDS)
每个已迁移的 pod 都会在第一条命令中报告 ${V2_REVISION},并会出现在 istioctl 列表中,其中 ISTIOD 列的值为 istiod-${V2_REVISION}。未出现在列表中的 pod 未连接到 Mesh v2 控制平面。istioctl proxy-status --revision "${V2_REVISION}" --verbosity=1 会将 SUBSCRIBED TYPES 列替换为每种配置类型各一列,每列报告 SYNCED 或 STALE。
NOTE
istioctl 需要 --revision,因为 Mesh v2 使用 RevisionBased 策略,且没有不带后缀的控制平面。如果没有该参数,istioctl 会报告没有 Istio pod 正在运行。
迁移单个工作负载
一个命名空间可以同时包含 Mesh 工作负载和明确不加入 Mesh 的工作负载。在保留 Mesh v1 注入策略的情况下,逐个迁移工作负载是安全的:
- 按上述步骤切换一次命名空间标签。不会发生重启。
- 应用第一个工作负载的补丁并进行验证。对下一个工作负载重复此操作。
尚未迁移的工作负载会继续使用其 Mesh v1 proxy 运行,而没有 sidecar 的工作负载仍不会添加 sidecar,因为注入策略要求使用 sidecar.istio.io/inject 注释。
NOTE
如果尚未迁移的工作负载因其他原因而重启 — 例如扩容、驱逐或节点排空 — 则其新 pod 会由 Mesh v2 而不是 Mesh v1 注入。这就是目标状态,并且两代版本可以互操作,但这意味着迁移顺序仅对由你控制的重启有保证。
验证两个控制平面之间的流量
迁移期间,部分命名空间使用 Mesh v1,其他命名空间使用 Mesh v2。每个命名空间迁移完成后,验证两个方向的调用仍然有效:
kubectl -n "${MESH_NAMESPACE}" exec "deploy/${WORKLOAD}" -c "${APP_CONTAINER}" -- \
curl -s -o /dev/null -w '%{http_code}\n' --max-time 5 "http://${PEER_SERVICE}.${PEER_NAMESPACE}:${PEER_PORT}/"
两个方向都会返回应用通常返回的状态代码。由于两个控制平面都使用同一个根证书签发证书,双向 TLS 仍可正常工作:
POD=$(kubectl -n "${MESH_NAMESPACE}" get pod -l "app=${WORKLOAD}" -o jsonpath='{.items[0].metadata.name}')
istioctl proxy-config secret "${POD}" -n "${MESH_NAMESPACE}" -o json \
| jq -r '.dynamicActiveSecrets[] | select(.name=="ROOTCA") | .secret.validationContext.trustedCa.inlineBytes' \
| base64 -d | openssl x509 -noout -subject
主题是 Mesh v1 Mesh 的根证书颁发机构,保持不变。
发布期间的预期情况
- 具有至少两个副本和
PodDisruptionBudget 的工作负载迁移时不会丢失请求。
- 只有一个副本的工作负载会在滚动重启的几秒内不可用。这是重启本身造成的,而不是迁移造成的。
- proxy 会作为原生 sidecar 注入:一个带有
restartPolicy: Always 的 init 容器。kubectl get pod 报告的容器数量会相应变化,并且应用容器仅会在 proxy 就绪后启动。
没有 Mesh 工作负载的命名空间
带有 revision 标签但没有注入 pod 的命名空间 — 例如仅使用 OpenTelemetry 自动检测的命名空间 — 无需在此进行任何操作。不要切换其标签。停用 Mesh v1 会移除遗留标签,而迁移可观测性堆栈 会迁移其检测配置。
验证
- 每个已迁移的 pod 都会在其
sidecar.istio.io/status 注解中报告 ${V2_REVISION}。
- 除单副本工作负载的重启窗口外,探针不会报告任何故障。
- Kiali 中的流量图显示与迁移前相同的服务和边。
- 已迁移工作负载仍会生成访问日志。
继续阅读迁移网关。