什么是 Envoy Gateway?
Envoy Gateway 是基于 Envoy 实现 Gateway API 的 Kubernetes 网关。通过定义
Gateway API
中的 Gateway
、HTTPRoute
等资源,您可以管理 Kubernetes 的南北向流量。为什么要用 Gateway API?
Kubernetes 提供了
Ingress API
来接入七层南北向流量,但功能较弱,每种实现都带有不同的 annotation 来增强 Ingress 的能力,灵活性和扩展性较差。社区推出了 Gateway API
作为更好的解决方案,解决 Ingress API 的痛点,同时统一了四七层南北向流量,并支持服务网格的东西向流量(参考 GAMMA),各个云厂商以及开源代理软件都在积极适配 Gateway API
,可参考 Gateway API 的实现列表,其中 Envoy Gateway 是一个很流行的实现。安装 Envoy Gateway
方法一:通过应用市场安装
1. 在目标 TKE 集群中新建一个名为
envoy-gateway-system
的命名空间。操作详情请参见 创建命名空间。2. 在 TKE 应用市场 搜索并选择
envoygateway
。3. 单击创建应用,选择好目标 TKE 集群,名称可写
eg
,命名空间选 envoy-gateway-system
。
4. 根据需求配置参数完后,单击创建即可将 Envoy Gateway 安装到集群中。
方法二:按照 Envoy Gateway 官方文档安装
配置 kubectl 访问集群
Envoy Gateway 使用的是 Gateway API 而不是 Ingress API,在 TKE 控制台无法直接创建,可通过 kubectl 命令进行创建,请参考 连接集群 这篇文档配置 kubectl。
创建 GatewayClass
类似
Ingress
需要指定 IngressClass
,Gateway API 中每个 Gateway
都需要引用一个 GatewayClass
,GatewayClass
相当于是网关实例除监听器外的配置(如部署方式、网关 Pod 的 template、副本数量、关联的 Service 等),所以先创建一个 GatewayClass
:apiVersion: gateway.networking.k8s.io/v1kind: GatewayClassmetadata:name: egspec:controllerName: gateway.envoyproxy.io/gatewayclass-controller
说明:
GatewayClass
是 non-namespaced 资源,无需指定命名空间。创建 Gateway
每个
Gateway
对应一个 CLB,在 Gateway
上声明端口相当于在 CLB 上创建响应协议的监听器:说明:
apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata:name: test-gwnamespace: testspec:gatewayClassName: eglisteners:- name: httpprotocol: HTTPport: 8080allowedRoutes:namespaces:from: All
说明:
Gateway
可以指定命名空间,可以被 HTTPRoute
等路由规则跨命名空间引用。Gateway
创建后,EnvoyGateway
会自动为其创建一个 LoadBalancer 类型的 Service,也就是一个 CLB。在 TKE 上,LoadBalancer 类型的 Service 默认是一个公网 CLB,如果要自定义,可参考常见问题中的 如何自定义 CLB。说明:
Gateway 通过 LoadBalancer 类型的 Service 对外暴露流量,所以 CLB 只会用到四层监听器(TCP/UDP),七层流量也是先进入 CLB 四层监听器,转发给 EnvoyGateway 的 Pod,再由 EnvoyGateway 解析四七层流量并根据配置规则进行转发。
如何获取
Gateway
对应的 CLB 地址呢?可以通过 kubectl get gtw
查看:$ kubectl get gtw test-gw -n testNAME CLASS ADDRESS PROGRAMMED AGEtest-gw eg 139.155.64.52 True 358d
其中
ADDRESS
就是 CLB 的地址(IP 或域名)。创建 HTTPRoute
HTTPRoute
用于定义 HTTP 转发规则(七层流量),也是 Gateway API 中最常用的转发规则,类似 Ingress API 中的 Ingress
资源。下面给出一个示例:
说明:
apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:name: nginxnamespace: testspec:parentRefs:- name: test-gwnamespace: testhostnames:- "test.example.com"rules:- backendRefs:- name: nginxport: 80
注意:
1.
parentRefs
中指定要引用 Gateway
(CLB),表示将该规则应用到这个 Gateway
中。2.
hostnames
定义转发规则使用的的域名,确保该域名解析到 Gateway
对应的 CLB,这样可以通过域名访问集群内的服务。3.
backendRefs
定义该条转发规则对应的后端 Service。创建 TCPRoute 和 UDPRoute
TCPRoute
和 UDPRoute
用于定义 TCP 和 UDP 转发规则(四层流量),类似 LoadBalancer
类型的 Service
。下面是
TCPRoute
的示例:apiVersion: gateway.networking.k8s.io/v1alpha2kind: TCPRoutemetadata:name: foospec:parentRefs:- namespace: testname: test-gwsectionName: foorules:- backendRefs:- name: fooport: 6000
注意:
1.
parentRefs
指定要引用的Gateway
(CLB),表示将该 TCP 要监听到这个 Gateway
中。通常只使用 Gateway
中的一个端口,所以指定 sectionName
来指定使用哪个监听器暴露。2.
backendRefs
定义该条转发规则对应的后端 Service。下面是
UDPRoute
的示例,与 TCPRoute
类似:apiVersion: gateway.networking.k8s.io/v1alpha2kind: UDPRoutemetadata:name: barspec:parentRefs:- namespace: testname: test-gwsectionName: barrules:- backendRefs:- name: barport: 6000
引用的
Gateway
示例:apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata:name: test-gwnamespace: testspec:gatewayClassName: eglisteners:- name: fooprotocol: TCPport: 6000allowedRoutes:namespaces:from: All- name: barprotocol: UDPport: 6000allowedRoutes:namespaces:from: All
常见问题
如何自定义 CLB?
可通过创建
EnvoyProxy
自定义资源来自定义,下面是示例:apiVersion: gateway.envoyproxy.io/v1alpha1kind: EnvoyProxymetadata:name: proxy-confignamespace: testspec:provider:type: Kuberneteskubernetes:envoyDeployment:replicas: 1container:resources:requests:tke.cloud.tencent.com/eni-ip: "1"limits:tke.cloud.tencent.com/eni-ip: "1"pod:annotations:tke.cloud.tencent.com/networks: tke-route-enienvoyService:annotations:service.kubernetes.io/tke-existed-lbid: lb-5nhlk3nrservice.cloud.tencent.com/direct-access: "true"
以上示例中:
显式声明使用 VPC-CNI 网络模式且启用 CLB 直连 Pod。
使用已有 CLB,指定了 CLB 的 ID。
相应的,
GatewayClass
中需引用该 EnvoyProxy
配置:apiVersion: gateway.networking.k8s.io/v1kind: GatewayClassmetadata:name: egspec:controllerName: gateway.envoyproxy.io/gatewayclass-controllerparametersRef:group: gateway.envoyproxy.iokind: EnvoyProxyname: proxy-confignamespace: test
举几个常见的自定义例子:
1. 通过
service.cloud.tencent.com/specify-protocol
注解来修改监听器协议为 HTTPS 并正确引用 SSL 证书,以便让 CLB 能够接入 腾讯云 WAF。2. 通过
service.kubernetes.io/qcloud-loadbalancer-internal-subnetid
注解指定 CLB 内网 IP,实现自动创建内网 CLB 来接入流量。3. 通过
service.kubernetes.io/service.extensiveParameters
注解自定义自动创建的 CLB 更多属性,如指定运营商、带宽上限、实例规格、网络计费模式等。多个 HTTPRoute 如何复用同一个 CLB?
通常一个
Gateway
对象就对应一个 CLB,只要不同 HTTPRoute
的 parentRefs
引用的是同一个 Gateway
对象,那么它们就会复用同一个 CLB。注意:
如果多个
HTTPRoute
复用同一个 CLB,确保它们定义的 HTTP 规则不要冲突,否则可能转发行为可能不符预期。下面给个示例,第一个
HTTPRoute
,引用 Gateway test-gw
,使用域名 test1.example.com
:apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:name: test1namespace: testspec:parentRefs:- group: gateway.networking.k8s.iokind: Gatewayname: test-gwnamespace: testhostnames:- "test1.example.com"rules:- backendRefs:- group: ""kind: Servicename: test1port: 80
第二个
HTTPRoute
,也引用 Gateway test-gw
,域名则使用 test2.example.com
:apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:name: test2namespace: testspec:parentRefs:- group: gateway.networking.k8s.iokind: Gatewayname: test-gwnamespace: testhostnames:- "test2.example.com"rules:- backendRefs:- group: ""kind: Servicename: test2port: 80
如何实现四七层共用同一个 CLB?
使用 TKE 自带的
LoadBalancer
类型的 Service
,可以实现多个 Service
复用同一个 CLB,也就是多个四层端口(TCP/UDP)复用同一个 CLB;使用 TKE 自带的 Ingress
(CLB Ingress),无法与任何其它 Ingress
和 LoadBalancer
类型的 Service
复用同一个 CLB。所以,如果需要实现四七层共用同一个 CLB,直接使用 TKE 自带的 CLB Service 和 CLB Ingress 无法实现,而如果你安装了 EnvoyGateway
的话就可以实现。下面给个示例, 首先
Gateway
的监听器声明四层和七层的端口:注意:
使用
name
给每个监听器取个名字,方便后续通过 sectionName
引用。apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata:name: test-gwnamespace: testspec:gatewayClassName: eglisteners:- name: httpprotocol: HTTPport: 80allowedRoutes:namespaces:from: All- name: httpsprotocol: HTTPSport: 443allowedRoutes:namespaces:from: Alltls:mode: TerminatecertificateRefs:- kind: Secretgroup: ""name: https-cert- name: tcp-6000protocol: TCPport: 6000allowedRoutes:namespaces:from: All- name: udp-6000protocol: UDPport: 6000allowedRoutes:namespaces:from: All
HTTPRoute
里使用 Gateway
里的七层监听器(80 和 443):注意:
使用
sectionName
指定具体要绑定的监听器。apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:name: testnamespace: testspec:parentRefs:- name: test-gwnamespace: testsectionName: http- name: test-gwnamespace: testsectionName: httpshostnames:- "test.example.com"rules:- backendRefs:- group: ""kind: Servicename: nginxport: 80
TCPRoute
和 UDPRoute
里使用 Gateway
里的四层监听器(TCP/6000 和 UDP/6000):注意:
与
HTTPRoute
一样,使用 sectionName
指定具体要绑定的监听器。apiVersion: gateway.networking.k8s.io/v1alpha2kind: TCPRoutemetadata:name: foospec:parentRefs:- namespace: testname: test-gwsectionName: tcp-6000rules:- backendRefs:- name: fooport: 6000---apiVersion: gateway.networking.k8s.io/v1alpha2kind: UDPRoutemetadata:name: foospec:parentRefs:- namespace: testname: test-gwsectionName: udp-6000rules:- backendRefs:- name: fooport: 6000
如何实现自动重定向?
通过配置
HTTPRoute
的 filters
可实现自动重定向,下面给出示例。路径前缀
/api/v1
替换成 /apis/v1
:apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:name: redirect-api-v1namespace: testspec:hostnames:- test.example.comparentRefs:- name: test-gwnamespace: testsectionName: httpsrules:- matches:- path:type: PathPrefixvalue: /api/v1filters:- type: RequestRedirectrequestRedirect:path:type: ReplacePrefixMatchreplacePrefixMatch: /apis/v1statusCode: 301
说明:
http://test.example.com/api/v1/pods
会被重定向到 http://test.example.com/apis/v1/pods
。以
/foo
开头的统一重定向到 /bar
:apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:name: redirect-api-v1namespace: testspec:hostnames:- test.example.comparentRefs:- name: test-gwnamespace: testsectionName: httpsrules:- matches:- path:type: PathPrefixvalue: /foofilters:- type: RequestRedirectrequestRedirect:path:type: ReplaceFullPathreplaceFullPath: /barstatusCode: 301
说明:
https://test.example.com/foo/cayenne
和 https://test.example.com/foo/paprika
都会被重定向到 https://test.example.com/bar
。HTTP 重定向到 HTTPS:
apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:name: redirect-httpsnamespace: testspec:hostnames:- test.example.comparentRefs:- name: test-gwnamespace: testsectionName: httprules:- matches:- path:type: PathPrefixvalue: /filters:- type: RequestRedirectrequestRedirect:scheme: httpsstatusCode: 301port: 443
说明:
http://test.example.com/foo
会被重定向到 https://test.example.com/foo
。如何配置 HTTPS 或 TLS?
将证书和密钥存储在 Kubernetes 的 Secret 中:
说明:
apiVersion: v1kind: Secretmetadata:name: test-certnamespace: testtype: kubernetes.io/tlsdata:tls.crt: xxxtls.key: xxx
在
Gateway
的 listeners 中配置 TLS(HTTPS 或 TLS 协议),tls
字段里引用证书 Secret:apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata:name: test-gwnamespace: testspec:gatewayClassName: eglisteners:- name: httpsprotocol: HTTPSport: 443allowedRoutes:namespaces:from: Alltls:mode: TerminatecertificateRefs:- kind: Secretgroup: ""name: test-cert- name: tlsprotocol: TLSport: 9443allowedRoutes:namespaces:from: Alltls:mode: TerminatecertificateRefs:- kind: Secretgroup: ""name: test-cert
如何修改 HTTP Header?
在
HTTPRoute
中使用 RequestHeaderModifier
这个 filter 可以修改 HTTP 请求的 Header。以下是修改请求 Header 的例子:
说明:
对路径以
/foo
开头的请求修改 Header。apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:namespace: testname: foo-add-headerspec:hostnames:- test.example.comparentRefs:- name: test-gwnamespace: testrules:- matches:- path:type: PathPrefixvalue: /foofilters:- type: RequestHeaderModifierrequestHeaderModifier:add:- name: my-header-namevalue: my-header-valuebackendRefs:- name: fooport: 8080
apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:namespace: testname: foo-set-headerspec:hostnames:- test.example.comparentRefs:- name: test-gwnamespace: testrules:- matches:- path:type: PathPrefixvalue: /foofilters:- type: RequestHeaderModifierrequestHeaderModifier:set:- name: my-header-namevalue: my-header-valuebackendRefs:- name: fooport: 8080
apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:namespace: testname: foo-set-headerspec:hostnames:- test.example.comparentRefs:- name: test-gwnamespace: testrules:- matches:- path:type: PathPrefixvalue: /foofilters:- type: RequestHeaderModifierrequestHeaderModifier:remove: ["x-request-id"]backendRefs:- name: fooport: 8080
如果要改响应的 Header 也是类似的,
RequestHeaderModifier
改成 ResponseHeaderModifier
即可:filters:- type: ResponseHeaderModifierresponseHeaderModifier:add:- name: X-Header-Add-1value: header-add-1- name: X-Header-Add-2value: header-add-2- name: X-Header-Add-3value: header-add-3
探索更多用法
Gateway API 非常强大,可实现很多复杂的功能,如基于权重、header、cookie 等特征的路由、灰度发布、流量镜像、URL 重定向与重写、TLS 路由、GRPC 路由等,更详细的用法参考 Gateway API 官方文档。