Si su clúster de K8s realiza solicitudes externas — a API de socios, a marketplaces, a plataformas publicitarias — tarde o temprano se encontrará con bloqueos por IP. El Ingress Controller por sí mismo gestiona el tráfico entrante, pero para las solicitudes salientes a través de los pods se necesita un esquema separado con un proxy. En este artículo analizamos cómo configurarlo correctamente: desde la elección del tipo de proxy hasta configuraciones YAML específicas para NGINX Ingress y Traefik.
¿Por qué un proxy en Kubernetes: escenarios reales?
Kubernetes es un orquestador, y la mayoría de los artículos sobre él hablan del tráfico entrante: cómo configurar Ingress, cómo emitir un certificado TLS, cómo enrutar solicitudes a los servicios. Pero hay otro lado: el tráfico saliente de los pods. Aquí es donde surgen problemas con bloqueos, restricciones geográficas y límites por IP.
Consideremos tareas específicas donde un proxy en el clúster de K8s se vuelve una necesidad:
- Raspado y monitoreo de precios. Su clúster ejecuta un CronJob cada 15 minutos y recopila datos de Wildberries, Ozon, Amazon. Sin rotación de IP, después de 2–3 horas recibirá un bloqueo en todo el rango de IP de su proveedor de nube.
- Trabajo con API publicitarias. Facebook Marketing API, TikTok Ads API, Google Ads API — todos ellos rastrean el origen de las solicitudes. Si decenas de pods envían solicitudes desde una sola IP de un centro de datos, eso es un disparador para un bloqueo automático.
- Pruebas de geolocalización. Su servicio debe mostrar contenido diferente para usuarios de diferentes países. A través de un proxy, los pods pueden simular solicitudes desde las regiones necesarias para pruebas automáticas.
- Elusión de restricciones corporativas. En algunas infraestructuras, todo el tráfico saliente debe pasar a través de un proxy corporativo — este es un requisito de cumplimiento.
- Trabajo con API de socios con lista blanca de IP. Cuando un socio permite solicitudes solo desde IP específicas, y su clúster se escala y cambia direcciones — un proxy con IP estáticas resuelve el problema.
Es importante entender la separación arquitectónica: Ingress Controller gestiona el tráfico entrante (de los usuarios a sus servicios), y el proxy para las solicitudes salientes es una tarea separada. Sin embargo, están relacionados: la configuración del proxy a menudo se establece a nivel del mismo Ingress Controller o a través de anotaciones que lee.
💡 División clave
En este artículo analizamos dos escenarios: (1) configuración de Ingress Controller como un proxy inverso para servidores upstream externos, y (2) enrutamiento del tráfico saliente de los pods a través de un servidor proxy externo. Ambos escenarios se encuentran en producción.
Qué tipo de proxy elegir para el clúster de K8s
La elección del tipo de proxy afecta directamente cuánto tiempo podrá funcionar su clúster sin bloqueos. Las IP de centros de datos son las más baratas, pero son fáciles de identificar y bloquear. Las IP residenciales y móviles son más caras, pero significativamente más confiables para tareas donde es importante simular un usuario real.
| Tipo de proxy | Velocidad | Riesgo de bloqueo | Mejor escenario en K8s |
|---|---|---|---|
| Centros de datos | Alta | Alta | API internas, cumplimiento corporativo, integraciones de socios con lista blanca de IP |
| Residenciales | Media | Bajo | Raspado de marketplaces, trabajo con API publicitarias, pruebas geográficas |
| Móviles | Media | Mínimo | Facebook Ads API, TikTok API, tareas de alta carga con riesgo de bloqueo |
Para la mayoría de las tareas en el clúster de K8s relacionadas con el raspado o el trabajo con plataformas publicitarias, la elección óptima son los proxies residenciales con rotación. Proporcionan IP reales de usuarios domésticos, y los algoritmos de protección de sitios perciben tales solicitudes como tráfico normal de navegador.
Desde el punto de vista del protocolo, el más versátil para K8s es el proxy HTTP/HTTPS — es compatible con todos los clientes HTTP populares en cualquier lenguaje de programación a través de las variables de entorno estándar HTTP_PROXY y HTTPS_PROXY. SOCKS5 solo es necesario para protocolos no estándar o cuando se requiere proxy no solo para HTTP.
Configuración de proxy a través de NGINX Ingress Controller
NGINX Ingress Controller es la opción más común en K8s. Analicemos dos escenarios: la configuración de NGINX como un proxy inverso a un upstream externo a través de un servidor proxy, y la configuración global de un proxy saliente para el propio controlador.
Anotaciones para el proxy de solicitudes a upstream
NGINX Ingress admite anotaciones para gestionar el comportamiento del proxy. Si necesita que Ingress envíe solicitudes a un servicio externo a través de un proxy intermedio, utilice un ConfigMap con una configuración personalizada:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
data:
# Proxy HTTP global para solicitudes salientes de NGINX
http-snippet: |
proxy_connect_timeout 10s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
# Configuración de upstream a través de proxy
use-proxy-protocol: "false"
proxy-real-ip-cidr: "0.0.0.0/0"
Recurso Ingress con anotaciones para proxy
Para un recurso Ingress separado, se pueden establecer parámetros de proxy a través de anotaciones. Esto es útil cuando diferentes servicios requieren diferentes tiempos de espera y configuraciones:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-service-ingress
namespace: production
annotations:
kubernetes.io/ingress.class: "nginx"
# Tiempos de espera del proxy
nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
# Tamaño del búfer
nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
nginx.ingress.kubernetes.io/proxy-buffers-number: "8"
# Transmisión de la IP real del cliente
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
Configuración de un proxy externo para solicitudes salientes del pod NGINX
Si el propio NGINX Ingress Controller debe realizar solicitudes salientes a través de un proxy externo (por ejemplo, para verificar la salud de los endpoints externos), se deben establecer variables de entorno en el Deployment del controlador:
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"
Preste atención a la variable NO_PROXY — es críticamente importante. Sin ella, todo el tráfico intra-cluster también pasará a través del proxy externo, lo que romperá la interacción entre servicios. En NO_PROXY siempre incluya los rangos RFC-1918 y el sufijo .cluster.local.
Configuración de proxy en Traefik Ingress Controller
Traefik es una alternativa popular a NGINX Ingress, especialmente en combinación con Helm y en clústeres gestionados (k3s, Docker Swarm, Nomad). Traefik tiene su propio sistema de configuración a través de CRD (Definiciones de Recursos Personalizados) y configuraciones estáticas/dinámicas.
Configuración estática de Traefik con proxy
apiVersion: v1
kind: ConfigMap
metadata:
name: traefik-config
namespace: traefik
data:
traefik.yaml: |
# Configuración estática de Traefik
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
# Configuraciones para trabajar detrás de un proxy
serversTransport:
insecureSkipVerify: false
maxIdleConnsPerHost: 200
dialTimeout: 10s
responseHeaderTimeout: 30s
# Registro para diagnóstico de proxy
log:
level: INFO
accessLog:
fields:
headers:
defaultMode: keep
names:
X-Forwarded-For: keep
X-Real-IP: keep
Middleware para agregar encabezados de proxy
En Traefik se puede crear un Middleware que agregue o modifique encabezados al transmitir solicitudes. Esto es útil para la correcta transmisión de la IP real del cliente a través de la cadena de proxies:
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: proxy-headers
namespace: production
spec:
headers:
customRequestHeaders:
X-Forwarded-Proto: "https"
# Eliminamos encabezados que pueden revelar la infraestructura
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
Variables de entorno para el Deployment de Traefik
Al igual que con NGINX, para Traefik se establecen variables de entorno a través de los valores de Helm o directamente en el Deployment. Al usar el chart de Helm, esto se hace a través de values.yaml:
# values.yaml para el chart de Helm de Traefik
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"
Proxy a través de variables de entorno en los pods
La forma más versátil de configurar un proxy saliente para cualquier pod es a través de las variables de entorno estándar. Este método funciona independientemente del lenguaje de programación y el marco: Python requests, Go net/http, Node.js axios, Java HttpClient — todos ellos leen HTTP_PROXY y HTTPS_PROXY automáticamente.
Opción 1: Directamente en el 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"
⚠️ ¡No almacene credenciales en texto claro!
El ejemplo anterior se muestra para ilustración. En producción, las credenciales del proxy siempre deben almacenarse en Kubernetes Secret y transmitirse a través de secretKeyRef. Más detalles en la sección sobre Secrets a continuación.
Opción 2: A través de ConfigMap para varios pods
Si varios Deployments utilizan el proxy, es conveniente extraer la configuración en un ConfigMap y conectarlo a través de 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
Opción 3: Mutating WebhookAdmissionController
Para clústeres corporativos, donde todos los pods deben utilizar un proxy, se puede configurar un webhook de admisión mutante que agregue automáticamente las variables de entorno del proxy a todos los pods que se creen. Esta solución se utiliza en infraestructuras empresariales donde el cumplimiento requiere que todo el tráfico saliente pase a través de un proxy corporativo. La implementación de dicho webhook está más allá del alcance de este artículo, pero vale la pena saber de su existencia.
Rotación de IP y gestión del grupo de proxies en K8s
Un proxy estático es bueno para el cumplimiento corporativo, pero malo para tareas donde se necesita evitar bloqueos. Si 50 de sus pods envían solicitudes a través de la misma IP, esa IP será bloqueada muy rápidamente. La solución es la rotación de proxies.
Esquema con Proxy Sidecar
Uno de los patrones es ejecutar un agente proxy como un contenedor sidecar junto a la aplicación principal. El sidecar recibe solicitudes en localhost:8080 y las reenvía a través de un grupo rotatorio de proxies externos:
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:
# Aplicación principal
- 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: agente proxy local con rotación
- 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" # rotación cada 60 segundos
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "200m"
Esquema con Proxy Service central
Un enfoque alternativo es un único Proxy Service dentro del clúster, al que se dirigen todos los pods. Esto es más fácil de gestionar: basta con actualizar un Secret con las credenciales del proxy, y todos los pods comenzarán automáticamente a utilizar los nuevos datos.
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
---
# Los pods utilizan el proxy a través de DNS interno
# HTTP_PROXY=http://proxy-gateway.proxy-system.svc.cluster.local:8080
Para tareas donde la anonimidad y el riesgo mínimo de bloqueos son críticos (por ejemplo, raspado de marketplaces o trabajo con API publicitarias), recomendamos utilizar proxies residenciales con rotación — proporcionan IP reales de usuarios domésticos, lo que hace que el tráfico de su clúster sea indistinguible del tráfico normal de navegador.
Almacenamiento de credenciales de proxy en Kubernetes Secrets
El almacenamiento seguro de credenciales es un requisito obligatorio para cualquier clúster de producción. Nunca inserte el nombre de usuario/contraseña del proxy directamente en los manifiestos YAML que se almacenan en el repositorio de Git.
Creación de Secret para el proxy
# Creación a través de kubectl (los valores se codifican en base64 automáticamente)
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'
# Verificación del Secret creado
kubectl get secret proxy-credentials -n production -o yaml
YAML-manifiesto de Secret
apiVersion: v1
kind: Secret
metadata:
name: proxy-credentials
namespace: production
labels:
app: proxy-config
managed-by: ops-team
type: Opaque
# Los valores están codificados en base64
# echo -n 'http://user:pass@proxy:8080' | base64
stringData:
# stringData permite especificar valores en texto claro
# Kubernetes los codifica automáticamente en 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"
Uso de Secret en Deployment
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"
# Para aplicaciones que leen host/puerto por separado
- name: PROXY_HOST
valueFrom:
secretKeyRef:
name: proxy-credentials
key: proxy-host
- name: PROXY_PORT
valueFrom:
secretKeyRef:
name: proxy-credentials
key: proxy-port
Integración con almacenes de secretos externos
En un entorno empresarial, se recomienda utilizar el External Secrets Operator para sincronizar secretos desde HashiCorp Vault, AWS Secrets Manager o Azure Key Vault. Esto permite actualizar automáticamente las credenciales del proxy sin intervención manual y reinicio de pods:
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
Diagnóstico y errores comunes al configurar proxies en K8s
Incluso con una configuración correcta, pueden surgir problemas. Analicemos los más comunes y las formas de diagnóstico.
Problema 1: El tráfico intra-cluster pasa a través del proxy
Síntoma: los pods dejan de verse entre sí, la resolución DNS dentro del clúster se rompe, los servicios no responden. Causa — la variable NO_PROXY no está configurada.
El valor mínimo necesario para NO_PROXY para un clúster K8s estándar es:
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
Problema 2: El proxy no se aplica a un pod específico
Diagnóstico: entre al pod y verifique las variables de entorno:
# Verificación de variables de entorno en el pod
kubectl exec -it <pod-name> -n production -- env | grep -i proxy
# Prueba de conexión a través del proxy desde el pod
kubectl exec -it <pod-name> -n production -- curl -v --proxy http://proxy:8080 https://httpbin.org/ip
# Verificación de que el Secret se monta correctamente
kubectl exec -it <pod-name> -n production -- env | grep PROXY
Problema 3: Errores de TLS al usar un proxy HTTPS
Si el proxy utiliza un certificado autofirmado o un CA corporativo, las aplicaciones recibirán errores de verificación TLS. La solución es agregar el certificado CA del proxy a los confiables:
# Creación de ConfigMap con el certificado CA
kubectl create configmap proxy-ca-cert \
--from-file=proxy-ca.crt=./proxy-ca.crt \
-n production
# Montaje en el 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
Problema 4: Solicitudes lentas a través del proxy
Si las solicitudes a través del proxy son significativamente más lentas que las directas — verifique:
- Resolución DNS: asegúrese de que las solicitudes DNS no pasen a través del proxy (agregue a
NO_PROXY) - Conexiones Keep-alive: configure el grupo de conexiones al servidor proxy
- Ubicación geográfica: el servidor proxy debe estar físicamente cerca del recurso objetivo
- Tipo de proxy: para tareas de alta velocidad, considere proxies de centros de datos — proporcionan la menor latencia
Comandos útiles para diagnóstico
# Verificar los registros del Ingress Controller
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=100
# Verificar eventos del pod
kubectl describe pod <pod-name> -n production
# Prueba de conexión proxy desde el pod
kubectl run test-proxy --image=curlimages/curl --rm -it --restart=Never -- \
curl -x http://user:pass@proxy:8080 -v https://httpbin.org/ip
# Verificar que el Secret existe y contiene las claves necesarias
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]}...')
"
Conclusión y recomendaciones
Configurar un proxy en Kubernetes no es una tarea única, sino parte de la arquitectura de seguridad de red del clúster. Resumamos:
- Para NGINX Ingress Controller — use anotaciones para configurar los parámetros del proxy a nivel de recursos Ingress individuales, y variables de entorno en el Deployment para configuraciones globales del tráfico saliente.
- Para Traefik — combine la configuración estática a través de ConfigMap con Middleware para gestionar encabezados.
- Para los pods — las variables estándar
HTTP_PROXY/HTTPS_PROXYa través de Secrets — el enfoque más versátil y seguro. - Siempre configure NO_PROXY — de lo contrario, el tráfico intra-cluster se romperá.
- Nunca almacene credenciales del proxy en texto claro en manifiestos o en Git.
- Para producción — utilice el External Secrets Operator para la rotación automática de credenciales.
Si su clúster de K8s realiza tareas donde la estabilidad de IP y el riesgo mínimo de bloqueos son críticos — raspado, trabajo con API publicitarias, pruebas geográficas — recomendamos utilizar proxies residenciales. Proporcionan IP reales de usuarios domésticos, que los algoritmos de protección perciben como tráfico legítimo. Para tareas con altas demandas de velocidad y riesgos mínimos de bloqueos por parte de plataformas móviles, son ideales los proxies móviles — funcionan a través de redes reales 4G/5G de operadores.
```