Descubrir tus servicios en Kubernetes

Una de las necesidades que cubren los servicios dentro de Kubernetes es la capacidad de dar acceso a tus pods que residen en el clúster. Esto es sencillo de ver cuando tienes una IP pública o privada a la que apuntar. Sin embargo, otra de las capacidades que te dan estos servicios es que otros pods puedan internamente acceder a ellos sin necesidad de conocer dichas IPs, a través de diferentes mecanismos. En este artículo quiero contarte cómo descubrir tus servicios en Kubernetes.

Descubrir tus servicios a través de variables de entorno

Lo primero que debes saber es que cada vez que creas un nuevo servicio dentro de Kubernetes, su IPs y puertos son almacenados en todos los pods en forma de variables de entorno. Para comprobarlo, vamos a utilizar el mismo ejemplo que en el artículo anterior:

#nginx-discovering-services.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
      type: frontend
  template:
    metadata:
      labels:
        app: nginx
        type: frontend
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - name: http
              containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
    type: frontend
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 80
      targetPort: http

Para crear los recursos del YAML anterior puedes utilizar este comando:

kubectl create -f nginx-discovering-services.yaml

Si listas tus servicios, comprobarás que actualmente tienes dos disponibles: kubernetes y nginx-service:

kubectl get svc

Como primera prueba, vamos a comprobar las variables de entorno de las que dispone cualquiera de nuestros pods del deployment que acabamos de crear. Para ello, escoge uno cualquiera y ejecuta el comando env dentro del mismo:

kubectl exec nginx-deployment-68f6bbbc7f-7c85v env
kubectl exec nginx-deployment-68f6bbbc7f-7c85v env

Si te fijas, cada uno de los servicios están dados de alta con diferentes valores pero con un mismo patrón:

  • NOMBRE_DEL_SERVICIO_HOST
  • NOMBRE_DEL_SERVICIO_PORT_NUMERO_DE_PUERTO_PORT
  • NOMBRE_DEL_SERVICIO_PORT_NUMERO_DE_PUERTO_ADDR

Una prueba sencilla sería intentar de invocar, en este caso a nuestro servidor de nginx, dentro de un pod utilizando estas variables. Para ello, vamos a crear un nuevo pod con curl desde el cual hacer nuestras pruebas:

kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm

Desde la línea de comandos del pod que acabamos de crear ejecuta lo siguiente:

curl -s http://$NGINX_SERVICE_SERVICE_HOST

Comprobarás que a través de la variable de entorno hemos podido invocar a otro pod, obteniendo como resultado la página de inicio de Nginx:

curl utilizando la variable de entorno del servicio al que queremos llamar

Si bien esto es posible, a mi personalmente no me parece muy cómodo utilizar este mecanismo para llamar a los servicios de otros pods. No obstante, existe otra forma que es utilizando el servidor DNS que reside en nuestro Kubernetes.

Servidor de DNS dentro de Kubernetes

Para hacer nuestra vida un poco más fácil, uno de los pods que se instala como parte del sistema es un servidor DNS. Puedes comprobarlo a través del siguiente comando:

kubectl get po -n kube-system
coredns es el servidor DNS dentro de tu clúster

Todos los pods de tu clúster tienen configurado dicho servidor para su uso. Para comprobarlo, puedes visualizar el contenido del archivo /etc/resolv.conf

cat /etc/resolv.conf

Existen diferentes maneras de utilizarlo, dependiendo de quién, y donde, quiera consumir el servicio. Para que entiendas bien las diferentes formas de llamar a un servicio, dependiendo desde qué pod lo llame, prueba los siguientes supuestos:

Nombre completo del servicio

curl http://nginx-service.default.svc.cluster.local

Esta es la forma larga con la que podrás llamar al pod desde cualquier parte de tu clúster. Si te fijas, default es el nombre del namespace donde reside el servicio.

Nombre del servicio y namespace

La forma anterior se puede acortar y es necesaria cada vez que quieras invocar a un servicio que reside en un namespace diferente del que se hace la llamada:

curl http://nginx-service.default 

Solo con el nombre del servicio

Si el pod que quiere realizar la petición convive en el mismo namespace donde está el servicio objetivo basta con utilizar únicamente el nombre del mismo:

curl http://nginx-service

De esta forma evitas trabajar con IPs directamente, desacoplando de esta forma la llamadas a servicios dentro de tu Kubernetes.

¡Saludos!