Ingress en Kubernetes: exponiendo tu aplicación al mundo
1. Descripción general
Cuando ya tienes un Service ejecutándose en el cluster pero quieres que los usuarios externos accedan a través de un dominio bonito como api.nonladev.com en lugar de IP:puerto confuso, ese es el momento en que necesitas Ingress. Este artículo explica qué es Ingress, en qué se diferencia de Service tipo LoadBalancer, cómo instalar el Ingress Controller y cómo escribir recursos de Ingress para escenarios comunes: path-based routing, host-based routing, TLS. Después de leer, podrás exponer una aplicación Spring Boot al mundo exterior con un dominio y HTTPS.
2. ¿Por qué necesitas Ingress
2.1. El problema con Service
El tipo de Service NodePort o LoadBalancer funciona bien, pero tiene limitaciones:
LoadBalancer: cada Service necesita su propia IP externa → es costoso si tienes muchos services (cada cloud LB cobra por separado).NodePort: expone un puerto extraño (30000-32767), sin TLS termination, sin enrutamiento por dominio/path.
Supongamos que tienes 5 microservicios para exponer al exterior; usar LoadBalancer significa 5 cloud load balancers, 5 IPs externas. Ingress lo resuelve usando un único punto de entrada para enrutar tráfico a múltiples Services internos, basándose en hostname o path.
2.2. ¿Qué es Ingress?
Ingress es un objeto API que define reglas de enrutamiento HTTP/HTTPS desde el exterior hacia los Services dentro del cluster. Ingress no hace nada por sí solo — necesita un Ingress Controller (un Pod ejecutando un reverse proxy como NGINX, Traefik, HAProxy) para leer las reglas y ejecutarlas.
Client → Ingress Controller (LoadBalancer/NodePort) → Ingress rules → Service → Pod
Nota: Si el cluster aún no tiene instalado Ingress Controller, crear un recurso Ingress tampoco tendrá ningún efecto — es solo una configuración ‘esperando’ a que el controller la lea.
3. Instalar Ingress Controller
El más popular es ingress-nginx. Instálalo con Helm:
<!-- No aplica — usa Helm/kubectl, no es una dependencia Maven -->
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace
Este comando crea el namespace ingress-nginx, deploy del Ingress Controller junto con un Service tipo LoadBalancer para recibir tráfico desde el exterior. Verifica:
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
NAME TYPE EXTERNAL-IP PORT(S)
ingress-nginx-controller LoadBalancer 34.123.45.67 80:31234/TCP,443:31235/TCP
Esta IP externa es el único punto de entrada para todos los Ingresses en el cluster.
4. Deployment y Service de ejemplo
Antes de escribir Ingress, necesitas un Service para enrutar hacia él. Supongamos la aplicación order-service:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 2
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
El Service order-service expone el puerto 80, forward al puerto del contenedor 8080. Ten en cuenta que este Service no necesita tipo LoadBalancer, usar ClusterIP (por defecto) es suficiente, porque el Ingress Controller llamará al Service a través de la red interna.
5. Recurso Ingress básico — host-based routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: api.nonladev.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
Explicación de cada parte:
ingressClassName: nginx— especifica cuál controller manejará esta regla (importante cuando el cluster tiene múltiples controllers).rules.host— solo el tráfico con headerHost: api.nonladev.comcoincidirá con esta regla.path+pathType: Prefix— cualquier request que comience con/(es decir, todos) se reenviará aorder-service:80.- la anotación
rewrite-target— específica de ingress-nginx, se usa para reescribir la URL antes de reenviar (cada controller tiene anotaciones diferentes).
Aplica y prueba:
kubectl apply -f order-ingress.yaml
curl -H "Host: api.nonladev.com" http://34.123.45.67/
{"service":"order-service","status":"UP"}
En un entorno real, apuntas el registro DNS api.nonladev.com a la IP externa del Ingress Controller, sin necesidad de un header Host falso como el anterior.
6. Path-based routing — múltiples services compartiendo un dominio
Escenario común: un dominio, múltiples services en diferentes paths.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-gateway-ingress
spec:
ingressClassName: nginx
rules:
- host: api.nonladev.com
http:
paths:
- path: /orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
- path: /customers
pathType: Prefix
backend:
service:
name: customer-service
port:
number: 80
Las requests a api.nonladev.com/orders/123 se enrutan a order-service, mientras que /customers/55 se enruta a customer-service. pathType: Prefix significa coincidencia por prefijo — diferente de Exact (debe coincidir exactamente) e ImplementationSpecific (el controller lo interpreta, generalmente para regex).
Nota: El orden de los paths no afecta el matching en Ingress v1 — el controller elige automáticamente el path más largo que coincida primero. Pero escribe paths claros, sin solapamiento, para evitar bugs difíciles de depurar.
7. TLS — HTTPS para Ingress
Para tener HTTPS, necesitas un Secret que contenga el certificado y la clave, luego referenciarlo en Ingress.
kubectl create secret tls nonladev-tls \
--cert=tls.crt --key=tls.key
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-ingress-tls
spec:
ingressClassName: nginx
tls:
- hosts:
- api.nonladev.com
secretName: nonladev-tls
rules:
- host: api.nonladev.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
El bloque spec.tls declara qué host usa qué certificado. El controller realiza automáticamente TLS termination — es decir, desencripta HTTPS en Ingress, luego reenvía HTTP puro al Service interno. Tus Pods no necesitan saber nada sobre TLS.
En la práctica, generalmente usas cert-manager para solicitar automáticamente certificados Let’s Encrypt en lugar de crear Secrets manualmente:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-ingress-tls
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- api.nonladev.com
secretName: nonladev-tls
rules:
- host: api.nonladev.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
La anotación cluster-issuer solicita a cert-manager crear y renovar automáticamente el Secret nonladev-tls — sin necesidad de hacer nada manualmente.
8. Escribir tests para verificar endpoints después de exponer
Después de que Ingress esté funcionando, puedes escribir un test de integración simple para verificar que la clase controller maneje correctamente los paths (test en el nivel de la aplicación Spring Boot, no directamente el YAML):
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderControllerIT {
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldReturnOrderWhenCalledWithOrdersPrefix() {
ResponseEntity<String> response =
restTemplate.getForEntity("/orders/123", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).contains("orderId");
}
}
Este test asegura que la aplicación Spring Boot responda correctamente al path /orders/123 que Ingress reescribe — separando los errores del código backend de los errores de configuración de Ingress.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
9. Errores comunes
- Olvidar
ingressClassName: si el cluster tiene múltiples Ingress Controllers (por ejemplo, nginx y traefik), Ingress no sabe cuál controller manejarlo → no funciona o ambos lo manejan incorrectamente. - Tipo de Service incorrecto: dejar Service backend como
LoadBalanceres innecesario, causa desperdicio — solo necesitasClusterIP. pathTypeno coincide con lo esperado: usarExactcuando necesitasPrefixcausa que los subrequests (/orders/123) resulten en 404 porque solo/orderscoincide exactamente.- DNS no configurado: prueba con
curl -H "Host: ..."antes de configurar el DNS real, evita perder tiempo esperando la propagación de DNS. - Anotación de rewrite equivocada para el controller: las anotaciones de ingress-nginx no se aplican a Traefik o AWS ALB Ingress Controller — cada controller tiene su propio conjunto de anotaciones, lee cuidadosamente la documentación correspondiente.
Cuándo NO usar Ingress: si tu aplicación usa un protocolo que no es HTTP/HTTPS (por ejemplo, TCP puro, gRPC streaming complejo, o UDP), Ingress estándar no lo soporta bien — necesitas considerar
Gateway APIo Service tipoLoadBalancerdirectamente para TCP/UDP.
10. Conclusión
Ingress es una capa de enrutamiento HTTP/HTTPS centralizada que te ayuda a exponer múltiples Services al exterior a través de un único punto de entrada, ahorrando costos de LoadBalancer y facilitando la gestión de dominios/paths/TLS. Los puntos clave a recordar: los recursos de Ingress son solo reglas, necesitas un Ingress Controller para ejecutarlas; elige correctamente ingressClassName y pathType; usa cert-manager para automatizar TLS. Para aplicaciones que no son HTTP puro, considera usar Gateway API en lugar de forzar Ingress.