深色模式
Istio 流量管理 VirtualService / DestinationRule
摘要:本文面向需要在生产环境做灰度、重试、超时、熔断与故障注入的 SRE。解释 VirtualService(路由意图)与 DestinationRule(目标子集与连接池)的协作关系,给出完整 YAML(apiVersion
networking.istio.io/v1beta1),覆盖版本 Istio 1.31。
为什么需要流量管理抽象
原生 Kubernetes Service 只做同权负载均衡,无法按百分比切流、按 Header 路由、注入故障或熔断。Istio 在 Service 之上叠加两层 CRD:
- VirtualService(VS):声明“流量怎么走”——匹配条件、权重、重试、超时、故障注入、镜像。是一种意图,不直接路由,由 Envoy 执行。
- DestinationRule(DR):声明“目标是谁”——命名子集(subsets,来自 Pod 标签)、连接池限制、 outlier detection(熔断)、负载均衡策略、mTLS 模式。VS 的
subset必须能在 DR 中找到对应定义。
二者关系
VS 负责“选哪条路”,DR 负责“路尽头的那批实例怎么分组与保护”。VS 引用 DR 中定义的 subset 名;若 VS 引用的 subset 不存在,路由静默失效或回退到无 subset 的目标。
架构与数据流
生产实践:按比例切流 + 重试 + 超时
下列示例部署 reviews 服务的两个版本(标签 version: v1 / version: v2),并将 90% 流量给 v1、10% 给 v2,并配置重试与超时。
yaml
# DestinationRule:定义子集与连接池/熔断
apiVersion: networking.istio.io/v1beta1 # [版本相关] Istio 1.31 稳定;旧版 1.2x 同 group
kind: DestinationRule
metadata:
name: reviews
namespace: bookinfo
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 50
maxRequestsPerConnection: 2
outlierDetection:
consecutiveErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50 # 至多摘除 50%,保证可用性
---
# VirtualService:权重路由 + 重试 + 超时
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
namespace: bookinfo
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v2
weight: 10
retries:
attempts: 3
perTryTimeout: 2s
retryOn: "gateway-error,connect-failure,refused-stream,5xx"
timeout: 10s1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
熔断的可用性权衡
maxEjectionPercent: 50 保证系统性故障时仍保留一半容量,牺牲“完全正确”换取可用性。若把该值设成 100,一次误报可能导致全部实例被摘除,造成雪崩。生产请结合 consecutiveErrors 与真实错误基线调参。
按 Header / URI 路由(金丝雀与 A/B)
yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
namespace: bookinfo
spec:
hosts:
- reviews
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: reviews
subset: v2 # 命中 canary header 全部走 v2
- match:
- headers:
user-agent:
prefix: "Mobile"
route:
- destination:
host: reviews
subset: mobile
- route: # 默认路由
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v2
weight: 101
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
匹配按 VS 中
http列表的自上而下顺序生效,命中即停。把更具体的match放在前面。
故障注入(韧性测试)
在带特定测试 Header 的请求上注入延迟或错误,验证调用方韧性,不影响真实用户:
yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: ratings-fault
namespace: bookinfo
spec:
hosts:
- ratings
http:
- match:
- headers:
x-test-scenario:
exact: "latency"
fault:
delay:
percentage:
value: 100
fixedDelay: 7s
route:
- destination:
host: ratings
- match:
- headers:
x-test-scenario:
exact: "error"
fault:
abort:
percentage:
value: 50
httpStatus: 500
route:
- destination:
host: ratings
- route:
- destination:
host: ratings1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
生产危险
故障注入默认对所有匹配请求生效。上线前务必用 match + 测试 Header 限定范围,并在变更窗口结束后立即删除或置零 percentage,避免误伤生产流量。
验证、回滚与清理
bash
# 验证配置合法性与悬空引用
istioctl analyze -n bookinfo
# 查看单个 Sidecar 实际收到的 xDS(排障真相来源)
istioctl proxy-config routes deploy/reviews-v1 -n bookinfo
istioctl proxy-config clusters deploy/reviews-v1 -n bookinfo
# 回滚:把权重改回 100/0,或直接删除 VS/DR
kubectl apply -f vs-stable-only.yaml -n bookinfo
# 或 kubectl delete virtualservice reviews -n bookinfo
# 清理
kubectl delete virtualservice reviews reviews-fault -n bookinfo
kubectl delete destinationrule reviews -n bookinfo1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
故障排查:路由“不生效”的常见根因
- host 写错:VS 的
hosts与destination.host应为 K8s Service 名(或 FQDN)。拼写错误导致 Envoy 找不到匹配路由。 - subset 不匹配标签:DR 的
subset.labels必须与 Pod 实际标签一致;标签不符则子集为空,请求回退。 - revision 不一致:控制面金丝雀升级时,某命名空间用的是旧 revision,新配置未推送。用
proxy-config对比期望与实际。 - 跨命名空间 host:跨 ns 引用需写 FQDN
host.namespace.svc.cluster.local。
排障口诀
“我写的 YAML” ≠ “Envoy 实际收到的 xDS”。一切以 istioctl proxy-config 输出为准。
替代方案与权衡
- Gateway API(HTTPRoute):Istio 官方推荐的未来方向,vendor-neutral、可移植,新路由特性优先落在此。但
VirtualService仍是功能最全、存量最大的入口。 - Argo Rollouts / Flagger:在 VS/DR 之上做自动化渐进式交付,适合 CI/CD 集成。
- 与 Linkerd 对比:Linkerd 也支持流量拆分(
TrafficSplit),但高级路由(Header 路由、故障注入、镜像)能力弱于 Istio。