istio VirtualService 配置示例
0、概述
virtualservice是istio流量治理的核心配置,可以说是istio流量治理中最重要、最复杂的规则。 vs描述了一个具体的服务对象,在该服务对象内包含了对流量的各种处理,其主体是一个服务而不是一组规则。
0.1、主要字段
-
hosts:流量发送的目标,可以是一个dns名称或ip地址,对于k8s来说,在hosts中一般都是service的短域名。而在istio中,这种短域名的解析基于vs这个规则所在的命名空间,而不是service的命名空间。
-
gateway:应用这些流量规则的gateway。
- 场景1、服务只是在网格内访问的。
- 场景2、服务只是在网格外访问的。
- 场景3、在服务网格内和网格外都需要访问,这里至少配置两个元素,一个是外部访问的gateway,另一个是保留关键字“mesh”。
-
http:一个与httpRoute类似的路由集合,用于处理http的流量。
-
tls:是一个tlsRoute类型的路由集合,用于处理非终结的tls和https流量。
-
tcp:是一个tcpRoute类型的路由集合,用于处理tcp的流量,应用于所有其他非http和tls端口的流量。
-
exportTo: 用于控制vs跨命名空间的可见性,这样就可以控制一个命名空间下定义的vs是否可以被其他命名空间下的sidercar和gateway使用了。
0.2、三种协议路由规则对比
从规则构成上都是先定义一组匹配条件,然后对满足条件的流量执行对应的操作。
比较的内容 | http | tls | tcp |
---|---|---|---|
路由规则 | httpRoute | tlsRoute | tcpRoute |
流量匹配条件 | httpMatchRequest | tlsMatchAttributes | l4MatchAttributes |
条件属性 | uri、scheme、method、authority、port、sourceLabels、gateways | sniHosts、destinationSubnets、port、sourceLabels、gateways | destinationSubnets、port、sourceLabels、gateways |
流量操作 | route、redirect、rewrite、retry、timeout、faultInjection、corsPolicy | route | route |
目标路由定义 | httpRouteDestination | routeDestination | routeDestination |
目标路由属性 | destination、weight、headers | destination、weight | destination、weight |
1、基础环境
k8s v1.30.2
client version: 1.23.2
control plane version: 1.23.2
data plane version: 1.23.2 (9 proxies)
测试是基于我自定义的两个服务,为啥没用官方的服务呢?当然是自己弄两个方便测试修改。
graph LR
A[发起请求] --> B[service-a-vs];
B --> |http://service-b:8000/api/v2/users/| C[serviceb];
1.1、service-a
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-a
labels:
app: service-a
spec:
replicas: 2
selector:
matchLabels:
app: service-a
template:
metadata:
labels:
app: service-a
spec:
containers:
- name: service-a
image: service-a:v7
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: service-a-svc
spec:
selector:
app: service-a
ports:
- name: http
protocol: TCP
port: 8000
targetPort: 8000
1.2、service-b
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-b
labels:
app: service-b
spec:
replicas: 2
selector:
matchLabels:
app: service-b
template:
metadata:
labels:
app: service-b
spec:
containers:
- name: service-b
image: service-b:v6
ports:
- containerPort: 9000
---
apiVersion: v1
kind: Service
metadata:
name: service-b
spec:
selector:
app: service-b
ports:
- name: http
protocol: TCP
port: 9000
targetPort: 9000
1.3、配置注意点
- hosts 可以是带通配符前缀的 DNS 名称或 IP 地址。根据平台的不同,也可以使用短名称代替 FQDN(即名称中没有点)
- hosts 字段适用于 HTTP 和 TCP 服务。网格内的服务(即在服务注册表中找到的服务)必须始终使用其字母数字名称来引用。仅允许通过网关定义的服务使用 IP 地址。
- 要控制绑定到网格外部服务的流量的路由,必须先使用 ServiceEntry 资源将外部服务添加到 Istio 的内部服务注册表中。然后可以定义 VirtualServices 来控制绑定到这些外部服务的流量。
- 某个大佬说istio对grpc支持不好,线上很多请求返回异常,大佬给的解决方式。
2、单一VirtualService
官网上来就是gateway,我想肯定会有不用gateway的时候,那就试试没有gateway,没有dr的情况。
多个host共享规则,最终还是要根据uri或者其他规则转发到对应的服务。
2.1、不同服务,uri404
-
service-a-svc:8000
-
service-b: 8000
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: service-vs
spec:
hosts:
- service-a-svc
- service-b
http:
- match:
- uri:
prefix: /api/v1/
route:
- destination:
host: service-a-svc
port:
number: 8000
weight: 50
- destination:
host: service-b
port:
number: 8000
weight: 50
kubectl exec -it pod/reviews-v1-6584ddcf65-dhzsf -- /bin/sh
for i in `seq 1 100`;do curl http://service-a-svc:8000/api/v1/users/; echo -n "\n";done
""
执行结果:
{"detail":"Not Found"}
""
""
""
""
""
{"detail":"Not Found"}
结果不一样是因为此时service-a-svc和service-b两个destination处于负载均衡的状态:
- service-a-svc是调用service-b之前的操作是成功的,调用service-b是失败的,因为service-b uri 404。
- service-b是直接失败的,因为service-b uri 404。
2.2、不同服务,不同uri
服务a调用服务b
- service-a-svc:8000
-
service-b: 8000
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: service-vs spec: hosts: - service-a-svc - service-b http: - match: - uri: prefix: /api/v1/ route: - destination: host: service-a-svc port: number: 8000 - match: - uri: prefix: /api/v2/ route: - destination: host: service-b port: number: 8000
-
第一个curl,从测试pod到service-a,service-a到service-b
- 第二个curl,从测试pod到service-b
for i in `seq 1 1000`;do curl http://service-a-svc:8000/api/v1/users/; curl http://service-b:8000/api/v2/users/ddd; echo -n "\n";done 执行结果: "{\"message\":\"i am service b\"}"{"message":"self request"} "{\"message\":\"i am service b\"}"{"message":"self request"} "{\"message\":\"i am service b\"}"{"message":"self request"} "{\"message\":\"i am service b\"}"{"message":"self request"}
2.3、不同服务,不同uri,不同svc端口
service-a 请求http://service-b:9000/api/v2/users/ 改变service-b的端口
- service-a-svc:8000
- service-b: 9000
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: service-vs spec: hosts: - service-a-svc - service-b http: - match: - uri: prefix: /api/v1/ route: - destination: host: service-a-svc port: number: 8000 - match: - uri: prefix: /api/v2/ route: - destination: host: service-b port: number: 9000
执行匹配uri /api/v1/users/ 会转发到service-a-svc服务,然后从service-a-svc服务再去请求http://service-b:9000/api/v2/users/,然后再去vs中转发到service-b服务。
for i in `seq 1 100`;do curl http://service-a-svc:8000/api/v1/users/; echo -n "\n";done
返回:
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
从v1改成了v2,就变成直接请求service-b,不经过service-a-svc了,从service-a-svc返回的数据经过requests的处理不一样。
这个vs配置判断通过hosts和uri来分配目标服务。
$ for i in `seq 1 100`;do curl http://service-a-svc:8000/api/v2/users/; echo -n "\n";done
{"message":"i am service b"}
{"message":"i am service b"}
{"message":"i am service b"}
{"message":"i am service b"}
{"message":"i am service b"}
{"message":"i am service b"}
2.4、调用服务配置vs,被掉服务未配置vs
- service-a-svc:8000
- service-b: 9000 会从从kiali的调用链中发现,是没经过vs的。
3、vs配置
3.1、subset hosts缺少一个host引发的问题
两个服务的deployment, 区别就是service-b有v2的版本 service-a-svc配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-a
labels:
app: service-a
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: service-a
version: v1
template:
metadata:
labels:
app: service-a
version: v1
spec:
containers:
- name: service-a
image: service-a:v7
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: service-a-svc
labels:
app: service-a
spec:
selector:
app: service-a
ports:
- name: http
protocol: TCP
port: 8000
targetPort: 8000
service-b配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-b-v1
labels:
app: service-b
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: service-b
version: v1
template:
metadata:
labels:
app: service-b
version: v1
spec:
containers:
- name: service-b
image: service-b:v6
ports:
- containerPort: 9000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-b-v2
labels:
app: service-b
version: v2
spec:
replicas: 2
selector:
matchLabels:
app: service-b
version: v2
template:
metadata:
labels:
app: service-b
version: v2
spec:
containers:
- name: service-b
image: service-b:v6
ports:
- containerPort: 9000
---
apiVersion: v1
kind: Service
metadata:
name: service-b
labels:
app: service-b
spec:
selector:
app: service-b
ports:
- name: http
protocol: TCP
port: 9000
targetPort: 9000
vs的配置
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myservice-vs
spec:
hosts:
- service-a-svc
- service-b
http:
- name: "service-a-routes"
match:
- uri:
prefix: "/api/v1/"
route:
- destination:
host: service-a-svc
subset: v1
- name: "service-b-route"
route:
- destination:
host: service-b
subset: v2
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: service-a-svc-destination
spec:
host: service-a-svc
subsets:
- name: v1
labels:
version: v1
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: service-b-destination
spec:
host: service-b
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
for i in `seq 1 1000`;do curl http://service-a-svc:8000/api/v1/users/; echo -n "\n";done
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
"{\"message\":\"i am service b\"}"
3.1.1、将上述vs中的hosts中删除service-b会出现什么效果?
what???怎么会出现这种情况???
似乎应用了vs规则,经过测试,我给vs进行了故障注入,两个route分别注入,service-b-route并没有生效,service-a-routes生效了,所以虽然kiali中感觉vs生效了。所以还是走的svc,svc对应了两个deployment,发生了负载均衡。
这个测试可能是有问题的,可以忽略,内部服务,没有配置mesh gateway vs应该是不会生效的。 内部流量生效,需要参考mesh gateway 规则流量变化
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myservice-vs
spec:
hosts:
- service-a-svc
http:
- name: "service-a-routes"
match:
- uri:
prefix: "/api/v1/"
route:
- destination:
host: service-a-svc
subset: v1
fault:
delay:
percentage:
value: 10
fixedDelay: 5s
- name: "service-b-route"
route:
- destination:
host: service-b
subset: v2
fault:
delay:
percentage:
value: 10
fixedDelay: 5s
3.1.2、将上述vs中的hosts的service-b删除,http.name.service-b-route 也删掉
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myservice-vs
spec:
hosts:
- service-a-svc
http:
- name: "service-a-routes"
match:
- uri:
prefix: "/api/v1/"
route:
- destination:
host: service-a-svc
subset: v1
3.2、Delegate
将请求委托给某个vs执行。有点像反向代理中的服务器组。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: main-service
spec:
hosts:
- "myservice-a.com"
gateways:
- my-service-gw
http:
- match:
- uri:
prefix: "/api/v1/"
delegate:
name: service-a-vs
namespace: default
- match:
- uri:
prefix: "/api/v2/"
delegate:
name: service-b-vs
namespace: default
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: service-a-vs
spec:
http:
- match:
- uri:
prefix: "/api/v1/"
route:
- destination:
host: service-a-svc
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: service-b-vs
spec:
http:
- match:
- uri:
prefix: "/api/v2/"
route:
- destination:
host: service-b
使用gateway内网地址或者公网地址请求都可以,内网地址需要配置本地host
for i in `seq 1 1000`;do curl http://myservice-a.com/api/v1/users/; echo -n "\n";done
http://myservice-a.com/api/v1/users/
3.3、修改headers头
可以增加或者删除header头。
给a服务增加一个返回header的接口。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: main-service
spec:
hosts:
- "myservice-a.com"
gateways:
- my-service-gw
http:
- match:
- uri:
prefix: "/api/v1/"
route:
- destination:
host: service-a-svc
headers:
request:
set:
myservice-a: "true"
- match:
- uri:
prefix: "/api/v2/"
route:
- destination:
host: service-b
访问http://myservice-a.com/api/v1/users/get_headers/
{
"host": "myservice-a.com",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-encoding": "gzip, deflate",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"x-forwarded-for": "192.168.65.3",
"x-forwarded-proto": "http",
"x-request-id": "8f08441f-1ca9-9688-8ccd-dbe058180c34",
"x-envoy-attempt-count": "1",
"myservice-a": "true",
"x-envoy-internal": "true",
"x-forwarded-client-cert": "By=spiffe://cluster.local/ns/default/sa/default;Hash=347002b9c8ed65b802b993a91dc54ad26e75323828c78573636260378f7c291a;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"
}
3.4、tls路由-TLSRoute
官方案例
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: bookinfo-sni
spec:
hosts:
- "*.bookinfo.com"
gateways:
- mygateway
tls:
- match:
- port: 443
sniHosts:
- login.bookinfo.com
route:
- destination:
host: login.prod.svc.cluster.local
- match:
- port: 443
sniHosts:
- reviews.bookinfo.com
route:
- destination:
host: reviews.prod.svc.cluster.local
3.5、tcp路由-TCPRoute
官方案例
3.6、http匹配请求-HTTPMatchRequest
没啥好玩的就不自己测试了。
内容限制规则仅匹配 URL 路径以 /ratings/v2/ 开头且请求包含end-user值为jason的自定义标头的请求。
- If a root VirtualService have matched any property (path, header etc.) by regex, delegate VirtualServices should not have any other matches on the same property.
- If a delegate VirtualService have matched any property (path, header etc.) by regex, root VirtualServices should not have any other matches on the same property.
官方案例
3.7、HTTPRouteDestination
每条路由规则都与一个或多个服务版本相关联(请参阅文档开头的词汇表)。与版本相关联的权重决定了其接收的流量比例。例如,以下规则将“reviews”服务的 25% 流量路由到带有“v2”标签的实例,其余流量(即 75%)路由到“v1”。
官方例子
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews-route
spec:
hosts:
- reviews.prod.svc.cluster.local
http:
- route:
- destination:
host: reviews.prod.svc.cluster.local
subset: v2
weight: 25
- destination:
host: reviews.prod.svc.cluster.local
subset: v1
weight: 75
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews-destination
spec:
host: reviews.prod.svc.cluster.local
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
流量还可以分散到两个完全不同的服务中,而无需定义新的子集。例如,以下规则将 25% 的 reviews.com 流量转发到 dev.reviews.com
3.8、4层属性匹配-L4MatchAttributes
Gpt的样例
1、基于目标端口的流量匹配
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: my-service
spec:
hosts:
- my-service.default.svc.cluster.local
tcp:
- match:
- port: 8080 # 匹配 TCP 8080 端口
route:
- destination:
host: my-service
port:
number: 8080
- match:
- port: 80 # 匹配 TCP 80 端口
route:
- destination:
host: my-service
port:
number: 80
2、基于来源子网的流量匹配
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: internal-external-split
spec:
hosts:
- my-service.default.svc.cluster.local
tcp:
- match:
- sourceSubnet: "10.0.0.0/8" # 匹配内网流量
route:
- destination:
host: internal-service # 路由到内部服务
port:
number: 80
- match:
- sourceSubnet: "0.0.0.0/0" # 匹配所有公网流量
route:
- destination:
host: external-service # 路由到外部服务
port:
number: 80
3、基于源命名空间和标签的流量匹配 这种配置可以将流量源与命名空间或 Kubernetes 标签结合起来使用,用于跨命名空间的流量控制。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: restrict-prod-access
spec:
hosts:
- restricted-service.default.svc.cluster.local
tcp:
- match:
- sourceNamespace: prod # 仅允许来自 prod 命名空间的流量
route:
- destination:
host: restricted-service
port:
number: 8080
4、基于网关的流量匹配 如果你有多个网关并希望根据流量进入的网关进行不同处理,可以使用 gateway 字段。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: gateway-split
spec:
hosts:
- my-service.default.svc.cluster.local
tcp:
- match:
- gateway: internal-gateway # 内部网关的流量
route:
- destination:
host: internal-service
port:
number: 8080
- match:
- gateway: external-gateway # 外部网关的流量
route:
- destination:
host: external-service
port:
number: 8080
3.9、tls匹配属性-TLSMatchAttributes
Gpt示例
1、基于 SNI 的流量匹配 SNI(Server Name Indication)是客户端在发起 TLS 连接时发送的主机名,用于区分不同的 TLS 服务。通过匹配 SNI,可以在同一 IP 地址下路由到不同的服务。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: tls-routing
spec:
hosts:
- "*"
tls:
- match:
- port: 443 # 匹配 HTTPS/TLS 端口
sniHosts:
- "service1.example.com" # 匹配 SNI 为 service1.example.com 的流量
route:
- destination:
host: service1
port:
number: 443
- match:
- port: 443
sniHosts:
- "service2.example.com" # 匹配 SNI 为 service2.example.com 的流量
route:
- destination:
host: service2
port:
number: 443
2、基于来源子网的 TLS 流量匹配 可以使用 sourceSubnet 来区分来自不同来源的 TLS 流量。比如,你可能希望内网的 TLS 流量走不同的路由。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: tls-internal-external
spec:
hosts:
- "*"
tls:
- match:
- port: 443
sourceSubnet: "10.0.0.0/8" # 匹配来自内网的 TLS 流量
route:
- destination:
host: internal-tls-service
port:
number: 443
- match:
- port: 443
sourceSubnet: "0.0.0.0/0" # 匹配来自公网的 TLS 流量
route:
- destination:
host: external-tls-service
port:
number: 443
3、基于特定命名空间和标签的 TLS 流量匹配 可以根据 Kubernetes 命名空间或标签来匹配 TLS 流量源,从而灵活控制跨命名空间的 TLS 流量。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: restrict-tls-prod
spec:
hosts:
- restricted-tls-service.default.svc.cluster.local
tls:
- match:
- sourceNamespace: prod # 仅允许来自 prod 命名空间的 TLS 流量
route:
- destination:
host: restricted-tls-service
port:
number: 443
4、基于多个条件组合的 TLS 流量匹配
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: complex-tls-routing
spec:
hosts:
- "*"
tls:
- match:
- port: 443
sniHosts:
- "internal.example.com"
sourceSubnet: "10.0.0.0/8" # 仅匹配来自内网且 SNI 为 internal.example.com 的 TLS 流量
route:
- destination:
host: internal-service
port:
number: 443
- match:
- port: 443
sniHosts:
- "external.example.com"
sourceSubnet: "0.0.0.0/0" # 匹配来自公网且 SNI 为 external.example.com 的 TLS 流量
route:
- destination:
host: external-service
port:
number: 443
3.10、http重定向-HTTPRedirect
HTTPRedirect 可用于向调用者发送 301 重定向响应,其中响应中的 Authority/Host 和 URI 可以与指定值交换。例如,以下规则将 ratings 服务上的 /v1/getProductRatings API 请求重定向到 bookratings 服务提供的 /v1/bookRatings。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings-route
spec:
hosts:
- ratings.prod.svc.cluster.local
http:
- match:
- uri:
exact: /v1/getProductRatings
redirect:
uri: /v1/bookRatings
authority: newratings.default.svc.cluster.local
...
3.11、http直接响应-HTTPDirectResponse
HTTPDirectResponse 可用于向客户端发送固定响应。例如,以下规则向 /v1/getProductRatings API 请求返回带有正文的固定 503 状态。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings-route
spec:
hosts:
- ratings.prod.svc.cluster.local
http:
- match:
- uri:
exact: /v1/getProductRatings
directResponse:
status: 503
body:
string: "unknown error"
...
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings-route
spec:
hosts:
- ratings.prod.svc.cluster.local
http:
- match:
- uri:
exact: /v1/getProductRatings
directResponse:
status: 503
body:
bytes: "dW5rbm93biBlcnJvcg==" # "unknown error" in base64
...
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings-route
spec:
hosts:
- ratings.prod.svc.cluster.local
http:
- match:
- uri:
exact: /v1/getProductRatings
directResponse:
status: 503
body:
string: "{\"error\": \"unknown error\"}"
headers:
response:
set:
content-type: "text/plain"
...
3.12、HTTP重写-HTTPRewrite
HTTPRewrite 可用于在将请求转发到目标之前重写 HTTP 请求的特定部分。重写原语只能与 HTTPRouteDestination 一起使用。以下示例演示了如何在进行实际 API 调用之前将 API 调用的 URL 前缀 (/ratings) 重写为 ratings 服务。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings-route
spec:
hosts:
- ratings.prod.svc.cluster.local
http:
- match:
- uri:
prefix: /ratings
rewrite:
uri: /v1/bookRatings
route:
- destination:
host: ratings.prod.svc.cluster.local
subset: v1
3.13、HTTPRewrite vs HTTPRedirect
HTTPRewrite 用于修改请求中的路径或主机头信息,在 Istio 中用于将请求重写为不同的路径或主机地址,而不会改变请求的状态码。客户端并不会察觉到请求路径被修改,整个过程对客户端是透明的。
HTTPRedirect 用于发送 HTTP 重定向响应给客户端,并让客户端自己去访问新的 URL。通常用于将客户端请求重定向到另一个 URL 或进行协议切换(如 HTTP 转 HTTPS)。重定向操作会返回 3xx 状态码(如 301 Moved Permanently 或 302 Found),告诉客户端 URL 发生了变化。
功能 | HTTPRewrite | HTTPRedirect |
---|---|---|
作用 | 修改请求路径或主机,服务器端透明处理 | 返回 3xx 响应,客户端重新请求 |
客户端可见性 | 对客户端不可见(客户端感知不到路径的变化) | 客户端可见(客户端收到重定向并重新请求) |
状态码 | 不更改状态码,继续处理请求 | 返回 3xx 状态码(如 301、302) |
使用场景 | 内部路径或主机的调整、隐藏复杂性 | 协议切换、路径迁移、永久重定向 |
3.14、RegexRewrite
基于正则表达式对 HTTP 请求的 URI 进行修改的功能。它允许使用正则表达式匹配请求的 URI,然后通过替换的方式对匹配的部分进行重写。
Gpt示例
1、简单的正则表达式重写 假设你有一个请求路径 /foo/123/bar,你希望将其重写为 /baz/123/bar。这里的 123 可以是任何数字。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: regex-rewrite-example
spec:
hosts:
- my-app.com
http:
- match:
- uri:
regex: "^/foo/([0-9]+)/bar$" # 匹配路径 /foo/数字/bar
route:
- destination:
host: my-service
port:
number: 8080
rewrite:
regex:
pattern: "^/foo/([0-9]+)/bar$" # 匹配 /foo/数字/bar
substitution: "/baz/$1/bar" # 重写为 /baz/数字/bar,$1 代表第一个捕获组
2、复杂的重写 假设你希望将请求路径 /api/v1/product/abc123/details 重写为 /products/abc123/info,即将路径中的 /api/v1/product/ 重写为 /products/,并保留 abc123 的部分。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: complex-regex-rewrite
spec:
hosts:
- my-app.com
http:
- match:
- uri:
regex: "^/api/v1/product/([a-zA-Z0-9]+)/details$"
route:
- destination:
host: product-service
port:
number: 8080
rewrite:
regex:
pattern: "^/api/v1/product/([a-zA-Z0-9]+)/details$"
substitution: "/products/$1/info" # 将 /api/v1/product/abc123/details 重写为 /products/abc123/info
3、正则表达式重写并重定向到 HTTPS 有时你希望不仅修改路径,还希望将请求重定向到 HTTPS。比如,重写 /blog/2024/10/20/title 为 /article/2024/10/20/title,并切换到 HTTPS。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: redirect-and-rewrite
spec:
hosts:
- my-app.com
http:
- match:
- uri:
regex: "^/blog/([0-9]{4})/([0-9]{2})/([0-9]{2})/.*$" # 匹配 /blog/yyyy/mm/dd/title
redirect:
uri: "/article/$1/$2/$3" # 重写为 /article/yyyy/mm/dd
scheme: "https" # 将请求重定向到 HTTPS
3.15、StringMatch
StringMatch 提供了三种匹配方式:
- exact: 精确匹配,要求字符串完全相等。
- prefix: 前缀匹配,要求字符串以指定前缀开头。
- regex: 正则表达式匹配,基于正则表达式匹配字符串。
Gpt示例
1、精确匹配
3.16、http重试-HTTPRetry
描述 HTTP 请求失败时使用的重试策略。例如,以下规则将调用 ratings:v1 服务时的最大重试次数设置为 3,每次重试的超时时间为 2 秒。如果出现连接失败、refused_stream 或上游服务器响应服务不可用 (503),则会尝试重试。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings-route
spec:
hosts:
- ratings.prod.svc.cluster.local
http:
- route:
- destination:
host: ratings.prod.svc.cluster.local
subset: v1
retries:
attempts: 3
perTryTimeout: 2s
retryOn: gateway-error,connect-failure,refused-stream
3.17、跨域-CorsPolicy
示例
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings-route
spec:
hosts:
- ratings.prod.svc.cluster.local
http:
- route:
- destination:
host: ratings.prod.svc.cluster.local
subset: v1
corsPolicy:
allowOrigins:
- exact: https://example.com
allowMethods:
- POST
- GET
allowCredentials: false
allowHeaders:
- X-Foo-Bar
maxAge: "24h"
3.18、http故障注入-HTTPFaultInjection
官网示例
1、延迟规范用于将延迟注入请求转发路径。以下示例将从所有带有标签 env: prod 的 pod 向“reviews”服务的“v1”版本发出的每 1000 个请求中,有 1 个请求会延迟 5 秒
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews-route
spec:
hosts:
- reviews.prod.svc.cluster.local
http:
- match:
- sourceLabels:
env: prod
route:
- destination:
host: reviews.prod.svc.cluster.local
subset: v1
fault:
delay:
percentage:
value: 0.1
fixedDelay: 5s
2、中止规范用于提前中止具有预先指定的错误代码的请求。以下示例将对“评级”服务“v1”的每 1000 个请求中的 1 个返回 HTTP 400 错误代码。
3.19、HTTP镜像策略-HTTPMirrorPolicy
HTTPMirrorPolicy 可用于指定除原始目标之外的镜像 HTTP 流量的目标。镜像流量是尽最大努力的,其中 sidecar/网关不会等待镜像目标响应就返回原始目标的响应。将为镜像目标生成统计信息。
Gpt示例
1、镜像请求到另一个服务
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: my-virtualservice
spec:
hosts:
- primary-service.default.svc.cluster.local
http:
- route:
- destination:
host: primary-service.default.svc.cluster.local
port:
number: 80
mirror:
host: mirror-service.default.svc.cluster.local
port:
number: 8080
mirrorPercentage:
value: 100 # 将 100% 的请求镜像到 mirror-service
2、部分请求镜像
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: partial-mirror
spec:
hosts:
- primary-service.default.svc.cluster.local
http:
- route:
- destination:
host: primary-service.default.svc.cluster.local
port:
number: 80
mirror:
host: mirror-service.default.svc.cluster.local
port:
number: 8080
mirrorPercentage:
value: 50 # 只镜像 50% 的请求
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: mirror-to-subset
spec:
hosts:
- primary-service.default.svc.cluster.local
http:
- route:
- destination:
host: primary-service.default.svc.cluster.local
subset: v1
port:
number: 80
mirror:
host: primary-service.default.svc.cluster.local
subset: v2 # 将请求镜像到 v2 子集
port:
number: 80
mirrorPercentage:
value: 100
4、vs的典型应用
4.1、多个服务的组合
如下配置将一个weather应用的多个服务组装成一个大的虚拟服务。 根据不同的路径,流量会被分发到不同的后端服务上。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: weather
namespace: weather
spec:
hosts:
- weather.com
http:
- match:
- uri:
prefix: /recommendation
route:
- destination:
host: recommendation
- match:
- uri:
prefix: /forecast
route:
- destination:
host: forecast
- match:
- uri:
prefix: /advertisement
route:
- destination:
host: advertisement
4.2、路由规则的优先级
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: forecast
namespace: weather
spec:
hosts:
- forecast
http:
- match:
- uri:
prefix: /weather/data/
route:
- destination:
name: forecast
subset: v3
- match:
- uri:
prefix: /weather
route:
- destination:
name: forecast
subset: v2
- route:
- destination:
name: forecast
subset: v1
4.3、复杂条件路由
灰度发布等分流规则一般有两种方法:一种是基于请求的内容切分流量,另一种是按比例切分流量。
第一个条件检查请求的uri和headers,第二个条件检查请求的uri。第一个条件的两个属性是“与”逻辑,第一个条件和第二个条件之间是“或”逻辑。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: forecast
namespace: weather
spec:
hosts:
- forecast
http:
- match:
- headers:
cookie:
regex: "^(.*?;)?(local=north)(;.*)?"
uri:
prefix: "/weather"
- uri:
prefix: "/data"
route:
- destination:
name: forecast
subset: v2
weight: 20
- destination:
name: forecast
subset: v3
weight: 80
- route:
- destination:
name: forecast
subset: v1