Skip to content

SSL 502 #194

@aibangjuxin

Description

@aibangjuxin

问题分析

根据你描述的现象,核心矛盾点非常清晰:

访问路径 结果
Pod 内 curl :8080 ✅ 单条 Transfer-Encoding: chunked
Nginx 机器 curl SVC LoadBalancer IP ⚠️ 双重 Transfer-Encoding: chunked
GLB → Nginx → GKE SVC ❌ 502

重复 header 的根本原因:链路中某一层在已有 Transfer-Encoding: chunked 的情况下再次追加了该 header,而不是透传。


可能原因逐层排查

原因 1:GKE 集群内存在 Envoy/Istio Sidecar(最高嫌疑)

你的集群里同时存在 GKE Gateway,GKE Gateway 使用的是 Envoy 作为数据面。如果你的 Namespace 或 Pod 被注入了 Istio/Cloud Service Mesh sidecar,即使你绕开了 GKE Gateway 资源,流量依然会经过 sidecar proxy。

# 检查目标 Pod 是否有 sidecar 注入
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.containers[*].name}'

# 如果输出里包含 istio-proxy 或 envoy,则存在 sidecar
kubectl describe pod <pod-name> -n <namespace> | grep -i "istio\|envoy\|sidecar"

# 检查 namespace 是否开启了自动注入
kubectl get namespace <namespace> --show-labels | grep istio-injection

Sidecar 导致重复 header 的机制:

graph LR
    A[Nginx] --> B[SVC]
    B --> C[Envoy Sidecar]
    C --> D[App Container]
    D -->|Transfer-Encoding: chunked| C
    C -->|再次追加 chunked| B
    B --> A
    A -->|看到两条 chunked| E[502]
Loading

原因 2:GKE Gateway 的 BackendPolicy / HTTPRoute 仍在影响流量

即使你创建了新的普通 SVC(LoadBalancer),如果集群里的 GKE Gateway 有 HTTPRoute 通过 label selector 匹配到了你的 Pod,流量依然可能被 Gateway 数据面介入。

# 检查所有 HTTPRoute,看是否有匹配到你的 Service 或 Pod label
kubectl get httproute -A -o yaml | grep -A5 "backendRefs"

# 检查 GKEGateway 的 BackendPolicy
kubectl get gcpbackendpolicy -A
kubectl get healthcheckpolicy -A

原因 3:Nginx 自身的 chunked 处理配置问题

当上游返回 Transfer-Encoding: chunked 时,Nginx 作为反代默认会解码 chunked 然后重新封装。但如果上游同时返回了某些触发 Nginx 直接透传的条件(如 proxy_pass 使用了 HTTP/1.0,或配置了 proxy_buffering off),可能导致行为异常。

# 检查你的 nginx 反代配置
# 关键指令
proxy_http_version 1.1;          # 必须是 1.1,否则 chunked 行为异常
proxy_buffering off;              # 这个配置会改变 chunked 处理方式

# 建议配置
location / {
    proxy_pass http://<SVC-LB-IP>;
    proxy_http_version 1.1;
    proxy_set_header Connection "";        # 清除 hop-by-hop header
    proxy_set_header Host $host;
    
    # 明确处理 chunked
    chunked_transfer_encoding on;
}

原因 4:GLB(Internal HTTPS LB)的 HTTP/2 → HTTP/1.1 转换

Internal GLB 与后端(Nginx)之间默认使用 HTTP/2,而 HTTP/2 本身没有 Transfer-Encoding: chunked(使用 DATA frame 替代)。当 GLB 将响应从 HTTP/2 转换回 HTTP/1.1 时,如果后端响应已经包含了 chunked header,可能出现重复追加。

# 在 Nginx 机器上,测试直接 HTTP/1.1 vs HTTP/2 的响应差异
curl -v --http1.1 http://<SVC-LB-IP>/path 2>&1 | grep -i "transfer\|encoding"
curl -v --http2 https://<SVC-LB-IP>/path 2>&1 | grep -i "transfer\|encoding"

推荐排查步骤

graph TD
    A[开始排查] --> B{Pod 是否有 Sidecar}
    B -->|有 istio-proxy| C[检查 namespace 注入标签]
    C --> D[给该 namespace 打 sidecar.istio.io/inject=false\n或给 Pod 打 annotation 禁用]
    B -->|无 Sidecar| E{HTTPRoute 是否匹配该 Pod}
    E -->|有匹配| F[修改 HTTPRoute 或调整 Pod label 避免匹配]
    E -->|无匹配| G[抓包确认重复 header 产生位置]
    G --> H[tcpdump on Nginx 抓上游响应]
    H --> I{重复 header 来自上游}
    I -->|是| J[问题在 GKE 侧,检查 SVC/Endpoint]
    I -->|否| K[问题在 Nginx 配置,调整 proxy_http_version]
    D --> L[重新测试]
    F --> L
    K --> L
    J --> L
Loading

关键抓包命令

# 在 Nginx 机器上抓包,过滤到 SVC LB IP 的流量
sudo tcpdump -i eth0 -A host <SVC-LB-IP> and port 80 -w /tmp/capture.pcap

# 用 tcpdump 直接看 header(不写文件)
sudo tcpdump -i eth0 -A host <SVC-LB-IP> and port 80 2>/dev/null | grep -i "transfer-encoding"

快速验证方法

# Step 1: 确认 Pod 内响应干净
kubectl exec -it <pod> -- curl -sv http://localhost:8080/ 2>&1 | grep -i transfer

# Step 2: 确认 SVC ClusterIP 响应(在集群内节点上)
curl -sv http://<ClusterIP>:<port>/ 2>&1 | grep -i transfer

# Step 3: 确认 SVC LoadBalancer IP 响应(在 Nginx 机器上)
curl -sv http://<LB-IP>:<port>/ 2>&1 | grep -i transfer

# 如果 Step2 干净但 Step3 有重复 → 问题在 LB/Cloud 网络层
# 如果 Step2 已经重复 → 问题在集群内(sidecar 可能性极大)

注意事项

  • 优先排查 Sidecar 注入,这是混合使用 GKE Gateway 的集群里最常见的"幽灵干扰"来源
  • 确认 Nginx proxy_http_version 1.1proxy_set_header Connection "" 已配置
  • Internal GLB 后端协议建议统一为 HTTP/1.1,避免协议转换引入额外 header 操作
  • 如果确认是 sidecar 问题,不要直接删除 sidecar,而是通过 annotation 控制:
# 在 Pod spec 中禁用 sidecar 注入
annotations:
  sidecar.istio.io/inject: "false"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions