如何为VirtualService指定HTTP路由应用Envoy gRPC转码过滤器?
需求概述
希望在网关中暴露一个VirtualService,使其能在同一端口/主机同时响应gRPC和REST请求,通过Content-Type区分请求类型:当Content-Type为application/json时处理REST请求,其余情况处理gRPC请求。上游服务仅暴露gRPC端点,因此需要对REST路径使用gRPC转码过滤器。
VirtualService配置
kind: VirtualService metadata: labels: app.kubernetes.io/name: hello app.kubernetes.io/version: "1.0" name: hello spec: gateways: - istio-system/mesh-gateway hosts: - 'hello.mesh.control-cluster-raffa.demo.red-chesterfield.com' http: - name: transcoded match: - name: rest-content-type headers: "Content-Type": exact: "application/json" route: - destination: host: hello port: number: 9000 - name: primary route: - destination: host: hello port: number: 9000
待完善的EnvoyFilter配置
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: grpc-json-transcoder spec: workloadSelector: labels: istio: ingressgateway configPatches: - applyTo: NETWORK_FILTER # http connection manager is a filter in Envoy match: context: GATEWAY listener: filterChain: filter: name: "envoy.filters.network.http_connection_manager" patch: operation: INSERT_AFTER value: name: envoy.filters.http.grpc_json_transcoder typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/var/config/proto/proto.pb" services: ["hello.HelloGrpc"] print_options: add_whitespace: true always_print_primitive_fields: true always_print_enums_as_ints: false preserve_proto_field_names: false
技术问题
- 该过滤器需应用于ingress-gateway,若网关与VirtualService不在同一命名空间,过滤器应部署在哪个命名空间?
- 该网关为多个服务共享,如何确保过滤器不会影响其他VirtualService?
- 如何确保过滤器仅应用于VirtualService中名为"transcoded"的HTTP路由?
- 该过滤器需要读取文件,是否需要将该文件挂载到ingress-gateway中?
问题解答
部署命名空间选择:EnvoyFilter必须部署在ingress-gateway所在的命名空间(比如示例中的
istio-system)。因为EnvoyFilter通过workloadSelector匹配网关Pod的标签,跨命名空间无法匹配目标工作负载,只有同命名空间才能生效。避免影响其他VirtualService:
- 不能给整个网关的HTTP连接管理器全局添加过滤器,要采用路由级别的过滤器配置实现隔离。
- 利用EnvoyFilter的精准匹配条件,仅给目标VirtualService关联的路由添加转码过滤器,比如匹配对应VirtualService的host或者路由名称,避免全局生效。
仅应用于指定HTTP路由:
- 修改EnvoyFilter的规则,从全局HTTP连接管理器层面改为匹配具体路由:
- 将
applyTo改为HTTP_ROUTE,在match中指定路由名称(即VirtualService里的transcoded)和对应的虚拟主机(匹配VirtualService的host)。 - 示例修改方向:
applyTo: HTTP_ROUTE match: context: GATEWAY routeConfiguration: vhost: name: "hello.mesh.control-cluster-raffa.demo.red-chesterfield.com:80" # 对应VirtualService的host route: name: "transcoded" # 对应VirtualService中路由的name patch: operation: MERGE value: route: typed_per_filter_config: envoy.filters.http.grpc_json_transcoder: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/var/config/proto/proto.pb" services: ["hello.HelloGrpc"] print_options: add_whitespace: true always_print_primitive_fields: true always_print_enums_as_ints: false preserve_proto_field_names: false
- 将
- 这样就能确保转码过滤器仅作用于
transcoded这条路由,不会影响其他路由或VirtualService。
- 修改EnvoyFilter的规则,从全局HTTP连接管理器层面改为匹配具体路由:
文件挂载要求:必须将proto描述符文件(
proto.pb)挂载到ingress-gateway的Pod中,路径要和EnvoyFilter配置里的proto_descriptor一致(示例中的/var/config/proto/proto.pb)。具体步骤:- 创建
ConfigMap或Secret存储proto.pb文件内容。 - 修改ingress-gateway的Deployment,添加卷挂载,将ConfigMap/Secret挂载到指定路径。
- 重启网关Pod确保文件可访问,否则转码过滤器会初始化失败。
- 创建
内容的提问来源于stack exchange,提问作者user3157549
相关产品推荐
相关产品推荐

