如果您的 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 网络运营商工作。
```