Java 开发人员视角下的 Istio VirtualService(VS)

1. VirtualService 是什么?

  • Kubernetes Service:做服务发现与负载均衡
  • Istio VirtualService (VS):定义请求如何被路由
  • Istio DestinationRule
    (DR)
    :定义目标服务的子集(subsets)及连接策略。
  • Istio Gateway:把网格内服务暴露到外部。

一句话:Service 负责"找到谁",VirtualService
负责"怎么去",DestinationRule 负责"去向的细化与稳定性策略",Gateway
负责"从哪里进/出"。

2. 使用场景

  1. 金丝雀发布 / 蓝绿发布
  2. A/B 实验 / 精准灰度(Header/Cookie/Query)
  3. 路径路由与重写
  4. 超时/重试
  5. 流量镜像
  6. 故障注入
  7. gRPC 路由
  8. 网关入口路由

3. 示例:权重灰度

3.1 Deployment + Service

apiVersion: apps/v1
kind: Deployment
metadata:
  name: productpage-v1
spec:
  replicas: 1
  selector:
    matchLabels: { app: productpage, version: v1 }
  template:
    metadata:
      labels: { app: productpage, version: v1 }
    spec:
      containers:
        - name: app
          image: your-registry/productpage:v1
          ports: [{ containerPort: 8080 }]

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: productpage-v2
spec:
  replicas: 1
  selector:
    matchLabels: { app: productpage, version: v2 }
  template:
    metadata:
      labels: { app: productpage, version: v2 }
    spec:
      containers:
        - name: app
          image: your-registry/productpage:v2
          ports: [{ containerPort: 8080 }]

---
apiVersion: v1
kind: Service
metadata:
  name: productpage
spec:
  selector: { app: productpage }
  ports:
    - name: http
      port: 80
      targetPort: 8080

3.2 DestinationRule

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: productpage
spec:
  host: productpage.default.svc.cluster.local
  subsets:
    - name: v1
      labels: { version: v1 }
    - name: v2
      labels: { version: v2 }

3.3 VirtualService

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: productpage
spec:
  hosts:
    - productpage.default.svc.cluster.local
  http:
    - route:
        - destination:
            host: productpage.default.svc.cluster.local
            subset: v1
          weight: 90
        - destination:
            host: productpage.default.svc.cluster.local
            subset: v2
          weight: 10

4. A/B 实验(按 Header 路由)

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: productpage-ab
spec:
  hosts:
    - productpage.default.svc.cluster.local
  http:
    - match:
        - headers:
            x-user-role:
              exact: staff
      route:
        - destination:
            host: productpage.default.svc.cluster.local
            subset: v2
    - route:
        - destination:
            host: productpage.default.svc.cluster.local
            subset: v1

5. 路径路由与重写

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-gateway
spec:
  hosts: ["api.example.com"]
  gateways: ["ingress-gw"]
  http:
    - match:
        - uri:
            prefix: /api/
      rewrite:
        uri: /
      route:
        - destination:
            host: productpage.default.svc.cluster.local
            port: { number: 80 }

6. 超时与重试

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: productpage-retry
spec:
  hosts: ["productpage.default.svc.cluster.local"]
  http:
    - timeout: 2s
      retries:
        attempts: 2
        perTryTimeout: 1s
        retryOn: "5xx,gateway-error,connect-failure,retriable-4xx"
      route:
        - destination:
            host: productpage.default.svc.cluster.local
            subset: v1

7. 流量镜像

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: productpage-mirror
spec:
  hosts: ["productpage.default.svc.cluster.local"]
  http:
    - route:
        - destination:
            host: productpage.default.svc.cluster.local
            subset: v1
      mirror:
        host: productpage.default.svc.cluster.local
        subset: v2
      mirrorPercentage:
        value: 20.0

8. 故障注入

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: productpage-fault
spec:
  hosts: ["productpage.default.svc.cluster.local"]
  http:
    - match:
        - headers:
            x-chaos:
              exact: on
      fault:
        delay:
          fixedDelay: 3s
          percentage: { value: 100 }
      route:
        - destination:
            host: productpage.default.svc.cluster.local
            subset: v1

9. gRPC 路由

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: account-grpc
spec:
  hosts: ["account.default.svc.cluster.local"]
  http:
    - match:
        - uri:
            prefix: /account.AccountService/
      retries:
        attempts: 2
        perTryTimeout: 800ms
        retryOn: "cancelled,connect-failure,refused-stream,5xx"
      route:
        - destination:
            host: account.default.svc.cluster.local
            subset: v1

10. Java 代码协作示例

Feign 全局拦截器

@Component
public class CanaryHeaderPropagate implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        String header = CanaryContext.getHeader("x-user-role");
        if (header != null) {
            template.header("x-user-role", header);
        }
    }
}

Spring WebClient

WebClient client = WebClient.builder().build();

public Mono<String> callDownstream(ServerHttpRequest request) {
    String role = request.getHeaders().getFirst("x-user-role");
    return client.get()
        .uri("http://productpage/api")
        .headers(h -> { if (role != null) h.set("x-user-role", role); })
        .retrieve()
        .bodyToMono(String.class);
}

11. 常见坑与最佳实践

  • 子集必须存在
  • 匹配顺序:越精准的放前面
  • 权重总和最好 100
  • Header 名小写
  • 网关与主机名要一致
  • 观测先行:配合 Prometheus/Grafana/Kiali
  • DR 配合 VS:连接池/熔断/逐出在 DR

12. 验证命令

curl -H "Host: api.example.com" http://<ingress-ip>/api/hello
curl -H "x-user-role: staff" http://productpage.default.svc.cluster.local
curl -H "x-chaos: on" http://productpage.default.svc.cluster.local -w '\n%{time_total}\n'

13. 模板清单

  • 权重灰度
  • Header/Cookie/Query 定向
  • 路径路由 + 重写
  • 超时 + 重试
  • 流量镜像
  • 故障注入
  • gRPC
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容