库存在集群外,orders 找到的到底是谁

同一个 orders → inventory 报价,如果库存从 Kubernetes Pod 迁到独立虚拟机,应用的 URL 不一定需要变,但服务发现必须把 VM 地址纳入目标集合,且 VM 上的数据面要拿到可信身份。把一个普通 Pod 标注 vm=true,依然是 Pod、Kubernetes 身份与 Pod 网络,并没有验证跨边界注册。本文以 Istio 1.27 文档快照设计双端点用例;并未在当前机器安装 Istio。

Kubernetes 服务发现到独立 VM 的连接,VM 代理先获取控制面配置与身份

图上 orders → 源代理 →(视网络拓扑经网关)→ VM 代理 → inventory 是数据通道;Istiod → VM 的注册发现与证书/配置是独立的启动依赖。若 VM 在同网络且直接可达,不应无证据地画成必经东西向网关。反过来,仅在控制面看到 WorkloadEntry,也不能证明真实入口端口/防火墙可达。相关基础见 19 身份、29 控制面失效 和 32 多集群。

谁注册,谁发证,谁连接

VM 启动和接收请求之间存在依赖,不是一个“已注册”布尔值:

1
2
WorkloadEntry/服务发现 ──→ orders 看见候选 VM 地址
身份引导 ──→ VM 代理取证书 ──→ 建立受信连接 ──→ VM inventory 处理
注入的故障 orders 端可能看到 需要定位的边界
撤掉发现条目 新请求无 VM 候选 服务注册/端点收敛
错误 VM 凭据 地址可见,代理可能尚未就绪 身份签发、SDS 与对等验证分别检查
网络不通 有地址,连接超时/失败 防火墙/网关可达性

在受控 VM/独立宿主上运行 inventory 与 Istio 代理。按 Istio 1.27 VM 安装指南配置 WorkloadGroup 或 WorkloadEntry、服务关联、网络和代理启动参数;WorkloadEntry 是向网格描述非 Pod 工作负载的资源,应用进程仍须真的在独立地址提供 /quote。身份引导材料须通过合适的秘密分发机制进入 VM,代理在控制面可达时申请/轮换短期证书;不得提交引导 token、私钥或把测试证书复制进文章。先确认代理有有效身份和端点,再确认 orders 连接目标确实是 VM,而不是同名 Service 中仍在运行的 Pod。

对相同 sku=paper,正常路径保存注册资源、证书公开身份、代理端点视图、客户端带 ID 原始响应、VM inventory 应用日志和网络连接目标;失败路径先移除隔离实验里的 VM 发现条目,核对旧连接与新建连接是否仍能发出请求,再恢复。另一组以错误身份或过期凭据测试认证拒绝:发现失败与 TLS 拒绝是两种不同故障,不能只看“请求失败”混为一谈。VM 启动时若控制面/身份发放端暂不可达,新旧代理状态也不同;只有实际启动日志和证书状态能解释差异。

当前累计工程的 inventory-v1/v2 都是本机应用进程基线;它们既没有 VM 引导身份,也没有跨 Kubernetes 发现。宿主无 kubectl/context、VM 测试环境或可验证代理,因此 VM 注册、证书与连接实验 NOT_RUN;源码和官方配置阅读只支撑设计。复跑前分别核对隔离集群 context、独立宿主所有权及网络可达性,不操作生产 VM。

单网络实验:先固定连接地址,再启用身份

工程目录 examples/service-mesh/electives/E04/ 提供完整的 WorkloadGroup、ServiceEntry、WorkloadEntry 模板、入站与出站 TLS 策略,以及 VM 安装脚本。基线选择单网络:Kubernetes Pod 能直接连接独立 Linux VM 的 8080,VM 能连接暴露的 Istiod。跨网络部署还要增加网关与 network 映射,不能只把模板里的空字符串改掉就算完成。

inventory.vm.mesh:8080 是实验服务名,240.0.0.10 是代理捕获用的专用 VIP。VIP 不等于 VM 的物理地址。客户端使用 curl --resolve 明确把域名解析到 VIP,避免依赖宿主 DNS 配置;源 sidecar 再按 ServiceEntry 选择 VM 的真实地址。部署前必须保证这个 VIP 没有与实验网络其他用途冲突。

四种资源的责任不同:WorkloadGroup 给出一组 VM 的身份与标签模板;WorkloadEntry 登记某一台 VM 的地址;ServiceEntry 用选择器将条目组成服务;DestinationRule 指定源端如何发起 TLS。注册资源不会在 VM 上安装代理、启动 inventory,也不会自动放通防火墙。

在已安装 Istio 1.27.0 的隔离集群操作,SM_CONTEXT 必须是该集群的显式 context,ISTIO_DIR 是对应版本发行包目录。先暴露控制面,再建立服务资源:

1
2
3
4
5
"$ISTIO_DIR/samples/multicluster/gen-eastwest-gateway.sh" --single-cluster | istioctl --context "$SM_CONTEXT" install -y -f -
kubectl --context "$SM_CONTEXT" -n istio-system apply -f "$ISTIO_DIR/samples/multicluster/expose-istiod.yaml"
kubectl --context "$SM_CONTEXT" apply -f electives/E04/resources.yaml
VM_IP=192.0.2.10 envsubst < electives/E04/workload-entry.yaml.in > /tmp/e04-entry.yaml
kubectl --context "$SM_CONTEXT" apply -f /tmp/e04-entry.yaml

192.0.2.10 是文档地址,运行时换成实际 VM 地址;这里没有声称它可以连通。该基线采用手工 WorkloadEntry,不启用自动注册,因而删除条目的故障不会被 agent 立即重新注册所掩盖。若改用 --autoregister,还必须启用控制面的自动注册能力,并把健康检查作为独立条件。

引导文件如何变成工作负载证书

从集群导出 WorkloadGroup,并生成引导材料。E04_PRIVATE_DIR 放在仓库之外,只允许当前用户读取;材料中包含短期 ServiceAccount token,不进入证据目录。

1
2
3
4
umask 077
mkdir -p "$E04_PRIVATE_DIR"
kubectl --context "$SM_CONTEXT" -n vm-lab get workloadgroup inventory-vm -o yaml > "$E04_PRIVATE_DIR/workloadgroup.yaml"
istioctl --context "$SM_CONTEXT" x workload entry configure -f "$E04_PRIVATE_DIR/workloadgroup.yaml" -o "$E04_PRIVATE_DIR/bootstrap" --clusterID Kubernetes

--clusterID 必须等于控制面安装时的 clusterName;Kubernetes 只对应官方单集群默认例子。生成的 cluster.env 定义 namespace、ServiceAccount 和捕获端口,mesh.yaml 指向发现地址,hosts 提供 Istiod 名称解析,root-cert.pem 验证控制面,istio-token 用于申请工作负载证书。引导 token 与后来经 SDS 下发的证书不是同一份凭据。

在独立 Debian/Ubuntu VM 安装固定版本运行包,再通过受控通道传入 bootstrap 目录:

1
2
3
4
curl -fLO https://storage.googleapis.com/istio-release/releases/1.27.0/deb/istio-sidecar.deb
sudo dpkg -i istio-sidecar.deb
BOOTSTRAP_DIR=/secure/e04/bootstrap bash electives/E04/vm-install.sh
./mesh-demo -role inventory -listen 0.0.0.0:8080 -version vm

mesh-demo 来自累计工程的 apps/mesh-demo,应按 VM 架构交叉编译、传输并记录哈希。运行包也要记录来源、包信息和 SHA-256。应用不要以 istio-proxy 用户运行,否则它的流量可能落入用于避免代理回环的排除规则。VM 实际可接入的操作系统、内核权限和 iptables 行为需要现场核对。

TLS 失败的三个不同时间点

资源文件同时设置入站 PeerAuthentication STRICT、出站 DestinationRule ISTIO_MUTUAL,并把 SAN 约束为 spiffe://cluster.local/ns/vm-lab/sa/inventory-vm。这里用默认 trust domain;修改 trust domain 时要同步身份约束。STRICT 表示受策略覆盖的入站连接要求 mTLS;ISTIO_MUTUAL 表示源代理使用 Istio 管理的证书发起 mTLS,不能把它理解成已经握手成功。

如果错误 token 导致 VM 尚未取得证书,SDS 依赖的 listener 或 cluster 可能仍在 warming,代理也可能没有就绪,源端最终表现为连接失败或超时。此时没有证据说明对端收到证书后拒绝了身份。只有双方已经取得证书、实际发生握手,且错误链/SAN 被校验失败,才属于 TLS 身份验证拒绝。握手之后再被 AuthorizationPolicy 拒绝,则又是授权阶段。

三个检查应分别保存:istioctl proxy-status 的配置同步状态,VM agent 日志中 SDS 密钥对和 ROOTCA 的加载状态,以及真实请求的连接/握手错误。curl localhost:15021/healthz/ready 只证明 readiness 端点;仍需结合 curl localhost:15000/certs 的公开证书元数据确认有效期与身份,不导出私钥。源端 proxy-config secret 只说明源代理持有证书,不证明 VM 持有证书。

正常、撤销与恢复

在 vm-lab 部署发行包的 samples/curl/curl.yaml,其 namespace 已启用 sidecar 注入。先检查 Pod 确有 istio-proxy,再设置 SM_CLIENT_POD 并执行 bash scripts/run-E04.sh。脚本保存资源、客户端 SDS 摘要、端点视图和请求,VM 上另外保存 inventory 的 E04-probe 请求日志、代理 readiness 和证书摘要。成功条件是响应版本为 vm,源端选中的地址是 VM,VM 应用确有相同 ID,三者一致。

从同一隔离集群删除 inventory-vm-1,等待源端 EDS 不再包含其地址,然后发起新 curl 进程;恢复用原 /tmp/e04-entry.yaml。旧连接可能短暂继续使用旧状态,因此“删除对象后立即请求成功”不能直接判定策略无效。接着单独测试 VM 启动时错误 token,保存代理未就绪与 SDS 错误;恢复正确 token 并重启 agent,等待证书与 readiness 恢复后再请求。已经取得的证书不会因 bootstrap token 文件变化而瞬间消失,不应通过替换 token 假装完成证书撤销。

需求关键词 可迁移原则 本例执行点
地址有了但请求失败 分开检查发现、就绪、握手与应用执行 WorkloadEntry、SDS、代理、请求 ID
引导凭据更新 引导与持续身份分别管理 token 申请证书,证书有独立生命周期
撤销端点 区分控制面删除和数据面收敛 EDS 新视图及新连接

练习与参考解答

  1. 画出 WorkloadEntry 存在但 VM 代理未取得证书时,新请求会停在何处。参考解答:先核对 STRICT、ISTIO_MUTUAL 及流量是否被代理捕获;代理可能等待 SDS、listener 未就绪,也可能在连接阶段失败。不能直接写成对端身份拒绝。只有握手日志才支持握手归因,业务执行必须由 VM 的请求 ID 日志证明。
  2. 把 Pod 的 hostname 改成 inventory-vm,可以代替身份失败对照吗?参考解答:不能。Pod 仍由 Kubernetes 调度且走原工作负载身份/网络;应使用独立宿主的 WorkloadEntry 和 VM 代理,显式验证实际连接地址及证书身份。

资料与系列导航

Istio 1.27 VM 安装、WorkloadEntry API;源码入口 Istio 1.27.0 serviceentry registry。研究记录 writing-plans/service-mesh/research/E04.md。

系列导航:E03 Proxyless gRPC → E04 VM 接入(本文) → E05 Envoy 外部授权。