在传统的微服务架构中,路由逻辑通常散落在网关配置、客户端负载均衡器甚至业务代码里,改一条转发规则往往要重新发布服务。服务网格的出现改变了这个局面:它把路由决策统一收编到数据面的边车代理中,控制面只需要下发一份声明式配置,所有流量行为即可实时调整。这篇文章以最主流的Istio为例,讲清楚服务网格的路由规则到底是怎么设计和生效的。

一、服务网格路由的核心模型:VirtualService与DestinationRule
Istio的路由体系由两个关键资源构成。VirtualService负责定义“流量从哪里来、匹配什么条件、转发到哪里去”,可以理解为一张流量决策表;DestinationRule则描述“目的地内部还有哪些细分版本”,定义服务的子集合(subset)、负载均衡策略和连接池参数。
举个例子,一个订单服务部署了v1和v2两个版本,网格内的访问流程是这样的:请求先到达Pod内的Envoy边车,Envoy根据VirtualService的匹配条件判断该走哪个分支,再由DestinationRule把分支映射到具体的Pod标签上。二者配合,才完成一次完整的路由。下面是一个最小可用的配置:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service
spec:
hosts:
- order-service
http:
- route:
- destination:
host: order-service
subset: v1
weight: 100
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: order-service
spec:
host: order-service
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
这段配置的含义是:所有访问order-service的HTTP流量,百分之百转发到打了version: v1标签的实例上。注意hosts字段支持短域名,网格会自动补全namespace后缀,跨命名空间访问时则要写完整的FQDN。
二、三种常用的路由匹配方式:权重、请求特征与路径
第一种是基于权重的路由,也就是常说的按比例分流。把上面的weight改成90和10,就能实现90%流量走v1、10%流量走v2的灰度效果。这种方式实现金丝雀发布非常方便,配合监控指标逐步调整比例即可:
http:
- route:
- destination:
host: order-service
subset: v1
weight: 90
- destination:
host: order-service
subset: v2
weight: 10
第二种是基于请求特征的精确匹配,支持请求头、URL参数、命名空间甚至Pod标签等维度。最常见的场景是“内部员工先体验新版本”:判断请求头中是否携带特定的用户标识,命中则转发到v2。匹配条件放在match字段中,多个条件之间是“与”的关系,同一个match列表内的多个元素是“或”的关系,这一点容易搞混:
http:
- match:
- headers:
x-user-type:
exact: internal
route:
- destination:
host: order-service
subset: v2
- route:
- destination:
host: order-service
subset: v1
第三种是基于路径的路由,类似传统Nginx的location匹配,可以把/api/v2前缀的请求单独剥离出来。匹配动作除了exact精确匹配,还有prefix前缀和regex正则三种。需要提醒的是,正则匹配的性能开销明显高于前两者,在高QPS场景下尽量用前缀匹配替代复杂正则。
此外还有一种基于来源的匹配,通过sourceLabels限定调用方的Pod标签,只有打了特定标签的工作负载发起的请求才走指定分支,常用于多租户隔离或者对特定调用方做差异化策略。
三、路由规则不生效时的排查思路
配置写了却没生效,是网格使用中的高频问题。排查时建议按固定顺序走:首先用istioctl proxy-config route <pod>查看Envoy实际收到的路由表,确认配置确实下发到了数据面。如果控制面上有配置但Envoy里没有,多半是Pilot到边车的推送出了问题,检查istiod的日志和代理的连通性。
其次要核对DestinationRule中的subset标签与目标Pod的实际标签是否一致。这是新手最容易踩的坑:Pod没有打version标签,或者标签拼写不一致,subset匹配不到任何实例,请求就会返回没有健康上游的错误。用kubectl get pods -l version=v2 --show-labels快速验证。
最后还要注意规则的作用域问题。VirtualService如果不挂在正确的Gateway或者目标服务的命名空间上,规则压根不会生效。Istio的根命名空间里定义的规则对所有命名空间可见,而普通命名空间里的规则只作用于同命名空间的服务,跨命名空间引用时记得检查Sidecar资源是否限制了导入范围。另外,HTTPS场景下如果Gateway没有正确配置TLS透传或终止,路由匹配可能根本拿不到HTTP层的请求信息,只能退化为TCP转发,此时所有HTTP级别的match都会失效。
掌握这三块内容——路由模型、匹配方式和排查方法,服务网格的流量管理基本就通了。剩下的重试、超时、故障注入等高级能力,都是建立在这套路由模型之上的叠加配置,理解了VirtualService与DestinationRule的分工,学起来会非常快。