本文是从零学习envoy笔记第四章 - Client Traffic Policy
  • 从零学习envoy笔记 - Gateway总览
  • 从零学习envoy笔记 - 学习EG API和Gateway API
  • 从零学习envoy笔记 - 快速部署实验环境
  • 从零学习envoy笔记 - 官方Task Backend Routing
  • 从零学习envoy笔记 - 官方Task Backend Utilization Load Balancing
  • 从零学习envoy笔记 - 官方Task Bandwidth Limit
  • 从零学习envoy笔记 - 官方Task Client Traffic Policy

ClientTrafficPolicy

ClientTrafficPolicy 用于管理客户端到 Envoy Proxy 之间的行为,例如控制访问 Gateway 的客户端连接,例如 TCP keepalive、Proxy Protocol、XFF 客户端 IP 识别、客户端请求超时、连接 idle timeout、连接 buffer limit。

注意:这些都挂在 Gateway 上,不挂在 HTTPRoute (BackendTrafficPolicy ) 或 backend service 上 [1]

yaml
1
2
3
4
targetRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: eg

前置准备

创建一个新的 HTTPRoute

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: colorful-client-policy-route
  namespace: default
spec:
  parentRefs:
    - name: eg
  hostnames:
    - client.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: envoy-end-blue
          port: 90
          weight: 1
        - name: envoy-end-gray
          port: 90
          weight: 1
        - name: envoy-end-red
          port: 90
          weight: 1
EOF

合并官方所有示例的配置

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
  name: client-traffic-policy-demo
  namespace: default
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: eg

  tcpKeepalive:
    idleTime: 20m
    interval: 60s
    probes: 3

  clientIPDetection:
    xForwardedFor:
      numTrustedHops: 2

  timeout:
    http:
      requestReceivedTimeout: 2s
      idleTimeout: 5s

  connection:
    bufferLimit: 10
  enableProxyProtocol: true
EOF

验证策略是否已经绑定到 gateway

text
1
kubectl get clienttrafficpolicies.gateway.envoyproxy.io enable-tcp-keepalive-policy -n default -o jsonpath='{.status.ancestors[0].conditions[?(@.type=="Accepted")].message}'

准备后端服务,这里使用 ealen/echo-server,可以有效 header 回显

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-server
  namespace: default
  labels:
    app: echo-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo-server
  template:
    metadata:
      labels:
        app: echo-server
    spec:
      containers:
        - name: echo-server
          image: ealen/echo-server:latest
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: echo-server
  namespace: default
  labels:
    app: echo-server
spec:
  type: ClusterIP
  selector:
    app: echo-server
  ports:
    - name: http
      port: 80
      targetPort: 80
EOF

配置对应的 HTTPRoute

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo-client-ip-route
  namespace: default
spec:
  parentRefs:
    - name: eg
  hostnames:
    - echo.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: echo-server
          port: 80
EOF

实验1:验证TCP Keepalive

对应配置

yaml
1
2
3
clientIPDetection:
  xForwardedFor:
    numTrustedHops: 2

实验2验证

text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ curl -v \
  -H "Host: client.example.com" \
  http://127.0.0.1:8888/colorful \
  --next \
  -H "Host: client.example.com" \
  http://127.0.0.1:8888/colorful
*   Trying 127.0.0.1:8888...
* Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
> GET /colorful HTTP/1.1
> Host: client.example.com
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: application/json; charset=utf-8
< date: Fri, 19 Jun 2026 14:21:50 GMT
< content-length: 30
<
* Connection #0 to host 127.0.0.1 left intact
{"message":"hello with blue."}* Found bundle for host: 0x7ffff0a57620 [serially]
* Can not multiplex, even if we wanted to
* Re-using existing connection #0 with host 127.0.0.1
> GET /colorful HTTP/1.1
> Host: client.example.com
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: application/json; charset=utf-8
< date: Fri, 19 Jun 2026 14:21:50 GMT
< content-length: 30
<
* Connection #0 to host 127.0.0.1 left intact
{"message":"hello with gray."}

实验2:Client IP Detection

对应配置

yaml
1
2
3
clientIPDetection:
  xForwardedFor:
    numTrustedHops: 2

实验2验证

text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$~# curl -s http://127.0.0.1:8888/ \
  -H "Host: echo.example.com" \
  -H "X-Forwarded-Proto: https" \
  -H "X-Forwarded-For: 1.1.1.1,2.2.2.2" \
  | jq '.request.headers'
{
  "host": "echo.example.com",
  "user-agent": "curl/7.88.1",
  "accept": "*/*",
  "x-forwarded-proto": "https",
  "x-forwarded-for": "1.1.1.1,2.2.2.2,10.244.2.7",
  "x-request-id": "7eee5a60-f134-4fe7-b6b8-fd403dc110ed"
}

实验3:Request Received Timeout

对应配置

yaml
1
2
3
timeout:
  http:
    requestReceivedTimeout: 2s

客户端必须在 2 秒内把完整请求发完,否则 Envoy 直接超时。

验证

text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ curl -v http://127.0.0.1:8888/colorful \
  -H "Host: client.example.com" \
  -H "Content-Length: 10000"
*   Trying 127.0.0.1:8888...
* Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
> GET /colorful HTTP/1.1
> Host: client.example.com
> User-Agent: curl/7.88.1
> Accept: */*
> Content-Length: 10000
>
< HTTP/1.1 408 Request Timeout
< content-length: 15
< content-type: text/plain
< date: Fri, 19 Jun 2026 14:31:29 GMT
< connection: close
<
* Closing connection 0

实验4 HTTP Idle Timeout

对应配置

HTTP 连接空闲 5 秒没有活跃请求,Envoy 就关闭连接。

text
1
2
3
timeout:
  http:
    idleTimeout: 5s

验证

text
1
2
3
4
5
6
7
8
9
$~# nc 127.0.0.1 8888
GET /colorful HTTP/1.1
HTTP/1.1 408 Request Timeout
content-length: 15
content-type: text/plain
date: Fri, 19 Jun 2026 14:34:07 GMT
connection: close

request timeout

原理,nc 127.0.0.1 8888 输入后,输入后不要结束,会自动超时

text
1
GET /colorful HTTP/1.1

实验5 Buffer Limit

对应配置

yaml
1
2
connection:
  bufferLimit: 1024

对每个 “客户端到 Envoy listener 的下游连接”,读写 buffer 的软限制是 1024 bytes

实验6 Proxy Protocol

对应配置

yaml
1
enableProxyProtocol: true

这个功能开启表示,让 EG 的下游 listener 开启 proxy protocol 能力。例如,客户端请求先进到一个四层 LB(如云厂商 CLB/NLB/ELB)时,再转发给 EG 时,LB 会在 TCP 连接最前面加一段 PROXY protocol 头,告知 EG 原始客户端 IP 是谁、端口是多少、目标 IP/端口是多少。

验证结果,如果直接使用下面命令会出现 “Connection reset by peer”

text
1
curl -H "Host: xxx.example.com" http://127.0.0.1:8888/

相关日志

text
1
2
E0619 16:40:47.063558    3888 portforward.go:424] "Unhandled Error" err="an error occurred forwarding 8888 -> 10080: error forwarding port 10080 to pod 2bcc4574ab3987378f1388972bc73b7bb94d89d187e433bafb83c645da95e61b, uid : failed to execute portforward in network namespace \"/var/run/netns/cni-91ecd9ce-eca3-75d7-a9d9-f9e87f2d2a6b\": writeto tcp4 127.0.0.1:46104->127.0.0.1:10080: read tcp4 127.0.0.1:46104->127.0.0.1:10080: read: connection reset by peer"
error: lost connection to pod

所以需要使用参数 “–haproxy-protocol”

text
1
2
3
curl -v --haproxy-protocol \
  -H "Host: client.example.com" \
  http://127.0.0.1:8888/colorful

通常使用在 公网 LB / 四层 LB (携带 PROXY protocol) -> Envoy Gateway -> 后端。开启该功能后,前端的 LB 也需要携带 PROXY protocol。

他的原理就是

text
1
printf 'PROXY TCP4 1.2.3.4 10.0.0.10 12345 80\r\nGET / HTTP/1.1\r\nHost: client.example.com\r\n\r\n' | nc 127.0.0.1 8888

Reference

[1] Client Traffic Policy