Pruebas de disponibilidad para tus contenedores en Kubernetes

En el artículo anterior te conté cómo configurar pruebas que ayuden a Kubernetes a entender cuándo, a nivel de aplicación, un contenedor no se está comportando de manera correcta, y actúe en consecuencia. Hoy te quiero hablar de readiness probes, que son otro tipo de pruebas que están pensadas para comprobar si una aplicación está lista para atender peticiones por parte de los usuarios u otros pods.

Readiness probe

Si bien el tipo de pruebas que puedes lanzar son exactamente las mismas que configurarías para una liveness probe, en este caso Kubernetes se comporta de manera distinta: en lugar de reiniciar tu contenedor, con el objetivo de recuperar tu aplicación, lo que hace es que saca al mismo de su balanceador para que no le lleguen peticiones. El pod no estará disponible hasta que la prueba se ejecute de manera satisfactoria.
Este mecanismo es útil cuando tu aplicación se está iniciando y no puede estar disponible de inmediato para recibir peticiones. También puede ocurrir que un frontal no sea capaz de llegar a un servicio, por el motivo que sea, y es preferible que no acepte más conexiones hasta que se solucione dicha comunicación.

Como ves, en el caso de las readiness probe están involucradas más piezas dentro de nuestro clúster. Para verlo todo con un ejemplo vamos a jugar con los siguientes recursos:

#nginx-readiness.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
          readinessProbe:
            exec:
              command:
                - cat
                - /tmp/iamready

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
    type: frontend
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 80
      targetPort: http

Lo primero que defino en este YAML es un recurso de tipo Deployment que, de manera interna, creará un ReplicaSet, el cual tiene como cometido gestionar las tres réplicas que se piden. Los pods que se generen tendrán configurada una prueba de disponibilidad a través del apartado readinessProbe, donde he utilizado el tipo exec que lanzará el comando cat /tmp/iamready esperando que dicho archivo al menos exista.

Por último, también he creado un servicio del tipo LoadBalancer que me permitirá acceder a los pods a través de una IP pública, ya que estoy utilizando Azure Kubernetes Service.

Crea el despliegue y el servicio en tu clúster a través del siguiente comando:

kubectl create -f nginx-readiness.yaml

Nota: este proceso puede tardar unos segundos, ya que si has elegido como yo el tipo de servicio LoadBalancer tendrás que esperar a que tu proveedor cloud te asigne una IP pública.

Si compruebas el listado de tus pods, verás que las tres replicas de nginx están en estado Running pero en la columna READY verás que todas ellas permanecen como 0/1.

kubectl get po,svc

Esto es así porque la prueba de disponibilidad no se está cumpliendo, por lo que los kubelets de los nodos no pueden considerar que los pods esté listos para su uso. Si intentas acceder a tus pods por la IP pública asignada al servicio, comprobarás que esta no funciona al no tener ninguno disponible. Revisa el estado del servicio a través del siguiente comando:

kubectl describe svc nginx-service

Al igual que en los pods, el comando describe te da mucha información sobre el recurso que estás solicitando, en este caso un servicio. Como ya te hablé en el pasado, los servicios no son más que balanceadores que nos ayudan a gestionar el acceso a los pods de diferentes formas. Por supuesto, también se apoyan en el funcionamiento de las etiquetas para saber qué pods deben manejar. Sin embargo, también se ayudan de un recurso llamado endpoint:

kubectl describe svc nginx-service

Aunque parezca un apartado dentro del servicio, se trata de hecho de un recurso más dentro de Kubernetes, el cual tiene como cometido almacenar todas las IPs de los pods que está gestionando el balanceador. Ya que se trata de un recurso más, puedes lanzar un describe al mismo utilizando como nombre el de nuestro servicio, ya que al crearlo este por nosotros de manera automática se le asigna el mismo nombre:

kubectl describe endpoints nginx-service
kubectl describe endpoints nginx-service

El resultado es súper descriptivo en este caso, ya que nos está diciendo que las tres IPs de nuestras tres réplicas las tiene en consideración pero que no están listas. Como te puedes imaginar, el motivo de que esto sea así es a causa de tu readiness probe, el cual no se está cumpliendo. Aunque las etiquetas se cumplan para un número de pods, al no pasar la prueba de readiness, sus IPs no serán transmitidas al servicio y por lo tanto no será posible acceder a dichos pods.

Para verificar que todo lo que te digo es cierto, escoge uno de los tres pods que tienes a tu disposición y lanza un describe sobre él:

kubectl describe po nginx-deployment-558fd78484-25rrf

En el apartado Events podrás ver que nuestro amigo kubelet está diciendo que la prueba de readiness ha fallado porque no existe el archivo. Además, si te fijas, la IP de este pod se corresponde con una de las que aparece en el apartado NotReadyAddresses del endpoint.

Ahora entenderás todo, te lo prometo 🙂 El último paso es arreglar la prueba de readiness para que nuestro pod esté disponible y que nuestro servicio quiera enrutar peticiones a él. Como la prueba trata de que al menos el archivo /tmp/iamready exista, vamos a crearlo solo para este pod con el siguiente comando:

kubectl exec nginx-deployment-558fd78484-25rrf touch /tmp/iamready

Tras unos segundos, comprobarás que tu pod ya está disponible y que además ahora el recurso endpoint refleja la IP de este.

kubectl get po,endpoints -o wide

De hecho, si vuelves a describir el endpoint comprobarás que la IP del pod ahora se encuentra en el apartado Addresses.

kubectl describe endpoints nginx-service

Si quisieras revertir su disponibilidad, basta con eliminar el archivo del mismo:

kubectl exec nginx-deployment-558fd78484-25rrf rm /tmp/iamready

Con ello conseguirás que la prueba vuelva a fallar, que el recurso endpoint se actualice, moviendo de nuevo la IP de dicho pod al apartado NotReadyAddresses.

¡Saludos!