服务网格Istio流量治理实践 十八岁 2026-11-07 2026-11-07
Istio流量治理实践 1.Bookinfo项目介绍 项目介绍:https://istio.io/latest/docs/examples/bookinfo/
后面所有练习都基于该项目:
2.部署Bookinfo项目 首先在 Kubernetes 集群中,部署 Bookinfo 项目。
Istio 提供了用于测试功能的应用程序 Bookinfo,可以直接通过下述命令创建 Bookinfo:
1 2 3 4 5 6 7 8 9 10 # 创建该项目的命名空间bookinfo [root@k8s-master01 istio-1.24.3]# kubectl create ns bookinfo namespace/bookinfo created # 使用istio管理bookinfo命名空间中的所有pod,给命名空间添加istio-injection=enabled标签,这样在bookinfo命名空间的所有pod都会被自动注入sidecar。也可以在kiali界面操作 # 使用命令添加标签 [root@k8s-master01 istio-1.24.3]# kubectl label ns bookinfo istio-injection=enabled namespace/bookinfo labeled # 在kiali控制台操作参考下图:
找到要管理的命名空间,这里是bookinfo,点击命名空间右侧的三个点,如下图:
然后再次点击Enable Auto Injection
在弹出的提示框选择Enable
看到该提示表示成功Namespace updated
验证bookinfo命名空间是否被添加istio-injection=enabled标签
1 2 3 4 [root@k8s-master01 istio-1.24.3]# kubectl get ns bookinfo --show-labels NAME STATUS AGE LABELS bookinfo Active 9m35s istio-injection=enabled,kubernetes.io/metadata.name=bookinfo # 可以看到LABELS列中已经有了istio-injection=enabled标签
创建项目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 在下载的istio安装包中已经自带了bookinfo项目所用的资源文件,不过需要修改镜像下载地址,我已经把用到的镜像同步到了自己的仓库中。 # 老规矩,在创建项目前修改yaml中的镜像下载地址,共修改6个镜像地址 文件全路径:/root/26-istio/istio-1.24.3/samples/bookinfo/platform/kube/bookinfo.yaml 修改后的内容参考如下: [root@k8s-master01 istio-1.24.3]# cat samples/bookinfo/platform/kube/bookinfo.yaml |grep image image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/examples-bookinfo-details-v1:1.20.2 imagePullPolicy: IfNotPresent image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/examples-bookinfo-ratings-v1:1.20.2 imagePullPolicy: IfNotPresent image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/examples-bookinfo-reviews-v1:1.20.2 imagePullPolicy: IfNotPresent image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/examples-bookinfo-reviews-v2:1.20.2 imagePullPolicy: IfNotPresent image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/examples-bookinfo-reviews-v3:1.20.2 imagePullPolicy: IfNotPresent image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/examples-bookinfo-productpage-v1:1.20.2 imagePullPolicy: IfNotPresent
修改后开始创建bookinfo项目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # 使用-n参数 指定创建到bookinfo命名空间中 [root@k8s-master01 istio-1.24.3]# kubectl create -f samples/bookinfo/platform/kube/bookinfo.yaml -n bookinfo service/details created serviceaccount/bookinfo-details created deployment.apps/details-v1 created service/ratings created serviceaccount/bookinfo-ratings created deployment.apps/ratings-v1 created service/reviews created serviceaccount/bookinfo-reviews created deployment.apps/reviews-v1 created deployment.apps/reviews-v2 created deployment.apps/reviews-v3 created service/productpage created serviceaccount/bookinfo-productpage created deployment.apps/productpage-v1 created
查看pod和Service情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # pod [root@k8s-master01 istio-1.24.3]# kubectl get po -n bookinfo NAME READY STATUS RESTARTS AGE details-v1-5655bbb7bb-l8wkq 2/2 Running 0 77s productpage-v1-7f86d94f89-nkxnz 2/2 Running 0 76s ratings-v1-6dccb8487d-fn74n 2/2 Running 0 77s reviews-v1-5c8cf97b4d-npnqq 2/2 Running 0 76s reviews-v2-7685b9f4f4-md5v9 2/2 Running 0 76s reviews-v3-846946d6cb-l98z2 2/2 Running 0 76s # svc [root@k8s-master01 ~]# kubectl get svc -n bookinfo NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE details ClusterIP 10.96.79.2 <none> 9080/TCP 4m44s productpage ClusterIP 10.96.113.166 <none> 9080/TCP 4m43s ratings ClusterIP 10.96.151.91 <none> 9080/TCP 4m44s reviews ClusterIP 10.96.224.30 <none> 9080/TCP 4m44s
将名字是productpage的Service类型改为NodePort,来访问项目:
1 2 3 4 5 6 7 8 9 10 11 [root@k8s-master01 ~]# kubectl edit svc -n bookinfo productpage # 找到type : ClusterIP,并修改为NodePort类型 type: NodePort # 接下来可以通过30963端口访问项目 [root@k8s-master01 ~]# kubectl get svc -n bookinfo NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE details ClusterIP 10.96.79.2 <none> 9080/TCP 14m productpage NodePort 10.96.113.166 <none> 9080:30963/TCP 14m ratings ClusterIP 10.96.151.91 <none> 9080/TCP 14m reviews ClusterIP 10.96.224.30 <none> 9080/TCP 14m
3.Istio IngressGateway对外发布服务 测试可以使用NodePort类型的service进行访问,正式环境都是通过域名进行访问,这里着重讲解域名的方式,以bookinfo.ibstd.com域名为例:
整体流程如下:
①:配置DNS解析: 将 bookinfo.ibstd.com 解析到 Istio Ingress Gateway 的外部 IP 地址(我本地测试通过修改hosts实现)。
②:配置 Istio Gateway :创建一个 Istio Gateway 资源,用于配置域名、端口和协议(HTTP 或 HTTPS)。Gateway 负责管理进入 Istio 服务网格的流量。
③:配置 Istio VirtualService :创建一个 Istio VirtualService 资源,将 Gateway 中定义的流量路由到 Bookinfo 的 productpage 服务。VirtualService 定义了路由规则。
④:测试访问 :验证通过域名 bookinfo.ibstd.com 是否能够成功访问 Bookinfo 项目
具体操作参考下面步骤:
3.1配置DNS解析 将 bookinfo.ibstd.com 域名解析到Istio 的Ingress Gateway pod所在ip上,因为我测试环境配置的Ingress Gateway的Service类型是NodePort,本地 hosts 文件解析 是将 bookinfo.ibstd.com 解析 任意一个Kubernetes 节点的 IP。
1 2 3 # 配置如下:找到hosts文件添加192.168.0.81 bookinfo.ibstd.com # windwos的路径是:C:\Windows\System32\drivers\etc\hosts # centos7路径是:/etc/hosts
k8s节点IP如下:
3.2创建Gateway资源 在 Istio 中,Gateway 资源用于管理进入(Ingress)和离开(Egress)的流量。具体来说,Ingress Gateway 负责接收来自外部的请求,并根据配置的路由规则将流量转发到相应的服务。
1 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 # gateway资源配置解读: # ==================== 1. Gateway:定义 Istio 网关的监听规则 ==================== apiVersion: networking.istio.io/v1 # API 版本:表示使用 Istio 网络管理 API 的 v1 版本;集群必须已安装对应的 CRD kind: Gateway # 资源类型:Istio Gateway,负责配置入口网关的端口、协议和允许访问的主机名 metadata: # 定义资源元数据:描述这个 Gateway 资源本身 name: bookinfo-gateway #定义Gateway 资源的名称。名为 bookinfo-gateway,供 VirtualService.spec.gateways 引用;同一命名空间内必须唯一。 spec: # Gateway 的实际配置内容 selector: #标签选择器,指定由哪些网关 Pod 接收这份 Gateway 配置。 istio: ingressgateway # Pod 标签选择条件:选择带有 istio=ingressgateway 标签的工作负载处理流量,也就是说istio-ingressgateway的pod必须有istio=ingressgateway标签 servers: # 网关监听规则列表:一个 Gateway 可以配置多个监听端口或协议 - port: # 当前这条监听规则的端口配置 number: 80 #告诉 Envoy:“对于所有从 80 端口进来的流量,请按照我定义的规则(比如 host 是 bookinfo.ibstd.com)来处理。” 这里定义的端口号要和名字是istio-ingressgateway的Service的port字段保持一致。 name: http #为端口命名,便于在同一个 Gateway 中区分不同的端口和协议。名称应具有描述性唯一性,如 http、https 等。 protocol: HTTP # 监听协议:告诉 Istio 按 HTTP 协议处理进入网关的流量,要与 VirtualService.spec.http 的 HTTP 路由规则相对应 hosts: # 定义哪些主机名的请求将由此 Gateway 处理,允许处理的主机列表。必须与下面 VirtualService.spec.hosts 对应 - "bookinfo.ibstd.com" #定义bookinfo.ibstd.com的请求由此Gateway处理。可以根据需要添加多个主机名。 --- # ==================== 2. VirtualService:定义请求匹配及转发规则 ==================== apiVersion: networking.istio.io/v1 #指定api的版本 kind: VirtualService #定义创建资源的类型 metadata: # # VirtualService 自身的元数据 name: bookinfo # VirtualService资源的名字 spec: # 定义VirtualService 的实际配置内容,流量路由配置 hosts: #这条路由规则适用于哪些目标主机 - "bookinfo.ibstd.com" #应与 Gateway.spec.servers[].hosts 中允许的主机名匹配,这里定义的主机必须要被关联的Gateway.spec.servers.port[].host的值匹配到 gateways: # 指定哪些 Gateway 使用这套路由规则,与gateway资源关联 - bookinfo-gateway # 引用上述名字是 bookinfo-gateway 的 Istio Gateway 资源 # 如果VirtualService和Gateway在同一个空间,那直接写名字 # 如果 Gateway 位于 istio-system,而 VirtualService 位于 default # 则要加上命名空间,写成 istio-system/bookinfo-gateway http: # HTTP 路由规则列表:定义如何匹配 HTTP 请求及转发请求 - match: # 当前路由规则的匹配条件列表 # 下面的多个 match 条目之间是 OR(或)关系:只要任意一个条 目匹配,就可以执行当前 route - uri: # 第一个匹配条件:检查请求 URI 路径 exact: /productpage # 精确匹配 /productpage,例如 /productpage 匹配,但 /productpage/ 不匹配 - uri: # 第二个匹配条件 prefix: /static # 前缀匹配 /static 开头的路径,例如 /static/a.css 可以匹配 - uri: # 第三个匹配条件 exact: /login # 精确匹配,, /login匹配 , /login/不匹配 - uri: ## 第四个匹配条件 exact: /logout #精确匹配 /logout - uri: # 第五个匹配条件 prefix: /api/v1/products # 前缀匹配 /api/v1/products 开头的路径,例如 /api/v1/products 和 /api/v1/products/1 均可匹配 route: # 路由动作,指定上述路由规则匹配成功后的请求要转发到给谁处理 - destination: # 当前转发目标;route 是列表,可以配置一个或多个目标 host: productpage # 后端目标 Service 的名称,如果 Service 在其他命名空间,应使用完整名称,例如:productpage.bookinfo.svc.cluster.local port: # 后端目标服务的端口配置 number: 9080 # 目标 Service 端口号,应与 productpage Kubernetes Service 的 spec.ports[].port的值一致
3.3创建VirtualService VirtualService将网关接收的外部流量请求 路由到Kubernetes集群内的具体服务,实现了精确的流量控制。
VirtualService与Gateway资源协同工作:Gateway定义了进入网格的入口点,而VirtualService则定义了如何将进入的流量精确路由到集群内的各个服务。这种分离使得流量管理更加灵活和可配置。
总的来说,VirtualService在Istio服务网格中扮演”交通指挥官”的角色,确保外部访问请求被正确地导向集群内的目标服务。
1 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 apiVersion: networking.istio.io/v1 kind: Gateway metadata: name: bookinfo-gateway spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "bookinfo.ibstd.com" --- # VirtualService资源配置解析 apiVersion: networking.istio.io/v1 #指定了 Kubernetes API 的版本 kind: VirtualService #指定资源的类型是VirtualService。VirtualService 用于配置如何在服务网格内部路由和处理请求。 metadata: #定义资源的元数据 name: bookinfo #定义这个vs资源的名称,这里是bookinfo spec: #定义 VirtualService 的具体规格或配置。 hosts: #指定 VirtualService 要处理的目标主机 - "bookinfo.ibstd.com" #与上述 Gateway 中的 hosts 字段中的值匹配,也就是说只有请求的地址是bookinfo.ibstd.com时才会执行本vs规则中的定义的URL匹配和转发 gateways: #Gateway 接受请求后,Envoy 开始查找关联的 VirtualService - bookinfo-gateway #这里指定的是要绑定的Gateway资源的名字,把vs资源的路由逻辑(URI、权重、转发地址),挂载 Gateway 监听器上。 http: #定义了 HTTP 路由规则。可以定义多个路由规则。 - match: #定义了请求匹配条件。当请求满足所有匹配条件时,将应用对应的路由规则。 - uri: # exact: /productpage #匹配 URI 精确为 /productpage 的请求。 - uri: prefix: /static #匹配 URI 以 /static 为前缀的请求,通常用于静态资源。 - uri: exact: /login #匹配 URI 精确为 /login 的请求。 - uri: exact: /logout # 匹配 URI 精确为 /logout 的请求 - uri: prefix: /api/v1/products # 匹配 URI 以 /api/v1/products 为前缀的请求,通常用于产品 API 接口。 route: #定义了匹配请求的路由目标 - destination: #指定请求的目标地址。 host: productpage #指定目标服务的主机名为 productpage。在 Istio 服务网格中,服务通常通过其 Kubernetes Service Name 来引用。这里 productpage 是一个 Kubernetes Service 的名称。 port: #指定目标服务的端口。 number: 9080 #指定目标服务的端口号为 9080 # 下面是bookinfo项目的service信息: [root@k8s-master01 istio-1.24.3]# kubectl get svc -n bookinfo NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE details ClusterIP 10.96.76.76 <none> 9080/TCP 2d16h productpage ClusterIP 10.96.60.175 <none> 9080/TCP 2d16h ratings ClusterIP 10.96.99.140 <none> 9080/TCP 2d16h reviews ClusterIP 10.96.210.82 <none> 9080/TCP 2d16h
3.4测试访问 在本地hosts文件中添加一条解析:
1 2 # windwos的路径是:C:\Windows\System32\drivers\etc\hosts # 在文件最后新增一行192.168.0.81 bookinfo.ibstd.com
在浏览器输入bookinfo.ibstd.com:32662/productpage,可以看到服务可以正常访问。
这里的32662是istio-ingressgateway的service对外暴露的端口号:
多次刷新可以看到 Reviewer 处的星星会在黑色、红色和消失之间来回替换,是因为之前部署了三个不同版本的 reviews,每个版本具有不同的显示效果。
此时通过 Kiali 页面可以看到 Bookinfo 的调用链路:
3.5配置HTTPS访问 为bookinfo站点配置https证书。
首先生成证书,这里我测试用的自签名证书,在正式环境要购买证书。
1 2 3 4 5 6 7 8 9 [root@k8s-master01 26-istio]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout istiotls.key -out istiotls.crt -subj "/CN=bookinfo.ibstd.com" ........+.....+.+..............+....+..+....+...+++++++++++++++++++++++++++++++++++++++*....+........+...+....+++++++++++++++++++++++++++++++++++++++*...+...+......+............................+......+.....+......................+..+..........+..+...+.......+.....+.+...+...........+.+..+...+....+...+.....++++++ ..+......+.+........+.......+.....+..........+.....+.........+......+....+........+...+..........+..+.+.....+....+......+.....+............+....+++++++++++++++++++++++++++++++++++++++*.+++++++++++++++++++++++++++++++++++++++*...+.+..+...+...+......+.+........+..........+...............+......++++++ ----- # 生成的证书文件 [root@k8s-master01 26-istio]# ll istiotls.* -rw-r--r-- 1 root root 1139 Nov 5 14:23 istiotls.crt -rw------- 1 root root 1704 Nov 5 14:23 istiotls.key
接下来利用证书文件创建一个tls类型的Secret:
1 2 # 创建一个tls类型的Secret,创建的Secret要和istio-ingressgateway的pod在同一个命名空间,不要把Secret创建在微服务项目所在的空间,例如bookinfo [root@k8s-master01 26-istio]# kubectl create secret tls bookinfo-tls --cert=istiotls.crt --key=istiotls.key -n istio-system
最后修改gateway资源增加证书的配置:
1 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 53 54 [root@k8s-master01 26 -istio ] apiVersion: networking.istio.io/v1 kind: Gateway metadata: creationTimestamp: "2026-10-28T08:49:12Z" generation: 5 name: bookinfo-gateway namespace: bookinfo resourceVersion: "106214448" uid: de60baba-6d14-4aa3-badc-b6ebb7b58864 spec: selector: istio: ingressgateway servers: - hosts: - bookinfo.ibstd.com port: name: http number: 80 protocol: HTTP tls: httpsRedirect: true - hosts: - bookinfo.ibstd.com port: name: https number: 443 protocol: HTTPS tls: mode: SIMPLE credentialName: bookinfo-tls
web端访问测试:
3.6总结 用户从外部请求 http://bookinfo.ibstd.com:32662/productpage:
到达 Kubernetes 集群节点的 32662 端口 (NodePort)。
转发到 istio-ingressgateway Service 的 80端口。
Service 将请求转发到 istio-ingressgateway-xxx Pod 的 8080端口。
Pod 中的 istio-proxy 容器代理组件接收请求,根据请求的 Host 头部和到达Service 的端口(80)查找匹配的 Gateway 资源。
如果找到匹配的 Gateway,istio-proxy 应用该 Gateway 的配置。
istio-proxy 根据请求的 URL 路径和 VirtualService 的配置,将请求路由到 productpage 服务。在VirtualService中有个spec.gateways字段,该字段配置的是提前创建的Gateway资源的名字(metadata.name),它是一个字符串列表。这个列表明确指定了哪些 Gateway 的配置应该与此 VirtualService 关联。
productpage Service 将请求负载均衡到后端的 productpage Pod。
productpage Pod处理请求
4.Istio中域名重定向和地址重写 🔑 核心区别
维度
Redirect (重定向)
Rewrite (重写)
行为方
客户端(浏览器)
服务端(Istio/Envoy)
HTTP 状态码
301 / 302 / 307 / 308
无(对客户端透明)
浏览器地址栏
✅ 会变化
❌ 不变
请求次数
2次(原请求 + 新请求)
1次(内部改写后直接转发)
客户端感知
完全感知
完全无感
配置字段
redirect
rewrite
💡 一句话总结 :Redirect 是告诉客户端”你去别的地方找”;Rewrite 是服务端自己”偷偷改个地址再帮你找”。
4.1域名重定向(Redirect) 案例场景: 旧域名迁移到新域名,要求浏览器输入bookinfo.ibstd.com:32662/baidu 跳转到www.baidu.com
1 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 # 这里还是使用上述 3.2 和 3.3 中创建的资源演示 [root@k8s-master01 ~]# kubectl get vs -n bookinfo NAME GATEWAYS HOSTS AGE bookinfo ["bookinfo-gateway"] ["bookinfo.ibstd.com"] 45h # 修改VirtualService资源 [root@k8s-master01 ~]# kubectl edit vs -n bookinfo bookinfo apiVersion: networking.istio.io/v1 kind: VirtualService metadata: creationTimestamp: "2026-10-28T08:49:12Z" generation: 9 name: bookinfo namespace: bookinfo resourceVersion: "103986394" uid: 6b56885b-74c4-4583-a122-65e491b97fa4 spec: gateways: - bookinfo-gateway hosts: - bookinfo.ibstd.com http: - match: #用于定义路由规则的触发条件 - uri: #uri 用于匹配 HTTP 请求路径(Path) prefix: /baidu # prefix 表示“前缀匹配”:只要请求路径以指定字符串开头即视为命中 # 命中示例:/baidu、/baidu/、/baidu/news、/baidu/search?q=k8s # 未命中示例:/other/baidu、/bai redirect: # 当请求命中上述 match 条件时,执行重定向动作 authority: www.baidu.com #将请求的主机头(Host/Authority)修改为指定的www.baidu.com域名 uri: / # 显式覆盖重定向的目标路径为根路径 "/"(丢弃客户端后续的子路径) - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080
在浏览器输入bookinfo.ibstd.com:30080/baidu,回车观察地址变化
浏览器会自动跳转到www.baidu.com
在VirtualService中不同的http写法,作用是不一样的:
写法一:全站域名迁移 / 协议强制升级(通配兜底)
1 2 3 4 5 http: - redirect: scheme: https authority: www.example.com redirectCode: 301
含义:
**省略 match**:代表默认通配规则(Match All)。只要访问匹配到该 VirtualService 的 hosts,无论是根路径、具体文章还是 API 请求,都会无条件触发该规则。
**未配置 uri**:Envoy 只会替换协议为 https、域名为 [www.example.com](https://www.example.com),原请求的路径(Path)和查询字符串(Query)被完整透传。
路径效果:
1 2 原请求地址:http://old.example.com/api/v1?id=5 重定向后的地址:https://www.example.com/api/v1?id=5
适用场景 :全站换域名、HTTP 强跳 HTTPS、老域名整体 301 永久废弃迁移。
写法二:局部路由重定向并收敛到根目录(截断路径)
1 2 3 4 5 6 7 http: - match: - uri: prefix: /baidu redirect: authority: www.baidu.com uri: /
含义 :
**有 match**:仅拦截前缀为 /baidu 的请求,其他非 /baidu 开头的请求不会触发,会继续向下流转给后续规则。
**显式配置 uri: /:无论原请求在 /baidu 后面拼接了什么子路径或参数,重定向后的路径都会被 强制替换(Override)为根路径 /**。
路径效果 :
1 2 原请求地址:http://old.example.com/baidu/news?id=5 重定向后的地址:http://www.baidu.com/
适用场景 :外部快捷入口重定向(如用户访问当前站点的某个别名目录 /baidu、/docs,统一导向外部目标站点的首页)。
写法三:局部路由重定向且保持原路径透传(保留子路径)
1 2 3 4 5 6 http: - match: - uri: prefix: /baidu redirect: authority: www.baidu.com
含义 :
**有 match**:同样仅拦截前缀为 /baidu 的请求。
未配置 uri:由于没有指定具体的重写路径,Envoy 会仅将 Host 改为 [www.baidu.com](https://www.baidu.com),同时 完整保留客户端原本请求的全部路径(包含前缀 /baidu)与查询参数 。
路径效果 :
1 2 原请求地址:http://old.example.com/baidu/news?id=5 重定向后的地址:http://www.baidu.com/baidu/news?id=5
适用场景 :特定子目录业务平移,且目标站点上保留了完全相同的目录结构(例如目标站也有 /baidu/* 相关服务)。
对比总结:
配置写法
匹配范围
目标路径处理
示例原请求
重定向后的完整目标地址
写法 1 无 match 无 uri
全站所有请求 (无条件生效)
完整保留 原 Path 与 Query 参数
[http://old.example.com/api/v1?id=5]
[https://www.example.com/api/v1?id=5]
写法 2 prefix: /baidu uri: /
仅以 /baidu 开头的请求
**强制覆盖为根路径 /**(丢弃子路径)
[http://old.example.com/baidu/news?id=5]
[http://www.baidu.com/]
写法 3 prefix: /baidu 无 uri
仅以 /baidu 开头的请求
完整保留 原 Path(包含 /baidu 及后续路径)与 Query 参数
[http://old.example.com/baidu/news?id=5]
[http://www.baidu.com/baidu/news?id=5]
4.2地址重写(Rewrite) 在 Istio 中,可以通过 VirtualService(VS)的 rewrite 字段实现 URL 重写(URL Rewrite)。
URL 重写的核心作用是:客户端访问的 URL 保持不变,但 Istio 在将请求转发给后端服务之前,修改 HTTP 请求的 URI。
还是在3.2和3.3创建的资源中实验,要求访问bookinfo.ibstd.com:30080显示的是bookinfo项目的页面/productpage页面
1 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 [root@k8s-master01 ~]# kubectl edit vs -n bookinfo bookinfo # Please edit the object below. Lines beginning with a '#' will be ignored, # and an empty file will abort the edit. If an error occurs while saving this file will be # reopened with the relevant failures. # apiVersion: networking.istio.io/v1 kind: VirtualService metadata: creationTimestamp: "2026-10-28T08:49:12Z" generation: 10 name: bookinfo namespace: bookinfo resourceVersion: "104332954" uid: 6b56885b-74c4-4583-a122-65e491b97fa4 spec: gateways: - bookinfo-gateway hosts: - bookinfo.ibstd.com http: - match: # 流量匹配条件列表:请求必须满足以下条件才触发后续动作 - uri: exact: / # exact 表示“完全精确匹配” # 命中示例:http://bookinfo.ibstd.com/、http://bookinfo.ibstd.com/?a=1(Query 参数会被保留) #未命中示例:http://bookinfo.ibstd.com/productpage、http://bookinfo.ibstd.com/api rewrite: # 将命中上述匹配条件的请求路径重写为rui指定的值 uri: /productpage #客户端浏览器的地址栏不会改变,仅在转发给后端 Pod 时修改 HTTP 报文中的 Path route: # 流量转发目标 - destination: # host: productpage #指定后端服务的Service名字,如果不在同一个命名空间要写全地址,如:productpagee.default.svc.cluster.local port: # 后端 Service 暴露的服务端口 number: 9080 - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080
在浏览器地址栏输入:bookinfo.ibstd.com:30080,可以看到不在像之前那样还要输入后缀/productpage,因为在内部已经将url重写为/productpage,只是在浏览器端用户是无感知的
在 Istio 的 VirtualService 中,URL 重写(rewrite.uri)的核心运行法则是:**rewrite.uri 只用来替换在 match.uri 中被命中消耗的那一部分路径,未被命中的右侧子路径及查询参数会原样追加保留**。
下面将生产中最常见的配置模式、运行逻辑及地址变化完整汇总如下:
模式分类
典型场景
match.uri 配置
rewrite.uri 配置
客户端原始请求
重写后发往后端的实际请求
核心行为特征
场景 1
前缀剥离 (网关统一入口,后端服务仅监听根目录)
prefix: /api
uri: /
GET /api/v1/users
GET /v1/users
砍掉暴露给外网的路由前缀,保留业务层级与版本路径
场景 2
前缀替换 (服务名映射转换)
prefix: /service-a
uri: /backend-core
GET /service-a/items/123
GET /backend-core/items/123
仅把 /service-a 换成 /backend-core,保留后续的 /items/123
场景 3
静态默认首页映射 (根路径转发到特定服务首页)
exact: /
uri: /productpage
GET /
GET /productpage
全量绝对覆盖,精准替换单点路径,不影响其他接口
场景 4
单点绝对映射 (单点短路径映射为完整后台路由)
exact: /login
uri: /auth/v2/login
GET /login
GET /auth/v2/login
精确匹配,仅此一个 URL 触发整体重写,带子路径(如 /login/oauth)直接不命中
场景 5
查询参数(Query String)透传
prefix: /search
uri: /api/search
GET /search/all?q=istio&page=1
GET /api/search/all?q=istio&page=1
rewrite.uri 仅作用于 Path,查询参数始终完整保留
5.Istio实现灰度发布 灰度发布采用渐进式推进 模式。它在保留老版本(V1)的同时,先引入少量新版本(V2)实例,只将小部分流量引入新版本进行真实生产验证,随着稳定性得到验证再逐步扩大比例,直到流量全部切换到V2。
这里的案例还是用boojinfo项目演示:
1 2 3 4 5 6 7 8 9 10 11 # 服务的所有pod都已经部署好了,reviews服务有三个不同的版本,分别是v1,v2,v3 [root@k8s-master01 ~]# kubectl get po -n bookinfo NAME READY STATUS RESTARTS AGE details-v1-5655bbb7bb-98zbx 2/2 Running 2 (9m19s ago) 5d1h productpage-v1-7f86d94f89-6p28z 2/2 Running 2 (9m19s ago) 5d1h ratings-v1-6dccb8487d-q2cgd 2/2 Running 2 (9m18s ago) 5d1h reviews-v1-5c8cf97b4d-xnvhd 2/2 Running 2 (9m2s ago) 5d1h reviews-v2-7685b9f4f4-4g7b2 2/2 Running 2 (9m19s ago) 5d1h reviews-v3-846946d6cb-k9x7m 2/2 Running 2 (9m18s ago) 5d1h
针对不同版本,reviews服务的pod 都有一个版本的标签,例如version=v1
使用 Istio 进行细粒度的流量管理,需要使用 DestinationRule 资源对不同标签的 Pod 划分版本。
首先创建一个 DestinationRule 资源 将 reviews 服务分成三个版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@k8s-master01 26 -istio ] apiVersion: networking.istio.io/v1 kind: DestinationRule metadata: name: reviews-dr spec: host: reviews subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 - name: v3 labels: version: v3
创建DestinationRule资源:
1 2 3 # -n 指定资源创建在bookinfo这个命名空间中 [root@k8s-master01 26-istio]# kubectl create -f reviews-dr.yaml -n bookinfo destinationrule.networking.istio.io/reviews-dr created
查看创建的资源:
1 2 3 [root@k8s-master01 26-istio]# kubectl get dr -n bookinfo NAME HOST AGE reviews-dr reviews 54s
此时只是创建了 DestinationRule,并没有做任何限制,所以通过浏览器访问该服务时,并没 有什么变化。但是 Kiali 中可以看到 reviews 变成了三个版本:
接下来创建 VirtualService 资源将所有访问 reviews 服务的流量指向 reviews 的 v1 版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # 声明该资源遵循的 Istio 网络 API 版本。v1 为稳定版 API。 apiVersion: networking.istio.io/v1 # 定义咨资源的类型,声明创建一个 VirtualService(虚拟服务),用于定义流量进入网格后的路由分发规则。 kind: VirtualService metadata: #定义资源的元数据信息 name: reviews-vs # 定义该 VirtualService 在当前命名空间下的名称,自定义。 # 生产建议与被治理的目标服务同名,便于管理和排查。 spec: # 定义 VirtualService 的具体配置信息 hosts: #路由规则生效的流量目标主机(Host / Authority),告诉 Envoy“只要客户端请求的目标域名/服务名是 reviews,就由本规则接管”。 - reviews #需要与 Kubernetes Service 的服务名称对应,如果SVC和vs不在同一个命名空间需要写全名reviews.bookinfo.svc.cluster.local http: #七层(HTTP/HTTPS/gRPC)路由规则列表 - route: #路由转发动作定义,定义具体把流量导向哪一个目的地。多个 route 可用于配置权重拆分(如金丝雀发布)。 - destination: # 指定流量最终转发到哪里,固定字段 host: reviews #将流量转到到名字是reviews 的 Service上 #在常规的服务网格内部调用(东西向流量)和同服务灰度发布场景下,这里host定义的值通常和spec.host定义的值一致。 subset: v1 # 目标版本子集(Subset),v1是DestinationRule 的 spec.subsets[*].name的值,该名称必须提前在 DestinationRule声明 # 作用:强制将流量限定在特定版本的 Pod 实例池中(此处固定为 v1)。 # 若没有该 subset,Envoy 无法匹配到可用端点,会直接返回 503 异常。
不带注释的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 [root@k8s-master01 26-istio]# cat virtualService-all-v1.yaml apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: reviews-vs spec: hosts: - reviews http: - route: - destination: host: reviews subset: v1
创建该vs资源:
1 2 3 4 5 6 [root@k8s-master01 26-istio]# kubectl create -f virtualService-all-v1.yaml -n bookinfo virtualservice.networking.istio.io/reviews-vs created [root@k8s-master01 26-istio]# kubectl get vs -n bookinfo NAME GATEWAYS HOSTS AGE bookinfo ["bookinfo-gateway"] ["bookinfo.ibstd.com"] 4d21h reviews-vs ["reviews"] 46s
访问bookinfo项目,并在kiali的web端观察其流量走向,可以看到,流量只有到reviews的v1版本,并没有v2和v3,v1版本也不调用Ratings服务:
上述示例流量走向总结:
productpage 代码里写的 http://reviews:9080,reviews字段是reviews服务的Service的名字,表面上是在请求 Kubernetes Service,但因为productpage的 Pod 里注入了 Istio Sidecar(Envoy),这个请求根本不会直接走 Kubernetes 的 Service 负载均衡,而是被 Sidecar 拦截后,交给 Istio 的流量规则(VirtualService + DestinationRule)来处理。
详细拆解如下:
1 2 3 4 5 6 7 8 1. productpage服务的业务代码执行请求reviews服务: GET http://reviews:9080 # 这里的 reviews 确实是 Kubernetes Service 的名字。 2. 请求从 productpage 容器发出 3. 请求被 productpage 的 Sidecar(Envoy)拦截 (iptables 或 eBPF 把流量强制导向 Sidecar)
关键点来了:
1 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 53 54 55 56 4. Sidecar 看到目标是 reviews:9080 后,不会直接去问 Kubernetes “reviews 这个 Service 有哪些 Endpoints” 而是先问自己: “针对 host = reviews,有没有对应的 VirtualService 规则?” 如果有 VirtualService,就严格按照 VirtualService 的 route 规则走。 如果没有 VirtualService,才会退化成默认行为(相当于把流量打到该 Service 的所有 Endpoints,类似 kube-proxy)。 这里我们创建了相应的VirtualService资源: apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: reviews-vs spec: hosts: - reviews http: - route: - destination: host: reviews #这里的reviews有两个意思,①表示svc的名字,同时也会和DR资源关联 subset: v1 5. 找到 VirtualService 后: VirtualService 说:请把流量打到 subset: v1 6. Sidecar 再去查 DestinationRule: “reviews 的 v1 子集对应哪些 Pod?” → 根据 labels: version: v1 找到对应的 Pod IP # DestinationRule资源配置如下: apiVersion: networking.istio.io/v1 kind: DestinationRule metadata: name: reviews-dr spec: host: reviews subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 - name: v3 labels: version: v3 # VirtualService destination: host: reviews # ← vs的这个值必须和dr的spec.host的值匹配 # DestinationRule spec: host: reviews # ← dr中spec.host的值必须和上面vs中destination.host一致 # VirtualService 里的 destination.host 和 DestinationRule 里的 spec.host 必须指向同一个服务(值要一致),Istio 才能把它们正确关联起来。 7. 根据DR中划分的子级最终直接把请求发送到具体的 Pod IP(不再经过 kube-proxy)
假如有新版本上线,不想让新版本直接接入所有流量,可以使用 VirtualService 控制流量分配比例, 比如将 20%的流量路由到 v2 版本,其余的流量依旧路由到 v1 版本:
weight 字段用于按权重分配流量,是实现灰度发布(金丝雀发布)最常用的方式。
1 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 # 在上述案例中我将左右流量全部路由到V1版本,现在需要将20%流量路由到新上的V2版,修改之前创建的VirtualService资源 # 在vs中添加 weight 字段,实现按权重分配流量 [root@k8s-master01 26-istio]# kubectl edit vs -n bookinfo reviews-vs 1 # Please edit the object below. Lines beginning with a '#' will be ignored, 2 # and an empty file will abort the edit. If an error occurs while saving this file will be 3 # reopened with the relevant failures. 4 # 5 apiVersion: networking.istio.io/v1 6 kind: VirtualService 7 metadata: 8 creationTimestamp: "2026-11-02T06:03:22Z" 9 generation: 1 10 name: reviews-vs 11 namespace: bookinfo 12 resourceVersion: "104676539" 13 uid: 64f1f82a-691d-43ea-a094-ba25bcd63067 14 spec: 15 hosts: 16 - reviews 17 http: 18 - route: 19 - destination: 20 host: reviews 21 subset: v1 22 weight: 80 #通过配置weight字段来定义流量的权重 23 - destination: 24 host: reviews 25 subset: v2 26 weight: 20
weight 字段详细说明:
项目
说明
含义
该路由目标占总流量的比例
取值范围
0 ~ 100 的整数
计算方式
所有 weight 的总和应该等于 100(Istio 会按比例分配)
是否必填
当有多个 destination 时建议填写;只有一个 destination 时可省略(默认 100%)
接下来通过浏览器端访问bookinfo项目,观察其页面显示的内容,同时在kiali观察其流量的比例:
理想情况下是在没有星星和有黑色星星切换,没有星星显示的频率高,kiali观察其v1和v2的流量比例大概为2:8,同时v2版本的reviews服务还会调用ratings服务
当新版本经过测试没有问题后,可以将流量全部切到v2版本:
1 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 # 修改vs资源,把流量全部切到V2版本, 可以直接删除19行到22行,也可以把v1的权重设为0,v2的权重设为100 [root@k8s-master01 26-istio]# kubectl edit vs -n bookinfo reviews-vs 1 # Please edit the object below. Lines beginning with a '#' will be ignored, 2 # and an empty file will abort the edit. If an error occurs while saving this file will be 3 # reopened with the relevant failures. 4 # 5 apiVersion: networking.istio.io/v1 6 kind: VirtualService 7 metadata: 8 creationTimestamp: "2026-11-02T06:03:22Z" 9 generation: 2 10 name: reviews-vs 11 namespace: bookinfo 12 resourceVersion: "104726003" 13 uid: 64f1f82a-691d-43ea-a094-ba25bcd63067 14 spec: 15 hosts: 16 - reviews 17 http: 18 - route: 19 - destination: #可以直接删除19行到22行 20 host: reviews 21 subset: v1 22 weight: 0 23 - destination: 24 host: reviews 25 subset: v2 26 weight: 100
再次通过浏览器访问并观察kiali调用链,此时页面只会显示有黑色星星,kiali调用链中也不会再有reviews的v1:
6.Istio实现AB测试 基于条件的流量分配
与简单的权重分流(Canary)不同,AB 测试通常根据请求的特定条件 (如 HTTP Header、Cookie、Query Parameter 等)将流量路由到不同的版本:
版本 A :满足特定条件的用户访问旧版本
版本 B :满足其他条件的用户访问新版本
Istio 也支持基于请求头、uri、schema 等方式的细粒度流量管理,这种路由方式比较适用于 新版本上线时的 AB 测试。 Istio 请求头匹配介绍: https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest ,
在上述第5小节中,最后已经将全部的流量导向v2版本,也就是有黑色星星的版本。假如又开发了一个新版v3,此时只想要公司内部先进行测试,可以配置 VirtualService 指定部 分用户访问新版本。
1 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 53 54 55 56 57 58 59 # 还是基于名字是reviews-vs的VirtualService进行修改 [root@k8s-master01 26-istio]# kubectl get vs -n bookinfo NAME GATEWAYS HOSTS AGE bookinfo ["bookinfo-gateway"] ["bookinfo.ibstd.com"] 5d23h reviews-vs ["reviews"] 26h # 下面是修改前的原配置,所有流量都路由到v2版本 [root@k8s-master01 26-istio]# kubectl edit vs -n bookinfo reviews-vs apiVersion: networking.istio.io/v1 kind: VirtualService metadata: creationTimestamp: "2026-11-02T06:03:22Z" generation: 3 name: reviews-vs namespace: bookinfo resourceVersion: "104733182" uid: 64f1f82a-691d-43ea-a094-ba25bcd63067 spec: hosts: - reviews http: - route: - destination: host: reviews subset: v1 weight: 0 - destination: host: reviews subset: v2 weight: 100 # 配置请求头,将携带指定请求头的请求路由到v3版本,也就是能看到红色的星星 [root@k8s-master01 26-istio]# kubectl edit vs -n bookinfo reviews-vs apiVersion: networking.istio.io/v1 kind: VirtualService metadata: creationTimestamp: "2026-11-02T06:03:22Z" generation: 4 name: reviews-vs namespace: bookinfo resourceVersion: "105244686" uid: 64f1f82a-691d-43ea-a094-ba25bcd63067 spec: hosts: - reviews http: - match: - headers: # 定义请求头匹配条件集合 end-user: # 指定需检测的自定义 HTTP Header 键名(Key)是end-user exact: test-v3 #匹配模式为“精确匹配”:只有请求头中test-user的值严格等于test-v3时才算命中 route: # 命中上述匹配条件后的流量转发目标 - destination: host: reviews # 目标转发的服务名称 subset: v3 # 转发到该服务对应的 v3 版本子集,需要先在RD资源中划分好 - route: # 默认兜底路由规则,没有携带指定请求头的转发给reviews服务的v2版本子级 - destination: host: reviews subset: v2
通过浏览器访问,并观察页面变化:
没有配置请求头,无论页面如何刷新都只显示黑色的星星,也可以在kiali中观察流量走向,见下图:
配置请求头在请求,观察页面变化以及kiali的流量走向:
接下来登录 test-v3 用户(密码不限):这主要是因为 Bookinfo 官方示例的前端应用代码内部做了一层“登录用户名转请求头”的封装 。
表面上在网页上执行的是“输入用户名点击登录”,但在底层网络请求中,本质上依然是基于 HTTP 请求头(Header)在进行匹配 。
基于请求头的AB测试总结:
①:请求头不是 Istio 自动生成的业务身份信息,而是请求者本身携带的数据。它可以由浏览器、网关或者上游微服务设置和传递。
②:基于 Header 请求头做路由时,Key 和 Value 都必须要能匹配上VirtualService资源中定义的,例如:发起请求的一端携带了key是end-user,value是test-v3的请求头才可以根据请求头路由。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: reviews-vs namespace: bookinfo spec: hosts: - reviews http: - match: - headers: # 定义请求头匹配条件集合 end-user: #这里的end-user是请求头的key exact: test-v3 #这里的test-v3是请求头的value route: # 命中上述匹配条件后的流量转发目标 - destination: host: reviews # 目标转发的服务名称 subset: v3 # 转发到该服务对应的 v3 版本子集,需要先在RD资源中划分好
上述bookinfo案例的调用链路径如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 浏览器 (Client) │ ▼ istio-ingressgateway │ ▼ productpage (Python) │ ★ productpage 必须在代码中将 Header 转发给 reviews ▼ reviews (Java) │ ★ reviews 必须在代码中将 Header 转发给 ratings ▼ ratings (Node.js)
bookinfo项目 的productpage 并不是无条件将外部 HTTP 请求头里的 end-user 直接透传给下游,它的内部 end-user 是从用户登录会话(Session / Cookie)中提取出来的,所以通过插件工具自定义的请求头不起作用。
在微服务架构和服务网格(Service Mesh)中,只要目标服务不是链路的“第一跳”,仅靠运维在 Istio 中划分 subset 和配置 VirtualService 是无法独立完成基于 Header 的 A/B 测试的,必须依赖开发在服务调用链路中实现请求头透传(Header Propagation) 。
测试 reviews服务相关功能 :只需要 productpage 代码具备向下游透传该 Header 的逻辑即可;
测试 ratings服务相关功能 :不仅 productpage 要透传给 reviews,**reviews 的代码也必须同样具备将该 Header 往下传递给 ratings 的逻辑**。
一句话:reviews服务做AB测试,那么调用reviews的productpage服务要能在代码中将Header转发给reviews才行,ratings服务做AB测试,那么谁调用ratings谁就要具备能携带Header发起请求才行。携带请求头发起请求需要开发人员在代码中实现该逻辑。
只要整条链路中任何一个环节的服务断开了传递 ,下游所有依赖该 Header 进行匹配的 VirtualService 规则就都会失效,直接退化走默认兜底版本。
7.Istio负载均衡算法 Istio 的负载均衡(Load Balancing)由底层数据面 Envoy 代理 实现。在流量经过 VirtualService 路由决策后,由 DestinationRule(目标规则)决定如何将请求均匀或按特定规则分发到具体的 Pod / Endpoint 实例上。
Istio 原生支持多种负载均衡算法,比如 ROUND_ROBIN、LEAST_REQUEST、LEAST_CONN、 RANDOM 等。假如一个应用存在多个副本(Pod),可以使用上述算法对多个 Pod 进行定制化的 负载均衡配置。
常见的负载均衡算法如下:
ROUND_ROBIN:轮询算法,将请求按顺序依次分发给后端pod实例,不推荐使用。
LEAST_REQUEST:(最少请求算法) ,将请求分配给当前活跃请求数最少的健康实例,推荐使用。
RANDOM:随机算法,将请求随机分配给可用健康实例中的任意一个,不推荐使用。
PASSTHROUGH:(透传),不执行任何负载均衡,直接转发到调用方请求的原始目标 IP,不推荐使用。
UNSPECIFIED:未指定负载均衡算法,Istio 将选择一个合适的默认算法,不推荐使用。
配置示例:
负载均衡策略在 Istio 的 DestinationRule 资源中的 spec.trafficPolicy.loadBalancer 字段下进行配置。在 Istio 的 DestinationRule 中,负载均衡策略遵循继承与覆盖 原则:
如果在顶层的 spec.trafficPolicy.loadBalancer 定义了全局策略,所有子集(Subsets)默认继承该策略。
如果在具体的 spec.subsets[].trafficPolicy.loadBalancer 中定义了策略,该子集会覆盖 全局策略,仅对路由到该子集的流量生效。
①全局配置示例:
1 2 3 4 5 6 7 8 9 10 11 apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: my-service-lb namespace: default spec: host: my-service.default.svc.cluster.local trafficPolicy: loadBalancer: simple: LEAST_REQUEST
②:subsets 中单独配置负载均衡算法:
1 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 apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: my-service-subsets-lb namespace: default spec: host: my-service.default.svc.cluster.local trafficPolicy: loadBalancer: simple: ROUND_ROBIN subsets: - name: v1 labels: version: v1 trafficPolicy: loadBalancer: simple: LEAST_REQUEST - name: v2 labels: version: v2 trafficPolicy: loadBalancer: consistentHash: httpCookie: name: session-token ttl: 1800s - name: v3 labels: version: v3
删除之前配置的一些路由策略,然后把dr中的负载均衡算法改为ROUND_ROBIN,访问站点并观查 Kiali 中 reviews 的流量分配:
1 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 # 删除vs [root@k8s-master01 ~]# kubectl delete vs -n bookinfo reviews-vs virtualservice.networking.istio.io "reviews-vs" deleted # 修改dr的负载均衡算法为ROUND_ROBIN [root@k8s-master01 ~]# kubectl edit dr -n bookinfo reviews-dr apiVersion: networking.istio.io/v1 kind: DestinationRule metadata: creationTimestamp: "2026-11-01T09:50:11Z" generation: 2 name: reviews-dr namespace: bookinfo resourceVersion: "105752567" uid: ca082bbc-352f-46b6-9f52-5c4c0278b5d3 spec: host: reviews subsets: - labels: version: v1 name: v1 - labels: version: v2 name: v2 - labels: version: v3 name: v3 # 在spec下添加一下参数,和host同级,针对全局的子级都生效 trafficPolicy: loadBalancer: simple: ROUND_ROBIN
观察流量分配基本是1:1分配的:
8.Istio熔断机制 Istio 中的熔断机制(Circuit Breaking) 是一种微服务架构中的弹性设计模式,灵感来源于现实生活中的电路保险丝。
当某个服务或实例因为过载、异常、延迟增大而开始大量报错时,熔断机制会主动切断对该故障实例/服务的流量 ,让调用端快速失败,防止故障扩散。
熔断的核心目标是防止故障进一步蔓延 和控制增量负载 ,而不是暴力中止存量任务。熔断器被触发的那一刻,系统采取的是“关门打狗,消化存量,拒绝增量”的机制,也就是说触发熔断后不在接受新的请求,已经接受的请求正常处理。
在分布式微服务架构中,熔断机制主要用于解决以下问题:
防止服务雪崩(Cascading Failures)
场景 :服务 A 调用服务 B,由于服务 B 响应极慢甚至无响应。
问题 :服务 A 会因为等待 B 的响应而耗尽线程、连接池或内存,导致服务 A 也崩溃,进而连环拖垮上游的服务,导致整个系统瘫痪。
作用 :熔断后,请求直接快速失败,不再阻塞等待,保护了上游服务。
故障隔离与快速失败(Fast Failure)
客户端不必经历长时间的超时等待,而是几乎立刻收到错误响应(如 HTTP 503),提升用户体验或触发降级兜底逻辑。
给故障服务“自我恢复”的空间
如果后端实例过载,继续涌入流量只会让其雪崩。熔断切断流量后,后端实例可以处理完手头的堆积任务,释放内存或重启,逐步恢复正常。
主动剔除不健康实例(异常点检测)
Istio 支持基于统计数据(如连续返回 5xx 错误达到阈值),将特定的 Pod 实例移出负载均衡池一段时间,流量自动路由到健康的 Pod。
8.1Istio熔断过载保护配置 在 Istio 中,熔断保护策略是通过 DestinationRule (DR) 配置在被调用的目标服务(下游 / 服务提供方)上,但实际生效和执行熔断拦截的位置是在发起调用的客户端(上游 / 调用方)Sidecar Envoy 中 。
例如架构链路为:Service A (调用方/客户端) -> Service B (被调用方/服务端)
熔断策略必须绑定到服务 Service B 的 DestinationRule。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: service-b-circuit-breaker namespace: default spec: host: service-b.default.svc.cluster.local trafficPolicy: connectionPool: tcp: maxConnections: 100 http: http1MaxPendingRequests: 10 outlierDetection: consecutive5xxErrors: 3 interval: 10s baseEjectionTime: 30s maxEjectionPercent: 100
假设对 ratings 进行熔断,希望在并发请求数超过 3,并且存在 1 个以上的待处理请求,就触 发熔断,此时可以配置 ratings 的 DestinationRule 如下所示:
1 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 [root@k8s-master01 26 -istio ] apiVersion: networking.istio.io/v1 kind: DestinationRule metadata: name: ratings-dr namespace: bookinfo spec: host: ratings trafficPolicy: loadBalancer: simple: LEAST_REQUEST connectionPool: tcp: maxConnections: 3 http: http1MaxPendingRequests: 1 outlierDetection: consecutive5xxErrors: 1 interval: 20s baseEjectionTime: 30s maxEjectionPercent: 100 subsets: - name: v1 labels: version: v1
创建资源:
1 2 3 4 5 6 7 [root@k8s-master01 26-istio]# kubectl create -f ratings-dr.yaml -n bookinfo destinationrule.networking.istio.io/ratings-dr created [root@k8s-master01 26-istio]# kubectl get dr -n bookinfo NAME HOST AGE ratings-dr ratings 15s reviews-dr reviews 4d23h
接下来创建一个测试服务,istio官方提供的服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 创建测试服务,官方提供的测试服务路径位于istio-1.24.3/samples/httpbin/sample-client/fortio-deploy.yaml,创建pod前先修改默认的镜像下载地址,我已经将镜像同步到我的阿里云仓库,地址是registry.cn-beijing.aliyuncs.com/k8s-liujunwei/fortio:latest [root@k8s-master01 sample-client]# vim fortio-deploy.yaml # 将image中的地址改成同步后的镜像地址 labels: app: fortio spec: containers: - name: fortio image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/fortio:latest imagePullPolicy: Always ports: - containerPort: 8080 name: http-fortio - containerPort: 8079 name: grpc-ping
创建测试服务,服务创建在bookinfo空间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@k8s-master01 sample-client]# kubectl create -f fortio-deploy.yaml -n bookinfo service/fortio created deployment.apps/fortio-deploy created # 查看 [root@k8s-master01 sample-client]# kubectl get po -n bookinfo NAME READY STATUS RESTARTS AGE details-v1-5655bbb7bb-98zbx 2/2 Running 4 (4d4h ago) 10d fortio-deploy-768f5c8875-2r5s7 2/2 Running 0 30s productpage-v1-7f86d94f89-6p28z 2/2 Running 4 (4d4h ago) 10d ratings-v1-6dccb8487d-q2cgd 2/2 Running 4 (4d4h ago) 10d reviews-v1-5c8cf97b4d-xnvhd 2/2 Running 4 (4d4h ago) 10d reviews-v2-7685b9f4f4-4g7b2 2/2 Running 4 (4d4h ago) 10d reviews-v3-846946d6cb-k9x7m 2/2 Running 4 (4d4h ago) 10d
等待pod启动成功进行测试,发送一个请求,可以看到当前状态码为 200,即连接成功:
1 2 3 4 5 6 7 8 9 10 11 12 [root@k8s-master01 sample-client]# kubectl exec -ti -n bookinfo fortio-deploy-768f5c8875-2r5s7 -- fortio load -curl http://ratings:9080/ratings/0 10:33:09.201 r1 [INF] scli.go:124> Starting, command="Φορτίο", version="1.76.0 h1:CSeuqtbKDwX34cTsjcV0VDYjCCJeQ/sYTPDtIakFmrs= go1.26.9 amd64 linux", go-max-procs=4 HTTP/1.1 200 OK content-type: application/json date: Fri, 06 Nov 2026 10:33:09 GMT x-envoy-upstream-service-time: 51 server: envoy transfer-encoding: chunked 30 {"id":0,"ratings":{"Reviewer1":5,"Reviewer2":4}} 0
接下来增加并发连接(-c 1000)和总请求数量( -n 5000):
1 2 3 4 5 6 7 [root@k8s-master01 26-istio]# kubectl exec -ti -n bookinfo fortio-deploy-768f5c8875-2r5s7 -- fortio load -c 1000 -qps 0 -n 5000 -loglevel Warning http://ratings:9080/ratings/0 | grep Code Code 200 : 5000 (100.0 %) [root@k8s-master01 26-istio]# kubectl exec -ti -n bookinfo fortio-deploy-768f5c8875-2r5s7 -- fortio load -c 1000 -qps 0 -n 5000 -loglevel Warning http://ratings:9080/ratings/0 | grep Code Code 200 : 1310 (26.2 %) Code 503 : 3690 (73.8 %) [root@k8s-master01 26-istio]# kubectl exec -ti -n bookinfo fortio-deploy-768f5c8875-2r5s7 -- fortio load -c 1000 -qps 0 -n 5000 -loglevel Warning http://ratings:9080/ratings/0 | grep Code Code 503 : 5000 (100.0 %)
问题总结:
限流和熔断是两套完全独立、解决不同问题的保护机制 。它们经常一起配置,但触发条件、作用对象和行为模式截然不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 connectionPool: # 连接池配置,用于限制到目标服务的连接数和请求数 tcp: # TCP 连接池配置 maxConnections: 3 # 到目标主机的最大 TCP 连接数。超过此值的新连接会被拒绝 http: # HTTP 连接池配置 http1MaxPendingRequests: 1 #HTTP/1.1 最大等待请求数(排队请求上限)。超过此值的请求会被拒绝 outlierDetection: # 异常点检测(熔断)配置,用于自动剔除不健康的实例 consecutive5xxErrors: 1 # 连续返回 5xx 错误的次数阈值。达到此次数后将该实例标记为不健康并剔除 interval: 20s # 异常检测的扫描间隔。每隔 10 秒检查一次实例健康状态 baseEjectionTime: 30s # 熔断时长,该实例在30秒内不接受新的请求。 #实际驱逐时间 = baseEjectionTime × 当前累计驱逐次数(指数退避),防止抖动实例频繁上下线 maxEjectionPercent: 100 # 最大可剔除实例百分比。允许最多剔除 100% 的实例(即全部剔除) # 设为 100 表示即使所有实例都异常,也全部驱逐(此时流量将全部失败) # 生产环境建议设为 30~50,保留部分实例以维持最低可用性,避免全量摘除
⚠️ 常见混淆点:
❌ 误解 :”超过 maxConnections 就是熔断了”
✅ 正解 :超过 maxConnections 只是连接池限流,熔断必须依赖后端返回的错误
❌ 误解 :”两者配一个就够了”
✅ 正解 :它们互补而非替代
只有限流 → 后端某个实例挂了,流量仍会持续打向它(直到超时)
只有熔断 → 突发流量可能瞬间压垮所有健康实例(来不及积累5xx就被打挂)
两者配合 → 限流挡住突发洪峰,熔断隔离慢性故障
💡 一句话总结:
限流是”我不发太多”,熔断是”你不行了我就换人”。前者保护下游不被过载,后者保护自己不被拖死。生产环境中建议 同时配置 ,形成纵深防御。
9.Istio链路可靠性测试 Istio 的延迟注入(Delay Fault Injection)本质上是混沌工程(Chaos Engineering)工具,不是用来“常驻运行”的功能。 延迟注入本身不解决问题,它只是把问题“提前暴露出来”。
它的核心价值是:主动、可控地模拟上游服务变慢 ,从而验证你的微服务系统在真实网络抖动、下游过载、GC 停顿、跨区延迟等场景下,是否真的具备容错能力。
9.1Istio注入延迟故障 Bookinfo 的核心调用关系是:
1 2 3 4 5 6 7 8 用户 ↓ productpage ├──→ details (直接调用) └──→ reviews ├── reviews-v1:不调用 ratings ├── reviews-v2:显示黑色星星,并调用 ratings服务 └── reviews-v3:显示红色星星,并调用 ratings服务
测试场景是模拟当 productpage调用 details变慢时,整个链路会发生什么?所以目标是让 productpage→ details 这一跳变慢 。那么注入延迟的配置就应该在被调用的服务(details)的 VirtualService 上进行配置 。
原因:
Istio 的故障注入(fault injection)是由客户端代理 执行的(调用方的 sidecar 或 waypoint)。
你在 目标服务(ratings) 的 VirtualService 里写 fault.delay,Istio 会把这个规则下发给所有调用 details 的客户端代理。
当 productpage 的代理准备把请求发给 details 时,它会先按照规则延迟,再真正转发。
首先观察服务正常时的状态:
1 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 # 创建一个测试pod来测试延迟 [root@k8s-master01 26-istio]# kubectl create deploy -n bookinfo debug-tools --image=registry.cn-beijing.aliyuncs.com/dotbalo/debug-tools -- sleep 36000 deployment.apps/debug-tools created [root@k8s-master01 26-istio]# kubectl get po -n bookinfo NAME READY STATUS RESTARTS AGE debug-tools-79bc9d974b-dgq75 0/2 PodInitializing 0 22s details-v1-5655bbb7bb-98zbx 2/2 Running 6 (19h ago) 11d fortio-deploy-768f5c8875-2r5s7 2/2 Running 3 (19h ago) 21h productpage-v1-7f86d94f89-6p28z 2/2 Running 6 (19h ago) 11d ratings-v1-6dccb8487d-5hchn 2/2 Running 2 (19h ago) 19h reviews-v1-5c8cf97b4d-xnvhd 2/2 Running 6 (19h ago) 11d reviews-v2-7685b9f4f4-4g7b2 2/2 Running 6 (19h ago) 11d reviews-v3-846946d6cb-k9x7m 2/2 Running 6 (19h ago) 11d # 进入pod测试请求details服务的延迟 [root@k8s-master01 26-istio]# kubectl exec -ti -n bookinfo debug-tools-79bc9d974b-dgq75 -- bash (07:47 debug-tools-79bc9d974b-dgq75:/) time curl -I -s details:9080 HTTP/1.1 404 Not Found content-type: text/html; charset=ISO-8859-1 server: envoy date: Sat, 07 Nov 2026 08:01:33 GMT content-length: 268 x-envoy-upstream-service-time: 6 real 0m0.017s #很快响应 user 0m0.004s sys 0m0.004s
在通过web访问,可以看到没有注入延迟的情况下details也是正常显示的:
接下来通过创建ratings 服务的VirtualService 资源在注入延迟:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@k8s-master01 26 -istio ] apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: details-vs namespace: bookinfo spec: hosts: - "details" http: - fault: delay: percentage: value: 100 fixedDelay: 10s route: - destination: host: details port: number: 9080
在 Istio 的 VirtualService 中,gateways 字段是可选配置。它是否需要填写,完全取决于你要处理的是内部流量 还是外部流量 。
如果不填:默认只对集群内部生效 (默认值是 mesh):
如果你在编写 VirtualService 时省略了 gateways 字段,Istio 会隐式地将它设置为系统保留关键字 mesh。
作用 :这条路由规则只会在 Istio 网格内部生效,专门控制各个微服务之间的互相调用(东西向流量)。
应用场景 :比如你之前测试的 reviews 调用 ratings 服务,或者针对 ratings 配置延迟注入和熔断。这些都是内部服务间的调用,不需要 配置 gateways。
什么时候必须填:对外暴露服务时:
当你希望外部用户(如浏览器、手机 App)通过域名访问你的微服务时(南北向流量),就必须 填写 gateways 字段,并指定对应的 Gateway 资源名称(如你提到的 bookinfo-gateway)。
作用 :将这条路由规则绑定到边缘代理(istio-ingressgateway)上,让外网进来的流量知道该怎么转发。
应用场景 :比如你之前配置 bookinfo.ibstd.com 域名来访问前端 productpage 服务,如果不在此处的 VS 中绑定 bookinfo-gateway,外部流量到了网关就会因为找不到路由规则而返回 404。
创建上述VirtualService资源:
1 2 [root@k8s-master01 26-istio]# kubectl create -f details-vs.yaml -n bookinfo virtualservice.networking.istio.io/details-vs created
再次通过web端和测试pod测试,查看现象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #可以可能到响应时间大于10秒 [root@k8s-master01 26-istio]# kubectl exec -ti -n bookinfo debug-tools-79bc9d974b-dgq75 -- bash (130 08:05 debug-tools-79bc9d974b-dgq75:/) time curl -I -s details:9080 #测试命令 HTTP/1.1 404 Not Found content-type: text/html; charset=ISO-8859-1 server: envoy date: Sat, 07 Nov 2026 08:06:20 GMT content-length: 268 x-envoy-upstream-service-time: 53 real 0m10.065s #10秒后才响应 user 0m0.004s sys 0m0.004s (08:06 debug-tools-79bc9d974b-dgq75:/)
上图篮筐中的就是针对「超时 + 友好降级」的成功提示。这在生产环境中是健康的表现,而不是故障。这是针对响应慢的问题给出了友好的提示信息,是完全预期的行为,也是延迟注入要验证的核心点。
延迟注入的目的就是提前暴露和验证这种行为。
如果注入 10s 延迟后,整个页面都卡住或直接 500,说明你的超时和错误处理没有做好,需要去改代码的处理逻辑。
9.2Istio注入中断故障 Istio 的中断故障注入(Abort Fault Injection)是指:通过 Envoy 代理人为地、立刻掐断请求,并直接向上游服务返回一个指定的 HTTP 错误状态码(如 503、500 或 404)。
如果说之前的延迟注入(Delay)模拟的是“服务变卡、网络拥堵”,那么 中断注入(Abort)模拟的就是“服务直接挂掉、拒绝连接或内部崩溃”(Fail-Fast),用来检验系统在下游服务直接出错时的表现 。
它和之前的延迟注入(Delay) 是两种不同的故障类型:
类型
作用
模拟的真实场景
常见配置
Delay(延迟)
让请求卡住一段时间再转发
网络延迟、下游服务过载、慢查询
fixedDelay: 5s
Abort(中断)
直接返回错误,不转发到真实服务
服务宕机、进程崩溃、返回 5xx、连接被拒绝
httpStatus: 500 或 503
还是在bookinfo项目上测试,场景是模拟当 productpage调用 details时,details服务直接故障,整个链路会发生什么?所以目标是让 productpage→ details 这一跳故障 。那么注入故障的配置就应该在被调用的服务(details)的 VirtualService 上进行配置 。
创建故障注入的vs资源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # 中断故障注入只需要将 fault 的 delay 更改为 abort 即可 [root@k8s-master01 26-istio]# vim details-abort-vs.yaml apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: details-abort-vs namespace: bookinfo spec: # 1. 作用的主机名(域名或 K8s 内部短/长域名) hosts: - "details" http: - fault: # 注入故障参数,固定值 abort: #【故障类型二选一】abort 或 delay,也可同时配置 percentage: # 定义影响范围的百分比 value: 100 # 【影响比例】填 0 到 100 之间的数值。100 代表对所有满足条件的流量都注入abort故障。 httpStatus: 500 # 【伪造的状态码】(必填) 强制向上游调用方返回的具体 HTTP 错误码。常用如 500 (模拟内部代码崩溃)、503 (模拟过载或不可用)、403 (模拟权限拒绝) 等。 route: - destination: host: details port: number: 9080
创建资源:
1 2 [root@k8s-master01 26-istio]# kubectl create -f details-abort-vs.yaml -n bookinfo virtualservice.networking.istio.io/details-abort-vs created
web端展示的和延迟故障展示的一样,这里就不演示了,表示同样遇到服务故障也会友好的展示提示信息,而不是等着整个页面的加载。