Istio
Istio 在服务之间增加可配置的数据面,用来处理连接加密、服务身份、访问策略、路由和流量观测。应用继续调用服务地址,istiod 将服务发现与策略转换成代理配置;实际请求由 Envoy 或 ztunnel 处理,正常业务流量不经过 istiod。
同一个控制面支持两种部署方式。Sidecar 把 Envoy 放进每个应用 Pod;Ambient 将四层处理放到每个节点的 ztunnel,需要 HTTP 路由或请求级授权的目标再配置 waypoint。模式选择会改变资源分布、策略执行位置和升级影响范围。
组件与两种数据路径
控制面 istiod
├─ 读取 Service、EndpointSlice、路由和安全策略
├─ 向数据面发送配置
└─ 参与工作负载证书签发与信任分发
Sidecar
client → 源 Pod Envoy → 目标 Pod Envoy → 服务
Ambient
client → 源 ztunnel → 目标 ztunnel → 服务
└→ 目标 waypoint → 目标 ztunnel → 服务每个代理处理什么
Envoy 可以理解 HTTP、HTTP/2、gRPC,也可以转发原始 TCP。HTTP 请求能按路径或请求头选择后端;把同一端口声明为 TCP 后,就不能期待这些七层规则生效。应用自行建立的 HTTPS 通常表现为代理中的 TLS/TCP 流,除非在相应代理终止 TLS,否则看不到加密后的路径和请求头。UDP 不经过这里的 TCP 代理链。
ztunnel 处理工作负载身份、mTLS、四层授权和连接观测。它转发字节流,不解释 HTTP 方法、路径和状态码。waypoint 是目标侧 Envoy,按 Service 或工作负载附着,提供七层路由与策略。创建 waypoint 后还要绑定目标;请求直接访问 Pod IP 与访问 Service 是两种原始目标,附着条件不同。waypoint 配置列出了这些选择规则。
Sidecar 的资源随应用副本增加,代理升级通常要重建应用 Pod;Ambient 的节点代理由同节点工作负载共享,重启可能影响多个服务的连接。waypoint 又把若干目标的 HTTP 处理集中起来,需要独立安排副本与容量。少一个 Pod 内容器,并不意味着通信没有代理或资源成本。
配置、身份和业务逻辑的分工
Kubernetes Service 确定服务名、端口和后端集合。路由规则在这个集合上选择版本、匹配请求,连接策略再决定负载均衡、连接池和 TLS。身份策略约束谁可以访问;业务中的商品归属、订单金额和幂等处理仍由应用决定。网格重试会增加上游尝试次数,无法替应用判断一次写入是否可以安全重放。
Sidecar 中,istio-agent 管理代理启动和证书申请,Envoy 通过 xDS 接收监听器、路由、上游集群和端点等配置,通过本地 SDS 接收身份材料。短时失去控制面,代理可以继续使用已有配置;端点变更、策略更新和证书续期仍需要相应控制链恢复。诊断时应分别查看配置是否接受、代理是否更新,以及请求实际结果。
何时采用网格
多语言服务需要一致的连接加密、服务身份和请求路由时,集中维护网格策略可以减少各客户端库的差异。灰度规则随业务版本频繁变化、需要在同一处观察跨服务流量,也适合先选一组服务验证网格收益。
代价是新增控制面、节点组件和数据面升级任务。少量服务已经由应用与入口满足这些需求时,应先比较代理资源、平台维护和故障定位成本,再决定是否引入。需要逐 Pod 定制代理或依赖既有 Envoy 扩展时优先核对 Sidecar;希望分离应用发布与四层代理维护时可以评估 Ambient,同时验证共享节点代理及 waypoint 的容量和策略覆盖。
安装 Sidecar 并完成第一次服务调用
Linux 工具与专用集群
使用 Linux x86_64、Bash、curl、jq、Python 3.10 或以上、tar、sha256sum,以及已能运行容器的 Docker。kind 和 kubectl 按 kind 主文准备。安装者需要本实验集群的管理员权限:Istio 会创建 CRD、Webhook、RBAC 和节点级 CNI 组件,这些变化不能由一个 namespace 隔离。
选择 Istio 1.31.0 与 Kubernetes 1.35.5,二者在官方支持组合内。正式部署还应核对目标补丁的安全修复、主 CNI 与平台要求;不要把受测试但不受支持的 Kubernetes 版本当作生产支持承诺。
下载 Istio 实验附件并解压到新目录;工具包和安装输入保留在此目录,kubeconfig 放在仅自己可读的 private 目录。企业代理可用于官方发行包下载,内网环境应预先镜像相同发行包与容器镜像,并保留摘要;不通过关闭 TLS 校验解决下载问题。发行包说明介绍 CLI、Chart 和样例文件的组成。
set -euo pipefail
umask 077
test "$(uname -m)" = x86_64
export MESH_ROOT="$PWD"
mkdir private
VERSION=1.31.0
ARCHIVE="istio-${VERSION}-linux-amd64.tar.gz"
BASE="https://github.com/istio/istio/releases/download/$VERSION"
curl -q --fail-with-body --location "$BASE/$ARCHIVE" -o "$ARCHIVE"
curl -q --fail-with-body --location "$BASE/$ARCHIVE.sha256" -o "$ARCHIVE.sha256"
sha256sum -c "$ARCHIVE.sha256"
tar -xzf "$ARCHIVE"
ISTIOCTL="$MESH_ROOT/istio-$VERSION/bin/istioctl"
KCFG="$MESH_ROOT/private/mesh.kubeconfig"
"$ISTIOCTL" version --remote=false
kind create cluster --name istio-lab --config kind.yaml --kubeconfig "$KCFG" \
--image kindest/node:v1.35.5@sha256:ce977ae6d65918d0b58a5f8b5e940429c2ce42fa3a5619ec2bbc60b949c0ac95 --wait 120s
kubectl --kubeconfig "$KCFG" get nodes校验应通过,CLI 显示 1.31.0,两个节点均为 Ready。kind 节点是 Docker 容器,本机双节点便于观察节点组件分布,不等于跨物理主机高可用。Docker daemon 能创建节点容器的宿主账号拥有较高权限;应用容器使用固定非 root 身份,CNI 则必须访问节点网络和相关宿主目录,三者权限不同。
先确认服务本身可以运行
附件生成三个 namespace 的配置。mesh-sidecar 和 mesh-ambient 各有两个返回 v1/v2 的 catalog 服务、正常 client 和错误身份 outsider;mesh-plain 保留未纳管客户端。应用使用固定摘要的 Nginx 非 root 镜像,只读挂载配置,/version 返回版本,/fail 返回应用自己的 503,/healthz 用于就绪探针。
python3 prepare.py --output generated
for namespace in mesh-sidecar mesh-ambient mesh-plain; do
kubectl --kubeconfig "$KCFG" create -f "generated/$namespace.json"
kubectl --kubeconfig "$KCFG" rollout status deployment -n "$namespace" --timeout=180s
done
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-request-id: baseline-v1' http://catalog-v1:8080/version
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-request-id: baseline-v2' http://catalog-v2:8080/version两次响应分别为 v1、v2。这里的 curl 在 client 容器内运行,Service 名由集群 DNS 解析;宿主机不需要解析这些名字。-q 禁用 curlrc,--noproxy '*' 固定为集群直连,避免企业代理改变实验路径。--fail-with-body 使 HTTP 400 及以上的响应退出非零,而连接错误仍按传输失败处理。
镜像未拉取时先检查 Pod 事件;若程序启动失败,查看 app 容器日志。服务地址失败但直接版本 Service 成功时,再检查 selector 与 EndpointSlice。基线尚未成立,不安装网格掩盖原有应用或网络问题。
CNI 与 revision 的安装配置
install-sidecar.yaml 只启用 istiod 与 Istio CNI,没有额外安装外部入口。minimal 用于这组隔离实验,生产的副本、资源、入口和可用性配置需要另行选择。
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
hub: docker.io/istio
profile: minimal
revision: stable-a
components:
cni:
enabled: true
values:
pilot:
cni:
enabled: true
meshConfig:
accessLogFile: /dev/stdout
defaultConfig:
holdApplicationUntilProxyStarts: true此文件是 istioctl 的安装输入,不需要再部署旧的 Istio Operator 控制器。revision 标识这套控制面及注入模板;CNI 是节点共享组件,不按应用 revision 分出多套。
Sidecar 的默认安装可以用 istio-init 在业务 Pod 中设置网络重定向;选择 CNI 后,这一步由节点上的链式插件完成。values.pilot.cni.enabled 让注入模板采用 CNI 路径。新 Pod 中的 istio-validation 检查重定向是否建立,避免 CNI 尚未就绪时直接放行业务。节点插件不替代 kind 原有 CNI;两者分别负责基础网络和网格捕获。Istio CNI 说明给出了权限和生命周期关系。
"$ISTIOCTL" --kubeconfig "$KCFG" x precheck
"$ISTIOCTL" --kubeconfig "$KCFG" install -y -f install-sidecar.yaml
kubectl --kubeconfig "$KCFG" rollout status daemonset/istio-cni-node -n istio-system --timeout=180s
kubectl --kubeconfig "$KCFG" label namespace mesh-sidecar istio.io/rev=stable-a
kubectl --kubeconfig "$KCFG" rollout restart deployment -n mesh-sidecar
kubectl --kubeconfig "$KCFG" rollout status deployment -n mesh-sidecar --timeout=180s
kubectl --kubeconfig "$KCFG" get pods -n mesh-sidecar
"$ISTIOCTL" --kubeconfig "$KCFG" proxy-statusnamespace 标签作用于新 Pod 的注入,现有 Pod 需要滚动重建。客户端与服务会增加 istio-proxy,CNI 路径中有 istio-validation,没有负责重定向的 istio-init。代理应连接 stable-a;单看标签无法确认已经完成注入。
这组版本组合使用 Kubernetes 原生 sidecar:istio-proxy 位于 spec.initContainers,并设置 restartPolicy: Always,在应用运行期间持续工作。普通 init container 完成后退出,二者生命周期不同。只搜索 spec.containers 会误判注入失败;检查时合并普通容器和 init 容器:
kubectl --kubeconfig "$KCFG" get pods -n mesh-sidecar -o json |
jq '.items[] | {pod:.metadata.name, containers:([.spec.containers[], .spec.initContainers[]?] | map({name,image,restartPolicy}))}'工作负载的 sidecar.istio.io/nativeSidecar 注解可以覆盖控制面的选择;使用其他版本或注入配置时,应按实际 Pod spec 判断。注解取值见 Istio 注解参考,原生 sidecar 的启动与退出顺序见 Kubernetes sidecar 容器。
请求头选择版本
总 Service catalog 选择两个版本,版本 Service 则各自只选择一个版本。Sidecar 路由可以在总 Service 上定义 subset:DestinationRule 将 v1/v2 名称映射到 Pod 标签,VirtualService 决定哪个请求选择哪个 subset。
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: catalog
namespace: mesh-sidecar
spec:
host: catalog.mesh-sidecar.svc.cluster.local
subsets:
- name: v1
labels: {version: v1}
- name: v2
labels: {version: v2}
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: catalog
namespace: mesh-sidecar
spec:
hosts: [catalog]
http:
- name: candidate
match:
- headers:
x-catalog-version: {exact: v2}
route:
- destination: {host: catalog, subset: v2}
timeout: 2s
- name: stable
route:
- destination: {host: catalog, subset: v1}
timeout: 2s保存的文件是 route-sidecar.yaml。HTTP 规则从上向下匹配:带特定请求头时选 v2,其余请求落到 v1。subset 名字本身不会创建 Pod;它必须能在真实服务端点中匹配到对应标签。
kubectl --kubeconfig "$KCFG" apply -f route-sidecar.yaml
"$ISTIOCTL" --kubeconfig "$KCFG" proxy-config routes deployment/client -n mesh-sidecar
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-request-id: route-default' http://catalog:8080/version
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-catalog-version: v2' -H 'x-request-id: route-candidate' http://catalog:8080/version普通请求返回 v1,带请求头返回 v2。配置提交到 API Server 后,istiod 还要处理并下发,代理再确认更新;短时仍返回旧结果时先检查源代理的 route 与 endpoint,不连续改配置制造更多变化。常用匹配、流量权重及目的地字段可查 VirtualService和 DestinationRule。
有路由,却没有后端
只改变 v2 subset 的选择标签,让路由仍指向 v2,但没有可用端点:
kubectl --kubeconfig "$KCFG" patch destinationrule catalog -n mesh-sidecar \
--type=json -p '[{"op":"replace","path":"/spec/subsets/1/labels/version","value":"missing"}]'
"$ISTIOCTL" --kubeconfig "$KCFG" proxy-config endpoints deployment/client -n mesh-sidecar
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --silent --show-error --max-time 5 -w '\n%{http_code}\n' \
-H 'x-catalog-version: v2' -H 'x-request-id: route-missing' http://catalog:8080/version
kubectl --kubeconfig "$KCFG" logs -n mesh-sidecar deployment/client -c istio-proxy --tail=30等待对应 v2 endpoint 消失后,请求应返回 503。这个负例不使用 --fail-with-body,需要分别检查 curl 传输成功与 HTTP 状态;连接超时不是预期的 503。源代理日志中的 UH / no_healthy_upstream 指向没有健康后端,服务日志中不会出现这次请求。它与 /fail 主动返回的应用 503 处在不同层。
恢复 route-sidecar.yaml,确认 v2 endpoint 回来,再用同样的请求头读到 v2。失败后先恢复这个单字段差异,不重装控制面,不删除应用。观察命令中的 cluster 是 Envoy 的上游集合名,与 Kubernetes 集群不是同一个对象。
kubectl --kubeconfig "$KCFG" apply -f route-sidecar.yaml
"$ISTIOCTL" --kubeconfig "$KCFG" proxy-config endpoints deployment/client -n mesh-sidecar
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-catalog-version: v2' http://catalog:8080/version服务身份、访问策略与应用接入
mTLS 与授权分别控制什么
网格内工作负载使用与 namespace、ServiceAccount 关联的身份。client 在这里的 principal 为 cluster.local/ns/mesh-sidecar/sa/client,证书中的 URI SAN 带 spiffe:// 前缀。证书申请使用面向 Istio CA 的投射 Token;关闭应用的默认 ServiceAccount Token 挂载,并不会取消代理完成身份认证所需的独立投射卷。
PeerAuthentication 决定目标接受怎样的连接,STRICT 要求网格 mTLS;AuthorizationPolicy 再根据调用者身份和请求条件决定放行。已有应用 TLS 可以与代理之间的网格 mTLS 同时存在,但需分清每次握手的终止点。DestinationRule 的 TLS 配置控制出站发起方式,随意设置 DISABLE 或 SIMPLE 可能与目标要求冲突。模式与工作负载端口例外见 PeerAuthentication。
security-sidecar.yaml 对两个 catalog 版本启用 STRICT,并只允许正常 client 的 GET /version:
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: catalog-strict
namespace: mesh-sidecar
spec:
selector:
matchLabels: {app: catalog}
mtls: {mode: STRICT}
---
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: catalog-client
namespace: mesh-sidecar
spec:
selector:
matchLabels: {app: catalog}
action: ALLOW
rules:
- from:
- source:
principals: [cluster.local/ns/mesh-sidecar/sa/client]
to:
- operation:
methods: [GET]
paths: [/version]存在适用的 ALLOW 后,只有匹配某条 ALLOW 的请求才获准通过。CUSTOM 和 DENY 还可以先拒绝,不能用另一条 ALLOW 覆盖 DENY。多个规则之间、同一规则内 from/to/when 的组合语义要分别看待,字段明细见 AuthorizationPolicy。
kubectl --kubeconfig "$KCFG" apply -f security-sidecar.yaml
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-request-id: allowed' http://catalog:8080/version
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/outsider -c client -- \
curl -q --noproxy '*' --silent --show-error --max-time 5 -w '\n%{http_code}\n' \
-H 'x-request-id: denied-identity' http://catalog:8080/version
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --silent --show-error --max-time 5 -w '\n%{http_code}\n' \
-X POST -H 'x-request-id: denied-method' http://catalog:8080/version正常 client 读到 v1;outsider 虽然也有网格证书,仍得到 403;正常身份换成 POST 也得到 403。两次拒绝来自目标代理,应用没有对应请求日志。检查时对照目标 istio-proxy 与 app 日志,不能把任何 403 都归结为业务权限系统。
kubectl --kubeconfig "$KCFG" logs -n mesh-sidecar deployment/catalog-v1 -c istio-proxy --tail=30
kubectl --kubeconfig "$KCFG" logs -n mesh-sidecar deployment/catalog-v1 -c app --tail=30
"$ISTIOCTL" --kubeconfig "$KCFG" proxy-config secret deployment/client -n mesh-sidecar证书摘要显示有效状态及序列号等信息;配置转储和证书详细输出按内部资料保护,生产中不要公开完整 dump。实验请求头只是日志关联标记,客户端能够自己构造,不能用于认证。
证书续期与请求令牌
Sidecar 的 agent 生成私钥与 CSR,向 CA 提交身份凭据;签发后通过 SDS 向 Envoy 提供证书,并在有效期内续期。观察续期时比较同一代理先后加载的序列号和有效期,再建立新连接验证,不仅检查 Pod 是否重启。CA 暂不可达时,尚有效的证书可能维持现有调用;续期持续失败会让之后的握手失去有效凭据。工作负载证书自动续期与更换根信任是不同操作,后者还需让通信双方接受新的信任链。Istio 安全与 PKI说明了签发和分发过程。
终端用户 JWT 是另一种身份输入。RequestAuthentication 按 issuer、audience 和 JWKS 验证令牌,但单独配置它会接受不携带令牌的请求;若要强制登录,还需配合 AuthorizationPolicy 的请求身份条件,例如要求 requestPrincipals: ["*"],并审查其他 ALLOW 是否形成旁路。有效令牌只提供可用于判断的用户身份和声明,订单归属等权限仍需应用检查。字段及组合示例见 RequestAuthentication。
未纳管客户端与连接错误
从 mesh-plain 发起明文请求:
if kubectl --kubeconfig "$KCFG" exec -n mesh-plain deployment/client -c client -- \
curl -q --noproxy '*' --silent --show-error --max-time 5 -w '\n%{http_code}\n' \
http://catalog.mesh-sidecar:8080/version; then
printf '%s\n' '异常:明文连接完成,检查 STRICT 是否选中目标及实际请求路径' >&2
exit 1
fi本环境中连接被重置,curl 非零且没有 HTTP 状态。这与已完成 mTLS 后的 403 不同。若得到 DNS 错误或超时,先恢复未纳管客户端到同集群普通 Service 的连通性,再判定 STRICT;这些失败本身不能证明 TLS 拒绝。检查目标策略 selector、实际目标 Pod 和源容器是否确实未注入,再观察目标代理,避免把网络故障当安全效果。
接进现有应用时保留的职责
应用仍使用现有 HTTP/gRPC 客户端和 Service 地址,不需要为基本透明代理添加 Istio SDK。需要显式设置连接、读取与总超时,传播链路上下文,并明确哪些请求可以重试。客户端、入口和网格各自重试时,尝试次数可能相乘;已提交写入但响应丢失,应由业务幂等键查询或重用原结果,不能用代理重试猜测写入失败。
健康检查、启动依赖和长连接影响纳管过程。当前 HTTP 探针由注入模板改写,应用的原始 /healthz 仍需要正常;检查 Pod 中实际 probe 和代理保存的原始探针信息,不把网格探针误当应用新增接口。原生 sidecar 的启动与终止行为要和 Job、数据库迁移、应用 init container 一起验证。严格授权会阻止直接调用应用探针地址的普通请求,kubelet 的探针链路则应按注入方式单独确认。
Trace 上下文由应用在入口处理与下游调用之间传播。代理可以生成 span,却无法知道业务代码中哪个下游调用属于当前请求。应用日志、代理日志、指标与 Trace 分别承担不同观察任务:日志定位一次请求,指标比较时间窗口,Trace 关联调用;没有采样到 Trace 时先查请求日志。
Ambient:节点代理与目标侧七层处理
补齐组件,再纳管已有应用
mesh-ambient 的应用仍是最初部署的版本,尚未注入 Sidecar。保留这个独立 namespace,避免误把 Sidecar 的 HTTP selector 策略交给只理解四层条件的 ztunnel。
Ambient 需要 Istio CNI 与 ztunnel。安装输入 install-ambient.yaml 将同一实验 revision 切换为 ambient profile,补齐节点组件;原 Sidecar 工作负载继续存在,不需要为了启用 Ambient 先删除它们。生产中的 CNI 是共享变更,应先核对主 CNI、网络目录和平台差异,不在已有集群无条件照抄实验安装。
waypoint 还需要 Gateway API CRD。这组安装使用 1.6.0 experimental 清单,与 Ambient 安装说明对应。它包含集群级 API;不是在单 namespace 内安装一份私有接口。
GW_VERSION=v1.6.0
curl -q --fail-with-body --location \
"https://github.com/kubernetes-sigs/gateway-api/releases/download/$GW_VERSION/experimental-install.yaml" \
-o generated/gateway-experimental.yaml
printf '%s %s\n' f0d5c2b0bef2b9d80ba6ba909e5e5dbde0800638437608353f41a6ebd3afcd9f \
generated/gateway-experimental.yaml | sha256sum -c -
kubectl --kubeconfig "$KCFG" apply --server-side -f generated/gateway-experimental.yaml
"$ISTIOCTL" --kubeconfig "$KCFG" install -y -f install-ambient.yaml
for name in istio-cni-node ztunnel; do
kubectl --kubeconfig "$KCFG" rollout status "daemonset/$name" -n istio-system --timeout=180s
done
kubectl --kubeconfig "$KCFG" label namespace mesh-ambient istio.io/dataplane-mode=ambient
"$ISTIOCTL" --kubeconfig "$KCFG" ztunnel-config workloads在本配置中,mesh-ambient 四个应用 Pod 显示 HBONE,mesh-plain 仍显示 TCP,应用 Pod 内没有新增 istio-proxy。Ambient 纳管已有 Pod 不要求先重建应用;这与 Sidecar 创建时注入不同。HBONE 使用带工作负载身份的 mTLS 保护 HTTP CONNECT 隧道,隧道中的业务仍可只是普通 TCP。
ztunnel 的证书与连接属于具体节点。先取 client 所在节点,再查询该节点代理;直接执行不带目标的证书命令可能报 ztunnel pod name or --node must be set。
CLIENT_NODE=$(kubectl --kubeconfig "$KCFG" get pods -n mesh-ambient -l app=client \
-o jsonpath='{.items[0].spec.nodeName}')
test -n "$CLIENT_NODE"
"$ISTIOCTL" --kubeconfig "$KCFG" ztunnel-config certificates --node "$CLIENT_NODE"
kubectl --kubeconfig "$KCFG" exec -n mesh-ambient deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-request-id: ambient-v1' http://catalog-v1:8080/version证书列表应出现此节点纳管的工作负载身份,调用返回 v1。检查日志也要选实际节点的 ztunnel;kubectl logs daemonset/ztunnel 可能任选另一个 Pod,读不到连接记录并不代表连接没有经过网格。
先在 ztunnel 限制身份与端口
security-ambient-l4.yaml 的 STRICT 选择两个 catalog,ALLOW 仅允许 cluster.local/ns/mesh-ambient/sa/client 访问工作负载 8080 端口。它不使用 methods 或 paths,因为 ztunnel 无法读取 HTTP 字段。
kubectl --kubeconfig "$KCFG" apply -f security-ambient-l4.yaml
kubectl --kubeconfig "$KCFG" exec -n mesh-ambient deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
http://catalog-v1:8080/version
if kubectl --kubeconfig "$KCFG" exec -n mesh-ambient deployment/outsider -c client -- \
curl -q --noproxy '*' --silent --show-error --max-time 5 http://catalog-v1:8080/version; then
printf '%s\n' '异常:错误身份被允许,停止增加后续策略' >&2
exit 1
fi正常身份返回 v1,错误身份在四层连接上失败,不是 HTTP 403。未纳管客户端也应失败。将源工作负载与身份、目标地址、时间窗口和目标 ztunnel 拒绝记录对应起来;TCP 层拒绝不能靠 HTTP 方法日志解释。
waypoint 附着与后端身份
创建只处理 Service 流量的 waypoint,并明确归属 stable-a:
"$ISTIOCTL" --kubeconfig "$KCFG" waypoint apply -n mesh-ambient \
--name catalog-waypoint --for service --revision stable-a --wait
kubectl --kubeconfig "$KCFG" rollout status deployment/catalog-waypoint -n mesh-ambient --timeout=180s
WAYPOINT_SA=$(kubectl --kubeconfig "$KCFG" get deployment catalog-waypoint -n mesh-ambient \
-o jsonpath='{.spec.template.spec.serviceAccountName}')
test -n "$WAYPOINT_SA"
kubectl --kubeconfig "$KCFG" patch authorizationpolicy catalog-l4 -n mesh-ambient \
--type=json -p "$(jq -nc --arg principal "cluster.local/ns/mesh-ambient/sa/$WAYPOINT_SA" \
'[{op:"add",path:"/spec/rules/0/from/0/source/principals/-",value:$principal}]')"
kubectl --kubeconfig "$KCFG" label service catalog -n mesh-ambient istio.io/use-waypoint=catalog-waypointwaypoint 接收原调用者的连接,再以自己的身份连接后端。因此,已有仅允许 client 的后端四层策略还要允许 waypoint 的实际 ServiceAccount。这里暂时同时允许 client 直连和 waypoint 转发,以便比较路径;七层策略生效后再限制直连,不能永久把两种路径当成相同的授权效果。
waypoint apply 创建 Gateway,istiod 再生成 Envoy Deployment。Gateway 存在与 Service 绑定是两步;没有 istio.io/use-waypoint 时,Service 请求仍可走原来的 ztunnel 路径。
用 HTTPRoute 选择两个版本
route-ambient.yaml 把 HTTPRoute 附着到 catalog Service,两个 backendRefs 指向已经存在的 catalog-v1/catalog-v2 Service。它复用相同请求头和默认版本,但不使用 Sidecar 的 DestinationRule subset:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: catalog
namespace: mesh-ambient
spec:
parentRefs:
- group: ""
kind: Service
name: catalog
port: 8080
rules:
- matches:
- headers:
- name: x-catalog-version
value: v2
backendRefs:
- name: catalog-v2
port: 8080
timeouts: {request: 2s}
- backendRefs:
- name: catalog-v1
port: 8080
timeouts: {request: 2s}kubectl --kubeconfig "$KCFG" apply -f route-ambient.yaml
kubectl --kubeconfig "$KCFG" get httproute catalog -n mesh-ambient -o yaml
"$ISTIOCTL" --kubeconfig "$KCFG" proxy-config routes deployment/catalog-waypoint -n mesh-ambient
kubectl --kubeconfig "$KCFG" exec -n mesh-ambient deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-catalog-version: v2' -H 'x-request-id: waypoint-v2' http://catalog:8080/versionRoute 的父对象条件应接受当前 generation,引用有效;waypoint 的实际 route 应有两个分支。带请求头返回 v2,去掉后返回 v1。若 Gateway 已 Programmed、HTTPRoute 已 Accepted,但结果仍不对,继续检查 Service 绑定、调用方数据面和实际路由,不重复创建 Gateway。
七层拒绝与直连旁路
security-ambient-l7.yaml 用 targetRefs 指向 catalog Service,只允许正常 client 的 GET /version。waypoint 策略不能沿用 Sidecar 的 workload selector;执行点已经改变。
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: catalog-http
namespace: mesh-ambient
spec:
targetRefs:
- group: ""
kind: Service
name: catalog
action: ALLOW
rules:
- from:
- source:
principals: [cluster.local/ns/mesh-ambient/sa/client]
to:
- operation:
methods: [GET]
paths: [/version]kubectl --kubeconfig "$KCFG" apply -f security-ambient-l7.yaml
kubectl --kubeconfig "$KCFG" exec -n mesh-ambient deployment/client -c client -- \
curl -q --noproxy '*' --silent --show-error --max-time 5 -w '\n%{http_code}\n' \
-X POST -H 'x-request-id: waypoint-denied' http://catalog:8080/version预期 403。换 outsider 做 GET 同样被拒绝,正常 client 的 GET 仍应成功。但当前后端四层策略还允许 client 身份,直接访问 Pod IP 可以绕过只附着于 Service 的 waypoint:
POD_IP=$(kubectl --kubeconfig "$KCFG" get pod -n mesh-ambient -l app=catalog,version=v1 \
-o jsonpath='{.items[0].status.podIP}')
test -n "$POD_IP"
kubectl --kubeconfig "$KCFG" exec -n mesh-ambient deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-X POST -H 'x-request-id: direct-bypass' "http://$POD_IP:8080/version"这次返回 v1,应用日志出现 POST。实验应用故意允许方法,用来观察请求是否真正经过 waypoint;真实业务不能因此取消应用自身授权。
所有合法调用已能通过 waypoint 后,把后端 ALLOW 收窄为 waypoint 的实际身份:
kubectl --kubeconfig "$KCFG" patch authorizationpolicy catalog-l4 -n mesh-ambient \
--type=json -p "$(jq -nc --arg principal "cluster.local/ns/mesh-ambient/sa/$WAYPOINT_SA" \
'[{op:"replace",path:"/spec/rules/0/from/0/source/principals",value:[$principal]}]')"
kubectl --kubeconfig "$KCFG" exec -n mesh-ambient deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-request-id: waypoint-enforced' http://catalog:8080/version
if kubectl --kubeconfig "$KCFG" exec -n mesh-ambient deployment/client -c client -- \
curl -q --noproxy '*' --silent --show-error --max-time 5 \
-X POST -H 'x-request-id: direct-denied' "http://$POD_IP:8080/version"; then
printf '%s\n' '异常:直连旁路仍然可用' >&2
exit 1
fiService 请求保持成功,直连连接被拒绝。相应源配置也要保存为只允许 waypoint,不能随后重新应用起步的 client-only 文件。多个 ALLOW 会合并放行,生产中还要审查其他命中后端的策略;这里的限定效果建立在专用 namespace 只有这条相关 ALLOW 的条件上。
只允许 waypoint 会同时影响直接访问版本 Service、Pod IP 和尚未迁移的旧调用方。迁移期间若保留这些来源,就仍存在绕过七层策略的路径,应先补等价限制或安排受控切换,不能把临时例外当最终配置。
超时、重试与运行观测
一次调用为何在后端出现三次
客户端等待一次响应,代理可以在这段等待中发出多次上游请求。retries.attempts: 2 表示最多再试两次;首次请求加上重试,后端最多收到三次。实际次数还受总超时、单次超时和重试条件影响。VirtualService 的重试与超时字段定义了这些限制。
先用始终返回 503 的只读测试路径观察这个过程。retry-sidecar.yaml 保留已有两个版本分支,在最前面增加:
- name: retry-read
match:
- uri: {exact: /fail}
route:
- destination: {host: catalog, subset: v1}
retries:
attempts: 2
perTryTimeout: 1s
retryOn: 5xx
timeout: 4s命令仍在同一管理终端执行。先临时允许测试路径,再替换路由;其他来源和方法限制保持不变。
kubectl --kubeconfig "$KCFG" patch authorizationpolicy catalog-client -n mesh-sidecar \
--type=json -p '[{"op":"add","path":"/spec/rules/0/to/0/operation/paths/-","value":"/fail"}]'
kubectl --kubeconfig "$KCFG" apply -f retry-sidecar.yaml
REQUEST_ID="retry-$(cat /proc/sys/kernel/random/uuid)"
STATUS=$(kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --silent --show-error --max-time 6 -o /dev/null -w '%{http_code}' \
-H "x-request-id: $REQUEST_ID" http://catalog:8080/fail) || exit 1
test "$STATUS" = 503 || { printf '非预期状态:%s\n' "$STATUS" >&2; exit 1; }
kubectl --kubeconfig "$KCFG" logs -n mesh-sidecar deployment/catalog-v1 -c app --tail=100 \
| jq -c --arg id "$REQUEST_ID" 'select(.request_id == $id)'预期应用日志有三条同 ID 的 GET /fail,状态都是 503。客户端只收到最后一次失败。这组输出能区分“应用返回错误后代理重试”和“请求根本没有到应用”;前面的空 subset 实验属于后者。若日志尚未出现,先重新读取同一 ID 的日志,勿重复发送请求后混数。没有三条时检查实际 route、代理同步状态及响应来源,不能仅凭 YAML 中写了 attempts 判断重试已发生。
恢复正常路径和授权:
kubectl --kubeconfig "$KCFG" apply -f route-sidecar.yaml
kubectl --kubeconfig "$KCFG" apply -f security-sidecar.yaml
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
http://catalog:8080/version应返回 v1。/fail 此后又被授权策略拒绝,不继续暴露为正常应用入口。
如何为真实接口设置等待预算
以调用方最多等待 5 秒的查询为例,可以让代理的总请求时间小于这个上限,再为连接、应用处理和重试分配时间。这里的 4 秒总超时、1 秒单次超时用于观察行为,不是所有查询的推荐值。要依据接口延迟分布、连接建立时间和故障期间的并发量重新测量。
下游变慢时增加重试次数会增加请求量。假设每层都最多尝试三次,两层重试可能让一次用户请求触发九次最末端调用;总超时通常还会截断其中一部分。先确认 SDK、业务代码、网关和网格各自在哪重试,再保留有明确作用的一层或分配总预算。
写入接口还需要业务幂等。服务端可能已经提交订单,只是返回响应时断开了连接;换一个实例重试仍可能创建第二笔订单。业务幂等键、去重记录和结果查询负责解决这种不确定性,网格无法从一次连接失败判断事务是否提交。测试这类接口时应记录操作编号,核对最终订单数,不能仅统计 HTTP 成功比例。
连接池上限和异常实例摘除作用在不同位置:连接池限制代理向上游投入多少并发资源,outlier detection 暂时减少对连续失败端点的选择。它们都需要与实例数、每实例处理能力和故障后剩余容量一起配置。只有一个可用端点时,把它摘掉可能让请求直接失败;某个端点业务报错也不必然说明机器故障。具体字段见 DestinationRule 的连接池与异常检测。
把一次错误对应到执行位置
先保存请求 ID、目标主机与端口、调用方 Pod、方法、响应码及等待时长。然后在实际经过的代理和应用中查同一请求,而不是先把所有组件日志导出。
| 现象 | 首先核对 | 接着判断 |
|---|---|---|
503,UH、no_healthy_upstream | 源代理 route、cluster、endpoint;目标 EndpointSlice | subset 是否选空、Pod 是否就绪,或端点是否被摘除 |
| 403,应用没有同 ID 日志 | 目标 Sidecar 或 waypoint 的拒绝日志 | 来源 principal、方法、路径、命中的 DENY/ALLOW |
| 建连被重置,没有 HTTP 响应 | 源/目标 ztunnel、目标 Sidecar TLS 日志 | 是否明文碰到 STRICT、四层身份被拒绝,或网络本身不通 |
| 应用有多条同 ID 失败记录 | 源代理实际 retries 配置与调用方重试 | 哪一层发出了额外请求,是否超出接口幂等范围 |
| 路由已更新但部分调用走旧版本 | 出问题的源代理或 waypoint 的实际配置 | 配置是否同步、调用是否绕过目标 waypoint、长连接是否复用旧后端 |
例如查看源 Sidecar 和目标应用:
kubectl --kubeconfig "$KCFG" logs -n mesh-sidecar deployment/client -c istio-proxy --tail=80
kubectl --kubeconfig "$KCFG" logs -n mesh-sidecar deployment/catalog-v1 -c app --tail=80
"$ISTIOCTL" --kubeconfig "$KCFG" proxy-config routes deployment/client -n mesh-sidecar
"$ISTIOCTL" --kubeconfig "$KCFG" proxy-config endpoints deployment/client -n mesh-sidecar实验已把 Envoy 访问日志输出到 stdout。生产可通过 Telemetry API 配置访问日志控制启用范围,并由日志系统持久化;路径参数、认证头和用户标识要按敏感程度过滤。Pod 删除后仍需调查的日志不能只留在容器本地。
Ambient 四层排障要定位节点。一个 DaemonSet 有多份日志,kubectl logs daemonset/ztunnel 任选的一份可能根本没有处理此次连接:
CLIENT_NODE=$(kubectl --kubeconfig "$KCFG" get pod -n mesh-ambient -l app=client \
-o jsonpath='{.items[0].spec.nodeName}')
test -n "$CLIENT_NODE"
kubectl --kubeconfig "$KCFG" get pod -n istio-system -l app=ztunnel \
--field-selector "spec.nodeName=$CLIENT_NODE" -o name
ZTUNNEL_POD=$(kubectl --kubeconfig "$KCFG" get pod -n istio-system -l app=ztunnel \
--field-selector "spec.nodeName=$CLIENT_NODE" -o json \
| jq -er '.items | if length == 1 then .[0].metadata.name else error("ztunnel 数量不为 1") end') || exit 1
kubectl --kubeconfig "$KCFG" logs -n istio-system "$ZTUNNEL_POD" --tail=100源节点看不到目标拒绝原因时,用目标 Pod 的 spec.nodeName 查目标 ztunnel。七层路由和方法拒绝则继续查 deployment/catalog-waypoint。ztunnel 转发加密连接,通常不能给出应用 HTTP 路径与状态码。
指标缺失与指标为零要分开处理
查看当前代理的内存分配统计:
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c istio-proxy -- \
pilot-agent request GET 'stats?filter=server.memory_allocated'输出形如 server.memory_allocated: 9459056,单位为字节,数值随连接和配置改变。这是 Envoy 内部分配统计;容器 RSS、working set 和内存上限还要从容器运行时或监控系统读取,不能直接相互替代。
默认 Envoy 只记录部分内部统计,应用上游的重试计数可能没有生成。查询不到 upstream_rq_retry 时,先检查 bootstrap 的 stats matcher。需要这些指标可在选定工作负载的 proxy.istio.io/config 中增加匹配规则并滚动重建,具体配置见 Envoy Statistics。先限定实验 Pod 和指标集合,避免全网格启用大量序列后才发现采集成本过高。
服务级请求量、错误率和延迟应持续采集,并区分来源、目标及数据面模式;纯 ztunnel 提供连接级信息,HTTP 指标需要可解析 HTTP 的代理。Trace 还要求应用把上下文传播到下一次调用,网格中的两个代理无法自行判断两条独立请求是否属于同一业务操作。
生产部署与资源规划
控制面副本、节点分布与扩缩容
独立 kind 节点仍共享同一台物理机。生产中把两个 istiod Pod 放在不同工作节点,才能承受其中一个节点故障;若还要求跨可用区容灾,就要进一步检查区域分布、网络和 Kubernetes 控制面的可用性。
附件 install-production-sidecar.yaml 给出一个可调整的控制面配置起点:revision 为 prod-a,istiod 至少两个副本,HPA 范围 2–5,CPU 利用率目标 70%,PDB 至少保留一个可用副本;按 hostname 做最大偏差为 1 的分布。单副本资源请求为 500m CPU、512Mi,限制为 2 CPU、2Gi。这些参数用于说明各字段怎样配合,不构成业务容量承诺。
在管理终端先生成清单,不连接生产集群:
"$ISTIOCTL" manifest generate -f install-production-sidecar.yaml > production-rendered.yaml重点查看 Deployment/istiod-prod-a、对应 HPA 和 PDB,以及 CNI DaemonSet。渲染检查应看到以下几组字段:
# Deployment.spec.template.spec
topologySpreadConstraints:
- maxSkew: 1
minDomains: 2
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: istiod
istio.io/rev: prod-a
# HorizontalPodAutoscaler.spec
minReplicas: 2
maxReplicas: 5
# PodDisruptionBudget.spec
minAvailable: 1实际生产安装前,管理员应使用单独的 PROD_KCFG 核对集群名称、节点和权限,不复用实验 kubeconfig。已有 Helm 或平台 Operator 管理的安装,应在原管理方式中表达这些参数,不让另一个安装器接管同一批资源。这个配置已做清单渲染核对;多物理节点故障与自动扩缩容仍需在自己的环境验证。
HPA 依赖可用的资源指标 API。kubectl top 不可用、HPA 的 CPU 显示 unknown 时,应先检查 Metrics Server 和资源 requests,而不是继续提高 maxReplicas。PDB 主要约束自愿驱逐,节点突然失联仍可能使副本不可用。minDomains: 2 配合 DoNotSchedule 要求为分布计算保留至少两个域的基准;只有一台合适节点时,不能把第二份副本也都堆上去。出现 Pending 应检查节点标签、污点、资源余量及拓扑分布约束。
从业务负载估算网格开销
Sidecar 的主要预算按业务 Pod 数累加:每个 Pod 多一个代理,副本扩容也会增加代理开销。Ambient 的四层预算按节点上的 ztunnel 计算,七层预算再按 waypoint 副本计算。减少代理数量后,多个业务共享代理的资源竞争和故障影响也随之集中,需要单独压测高流量服务。
测量时固定协议、请求/响应大小、连接复用方式、并发和启用的遥测功能,分别运行无网格、Sidecar、Ambient 四层、Ambient 加 waypoint。比较稳定负载及突发负载下的 CPU、内存、P95/P99、错误率,再测试少一份代理或少一台节点后的剩余容量。每组使用相同业务镜像和负载输入,否则差值里会混入应用变化。
istiod 还受代理数、服务与路由配置规模、端点变更频率影响;大量工作负载同时发布时,关注配置同步耗时和控制面峰值。官方 Performance and Scalability提供测量维度与历史测试条件,页面中的历史版本结果不能直接当作 1.31 或当前业务的容量值。
入口网关:域名、路由与后端身份
入口网关接收网格外请求,再作为一个网格工作负载调用后端。公网负载均衡地址、DNS 与证书属于入口侧;网关到 catalog 的 mTLS 和授权属于网格侧,要分别检查。
在主实验 $KCFG 中应用 ingress-sidecar.yaml。Gateway 和 HTTPRoute 都位于 mesh-sidecar,监听主机 catalog.lab.example 的 HTTP 80 端口,只开放 /version,后端固定为 catalog-v1:8080。allowedRoutes.namespaces.from: Same 限制附着路由的 namespace。
kubectl --kubeconfig "$KCFG" apply -f ingress-sidecar.yaml
kubectl --kubeconfig "$KCFG" rollout status deployment/catalog-edge-istio \
-n mesh-sidecar --timeout=180s
EDGE_SA=$(kubectl --kubeconfig "$KCFG" get deployment catalog-edge-istio -n mesh-sidecar \
-o jsonpath='{.spec.template.spec.serviceAccountName}')
test -n "$EDGE_SA"
kubectl --kubeconfig "$KCFG" patch authorizationpolicy catalog-client -n mesh-sidecar \
--type=json -p "$(jq -nc --arg principal "cluster.local/ns/mesh-sidecar/sa/$EDGE_SA" \
'[{op:"add",path:"/spec/rules/0/from/0/source/principals/-",value:$principal}]')"自动生成的 Deployment 和 Service 名为 catalog-edge-istio。Kubernetes Gateway API 与 Istio说明了这种自动部署方式,以及它和旧 Istio Gateway API 的区别。
kind 没有云负载均衡控制器,外部地址可能一直 Pending。先从未纳管的 mesh-plain 客户端访问入口 Service,不必创建公网资源:
kubectl --kubeconfig "$KCFG" exec -n mesh-plain deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'Host: catalog.lab.example' -H 'x-request-id: edge-allowed' \
http://catalog-edge-istio.mesh-sidecar/version
kubectl --kubeconfig "$KCFG" exec -n mesh-plain deployment/client -c client -- \
curl -q --noproxy '*' --silent --show-error --max-time 5 -w '\n%{http_code}\n' \
-H 'Host: wrong.lab.example' http://catalog-edge-istio.mesh-sidecar/version正确主机返回 v1,错误主机返回 404。若是 503,继续检查网关的端点、到后端的连接和 TLS;若是 403,查看 catalog 的授权日志,确认策略允许的是实际网关 ServiceAccount。
这里网关对外未启用用户认证,所以访问者只要能到达入口就能请求该演示路径。后端识别到的网关 principal 不能代替终端用户身份。正式 API 应继续验证用户令牌与业务权限,避免把“允许网关调用”扩大解释成“所有外部用户都已认证”。
入口改为调用 Ambient 目标时,还要单独核对 waypoint 路径。仅设置 istio.io/use-waypoint 不足以让入口请求经过它;需确认 istiod 已启用 ENABLE_INGRESS_WAYPOINT_ROUTING,并在目标 Service 或 namespace 设置 istio.io/ingress-use-waypoint=true。这会形成入口和 waypoint 两层七层处理,应同时核对两跳授权、路由和延迟,不能把前面 Sidecar 后端的成功结果外推到这条路径。waypoint 的入口规则给出具体适用条件。
HTTPS 终止与证书拒绝检查
为专用实验生成短期自签名证书,需要管理机安装 OpenSSL 3。证书只匹配 catalog.lab.example,不加入系统信任库:
mkdir private/edge-tls
openssl req -x509 -newkey rsa:2048 -sha256 -noenc -days 2 \
-subj '/CN=catalog.lab.example' -addext 'subjectAltName=DNS:catalog.lab.example' \
-keyout private/edge-tls/catalog.key -out private/edge-tls/catalog.crt
kubectl --kubeconfig "$KCFG" create secret tls catalog-edge-cert -n mesh-sidecar \
--key=private/edge-tls/catalog.key --cert=private/edge-tls/catalog.crt
kubectl --kubeconfig "$KCFG" apply -f ingress-tls.yamlingress-tls.yaml 将同一 Gateway 改为仅监听 HTTPS 443,HTTPRoute 的 sectionName 改为 https。证书引用放在该监听器中:
tls:
mode: Terminate
certificateRefs:
- name: catalog-edge-certSecret 和 Gateway 位于同一 namespace。只有证书公有部分需要提供给测试客户端:
kubectl --kubeconfig "$KCFG" create configmap catalog-test-ca -n mesh-plain \
--from-file=ca.crt=private/edge-tls/catalog.crt
kubectl --kubeconfig "$KCFG" patch deployment client -n mesh-plain --type=strategic \
-p '{"spec":{"template":{"spec":{"volumes":[{"name":"test-ca","configMap":{"name":"catalog-test-ca"}}],"containers":[{"name":"client","volumeMounts":[{"name":"test-ca","mountPath":"/test-ca","readOnly":true}]}]}}}}'
kubectl --kubeconfig "$KCFG" rollout status deployment/client -n mesh-plain --timeout=180s
kubectl --kubeconfig "$KCFG" exec -n mesh-plain deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
--cacert /test-ca/ca.crt \
--connect-to catalog.lab.example:443:catalog-edge-istio.mesh-sidecar:443 \
https://catalog.lab.example/version预期返回 v1。--connect-to 改变连接地址,URL 中的主机仍用于 SNI 和证书主机名校验;不需要把测试域名加入公网 DNS。去掉 --cacert 后,客户端应以证书不受信任结束,curl 退出码为 60。不要加 -k 让这个负例变成成功。
网关证书未加载时,查 Secret 名称、证书链、私钥匹配关系以及 Gateway listener 条件;istioctl proxy-config secret deployment/catalog-edge-istio -n mesh-sidecar 可检查代理已加载的证书信息。公网部署需要由受信任 CA 签发真实域名证书,并配置更新流程。Secure Gateways提供单向 TLS、双向 TLS 和多证书入口配置。
完成实验后先撤入口,再还原后端允许来源,防止后续迁移时遗留额外调用方:
kubectl --kubeconfig "$KCFG" delete -f ingress-tls.yaml
kubectl --kubeconfig "$KCFG" apply -f security-sidecar.yaml
kubectl --kubeconfig "$KCFG" delete secret catalog-edge-cert -n mesh-sidecar这会删除专用入口和测试证书 Secret,保留 catalog 服务、业务 Deployment 和本机证书文件。以后恢复入口时重新创建 Secret,再应用相应清单。
出口访问:注册目标和强制经过网关
应用访问外部支付、对象存储或 SaaS 时,先确定谁建立 TLS:应用直接发 HTTPS,代理通常只能按连接和 SNI 处理;应用发 HTTP、由出口代理发起 TLS,则代理能处理请求内容,也承担服务端证书验证配置。重复在两层发起 TLS 可能造成双重封装,不能只凭外部地址用了 443 判断配置正确。
ServiceEntry 把外部主机、端口和解析方式加入服务注册信息;REGISTRY_ONLY 可帮助发现未登记的外部依赖。它们没有把应用进程变成不可信代码也无法绕过的出口防火墙。要强制从指定出口访问,应同时安排 Egress Gateway、底层网络策略和出口节点网络限制,并验证直连目标 IP、绕过代理与非预期端口都被阻断。Security Best Practices明确区分这两类用途。
出口实施时先列出目标域名、端口、DNS 解析位置、TLS 签发链和网关身份,再按 Egress Gateways建立“源代理 → 出口网关 → 外部服务”的两段路由。采样同时查看源代理选择的出口、出口代理连接的外部地址、远端响应,以及关闭出口后的直连负例。仅在网关日志中看见一条请求,还不能说明其他路径全部受控。
多集群先确定网络与信任
两座集群各安装一套 Istio 后,服务不会自动组成一个可互访网格。至少需要确定这几项:
| 配置对象 | 两集群如何安排 | 错误时常见现象 |
|---|---|---|
meshID | 同一网格使用同一值 | 组成关系与预期不一致 |
clusterName | 每座集群唯一 | 端点所属集群混淆 |
network | Pod 地址可直达的网络使用同一网络标识;不同网络分别命名 | 选择无法直达的 Pod 地址或错误网关 |
| 信任根与签发配置 | 建立相互信任,通常各主集群使用共同根签发的中间 CA | 已发现端点但 mTLS 握手失败 |
| API Server 访问 | 各控制面能读取需要发现的远端资源,凭据可轮换 | 远端端点缺失或长期陈旧 |
| 业务路径 | 同网络验证 Pod 间路由;异网络验证 east-west gateway 可达 | 端点存在但连接超时 |
双主、双网络的一组安装参数如下。它们只表示拓扑输入,不能单独替代 CA、网关与远端发现步骤:
# cluster1 的 IstioOperator.spec.values
global:
meshID: business-mesh
multiCluster:
clusterName: cluster1
network: network1
# cluster2 使用相同 meshID,clusterName=cluster2,network=network2先按 多集群前置准备检查 Kubernetes 版本、两侧 API 地址和信任配置,再选择 不同网络的 Multi-Primary 安装中的 east-west gateway、服务暴露和双向远端发现步骤。这里没有在本地单机实验中建立两套独立网络与企业 CA,不能拿前面的双节点 kind 结果替代这些检查。
remote secret 让一个控制面读取另一座集群;它的服务端地址必须从控制面所在网络可达,不能使用管理机专用的 127.0.0.1 端口转发地址。凭据输出不进文章附件、日志和普通 Git。验证时在两侧分别发请求,检查实际远端端点和响应归属,再隔离一侧业务端点测试剩余路径。跨集群调用延迟、网络费用和业务数据复制仍需各自评估,网格不会自动复制数据库。
已有单集群自签名 CA 时,不要直接重装一个新 CA 后接入多集群。应先选择证书轮换与信任迁移方案,明确旧代理的信任材料如何更新,以及失败时哪些旧证书仍有效。
VM 接入与 Kubernetes 工作负载的差别
VM 没有 Kubernetes Pod 注入流程。需要在 VM 上安装并运行 agent/Envoy,为它准备工作负载身份、控制面发现地址和信任材料,并让服务注册信息指向 VM 地址。
WorkloadGroup 描述这一组 VM 的标签、ServiceAccount 和网络等启动信息;具体实例由 WorkloadEntry 表示。主机名与服务发现还要由相应 Service/ServiceEntry 建立,不能只创建 WorkloadGroup 就期待其他应用解析到 VM 服务。WorkloadGroup 参考解释了这一区分。
部署前应明确管理者能够访问 VM、VM 能访问控制面暴露地址、业务网络可达、时钟与 DNS 正常,再按 Virtual Machine Installation生成并受控传送启动文件。里面的 istio-token 是身份引导凭据,不是普通示例文件;启动后检查证书申请、xDS 连接、实例健康及双向业务调用。VM 重启后 agent 未启动、引导凭据失效、地址变化和旧 WorkloadEntry 残留都需要单独验证。
升级、回退与控制面恢复
revision 切换和共享组件是两件事
revision 为两套 istiod 和注入配置提供不同名称;revision tag 把业务 namespace 指向其中一套。修改 tag 后,已有 Pod 仍使用原代理,重建后的 Pod 才获得新注入配置。因此可以先升级客户端,再升级服务端,观察不同版本代理之间的请求。
CRD 与 CNI 具有集群级影响,不能跟每个业务 Pod 一起逐个回切。Canary Upgrades说明 revision 的升级与回退方式;安装方式、共享资源和 API 变化仍需另外核对。
下面使用独立单节点实验验证 1.30.4 → 1.31.0 → 1.30.4。应用仍是无状态 catalog,没有引入新版本专属路由字段。生产升级应先查 1.31 Upgrade Notes,并检查当前 Kubernetes 和控制面/数据面版本关系。
建立旧版本基线
在前面的 Linux 管理终端、附件目录中执行。另建 kubeconfig,避免覆盖主实验:
UPGRADE_KCFG="$MESH_ROOT/private/upgrade.kubeconfig"
test ! -e "$UPGRADE_KCFG" || { printf '%s\n' '升级实验 kubeconfig 已存在' >&2; exit 1; }
kind create cluster --name istio-upgrade-lab --config kind-upgrade.yaml \
--image kindest/node:v1.35.5@sha256:ce977ae6d65918d0b58a5f8b5e940429c2ce42fa3a5619ec2bbc60b949c0ac95 \
--kubeconfig "$UPGRADE_KCFG" --wait 120s从 Istio 1.30.4 发行页下载旧版本包,保留独立目录,不修改主实验的 $ISTIOCTL:
OLD_ARCHIVE=istio-1.30.4-linux-amd64.tar.gz
OLD_BASE=https://github.com/istio/istio/releases/download/1.30.4
curl -q --fail-with-body --location "$OLD_BASE/$OLD_ARCHIVE" -o "$OLD_ARCHIVE"
curl -q --fail-with-body --location "$OLD_BASE/$OLD_ARCHIVE.sha256" -o "$OLD_ARCHIVE.sha256"
sha256sum -c "$OLD_ARCHIVE.sha256"
tar -xzf "$OLD_ARCHIVE"
OLD_ISTIOCTL="$MESH_ROOT/istio-1.30.4/bin/istioctl"
NEW_ISTIOCTL="$MESH_ROOT/istio-1.31.0/bin/istioctl"
"$OLD_ISTIOCTL" version --remote=false
"$NEW_ISTIOCTL" version --remote=false预期分别是 1.30.4、1.31.0。配置文件中的 hub: docker.io/istio 固定使用 Istio 官方 Docker Hub 命名空间;安装时镜像标签由对应 CLI 版本确定。私有镜像仓库应先同步并核对摘要,再修改 hub,不能因拉取失败关闭 TLS 校验。
"$OLD_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" install -f install-sidecar.yaml --skip-confirmation
kubectl --kubeconfig "$UPGRADE_KCFG" create -f generated/mesh-sidecar.json
kubectl --kubeconfig "$UPGRADE_KCFG" label namespace mesh-sidecar istio.io/rev=stable-a
kubectl --kubeconfig "$UPGRADE_KCFG" rollout restart deployment -n mesh-sidecar
kubectl --kubeconfig "$UPGRADE_KCFG" rollout status deployment -n mesh-sidecar --timeout=180s
kubectl --kubeconfig "$UPGRADE_KCFG" apply -f route-sidecar.yaml -f security-sidecar.yaml
kubectl --kubeconfig "$UPGRADE_KCFG" get deployment -n mesh-sidecar -o json > upgrade-app-before.json
kubectl --kubeconfig "$UPGRADE_KCFG" get crd -o json > upgrade-crd-before.json用以下请求作为每一步后的相同检查,正常结果应始终为 v2:
upgrade_request() {
kubectl --kubeconfig "$UPGRADE_KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-catalog-version: v2' http://catalog:8080/version
}
test "$(upgrade_request)" = v2 || exit 1并存新控制面,分批更新代理
"$NEW_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" x precheck --from-version=1.30
"$NEW_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" install -f install-sidecar.yaml \
--revision=stable-b --set components.cni.enabled=false --skip-confirmation
kubectl --kubeconfig "$UPGRADE_KCFG" get deployment,daemonset -n istio-system
test "$(upgrade_request)" = v2 || exit 1
kubectl --kubeconfig "$UPGRADE_KCFG" get crd -o json > upgrade-crd-after.json新 revision 暂不安装另一份 CNI,已有 istio-cni-node 应继续存在;若实际安装方案删掉或改变了它,先停止后续代理迁移。此时两个 istiod 都应就绪,业务代理仍是 1.30.4。完整升级的 CNI 更新放在后面的独立步骤,不把当前状态当最终版本一致。
CRD 快照应比较 spec 中的版本、schema、conversion 以及 status.storedVersions。这组版本中有多项 CRD schema 发生变化,但本实验使用的旧路由与授权字段仍可由旧控制面处理。回退只切回已保留的旧 revision,不把整个旧 CRD 清单覆盖回去,也不删除 CRD 重建;后两种操作可能裁剪字段或删除自定义资源。
"$NEW_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" tag set mesh-canary \
--revision stable-b --skip-confirmation
kubectl --kubeconfig "$UPGRADE_KCFG" label namespace mesh-sidecar \
istio.io/rev=mesh-canary --overwrite
kubectl --kubeconfig "$UPGRADE_KCFG" rollout restart deployment/client -n mesh-sidecar
kubectl --kubeconfig "$UPGRADE_KCFG" rollout status deployment/client -n mesh-sidecar --timeout=180s
test "$(upgrade_request)" = v2 || exit 1
kubectl --kubeconfig "$UPGRADE_KCFG" rollout restart \
deployment/catalog-v1 deployment/catalog-v2 deployment/outsider -n mesh-sidecar
kubectl --kubeconfig "$UPGRADE_KCFG" rollout status deployment -n mesh-sidecar --timeout=180s
test "$(upgrade_request)" = v2 || exit 1
"$NEW_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" proxy-status第一次请求检查新版源代理到旧版目标代理;第二次检查全部迁移后的路径。再从每个当前 Pod 的 containers 和 initContainers 查 istio-proxy 镜像,确认标签为 1.31.0。只看到 namespace 标签变化还不足以判断代理更新完成。
回切旧 revision,再完成共享 CNI 更新
保留 stable-a 的前提下回退:
"$NEW_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" tag set mesh-canary \
--revision stable-a --overwrite --skip-confirmation
kubectl --kubeconfig "$UPGRADE_KCFG" rollout restart deployment -n mesh-sidecar
kubectl --kubeconfig "$UPGRADE_KCFG" rollout status deployment -n mesh-sidecar --timeout=180s
test "$(upgrade_request)" = v2 || exit 1
"$OLD_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" proxy-status代理应回到 1.30.4,应用 Deployment UID 不变,Pod UID 因重建而改变。这是配置和代理版本回退,业务数据没有随之回滚。若应用已经依赖新版功能、使用了旧控制面无法解释的字段,或者已经删除旧 revision,不能直接套用这条回切路径。
要结束升级实验,重新把 tag 指向 stable-b 并滚动重建,再更新唯一的 CNI:
"$NEW_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" tag set mesh-canary \
--revision stable-b --overwrite --skip-confirmation
kubectl --kubeconfig "$UPGRADE_KCFG" rollout restart deployment -n mesh-sidecar
kubectl --kubeconfig "$UPGRADE_KCFG" rollout status deployment -n mesh-sidecar --timeout=180s
test "$(upgrade_request)" = v2 || exit 1
"$NEW_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" install -f install-sidecar.yaml \
--revision=stable-b --skip-confirmation
kubectl --kubeconfig "$UPGRADE_KCFG" rollout status daemonset/istio-cni-node \
-n istio-system --timeout=180s
kubectl --kubeconfig "$UPGRADE_KCFG" get daemonset istio-cni-node -n istio-system \
-o jsonpath='{.spec.template.spec.containers[*].image}'
test "$(upgrade_request)" = v2 || exit 1CNI 镜像应为 1.31.0。这一步改变节点组件,生产上应单独安排节点更新与 Pod 创建测试。Ambient 还要更新 ztunnel 和 waypoint,不能直接照搬只含 Sidecar 的顺序;Ambient Upgrade with Helm分别说明共享 CRD、控制面、CNI、ztunnel 与 waypoint 的更新安排。ztunnel 变动会影响节点上的连接,长连接恢复也要由客户端验证。
已有代理仍能请求,不代表控制面已经恢复
现有代理保留已接收配置和有效证书时,短暂丢失 istiod 后仍可能继续服务。此时新增代理、端点变化、配置下发和后续证书续期都可能受影响。恢复检查至少包含一次新 Pod 的创建和调用,而不是只对旧 Pod 发请求。
以下只在刚建立的专用升级集群演练,保留 namespace、Service、RBAC、配置、CA Secret 和所有业务资源。先保存测试 Deployment:
kubectl --kubeconfig "$UPGRADE_KCFG" get deployment istiod-stable-b -n istio-system -o json \
| jq '{apiVersion,kind,metadata:{name:.metadata.name,namespace:.metadata.namespace,labels:.metadata.labels},spec}' \
> recovery-istiod.json
jq -e '.kind == "Deployment" and .metadata.name == "istiod-stable-b" and .metadata.namespace == "istio-system"' \
recovery-istiod.json >/dev/null || exit 1
kubectl --kubeconfig "$UPGRADE_KCFG" get secret istio-ca-secret -n istio-system \
-o jsonpath='{.metadata.uid}' > recovery-ca-uid.txt
kubectl --kubeconfig "$UPGRADE_KCFG" delete deployment istiod-stable-b -n istio-system --wait=true
test "$(upgrade_request)" = v2 || exit 1
kubectl --kubeconfig "$UPGRADE_KCFG" create -f recovery-istiod.json
kubectl --kubeconfig "$UPGRADE_KCFG" rollout status deployment/istiod-stable-b \
-n istio-system --timeout=180s
kubectl --kubeconfig "$UPGRADE_KCFG" rollout restart deployment/client -n mesh-sidecar
kubectl --kubeconfig "$UPGRADE_KCFG" rollout status deployment/client -n mesh-sidecar --timeout=180s
test "$(upgrade_request)" = v2 || exit 1
"$NEW_ISTIOCTL" --kubeconfig "$UPGRADE_KCFG" proxy-status重建后的新客户端应完成注入、取得证书、同步配置并返回 v2。再读 istio-ca-secret 的 UID 与先前记录比较,确认 CA Secret 未被替换;这里没有导出私钥。
完整备份还应保存固定版本的安装输入、CRD/API 状态、路由与安全策略、Gateway 配置、revision/tag 对应关系,以及由专门密钥流程保护的 CA/外部签发配置。控制面 Deployment 的副本不包含这些内容。整个集群丢失时,需要按备份系统和 PKI 方案恢复信任、配置与依赖,不能指望重新安装后自动生成的新 CA 继续被所有旧代理信任。业务数据库和消息系统的数据恢复由各自的持久化方案承担。
模式迁移与保留应用退出
混合模式必须按调用方向检查
主实验的 mesh-sidecar 与 mesh-ambient 使用不同 namespace、不同 ServiceAccount principal。即使都叫 client,也要分别授权。先重建旧 Sidecar,使其读取 Ambient profile 加入的 ISTIO_META_ENABLE_HBONE=true:
kubectl --kubeconfig "$KCFG" rollout restart deployment -n mesh-sidecar
kubectl --kubeconfig "$KCFG" rollout status deployment -n mesh-sidecar --timeout=180s
kubectl --kubeconfig "$KCFG" get pod -n mesh-sidecar -l app=client -o json \
| jq '.items[] | [.spec.containers[],.spec.initContainers[]?][] | select(.name == "istio-proxy") | .env[] | select(.name == "ISTIO_META_ENABLE_HBONE")'应看到该变量值为 true。只是安装了 ztunnel,还没有改变已运行 Sidecar 的启动配置。安装 Ambient 迁移组件说明了这个重建要求及混合路径的限制。
| 调用方向 | 该实验中的处理与结果 |
|---|---|
| Sidecar → Sidecar | 源代理路由、目标代理授权,仍能返回两个版本 |
| Ambient → Ambient | catalog Service 经过 waypoint;GET 允许、POST 拒绝 |
| Ambient → Sidecar | 显式允许来源 principal 后,可以访问目标版本 Service;不要期待 ztunnel 执行源 Sidecar 的 VirtualService |
| Sidecar → Ambient | Sidecar 的 HBONE 路径绕过目标 waypoint;后端只允许 waypoint 时被拒绝 |
最后一行会直接影响增量迁移。暂时把 Sidecar 来源加进后端 L4 允许集合,能让旧调用方继续访问,但也允许它绕过 waypoint 的方法与路径限制。实验中这种来源通过 catalog Service 发 POST 可以到达应用,和普通 Ambient 客户端的 403 形成对照。
如果安全要求不允许这段差异,应先在应用或既有入口补等价授权,或安排暂停调用的迁移窗口。不能为了让连通测试通过,把整片 namespace 无条件加入允许集合后就结束迁移。
在维护窗口把同一组服务迁移到 Ambient
以下操作只处理主实验的 mesh-sidecar;名称保留,数据面改变。mesh-ambient 的原实验不动。入口实验应已按前面的步骤撤销,合成客户端没有持续业务负载。真实系统还要停止其他入口、定时任务和外部调用,等待在途请求结束。
先创建目标 waypoint,保存业务对象身份,然后准备三份迁移配置:
kubectl --kubeconfig "$KCFG" get deployment -n mesh-sidecar -o json > migration-app-before.json
"$ISTIOCTL" --kubeconfig "$KCFG" waypoint apply -n mesh-sidecar \
--name catalog-waypoint --for service --revision stable-a --wait
WAYPOINT_SA=$(kubectl --kubeconfig "$KCFG" get deployment catalog-waypoint -n mesh-sidecar \
-o jsonpath='{.spec.template.spec.serviceAccountName}')
test -n "$WAYPOINT_SA"
python3 migration-files.py --output migration-generated --waypoint-sa "$WAYPOINT_SA"生成器只把已有 Ambient 文件中的 namespace 改为 mesh-sidecar,并把后端 L4 允许身份设为刚读出的 waypoint SA;不会安装或修改集群。目标规则应为:Service HTTPRoute 区分 v1/v2;targetRefs 授权允许 mesh-sidecar/sa/client 的 GET /version;后端 L4 只允许 mesh-sidecar/sa/$WAYPOINT_SA 访问 8080。
暂停两个实验客户端,确认它们的旧 Pod 已退出:
kubectl --kubeconfig "$KCFG" scale deployment/client deployment/outsider -n mesh-sidecar --replicas=0
kubectl --kubeconfig "$KCFG" wait --for=delete pod -n mesh-sidecar -l app=client --timeout=120s
kubectl --kubeconfig "$KCFG" wait --for=delete pod -n mesh-sidecar -l app=outsider --timeout=120s
kubectl --kubeconfig "$KCFG" delete -f route-sidecar.yaml
kubectl --kubeconfig "$KCFG" apply -f migration-generated
kubectl --kubeconfig "$KCFG" label service catalog -n mesh-sidecar \
istio.io/use-waypoint=catalog-waypoint
kubectl --kubeconfig "$KCFG" delete authorizationpolicy catalog-client -n mesh-sidecar
kubectl --kubeconfig "$KCFG" label namespace mesh-sidecar istio.io/rev-
kubectl --kubeconfig "$KCFG" label namespace mesh-sidecar istio.io/dataplane-mode=ambient
kubectl --kubeconfig "$KCFG" rollout restart deployment/catalog-v1 deployment/catalog-v2 -n mesh-sidecar
kubectl --kubeconfig "$KCFG" rollout status deployment -n mesh-sidecar --timeout=180s
kubectl --kubeconfig "$KCFG" scale deployment/client deployment/outsider -n mesh-sidecar --replicas=1
kubectl --kubeconfig "$KCFG" rollout status deployment -n mesh-sidecar --timeout=180s旧 selector L7 策略和新 waypoint 策略的执行点不同;切换期间有策略替换过程,不能对有连续授权要求的业务直接宣称无缝迁移。Migrate policies还列出了 EnvoyFilter、WasmPlugin 等依赖的迁移限制;现有功能没有等价承接时应先停在这里解决。
检查业务 Pod 中已经没有 istio-proxy,但 waypoint Pod 仍应运行 Envoy。ztunnel-config workloads 从 istio-system 的 ztunnel 读取工作负载表,不要用 -n mesh-sidecar 把命令的代理目标改到错误 namespace:
kubectl --kubeconfig "$KCFG" get pod -n mesh-sidecar -l 'app in (catalog,client,outsider)' -o json \
| jq '.items[] | {name:.metadata.name,containers:[.spec.containers[].name],initContainers:[.spec.initContainers[]?.name]}'
"$ISTIOCTL" --kubeconfig "$KCFG" ztunnel-config workloads
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-H 'x-catalog-version: v2' http://catalog:8080/version应返回 v2,去掉版本头应返回 v1。再运行 outsider GET 和正常 client POST,二者都应为 403。业务 Deployment UID 应与迁移前一致,Pod UID 改变。若只有正常请求通过、负例没有复查,尚不能判断授权迁移正确。
回到 Sidecar
回迁同样暂停调用。先解绑 waypoint 并撤除 Ambient 路由与授权,再恢复旧配置和注入标签,不能把只允许 waypoint 的 L4 策略留给旧 Sidecar 后端:
kubectl --kubeconfig "$KCFG" scale deployment/client deployment/outsider -n mesh-sidecar --replicas=0
kubectl --kubeconfig "$KCFG" wait --for=delete pod -n mesh-sidecar -l app=client --timeout=120s
kubectl --kubeconfig "$KCFG" wait --for=delete pod -n mesh-sidecar -l app=outsider --timeout=120s
kubectl --kubeconfig "$KCFG" label service catalog -n mesh-sidecar istio.io/use-waypoint-
kubectl --kubeconfig "$KCFG" delete httproute catalog -n mesh-sidecar
kubectl --kubeconfig "$KCFG" delete authorizationpolicy catalog-l4 catalog-http -n mesh-sidecar
kubectl --kubeconfig "$KCFG" apply -f security-sidecar.yaml -f route-sidecar.yaml
kubectl --kubeconfig "$KCFG" label namespace mesh-sidecar istio.io/dataplane-mode-
kubectl --kubeconfig "$KCFG" label namespace mesh-sidecar istio.io/rev=stable-a
kubectl --kubeconfig "$KCFG" rollout restart deployment/catalog-v1 deployment/catalog-v2 -n mesh-sidecar
kubectl --kubeconfig "$KCFG" rollout status deployment -n mesh-sidecar --timeout=180s
kubectl --kubeconfig "$KCFG" scale deployment/client deployment/outsider -n mesh-sidecar --replicas=1
kubectl --kubeconfig "$KCFG" rollout status deployment -n mesh-sidecar --timeout=180s业务 Pod 应再次带有 Sidecar。两个版本与两种 403 负例都恢复后,再删除已闲置的迁移 waypoint:
kubectl --kubeconfig "$KCFG" delete gateway.gateway.networking.k8s.io catalog-waypoint \
-n mesh-sidecar --wait=true这里删除的是网关资源及其受控代理,业务 Deployment、Service 与数据不删除。原 mesh-ambient 的 waypoint 位于另一个 namespace,不受这条命令影响。
移出两种数据面,保留业务资源
移除网格后,原来由代理提供的 mTLS、方法限制、路由和重试会消失。真实系统应先把仍然需要的能力迁入应用、入口或其他平台,并验证替代路径。下面的 catalog 是允许直接访问的合成服务,因此可以在暂停客户端后撤掉这些实验规则。
先确保已经完成上一节回迁,两个 namespace 分别回到 Sidecar 和 Ambient 基线。记录对象后暂停调用:
kubectl --kubeconfig "$KCFG" get deployment -n mesh-sidecar -o json > exit-sidecar-before.json
kubectl --kubeconfig "$KCFG" get deployment -n mesh-ambient -o json > exit-ambient-before.json
for NS in mesh-sidecar mesh-ambient; do
kubectl --kubeconfig "$KCFG" scale deployment/client deployment/outsider -n "$NS" --replicas=0
kubectl --kubeconfig "$KCFG" wait --for=delete pod -n "$NS" -l app=client --timeout=120s
kubectl --kubeconfig "$KCFG" wait --for=delete pod -n "$NS" -l app=outsider --timeout=120s
done
kubectl --kubeconfig "$KCFG" delete -f security-sidecar.yaml -f route-sidecar.yaml
kubectl --kubeconfig "$KCFG" delete -f security-ambient-l4.yaml \
-f security-ambient-l7.yaml -f route-ambient.yaml
kubectl --kubeconfig "$KCFG" label service catalog -n mesh-ambient istio.io/use-waypoint-
kubectl --kubeconfig "$KCFG" label namespace mesh-sidecar istio.io/rev-
kubectl --kubeconfig "$KCFG" label namespace mesh-ambient istio.io/dataplane-mode-
kubectl --kubeconfig "$KCFG" rollout restart deployment/catalog-v1 deployment/catalog-v2 -n mesh-sidecar
kubectl --kubeconfig "$KCFG" rollout status deployment -n mesh-sidecar --timeout=180s
for NS in mesh-sidecar mesh-ambient; do
kubectl --kubeconfig "$KCFG" scale deployment/client deployment/outsider -n "$NS" --replicas=1
kubectl --kubeconfig "$KCFG" rollout status deployment -n "$NS" --timeout=180s
doneSidecar 从 Pod 模板注入,移除标签后仍要重建旧 Pod;Ambient 的节点捕获可以在标签撤除后解除,不需要为了删除 Pod 内容器而重建业务。实验中 Ambient 两个 catalog Pod 的 UID 保持不变;客户端因为人为缩容后重新启动,UID 会变化。
业务 Pod 中不再有代理后,逐个版本 Service 读回,避免把普通 Service 的随机负载均衡误判为版本路由残留:
for NS in mesh-sidecar mesh-ambient; do
for EXPECTED in v1 v2; do
ACTUAL=$(kubectl --kubeconfig "$KCFG" exec -n "$NS" deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
"http://catalog-$EXPECTED:8080/version") || exit 1
test "$ACTUAL" = "$EXPECTED" || exit 1
done
done
kubectl --kubeconfig "$KCFG" exec -n mesh-plain deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
-X POST http://catalog-v1.mesh-ambient:8080/version最后一次 POST 现在返回 v1,表明原网格七层拒绝已不再保护该演示服务。生产若仍要求拒绝这类请求,必须先让替代授权机制通过负例,不能照此撤掉保护。
只在专用集群卸载共享组件
业务已移出数据面、入口也已撤下之后,删除剩余实验 waypoint:
kubectl --kubeconfig "$KCFG" delete gateway.gateway.networking.k8s.io catalog-waypoint \
-n mesh-ambient --wait=true
"$ISTIOCTL" --kubeconfig "$KCFG" uninstall --purge --dry-run --skip-confirmation预览会包含 istiod、CNI、ztunnel、Istio RBAC、Webhook 与 Istio CRD。--purge 会移除共享 API,其下的自定义资源也会随之删除;不能用于还有其他团队工作负载的共享集群。先保存需要恢复的安装、路由和安全配置,再确认这是从起步步骤创建的专用 kind:
CLUSTER_NAME=$(kubectl --kubeconfig "$KCFG" config view --minify -o json \
| jq -er '.clusters[0].name') || exit 1
test "$CLUSTER_NAME" = kind-istio-lab || { printf '%s\n' '目标不是专用 istio-lab' >&2; exit 1; }
read -r -p '输入 REMOVE-ISTIO-LAB 确认卸载该集群的全部 Istio:' CONFIRM
if [ "$CONFIRM" = REMOVE-ISTIO-LAB ]; then
"$ISTIOCTL" --kubeconfig "$KCFG" uninstall --purge --skip-confirmation
else
printf '%s\n' '未卸载'
exit 1
fi安装器返回后,API 对象可能仍在异步删除。只等待仍存在的 Istio CRD,不删除其他产品的 CRD:
kubectl --kubeconfig "$KCFG" get crd -o json > private/exit-crds.json
mapfile -t ISTIO_CRDS < <(jq -r '.items[] | select(.spec.group | endswith("istio.io")) | "crd/" + .metadata.name' private/exit-crds.json)
if ((${#ISTIO_CRDS[@]})); then
kubectl --kubeconfig "$KCFG" wait --for=delete "${ISTIO_CRDS[@]}" --timeout=120s
fi
kubectl --kubeconfig "$KCFG" get crd -o json \
| jq -e '[.items[] | select(.spec.group | endswith("istio.io"))] | length == 0'
kubectl --kubeconfig "$KCFG" rollout restart deployment/client -n mesh-sidecar
kubectl --kubeconfig "$KCFG" rollout status deployment/client -n mesh-sidecar --timeout=180s
kubectl --kubeconfig "$KCFG" exec -n mesh-sidecar deployment/client -c client -- \
curl -q --noproxy '*' --fail-with-body --silent --show-error --max-time 5 \
http://catalog-v2:8080/version新客户端应成功创建并返回 v2,这同时检查了退出后新 Pod 的网络创建路径。业务 Deployment UID 与退出前保持一致。Gateway API CRD、Kubernetes 集群、业务 namespace、应用以及部分未由卸载器管理的密钥对象可能仍保留,应按各自管理方式处理,不再追加一个不加选择的 namespace 或集群删除命令。
权威资料与规范地址
按当前配置对象查阅 API 字段、版本限制与完整操作说明。
安装、版本与 Pod 生命周期
- Istio 支持组合:https://istio.io/latest/docs/releases/supported-releases/
- 发行包:https://istio.io/latest/docs/setup/additional-setup/download-istio-release/
- CNI:https://istio.io/latest/docs/setup/additional-setup/cni/
- 注解参考:https://istio.io/latest/docs/reference/config/annotations/
- Kubernetes 原生 Sidecar:https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/
- Pod 拓扑分布:https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/
- Ambient 安装:https://istio.io/latest/docs/ambient/install/istioctl/
路由、身份与安全
- VirtualService:https://istio.io/latest/docs/reference/config/networking/virtual-service/
- DestinationRule:https://istio.io/latest/docs/reference/config/networking/destination-rule/
- PeerAuthentication:https://istio.io/latest/docs/reference/config/security/peer_authentication/
- AuthorizationPolicy:https://istio.io/latest/docs/reference/config/security/authorization-policy/
- waypoint:https://istio.io/latest/docs/ambient/usage/waypoint/
- 安全实践:https://istio.io/latest/docs/ops/best-practices/security/
- 身份与 PKI:https://istio.io/latest/docs/concepts/security/
- 请求令牌验证:https://istio.io/latest/docs/reference/config/security/request_authentication/
入口、出口、多集群与 VM
- Gateway API:https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/
- HTTPS 网关:https://istio.io/latest/docs/tasks/traffic-management/ingress/secure-ingress/
- 出口网关:https://istio.io/latest/docs/tasks/traffic-management/egress/egress-gateway/
- 多集群前置准备:https://istio.io/latest/docs/setup/install/multicluster/before-you-begin/
- 不同网络的双主部署:https://istio.io/latest/docs/setup/install/multicluster/multi-primary_multi-network/
- WorkloadGroup:https://istio.io/latest/docs/reference/config/networking/workload-group/
- VM 安装:https://istio.io/latest/docs/setup/install/virtual-machine/
观测与性能
- 访问日志:https://istio.io/latest/docs/tasks/observability/logs/access-log/
- Envoy 统计:https://istio.io/latest/docs/ops/configuration/telemetry/envoy-stats/
- 性能与伸缩:https://istio.io/latest/docs/ops/deployment/performance-and-scalability/
升级与模式迁移
- revision 升级:https://istio.io/latest/docs/setup/upgrade/canary/
- 1.31 升级说明:https://istio.io/latest/news/releases/1.31.x/announcing-1.31/upgrade-notes/
- 1.30.4 发行:https://github.com/istio/istio/releases/tag/1.30.4
- Ambient 组件升级:https://istio.io/latest/docs/ambient/upgrade/helm/
- 混合模式与 HBONE:https://istio.io/latest/docs/ambient/migrate/install-ambient-components/
- 策略迁移:https://istio.io/latest/docs/ambient/migrate/migrate-policies/
