← 返回博客

Kubernetes Ingress Controller 的代理:K8s 集群完整配置与示例配置

我们将讨论如何在Kubernetes Ingress Controller中正确设置代理服务器——从NGINX的基本配置到轮换驻留IP以绕过封锁。

📅2026年8月10日
```html

如果您的 K8s 集群进行外部请求——向合作伙伴的 API、市场、广告平台——迟早会遇到 IP 封锁。Ingress Controller 本身管理入站流量,但对于通过 Pod 的出站请求,需要单独的代理方案。本文将讨论如何正确设置:从选择代理类型到 NGINX Ingress 和 Traefik 的具体 YAML 配置。

Kubernetes 中的代理:真实场景

Kubernetes 是一个编排工具,大多数关于它的文章讨论的是入站流量:如何设置 Ingress,如何颁发 TLS 证书,如何将请求路由到服务。但还有另一面:来自 Pod 的出站流量。正是在这里,封锁、地理限制和 IP 限制问题出现。

让我们看看在 K8s 集群中,代理成为必需的具体任务:

  • 价格解析和监控。 您的集群每 15 分钟运行一次 CronJob,从 Wildberries、Ozon、Amazon 收集数据。如果不进行 IP 轮换,2-3 小时后您将会被整个云服务提供商的 IP 范围封锁。
  • 与广告 API 的交互。 Facebook Marketing API、TikTok Ads API、Google Ads API——它们都跟踪请求的来源。如果数十个 Pod 从同一个数据中心的 IP 发送请求,这将触发自动封锁。
  • 地理位置测试。 您的服务必须为来自不同国家的用户显示不同的内容。通过代理,Pod 可以模拟来自所需地区的请求以进行自动测试。
  • 绕过企业限制。 在某些基础设施中,所有出站流量必须通过企业代理——这是合规要求。
  • 与白名单 IP 的合作伙伴 API 的交互。 当合作伙伴只允许来自特定 IP 的请求,而您的集群在扩展并更改地址时——使用具有静态 IP 的代理解决了这个问题。

重要的是要理解架构上的分离:Ingress Controller 管理入站流量(从用户到您的服务),而出站请求的代理是一个单独的任务。尽管如此,它们是相关的:代理的配置通常在同一 Ingress Controller 的级别上设置,或通过它读取的注释。

💡 关键区分

在本文中,我们讨论了两种场景:(1)将 Ingress Controller 设置为外部上游服务器的反向代理,以及(2)通过外部代理服务器路由 Pod 的出站流量。这两种场景在生产环境中都很常见。

为 K8s 集群选择哪种类型的代理

选择代理的类型直接影响您的集群能够在没有封锁的情况下运行多久。数据中心 IP 是最便宜的,但容易被识别和封锁。驻留和移动 IP 更贵,但在需要模拟真实用户的任务中更可靠。

代理类型 速度 封锁风险 K8s 中的最佳场景
数据中心 高 高 内部 API、企业合规、与白名单 IP 的合作伙伴集成
驻留 中 低 市场解析、与广告 API 的交互、地理测试
移动 中 最低 Facebook Ads API、TikTok API、高负载任务的封锁风险

对于 K8s 集群中与解析或广告平台交互相关的大多数任务,最佳选择是 具有轮换的驻留代理。它们提供真实的家庭用户 IP,网站保护算法将这些请求视为普通的浏览器流量。

从协议的角度来看,对于 K8s 来说,最通用的是 HTTP/HTTPS 代理——所有流行的 HTTP 客户端在任何编程语言中都通过标准环境变量 HTTP_PROXY 和 HTTPS_PROXY 支持。SOCKS5 仅在需要代理非 HTTP 的非标准协议时使用。

通过 NGINX Ingress Controller 设置代理

NGINX Ingress Controller 是 K8s 中最常见的选项。我们将讨论两种场景:将 NGINX 设置为通过代理服务器连接到外部上游的反向代理,以及为控制器本身设置全局出站代理。

用于代理请求到上游的注释

NGINX Ingress 支持用于管理代理行为的注释。如果您需要 Ingress 通过中间代理将请求转发到外部服务,请使用带有自定义配置的 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
data:
  # NGINX 出站请求的全局 HTTP 代理
  http-snippet: |
    proxy_connect_timeout 10s;
    proxy_read_timeout 30s;
    proxy_send_timeout 30s;
  # 通过代理设置上游
  use-proxy-protocol: "false"
  proxy-real-ip-cidr: "0.0.0.0/0"

带有代理注释的 Ingress 资源

对于单个 Ingress 资源,可以通过注释设置代理参数。这在不同服务需要不同超时和设置时很有用:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-service-ingress
  namespace: production
  annotations:
    kubernetes.io/ingress.class: "nginx"
    # 代理超时
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
    # 缓冲区大小
    nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "8"
    # 转发真实客户端 IP
    nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
    nginx.ingress.kubernetes.io/forwarded-for-header: "X-Forwarded-For"
spec:
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80

为 NGINX Pod 的出站请求设置外部代理

如果 NGINX Ingress Controller 本身需要通过外部代理进行出站请求(例如,进行外部端点的健康检查),则需要在控制器的 Deployment 中设置环境变量:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  template:
    spec:
      containers:
      - name: controller
        image: registry.k8s.io/ingress-nginx/controller:v1.9.4
        env:
        - name: HTTP_PROXY
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: http-proxy-url
        - name: HTTPS_PROXY
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: https-proxy-url
        - name: NO_PROXY
          value: "localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.cluster.local"

请注意 NO_PROXY 变量——它至关重要。没有它,所有集群内部流量也将通过外部代理,这将破坏服务之间的交互。在 NO_PROXY 中始终包括 RFC-1918 范围和后缀 .cluster.local。

在 Traefik Ingress Controller 中设置代理

Traefik 是 NGINX Ingress 的流行替代品,特别是在与 Helm 和托管集群(k3s、Docker Swarm、Nomad)结合使用时。Traefik 通过 CRD(自定义资源定义)和静态/动态配置有自己的配置系统。

带代理的 Traefik 静态配置

apiVersion: v1
kind: ConfigMap
metadata:
  name: traefik-config
  namespace: traefik
data:
  traefik.yaml: |
    # Traefik 的静态配置
    entryPoints:
      web:
        address: ":80"
      websecure:
        address: ":443"
    
    # 通过代理的工作设置
    serversTransport:
      insecureSkipVerify: false
      maxIdleConnsPerHost: 200
      dialTimeout: 10s
      responseHeaderTimeout: 30s
    
    # 用于代理诊断的日志记录
    log:
      level: INFO
    
    accessLog:
      fields:
        headers:
          defaultMode: keep
          names:
            X-Forwarded-For: keep
            X-Real-IP: keep

用于添加代理头的中间件

在 Traefik 中,可以创建一个中间件,在传递请求时添加或修改头。这对于通过代理链正确传递真实客户端 IP 很有用:

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: proxy-headers
  namespace: production
spec:
  headers:
    customRequestHeaders:
      X-Forwarded-Proto: "https"
    # 删除可能暴露基础设施的头
    customResponseHeaders:
      X-Powered-By: ""
      Server: ""
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: my-app-route
  namespace: production
spec:
  entryPoints:
    - websecure
  routes:
  - match: Host(`myapp.example.com`)
    kind: Rule
    middlewares:
    - name: proxy-headers
    services:
    - name: my-service
      port: 80

Traefik Deployment 的环境变量

类似于 NGINX,对于 Traefik,我们通过 Helm 值或直接在 Deployment 中设置环境变量。在使用 Helm 图表时,通过 values.yaml 进行设置:

# Traefik Helm 图表的 values.yaml
deployment:
  enabled: true
  kind: Deployment
  
env:
  - name: HTTP_PROXY
    valueFrom:
      secretKeyRef:
        name: proxy-credentials
        key: http-proxy-url
  - name: HTTPS_PROXY
    valueFrom:
      secretKeyRef:
        name: proxy-credentials
        key: https-proxy-url
  - name: NO_PROXY
    value: "localhost,127.0.0.1,10.96.0.0/12,10.244.0.0/16,.cluster.local,.svc"

通过 Pod 中的环境变量设置代理

为任何 Pod 设置出站代理的最通用方法是通过标准环境变量。此方法独立于编程语言和框架:Python requests、Go net/http、Node.js axios、Java HttpClient——它们都会自动读取 HTTP_PROXY 和 HTTPS_PROXY。

选项 1:直接在 Pod Spec 中

apiVersion: v1
kind: Pod
metadata:
  name: scraper-pod
  namespace: production
spec:
  containers:
  - name: scraper
    image: mycompany/scraper:latest
    env:
    - name: HTTP_PROXY
      value: "http://username:[email protected]:8080"
    - name: HTTPS_PROXY
      value: "http://username:[email protected]:8080"
    - name: NO_PROXY
      value: "localhost,127.0.0.1,10.0.0.0/8,.cluster.local"
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "500m"

⚠️ 请勿以明文形式存储凭据!

上面的示例仅用于说明。在生产环境中,代理凭据 始终 应存储在 Kubernetes Secret 中,并通过 secretKeyRef 传递。更多信息请参见下面的 Secrets 部分。

选项 2:通过 ConfigMap 为多个 Pod 设置

如果多个 Deployment 使用代理,将设置提取到 ConfigMap 中并通过 envFrom 连接是方便的:

apiVersion: v1
kind: ConfigMap
metadata:
  name: proxy-config
  namespace: production
data:
  NO_PROXY: "localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,.cluster.local,.svc.cluster.local"
  PROXY_TIMEOUT: "30"
  PROXY_MAX_RETRIES: "3"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: price-monitor
  namespace: production
spec:
  replicas: 5
  selector:
    matchLabels:
      app: price-monitor
  template:
    metadata:
      labels:
        app: price-monitor
    spec:
      containers:
      - name: monitor
        image: mycompany/price-monitor:v2.1
        envFrom:
        - configMapRef:
            name: proxy-config
        - secretRef:
            name: proxy-credentials
        ports:
        - containerPort: 8080

选项 3:变更 Webhook AdmissionController

对于企业集群,所有 Pod 都必须使用代理,可以设置一个变更的 admission webhook,自动将代理环境变量添加到所有创建的 Pod 中。这种解决方案在企业基础设施中使用,其中合规性要求所有出站流量通过企业代理。实现这样的 webhook 超出了本文的范围,但值得了解它的存在。

K8s 中的 IP 轮换和代理池管理

静态代理对于企业合规性是好的,但对于需要避免封锁的任务来说不好。如果您的 50 个 Pod 通过同一个 IP 发送请求,该 IP 会很快被封锁。解决方案是代理轮换。

Proxy Sidecar 方案

一种模式是将代理代理作为 sidecar 容器与主应用程序一起运行。Sidecar 在 localhost:8080 接受请求,并通过轮换的外部代理池转发它们:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: scraper-with-proxy-sidecar
  namespace: production
spec:
  replicas: 10
  selector:
    matchLabels:
      app: scraper
  template:
    metadata:
      labels:
        app: scraper
    spec:
      containers:
      # 主应用程序
      - name: scraper
        image: mycompany/scraper:latest
        env:
        - name: HTTP_PROXY
          value: "http://localhost:8080"
        - name: HTTPS_PROXY
          value: "http://localhost:8080"
        - name: NO_PROXY
          value: "localhost,127.0.0.1,.cluster.local"
      
      # Sidecar:带轮换的本地代理代理
      - name: proxy-rotator
        image: mycompany/proxy-rotator:latest
        ports:
        - containerPort: 8080
        env:
        - name: PROXY_LIST_URL
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: api-endpoint
        - name: ROTATION_INTERVAL
          value: "60"  # 每 60 秒轮换一次
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "128Mi"
            cpu: "200m"

中央 Proxy Service 方案

另一种方法是在集群内部设置一个统一的 Proxy Service,所有 Pod 都可以访问。这更易于管理:只需更新一个包含代理凭据的 Secret,所有 Pod 将自动开始使用新数据。

apiVersion: v1
kind: Service
metadata:
  name: proxy-gateway
  namespace: proxy-system
spec:
  selector:
    app: proxy-gateway
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  - name: socks5
    port: 1080
    targetPort: 1080
  type: ClusterIP
---
# Pod 通过内部 DNS 使用代理
# HTTP_PROXY=http://proxy-gateway.proxy-system.svc.cluster.local:8080

对于需要匿名性和最低封锁风险的任务(例如,市场解析或与广告 API 的交互),我们建议使用 具有轮换的驻留代理——它们提供真实的家庭用户 IP,使您集群的流量与普通浏览器流量无异。

在 Kubernetes Secrets 中存储代理凭据

安全存储凭据是任何生产集群的强制要求。切勿将代理的用户名/密码直接插入存储在 Git 存储库中的 YAML 清单中。

为代理创建 Secret

# 通过 kubectl 创建(值会自动编码为 base64)
kubectl create secret generic proxy-credentials \
  --namespace=production \
  --from-literal=http-proxy-url='http://user:[email protected]:8080' \
  --from-literal=https-proxy-url='http://user:[email protected]:8080' \
  --from-literal=socks5-proxy-url='socks5://user:[email protected]:1080'

# 检查创建的 Secret
kubectl get secret proxy-credentials -n production -o yaml

Secret 的 YAML 清单

apiVersion: v1
kind: Secret
metadata:
  name: proxy-credentials
  namespace: production
  labels:
    app: proxy-config
    managed-by: ops-team
type: Opaque
# 值已编码为 base64
# echo -n 'http://user:pass@proxy:8080' | base64
stringData:
  # stringData 允许以明文形式指定值
  # Kubernetes 会自动将其编码为 base64
  http-proxy-url: "http://user:[email protected]:8080"
  https-proxy-url: "http://user:[email protected]:8080"
  proxy-host: "gate.proxycove.com"
  proxy-port: "8080"
  proxy-username: "user"
  proxy-password: "password"

在 Deployment 中使用 Secret

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-worker
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-worker
  template:
    metadata:
      labels:
        app: api-worker
    spec:
      containers:
      - name: worker
        image: mycompany/api-worker:latest
        env:
        - name: HTTP_PROXY
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: http-proxy-url
        - name: HTTPS_PROXY
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: https-proxy-url
        - name: NO_PROXY
          value: "localhost,127.0.0.1,10.0.0.0/8,.cluster.local"
        # 对于读取主机/端口的应用程序
        - name: PROXY_HOST
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: proxy-host
        - name: PROXY_PORT
          valueFrom:
            secretKeyRef:
              name: proxy-credentials
              key: proxy-port

与外部秘密存储的集成

在企业环境中,建议使用 External Secrets Operator 从 HashiCorp Vault、AWS Secrets Manager 或 Azure Key Vault 同步秘密。这允许自动更新代理凭据,而无需手动干预和重启 Pod:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: proxy-credentials-ext
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: proxy-credentials
    creationPolicy: Owner
  data:
  - secretKey: http-proxy-url
    remoteRef:
      key: secret/proxy/proxycove
      property: http_proxy_url
  - secretKey: https-proxy-url
    remoteRef:
      key: secret/proxy/proxycove
      property: https_proxy_url

故障排除和常见错误

即使在正确配置的情况下,也可能会出现问题。让我们讨论一些最常见的问题及其诊断方法。

问题 1:集群内部流量通过代理

症状:Pod 之间无法相互看到,集群内部的 DNS 解析失败,服务没有响应。原因是 NO_PROXY 变量未设置。

对于标准 K8s 集群,NO_PROXY 的最小必要值:

NO_PROXY=localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.cluster.local,.svc,.svc.cluster.local,kubernetes.default.svc

问题 2:代理未应用于特定 Pod

诊断:进入 Pod 并检查环境变量:

# 检查 Pod 中的环境变量
kubectl exec -it <pod-name> -n production -- env | grep -i proxy

# 测试通过代理从 Pod 的连接
kubectl exec -it <pod-name> -n production -- curl -v --proxy http://proxy:8080 https://httpbin.org/ip

# 检查 Secret 是否正确挂载
kubectl exec -it <pod-name> -n production -- env | grep PROXY

问题 3:使用 HTTPS 代理时的 TLS 错误

如果代理使用自签名证书或企业 CA,应用程序将收到 TLS 验证错误。解决方案是将代理的 CA 证书添加到受信任的列表中:

# 创建带有 CA 证书的 ConfigMap
kubectl create configmap proxy-ca-cert \
  --from-file=proxy-ca.crt=./proxy-ca.crt \
  -n production

# 挂载到 Pod
spec:
  containers:
  - name: app
    volumeMounts:
    - name: proxy-ca
      mountPath: /etc/ssl/certs/proxy-ca.crt
      subPath: proxy-ca.crt
  volumes:
  - name: proxy-ca
    configMap:
      name: proxy-ca-cert

问题 4:通过代理的请求缓慢

如果通过代理的请求明显比直接请求慢,请检查:

  • DNS 解析:确保 DNS 请求不通过代理(将其添加到 NO_PROXY)
  • 保持连接:配置与代理服务器的连接池
  • 地理位置:代理服务器应物理靠近目标资源
  • 代理类型:对于高速任务,请考虑使用 数据中心代理——它们提供最低延迟

诊断的有用命令

# 检查 Ingress Controller 的日志
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=100

# 检查 Pod 的事件
kubectl describe pod <pod-name> -n production

# 测试从 Pod 的代理连接
kubectl run test-proxy --image=curlimages/curl --rm -it --restart=Never -- \
  curl -x http://user:pass@proxy:8080 -v https://httpbin.org/ip

# 检查 Secret 是否存在并包含所需的键
kubectl get secret proxy-credentials -n production -o jsonpath='{.data}' | python3 -c "
import sys, json, base64
data = json.load(sys.stdin)
for k, v in data.items():
    print(f'{k}: {base64.b64decode(v).decode()[:20]}...')
"

结论和建议

在 Kubernetes 中设置代理不是一次性任务,而是集群网络安全架构的一部分。让我们总结一下:

  • 对于 NGINX Ingress Controller——使用注释在单个 Ingress 资源级别设置代理参数,并在 Deployment 中使用环境变量进行全局出站流量设置。
  • 对于 Traefik——将通过 ConfigMap 的静态配置与中间件结合使用以管理头。
  • 对于 Pod——通过 Secrets 使用标准变量 HTTP_PROXY / HTTPS_PROXY 是最通用和安全的方法。
  • 始终设置 NO_PROXY——否则集群内部流量将会中断。
  • 切勿以明文形式存储代理凭据在清单或 Git 中。
  • 对于生产环境——使用 External Secrets Operator 进行凭据的自动轮换。

如果您的 K8s 集群执行需要 IP 稳定性和最低封锁风险的任务——解析、与广告 API 的交互、地理测试——我们建议使用 驻留代理。它们提供真实的家庭用户 IP,保护算法将其视为合法流量。对于对速度要求高且需要最低封锁风险的任务,移动代理是绝佳选择,它们通过真实的 4G/5G 网络运营商工作。

```