2024-08-02 TEG与istio集成

介绍 Tetrate Enterprise Gateway 及与 Istio 集成:云原生应用的全面网关解决方案

深入了解 Tetrate Enterprise Gateway (TEG) 及其如何与 Istio 服务网格集成 —— 一种基于 Envoy 的企业级网关解决方案,包括它的架构、基本功能以及如何在 Kubernetes 中使用 TEG 来暴露和管理应用。

TEG 简介 #

Tetrate Enterprise Gateway(TEG)是基于 Envoy Gateway (EG) 的企业级解决方案,专门针对 Envoy Proxy 设计,通过 Kubernetes Gateway API 提供更易于消费的 Envoy 代理配置和管理包。TEG 结合了 Kubernetes Gateway API 的特性,支持在 Kubernetes 中轻松暴露服务和应用程序。

TEG 相对于 Envoy Gateway 的主要新增特性包括:

  1. 全局速率限制(Rate Limiting):TEG 支持基于 IP 5-tuple、请求头等进行流量控制,需要通过 Redis 实例管理。
  2. WAF 功能(Web Application Firewall):TEG 提供了与 mod_security 兼容的 WAF 功能,增强了安全防护能力。
  3. OIDC/OAuth2认证:支持在网关级别进行 OIDC/OAuth2 认证,应用程序可以按路由配置认证方式。
  4. 使用 Kubernetes Gateway API:相较于其他 API,Kubernetes Gateway API 的设计更加现代,结合了众多 Ingress 实现的经验,将网关的配置与流量的路由分离,使平台所有者可以管理网关,而应用团队可以掌控流量路由。

TEG 将 Envoy 的高级网络流量处理能力带入 Kubernetes 环境,提供了一种简化的方法来部署和管理负载平衡、API 网关功能、安全控制等,同时支持现代的、开放的应用程序暴露 API,如 Kubernetes Gateway API。这些特性使 TEG 成为一个功能丰富、易于管理的企业级网关解决方案。

TEG 的能力 #

Tetrate Enterprise Gateway for Envoy (TEG) 构建于 Envoy Gateway 项目之上,提供了一种易于使用和操作的入口,具有先进的按请求流量控制功能、与现有环境的轻松集成,以及一流的可观测性,以理解应用流量和入口健康状况。

易于安装、操作和升级 #

TEG 从头到尾注重易用性:从首次安装到启用应用团队,从故障排查到执行升级。TEG 的初始安装只需几分钟,你就可以开始使用高级功能,如速率限制、单点登录和金丝雀流量路由。TEG 还简化了运维流程,与你现有的指标、跟踪和日志记录管道相适应,我们还提供了一个完整的、预配置的可观测性堆栈,以评估 EG 产生的数据,并帮助你计划如何将 TEG 集成到你的现有指标堆栈中。

操作性:一流的功能 #

TEG 由在生产环境中运行大型、关键系统的经验丰富团队构建。TEG 简化了漏洞检查和持续升级过程,与你现有的指标和跟踪提供商轻松集成,并为你现有的 Grafana 部署提供了一套强大的入口可观测性仪表板。

与现有的环境集成 #

TEG 不仅适用于绿地部署的启动,还可以直接与传统环境以及现代云原生环境集成。它可以帮助你在现有的应用生态系统和你正在构建的云原生目标之间架起桥梁。

引入现有的可观测性堆栈 #

你的组织可能已经有一个可观测性系统,你的应用和运营团队已经训练有素地使用它。TEG 可以轻松地嵌入到现有的基础设施中,并在你的组织中运行。TEG 将使 Envoy 的丰富指标集导出,让你的应用团队对其应用流量的行为有最佳的洞察,并看到他们所做配置更改的效果。TEG 还为运行它的平台团队提供了仪表板和警报功能,使你能够自信地操作并快速解决发现的问题。

简单的负载平衡 #

Envoy 非常强大,但要使其启动并运行简单用例可能很难——像 Istio 这样的系统提供 Envoy 入口管理作为更广泛功能套件的一部分,也附带了许多与简单、流畅的操作体验相冲突的额外功能。这就是 Envoy Gateway 存在的原因:使 Envoy 的强大功能易于用于入口用例。

简单的 API 网关 #

组织中绝大多数 API 网关的使用归结为三件事:认证发起请求的用户;限制用户对服务的访问;在此 API 端点的服务实例之间进行负载平衡。TEG 简化了在传统和云原生环境中完成这三项任务的过程。

TEG 的架构 #

下图展示的是 TEG 的架构图。

image-20240801101123036

从架构图中可以看出,Tetrate Enterprise Gateway for Envoy (TEG) 的架构设计包括以下主要组件和流程:

主要组件 #

  1. Kubernetes Cluster
    • Envoy Gateway:作为控制平面,配置和管理 Envoy 代理,消费 Kubernetes Gateway API 的配置。
    • Metrics Collection:使用 Prometheus 或 OpenTelemetry (OTEL) 作为指标收集点,用于监控 Envoy Proxy 的性能和健康状态。
  2. Envoy Proxy
    • 作为数据平面,直接处理所有进入的流量,支持基于 Kubernetes Gateway API 的配置。
  3. Coraza WAF
    • 作为 TEG 的一部分部署,执行 WAF 规则以保护应用免受恶意请求攻击。
  4. Redis Rate Limit Store
    • 作为全局速率限制的存储解决方案,用于跨所有 Envoy 实例维护统一的速率限制计数。
  5. Your OIDC Server
    • 处理 OAuth2.0 和 OIDC 认证流程,确保只有经过认证的用户可以访问特定的路由和服务。

工作流程 #

  1. 流量入口
    • 所有外部流量首先通过上游的负载均衡器,然后被路由到 Envoy Proxy。
  2. Envoy Proxy 处理
    • Envoy Proxy 根据 Kubernetes Cluster 中的 Envoy Gateway 的配置处理流量。
    • 配置信息包括路由规则、安全策略(如 WAF 和速率限制)等。
  3. 安全和认证
    • Coraza WAF:在流量到达应用前,根据配置的 WAF 规则进行检查和过滤,提高安全性。
    • OIDC 认证:OIDC Server 处理认证,Envoy Proxy 根据 OIDC Server 的验证结果决定是否允许访问。
  4. 速率限制
    • 使用 Redis 存储进行速率限制,Envoy Proxy 将根据从 Redis 获取的数据执行速率限制策略。
  5. 性能监控
    • Envoy Proxy 的性能和健康状态通过集成的指标收集系统(Prometheus 或 OTEL)进行监控。

配置和管理 #

  • 用户可以通过 Kubernetes Gateway API 定义和应用 Envoy Proxy 的配置。
  • 这包括定义专用网关的具体配置,如安全规则、路由策略等。

这种架构设计利用了 Kubernetes 的灵活性和扩展性,并通过 Envoy 提供了强大的流量管理和安全功能。

部署 TEG #

执行下面的命令部署 TEG V0.0.0:

export REGISTRY="oci://docker.io/tetrate"
export CHART_VERSION="v0.0.0-latest"
helm install teg ${REGISTRY}/teg-envoy-gateway-helm \
 --version ${CHART_VERSION} \
 -n envoy-gateway-system --create-namespace

检查部署:

kubectl get pod -n envoy-gateway-system

你将看到下面的结果:

NAMESPACE              NAME                                                       READY   STATUS    RESTARTS        AGE
envoy-gateway-system   envoy-gateway-596dfbcb88-tx7xb                             1/1     Running   0               3m55s
envoy-gateway-system   envoy-ratelimit-674b8c955c-jhlfn                           2/2     Running   2 (3m48s ago)   3m54s
envoy-gateway-system   teg-envoy-gateway-64fd8c8fbb-59b4l                         1/1     Running   0               3m55s
envoy-gateway-system   teg-redis-86bb7d9b9d-27n44                                 1/1     Running   0               3m55s

部署示例应用:

kubectl create namespace httpbin
kubectl apply -n httpbin -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml

部署 Envoy Proxy:

cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: dedicated-gateway
  namespace: httpbin
spec:
  gatewayClassName: teg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
EOF

然后你会在 envoy-gateway-system 命名空间下看到一个新的 Envoy 代理。

部署 HTTPRoute,给网关配置路由:

cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httpbin
  namespace: httpbin
spec:
  parentRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: dedicated-gateway
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /httpbin/
      filters:
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /
      backendRefs:
        - group: ""
          kind: Service
          name: httpbin
          port: 8000
EOF

这个路由配置中有一个 URLRewrite filter,重写 URL 前缀,去掉了 /httpbin/ 部分。

发送流量测试:

export DEDICATED_GATEWAY_IP=$(kubectl get gateway/dedicated-gateway -n httpbin -o jsonpath='{.status.addresses[0].value}')
curl -i http://${DEDICATED_GATEWAY_IP}/httpbin/get

为什么使用 /httpbin/get?

在通过 Tetrate Enterprise Gateway for Envoy (TEG) 暴露 httpbin 应用时,选择 /httpbin/get 作为访问路径的原因主要是为了在同一个 Envoy 网关下能够同时支持多个应用或服务,并能根据不同的路径将流量正确地路由到指定的服务。

这种路径前缀的设置方法允许系统管理员或开发人员为每个服务配置独立的路径前缀,从而通过单一的入口点(即 Envoy 网关)来管理对多个后端服务的访问。这样的配置增加了路由的灵活性,使得在不更改现有服务配置的情况下,轻松地扩展或修改服务的暴露方式。

作为 Istio 的入口网关 #

Istio 提供了成熟且灵活的入口网关支持,基于与 Tetrate Enterprise Gateway(TEG)相同的 Envoy 代理。Istio 主要专注于处理集群内服务之间的通信。相较之下,TEG 设计用于向外界暴露应用,处理人类用户的请求,并支持如 OIDC 单点登录等高级功能。通过结合 Istio 网格和 TEG 的高级网关功能,两者可以共同使用,以提升整体应用的可访问性和安全性。

以下图示展示了 Istio 网格中入口网关的流量路径。

image-20240801101449547

下图展示了在引入 TEG 之后,流量如何从 Istio 网格边缘进入到内部。

image-20240801101506006

将 TEG 集成到 Istio 网格中,通过在 TEG 上配置 sidecar 来颁发证书,同时避免 sidecar 拦截 TEG 中的流量。然后通过 Envoy Gateway 控制入口网关的流量路径。

为 TEG 与 Istio 的互操作做准备 #

为了使 TEG 作为 Istio 的入口网关,应注意以下关键点:

  • 在安装 Istio 时,避免启用 Ingress Gateway。我们将手动安装并配置 TEG 作为 Istio 的入口网关。
  • 由于 Istio 和 TEG 都使用 Envoy 作为代理,我们需要让 Istio 为 TEG 的网关 Pod 注入 Envoy sidecar,以便 TEG 可以安全地与 Istio 数据平面通信。
  • 配置 Envoy Gateway 创建的 Envoy 代理的路由类型Service 而非 Endpoint,确保 Envoy 代理能正确找到路由。

为 TEG 的命名空间添加标签,以确保数据平面获得 Istio sidecar 的注入。

kubectl label namespace envoy-gateway-system --overwrite=true istio-injection=enabled

我们还需要配置 TEG 的 sidecar,使其不处理进入网关的 Envoy 流量。注入 sidecar 的目的是使 Envoy Gateway 的组件及其创建的代理能够被纳入 Istio 网格,并挂载正确的证书进行安全通信。

control-plane-tls.yaml
spec:
  ports:
  - port: 18000
    appProtocol: tls
kubectl patch service -n envoy-gateway-system envoy-gateway \
     --type strategic --patch-file control-plane-tls.yaml

配置 Envoy Gateway 中的 sidecar 不拦截流量:

teg-sidecars-no-inbound.yaml
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: data-plane-sidecars
  namespace: envoy-gateway-system
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        pod:
          annotations:
            traffic.sidecar.istio.io/includeInboundPorts: ""
  routingType: Service
kubectl apply -f teg-sidecars-no-inbound.yaml

修改 GatewayClass 的配置,将上述 sidecar 配置应用到 Envoy Gateway 数据平面的所有 EnvoyProxy 上:

gtwcls-use-envoyproxy.yaml
---
spec:
  parametersRef:
    group: gateway.envoyproxy.io
    kind: EnvoyProxy
    namespace: envoy-gateway-system
    name: data-plane-sidecars
kubectl patch gatewayclass teg --patch-file gtwcls-use-envoyproxy.yaml --type merge

安装 Istio #

使用 minimal profile 部署 Istio,从而不部署 Ingress Gateway:

istioctl install --set profile=minimal -y

重启 TEG 控制平面 #

现在 Istio 的 sidecar 注入已准备就绪,我们将重启所有 TEG 控制平面 Pod,它们将带有 sidecar 重新启动。

for d in envoy-gateway envoy-ratelimit teg-envoy-gateway teg-redis; \
    do kubectl rollout restart deployment -n envoy-gateway-system $d; \
    done

部署测试应用 #

此步应在安装 Istio 之后进行,以确保它们也获得 sidecar 的注入。

kubectl create namespace httpbin
kubectl label namespace httpbin --overwrite=true istio-injection=enabled
kubectl apply -n httpbin -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml

配置 TEG #

现在我们配置 TEG 处理边缘流量。

apps-gateway.yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: apps
  namespace: httpbin
spec:
  gatewayClassName: teg
  listeners:
  - name: http
    protocol: HTTP
    port: 80
kubectl apply -f apps-gateway.yaml

部署应用网关,包含以下容器:

  • istio-init:由 Istio 注入,负责修改 pod 中的 iptables
  • envoy:由 TEG 控制,作为入口网关
  • istio-proxy:由 Istio 注入,负责与集群内部 pod 联系
  • shutdown-manager:由 TEG 控制,负责 Pod 启停

创建 HTTP 路由:

httpbin-route.yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httpbin
  namespace: httpbin
spec:
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: apps
  hostnames:
  - "www.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /httpbin/
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
    - kind: Service
      name: httpbin
      port: 8000
kubectl apply -f httpbin-route.yaml

发送测试请求 #

获取网关的负载均衡器 IP 地址,并发送测试请求:

export GATEWAY_URL=$(kubectl get svc -n envoy-gateway-system -l gateway.envoyproxy.io/owning-gateway-name=apps -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')
curl -v -H Host:www.example.com http://$GATEWAY_URL/httpbin/get

你将看到来自 httpbin 服务的正确响应,如下所示:

*   Trying 34.41.0.90:80...
* Connected to 34.41.0.90 (34.41.0.90) port 80
> GET /httpbin/get HTTP/1.1
> Host:www.example.com
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< server: envoy
< date: Wed, 31 Jul 2024 08:21:58 GMT
< content-type: application/json
< content-length: 282
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 11
<
{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Host": "www.example.com",
    "User-Agent": "curl/8.7.1",
    "X-Envoy-Attempt-Count": "1",
    "X-Envoy-External-Address": "123.120.227.173"
  },
  "origin": "123.120.227.173",
  "url": "http://www.example.com/get"
}
* Connection #0 to host 34.41.0.90 left intact

启用严格的 mTLS #

运行下面的命令启用严格的 mTLS:

strict-mtls.yaml
---
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: "default"
  namespace: "istio-system"
spec:
  mtls:
    mode: STRICT
kubectl apply -f strict-mtls.yaml

修改我们之前创建的 HTTPRoute:

httpbin-route-mtls.yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httpbin
  namespace: httpbin
spec:
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: apps
  hostnames:
  - "httpbin.httpbin.svc.cluster.local"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /httpbin/
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
    - kind: Service
      name: httpbin
      port: 8000
kubectl apply -f httpbin-route-mtls.yaml

注意:这次我们使用 Host 是 httpbin 在集群内部的主机名。如果使用 www.example.com 作为请求的主机名访问将返回 404 错误。

curl -v -H Host:httpbin.httpbin.svc.cluster.local http://$GATEWAY_URL/httpbin/get

现在 httpbin 服务能够正常访问了。

为网关启用 TLS #

创建用于服务签名的根证书和私钥:

mkdir example_certs
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs/example.com.key -out example_certs/example.com.crt

www.example.com 创建证书和私钥:

openssl req -out example_certs/www.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs/www.example.com.key -subj "/CN=www.example.com/O=www organization"
openssl x509 -req -sha256 -days 365 -CA example_certs/example.com.crt -CAkey example_certs/example.com.key -set_serial 0 -in example_certs/www.example.com.csr -out example_certs/www.example.com.crt

为入口网关创建 secret:

kubectl create -n httpbin secret tls httpbin-credential \
  --key=example_certs/www.example.com.key \
  --cert=example_certs/www.example.com.crt

配置入口网关:

tls-apps-gateway.yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: apps
  namespace: httpbin
spec:
  gatewayClassName: teg
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      certificateRefs:
      - name: httpbin-credential
kubectl apply -f tls-apps-gateway.yaml

发送测试请求:

curl -v -H Host:httpbin.httpbin.svc.cluster.local --resolve "www.example.com:443:$GATEWAY_URL" \
  --cacert example_certs/example.com.crt "https://www.example.com:443/httpbin/get"

你将可以通过 HTTPS 访问网格内的 httpbin 服务。

总结 #

Tetrate Enterprise Gateway 为企业提供了一种强大的网关解决方案,能够在云原生环境中高效地暴露和管理应用服务。通过其基于 Envoy 的架构和对 Kubernetes Gateway API 的支持,TEG 不仅确保了高性能的流量管理,还大幅简化了网关的部署和维护。无论是面对复杂的安全需求还是高流量的业务场景,TEG 都能提供可靠的支持,帮助企业实现其业务连续性和技术创新。

参考 #