Ingress
¿Cuál es la diferencia entre service e ingress?
Vamos a imaginar el siguiente escenario.
Tenemos una app desplegada en el cluster. Para acceder a su base de datos apunta para el service de la base de datos que es del tipo ClusterIP que solo es accesible dentro del cluster.
Para que la aplicación sea accesible fuera del cluster necesitamos crear un service del tipo NodePort. Un puerto alto entre 30000 y 32767 fue escogido. Cada vez que un nodo reciba una requisición en este puerto reenviará para el service del app.
Un service del tipo NodePort crea otro service del tipo ClusterIP.
Si recordamos que un service son reglas, entonces crea dos reglas, una para todos los nodos en el puerto que deben apuntar para el service normal que es del tipo ClusterIP.
Para acceder a la aplicación necesitamos digitar la IP de alguno de los nodos de Kubernetes y el puerto. Podría ser la IP de cualquier nodo.
Si el tráfico aumenta el número de pods podemos aumentar el número de réplicas y el service se encargará de dividir el tráfico entre los pods.
Pero no queremos que el usuario digite la dirección utilizando la IP y el puerto de nuestros nodos. Como solo podemos usar puertos altos en NodePort ¿qué podríamos hacer para tener algo del tipo http://app.com?
Podríamos hacer un proxy. Un servidor que escuchara esa dirección en el puerto 80 que el usuario no necesitara digitar y reenviara para app.com:30080
Pero ese escenario es aquel que tienes el cluster hospedado en tu datacenter.
En el caso de un cluster hospedado en una cloud por ejemplo, podríamos crear el service que es del tipo NodePort como siendo LoadBalancer.
Cuando hacemos esto en una cloud, automáticamente los servicios de la cloud crean un load balancer que será usado para enrutar todo el tráfico para tu cluster en la nodeport que definiste.
Si creas otra aplicación y defines nuevamente un service del tipo LoadBalancer, otro load balancer será creado en la cloud apuntando para un nuevo nodeport. Y al final pagarás horrores.
Para resolver ese escenario podríamos tener una única aplicación dentro de nuestro cluster que actúa como un proxy reverso. Analiza la url y reenvía para el service correcto. De esa forma tendríamos solamente un load balancer en la cloud.
En vez de configurar esa aplicación manualmente el ingress es un objeto que configura eso para nosotros de forma que sea un objeto en kubernetes.
Es necesario desplegar una solución para ser el proxy reverso. Podemos usar varios, pero algunos están aquí.
- Nginx (Es el más popular)
- Istio
- Traefik
- HAProxy
Estos controllers están programados para monitorear kubernetes en busca de los objetos ingress para configurar sus entradas.
Si no tienes una de esas aplicaciones en tu cluster un objeto de ingress no funcionará.
Lo que estamos haciendo, es traer un proxy para dentro de kubernetes y crear objetos que configuren el mismo de forma fácil. Tener un punto de entrada que pueda ser filtrado y disparado para cuál service debe responder.
Vamos a concentrarnos en nginx ingress controller que es el más famoso. ¿Cómo desplegar nginx?
Normalmente utilizaríamos helm para eso, pero vamos de la manera tradicional.
La primera cosa que necesitamos es tener un configmap que tendrá todos los parámetros a fin de evitar pasar todos los args en el deployment. También queda más fácil hacer modificaciones.
Un resumen de lo que necesitaríamos.

Una vez configurado necesitamos de los objetos del ingress.
El objeto se resume en algo bien simple.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mi-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
# El nombre del ingress class que vamos a usar
ingressClassName: nginx
# La lista de reglas que vamos a pasar
rules: []
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-app
spec:
rules:
# En este caso estamos analizando también el dominio.
- host: "app.misitio.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service-app1
port:
number: 8080 # Puerto del service que redirigirá para el puerto del app.
# aquí estamos analizando solamente el camino de la url pasada sin importar cuál es el dominio.
- http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service-app2
port:
number: 8080 # Puerto del service que redirigirá para el puerto del app.
# Aquí sería una idea de que si la primera regla no fuera resuelta, pasamos para la segunda. Óptima para colocar algo para responder caso la url no exista como un error 404.
- host: "*.foo.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service-404
port:
number: 8989
El orden de la regla importa.
También podríamos dividir esto en varios ingresses separados, que sería buena práctica.