4. 流量治理基础
在 OpenShift Service Mesh 中,Istio 控制面把“流量怎么走”拆成三类可组合的声明式资源:Gateway 负责把集群外的南北向流量接进来,VirtualService 负责在服务网格内部决定请求匹配哪条路由并转发到哪个目标,DestinationRule 负责在选定目标之后定义“以什么策略访问这个目标”,例如子集划分、负载均衡、连接池与熔断。三者不是并列替代关系,而是入口 → 路由匹配 → 目标策略的接力关系。理解这条链路,是后续灰度发布、超时重试、故障注入等治理能力的基础。
先看职责边界。Gateway 只描述监听端口、协议、TLS 与主机名,它本身不决定后端服务;VirtualService 通过 gateways 字段绑定 Gateway,用 hosts 声明它服务哪些域名,再用 http 或 tcp 规则做匹配;DestinationRule 则用 host 指向某个 Kubernetes Service,并通过 subsets 把该 Service 的 Pod 按标签切成不同版本子集。一个常见的误区是:只在 VirtualService 里写 subset: v2,却没有在 DestinationRule 中定义 v2 子集,结果路由规则被 Envoy 拒绝或回退,表现为 503。因此子集必须先在 DestinationRule 中声明,VirtualService 才能引用。
下面用一个可运行的示例把三者串起来。假设集群中已有 product-svc,其 Pod 带有 version=v1 与 version=v2 两组标签。目标是把 shop.example.com/api/v1 的请求按 90/10 分流到 v2 与 v1,并对外只暴露 443 端口。
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: shop-gw
namespace: bookinfo
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: shop-cert
hosts:
- shop.example.com
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: product-vs
namespace: bookinfo
spec:
hosts:
- shop.example.com
gateways:
- shop-gw
http:
- match:
- uri:
prefix: /api/v1
route:
- destination:
host: product-svc
subset: v2
weight: 90
- destination:
host: product-svc
subset: v1
weight: 10
timeout: 3s
retries:
attempts: 2
perTryTimeout: 1s
retryOn: 5xx
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: product-dr
namespace: bookinfo
spec:
host: product-svc
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
connectionPool:
tcp:
maxConnections: 100
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
这段配置里,Gateway 的 selector: istio: ingressgateway 表示它由入口网关的 Pod 承载;credentialName: shop-cert 引用的是预先创建在 istio-system 命名空间下的 TLS Secret。VirtualService 的 gateways 字段把 shop-gw 与当前规则绑定,hosts 与 Gateway 的 hosts 保持一致,否则请求会因域名不匹配而落到默认 404。路由部分先按 URI 前缀匹配,再按权重分流;timeout 与 retries 写在路由级别,表示对本次匹配到的请求生效。DestinationRule 则把 product-svc 拆成 v1、v2 两个子集,并设置轮询负载均衡与连接池上限。
配置完成后,验证顺序建议从外到内。先确认入口网关地址与证书是否就绪:
oc -n bookinfo get gateway shop-gw -o yaml
oc -n istio-system get secret shop-cert
oc -n bookinfo get virtualservice product-vs -o yaml
oc -n bookinfo get destinationrule product-dr -o yaml
再用 istioctl analyze -n bookinfo 检查资源之间是否存在引用错误,例如 VirtualService 引用了未定义的 subset、Gateway 未被任何 VirtualService 绑定等。随后通过入口网关发起请求,观察响应头中的版本标识:
for i in $(seq 1 20); do
curl -sk -H "Host: shop.example.com" \
https://$(oc -n istio-system get route istio-ingressgateway -o jsonpath='{.spec.host}')/api/v1/info \
-o /dev/null -w "%{http_code}\n"
done
如果 v2 与 v1 的响应中带有不同的版本头,且比例大致接近 9:1,说明权重路由生效。若出现 503,优先检查 DestinationRule 中是否真的存在对应 subset,以及 subset 的标签是否与 Pod 实际标签一致;若出现 404,则检查 VirtualService 的 hosts 与 Gateway 的 hosts 是否一致、URI 前缀是否匹配。若请求全部落到 v1,则检查权重字段是否被写成了整数以外的格式,或是否存在更高优先级的匹配规则抢先命中。
selector 通常指向 istio: ingressgateway,而入口网关本身由 istio-system 命名空间下的 Deployment 与 Service 承载。若使用 OpenShift Route 暴露入口网关,Route 只负责把外部流量送到网关 Service,真正的域名与 TLS 终止仍由 Gateway 资源控制,两者不要混淆。最后需要强调匹配优先级。VirtualService 中的多条 http 规则按从上到下的顺序匹配,第一条命中的规则生效,因此精确匹配应放在前缀匹配之前。权重只在同一条路由的多个 destination 之间分配,不同路由之间不存在权重叠加。DestinationRule 的 trafficPolicy 可以写在顶层作为默认策略,也可以写在每个 subset 下覆盖默认值,例如给 v2 单独设置更小的连接池或不同的负载均衡算法。掌握这三类资源的分工与引用关系,后续的灰度发布、超时重试、熔断与故障注入才能在同一套路由骨架上稳定叠加。