跳转至

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
通过如下的脚本在官方的reviews-v1进行测试,根据上述配置会将hosts中指定的两个进行识别和应用,首先识别hosts,然后根据路径转发。
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"}
请求v1

结果不一样是因为此时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
    
    请求v2

  • 第一个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\"}"
请求v3

从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"}
请求v4

2.4、调用服务配置vs,被掉服务未配置vs

  • service-a-svc:8000
  • service-b: 9000
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: service-vs
    spec:
      hosts:
      - service-a-svc
      http:
      - match:
        - uri:
            prefix: /api/v1/
        route:
        - destination:
            host: service-a-svc
            port:
              number: 8000
    
    会从从kiali的调用链中发现,是没经过vs的。
    $ 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\"}"
    

请求v5

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
执行如下shell之后,效果应该是请求从service-a-svc的v1版本,到service-b的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\"}"
vs-dr-subset

3.1.1、将上述vs中的hosts中删除service-b会出现什么效果?

vs-dr-subset-2

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
这样访问service-b时就是走的service,不经过vs。 vs-dr-subset-2

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/
vs-delegate

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

官方案例
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: bookinfo-mongo
spec:
  hosts:
  - mongo.prod.svc.cluster.local
  tcp:
  - match:
    - port: 27017
    route:
    - destination:
        host: mongo.backup.svc.cluster.local
        port:
          number: 5555

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.
官方案例
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: ratings-route
spec:
  hosts:
  - ratings.prod.svc.cluster.local
  http:
  - match:
    - headers:
        end-user:
          exact: jason
      uri:
        prefix: "/ratings/v2/"
      ignoreUriCase: true
    route:
    - destination:
        host: ratings.prod.svc.cluster.local

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
以及关联的 DestinationRule

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

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: reviews-route-two-domains
spec:
  hosts:
  - reviews.com
  http:
  - route:
    - destination:
        host: dev.reviews.com
      weight: 25
    - destination:
        host: reviews.com
      weight: 75

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"
  ...
还可以指定二进制响应主体。这对于非基于文本的协议(例如 gRPC)非常有用。

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
  ...
在 HTTPRoute 和 direct_response 中添加标头是一种很好的做法,例如指定返回的 Content-Type。

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、精确匹配

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: product-service
spec:
  hosts:
    - my-product-service.com
  http:
    - match:
        - uri:
            exact: "/products/detail"  # 精确匹配路径 /products/detail
      route:
        - destination:
            host: product-service
            port:
              number: 8080

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 错误代码。

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
    fault:
      abort:
        percentage:
          value: 0.1
        httpStatus: 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% 的请求
3、镜像到不同的子集
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


本文阅读量  次

评论