Envoy Gateway 中使用 HTTP3/QUIC

Envoy Gateway 中使用 HTTP3/QUIC

本文介绍如何通过 Envoy Gateway 处理 HTTP3/QUIC 的流量

什么是 QUIC 协议

HTTP/3 是一种新的网络传输协议,它是 HTTP/2 的后续版本,旨在提高网络性能和安全性。HTTP/3 使用了一个名为 QUIC(Quick UDP Internet Connections)的底层传输协议,它基于 UDP(User Datagram Protocol)而不是传统的 TCP(Transmission Control Protocol)。

QUIC 协议的主要优势在于它可以减少网络延迟、提高连接的可靠性和安全性。以下是 QUIC 协议的一些关键特性:

  1. 连接建立速度更快:QUIC 使用零轮往返(0-RTT)握手,这意味着在建立连接时,客户端和服务器只需交换一次数据包即可。这大大减少了网络延迟,特别是在高延迟或丢包率较高的网络环境中。
  2. 内置加密:QUIC 协议内置了 TLS 1.3 加密,确保了数据传输的安全性。这使得 QUIC 协议在安全性方面与 HTTPS 类似,但传输效率更高。
  3. 更好的拥塞控制:QUIC 协议具有更先进的拥塞控制算法,可以更有效地处理网络拥塞,从而提高数据传输速度。
  4. 多路复用:与 HTTP/2 类似,HTTP/3 支持多路复用,允许在单个连接上同时传输多个请求和响应。这有助于减少网络延迟和提高带宽利用率。
  5. 连接迁移:QUIC 支持连接迁移,这意味着当客户端的 IP 地址或网络接口发生变化时,连接可以在不中断的情况下继续传输数据。这对于移动设备尤为重要,因为它们可能在使用过程中频繁切换网络。

如何在 Envoy Gateway 使用

前置条件

  1. 安装 openssl 用于生成 TLS 证书
  2. kubernetes 集群,并且包含 Service 的 Loadbalancer 实现

步骤

安装 Envoy Gateway 以及 Gateway API

helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace

确保 Envoy Gateway 可用:

kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available

创建 GatewayClass、Gateway、HTTPRoute 以及 Backend

创建 GatewayClass

cat <<EOF | kubectl apply -f -
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
EOF

创建 Gateway

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

创建 Backend

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend
---
apiVersion: v1
kind: Service
metadata:
  name: backend
  labels:
    app: backend
    service: backend
spec:
  ports:
    - name: http
      port: 3000
      targetPort: 3000
  selector:
    app: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
      version: v1
  template:
    metadata:
      labels:
        app: backend
        version: v1
    spec:
      serviceAccountName: backend
      containers:
        - image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e
          imagePullPolicy: IfNotPresent
          name: backend
          ports:
            - containerPort: 3000
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
EOF

创建 HTTPRoute:

cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: backend
spec:
  parentRefs:
    - name: eg
  hostnames:
    - "www.example.com"
  rules:
    - backendRefs:
        - group: ""
          kind: Service
          name: backend
          port: 3000
          weight: 1
      matches:
        - path:
            type: PathPrefix
            value: /
EOF

查看 GatewayClass、Gateway、HTTPRoute 状态

❯ kg gatewayclass
NAME   CONTROLLER                                      ACCEPTED   AGE
eg     gateway.envoyproxy.io/gatewayclass-controller   True       46s
❯ kg gateway
NAME   CLASS   ADDRESS          PROGRAMMED   AGE
eg     eg      172.18.255.200   True         50s
❯ kg httproute
NAME      HOSTNAMES             AGE
backend   ["www.example.com"]   54s

可以看到上述资源状态都为正常,并且能获取正确的 LB IP

TLS 证书

  1. 生成根证书和私钥 用来签署其他证书:

    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
    
  2. www.example.com 创建证书签名请求 (CSR) 和一个新的私钥:

    openssl req -out www.example.com.csr -newkey rsa:2048 -nodes -keyout www.example.com.key -subj "/CN=www.example.com/O=example organization"
    openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in www.example.com.csr -out www.example.com.crt
    
  3. 使用根证书签署 CSR 生成 www.example.com 的证书:

    openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in www.example.com.csr -out www.example.com.crt
    
  4. 将证书和密钥存储在 Kubernetes Secret 中

    kubectl create secret tls example-cert --key=www.example.com.key --cert=www.example.com.crt
    
  5. 更新 Kubernetes Gateway

    添加 HTTPS 监听器,监听 443 端口,并关联 example-cert Secret

      kubectl patch gateway eg --type=json --patch '[{
      "op": "add",
      "path": "/spec/listeners/-",
      "value": {
         "name": "https",
         "protocol": "HTTPS",
         "port": 443,
         "tls": {
           "mode": "Terminate",
           "certificateRefs": [{
             "kind": "Secret",
             "group": "",
             "name": "example-cert",
           }],
         },
       },
      }]'
    
  6. 使用 ClientTrafficPolicy 启用 HTTP3

    kubectl apply -f - <<EOF
    apiVersion: gateway.envoyproxy.io/v1alpha1
    kind: ClientTrafficPolicy
    metadata:
      name: enable-http3
    spec:
      http3: {}
      targetRef:
        group: gateway.networking.k8s.io
        kind: Gateway
        name: eg
        namespace: default
    EOF
    
  7. 检查 Gateway 状态:

    可以看到 Gateway 状态是正常

    ❯ kubectl get gateway/eg
    NAME   CLASS   ADDRESS          PROGRAMMED   AGE
    eg     eg      172.18.255.200   True         6m36s
    

测试

  1. 获取 Gateway 的 External IP

    export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}')
    
  2. 通过 Gateway 查询示例应用程序:

    下面的示例使用了自定义的 docker 镜像和内置 http3 的自定义 curl 二进制文件

    docker run --net=host --rm ghcr.io/macbre/curl-http3 curl -kv --http3 -HHost:www.example.com --resolve "www.example.com:443:${GATEWAY_HOST}" https://www.example.com/get
    
  3. 结果:

    可以看到 HTTP3 的请求成功被 Envoy Gateway 处理

    * Added www.example.com:443:172.18.255.200 to DNS cache
    * Hostname www.example.com was found in DNS cache
    *   Trying 172.18.255.200:443...
    * Connect socket 5 over QUIC to 172.18.255.200:443
    * Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
      0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Skipped certificate verification
    * Connected to www.example.com () port 443 (#0)
    * h2h3 [:method: GET]
    * h2h3 [:path: /get]
    * h2h3 [:scheme: https]
    * h2h3 [:authority: www.example.com]
    * h2h3 [user-agent: curl/7.84.0-DEV]
    * h2h3 [accept: */*]
    * Using HTTP/3 Stream ID: 0 (easy handle 0x4001e77ac0)
    > GET /get HTTP/3
    > Host:www.example.com
    > user-agent: curl/7.84.0-DEV
    > accept: */*
    >
    < HTTP/3 200
    < content-type: application/json
    < x-content-type-options: nosniff
    < date: Thu, 21 Dec 2023 09:05:52 GMT
    < content-length: 513
    < x-envoy-upstream-service-time: 0
    < alt-svc: h3=":10443"; ma=86400
    < server: envoy
    <
    { [513 bytes data]
    {
     "path": "/get",
     "host": "www.example.com",
     "method": "GET",
     "proto": "HTTP/1.1",
     "headers": {
      "Accept": [
       "*/*"
      ],
      "User-Agent": [
       "curl/7.84.0-DEV"
      ],
      "X-Envoy-Expected-Rq-Timeout-Ms": [
       "15000"
      ],
      "X-Envoy-Internal": [
       "true"
      ],
      "X-Forwarded-For": [
       "172.18.0.1"
      ],
      "X-Forwarded-Proto": [
       "https"
      ],
      "X-Request-Id": [
       "0c165237-5993-4539-8942-0899d6120409"
      ]
     },
     "namespace": "default",
     "ingress": "",
     "service": "",
     "pod": "backend-58d58f745-l878w"
    100   513  100   513    0     0   3977      0 --:--:-- --:--:-- --:--:-- 13864
    * Connection #0 to host www.example.com left intact
    }%
    

至此,你已经通过 Envoy Gateway 使用 HTTP3 监听下游服务,并访问到目标后端服务。

comments powered by Disqus

相关文章

EnvoyProxy 中 UDP 协议概览

EnvoyProxy 中 UDP 协议概览

本文结合一些基础的网络知识,介绍了 EnvoyProxy 中 UDP 协议的介绍与使用。

阅读更多