Guía visual para la solución de problemas en Kubernetes

Nota de traducción.: Este artículo forma parte de los materiales publicados de manera pública del proyecto learnk8s, diseñado para el aprendizaje de Kubernetes para empresas y administradores individuales. En él, Daniele Polencic, líder del proyecto, comparte una guía visual sobre qué pasos seguir cuando surgen problemas comunes en aplicaciones ejecutadas en un clúster de K8s.

Guía visual para la solución de problemas en Kubernetes

TL;DR: aquí hay un esquema que te ayudará a depurar el despliegue en Kubernetes:

Guía visual para la solución de problemas en Kubernetes

Diagrama para identificar y solucionar errores en el clúster. En inglés, está disponible en PDF y como imagen.

Al desplegar una aplicación en Kubernetes, normalmente es necesario definir tres componentes:

  • Deployment — es una especie de receta para crear copias de la aplicación, llamadas pods;
  • Servicio — un balanceador de carga interno que distribuye el tráfico entre los pods;
  • Ingress — una descripción de cómo el tráfico llegará al Service desde el mundo exterior.

Aquí hay un breve resumen gráfico:

1) En Kubernetes, las aplicaciones reciben tráfico del mundo exterior a través de dos capas de balanceadores de carga: interno y externo.

Guía visual para la solución de problemas en Kubernetes

2) El balanceador interno se llama Service, y el externo – Ingress.

Guía visual para la solución de problemas en Kubernetes

3) Deployment crea pods y los supervisa (no se crean manualmente).

Guía visual para la solución de problemas en Kubernetes

Supongamos que quieres desplegar una simple aplicación al estilo de Hello World. La configuración YAML para ella se verá de la siguiente manera:

apiVersion: apps/v1
kind: Deployment # <<<
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
      labels:
        any-name: my-app
    spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service # <<<
metadata:
  name: my-service
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    name: app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress # <<<
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
        serviceName: app
        servicePort: 80
      path: /

La definición es bastante larga, y es fácil confundirse sobre cómo están relacionados los componentes entre sí.

Por ejemplo:

  • ¿Cuándo se debe usar el puerto 80 y cuándo el 8080?
  • ¿Se debe crear un nuevo puerto para cada servicio para que no haya conflictos?
  • ¿Importan los nombres de las etiquetas? ¿Deben ser los mismos en todos lados?

Antes de centrarnos en la depuración, recordemos cómo están relacionados los tres componentes. Comencemos con Deployment y Service.

Relación entre Deployment y Service

Te sorprenderá, pero los Deployment y los Service no están relacionados de ninguna manera. En cambio, el Service apunta directamente a los Pods eludiendo al Deployment.

Por lo tanto, nos interesa cómo se relacionan los Pods y los Services. Hay que recordar tres cosas:

  1. El selector (selector) del Service debe coincidir con al menos una etiqueta del Pod.
  2. targetPort debe coincidir con containerPort del contenedor dentro del Pod.
  3. port El Service puede ser cualquiera. Diferentes servicios pueden usar el mismo puerto, ya que tienen direcciones IP diferentes.

El siguiente esquema presenta todo lo anterior en forma gráfica:

1) Supongamos que el servicio dirige el tráfico a un pod:

Guía visual para la solución de problemas en Kubernetes

2) Al crear el pod, es necesario especificar containerPort para cada contenedor en los pods:

Guía visual para la solución de problemas en Kubernetes

3) Al crear el servicio, es necesario indicar port y targetPort. ¿Pero a través de cuál de ellos se establece la conexión con el contenedor?

Guía visual para la solución de problemas en Kubernetes

4) A través de targetPort. Debe coincidir con containerPort.

Guía visual para la solución de problemas en Kubernetes

5) Supongamos que el puerto 3000 está abierto en el contenedor. Entonces el valor targetPort debe ser el mismo.

Guía visual para la solución de problemas en Kubernetes

En el archivo YAML, las etiquetas y ports / targetPort deben coincidir:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
     labels:  # <<<
        any-name: my-app  # <<<
   spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
       - containerPort: 8080  # <<<
---
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - port: 80
   targetPort: 8080  # <<<
 selector:  # <<<
    any-name: my-app  # <<<

¿Qué pasa con la etiqueta track: canary en la parte superior de la sección Deployment? ¿Debería coincidir?

Esta etiqueta se refiere al despliegue y no es utilizada por el servicio para enrutar el tráfico. En otras palabras, se puede eliminar o asignar un valor diferente.

¿Y qué hay del selector matchLabels?

Siempre debe coincidir con las etiquetas del Pod, ya que es utilizado por el Deployment para rastrear los pods.

Supongamos que has hecho las correcciones correctas. ¿Cómo verificarlas?

Puedes verificar la etiqueta de los pods con el siguiente comando:

kubectl get pods --show-labels

O, si los pods pertenecen a varias aplicaciones:

kubectl get pods --selector any-name=my-app --show-labels

Donde any-name=my-app — esta es la etiqueta any-name: my-app.

¿Sigues teniendo problemas?

¡Puedes conectarte al pod! Para hacer esto, debes usar el comando port-forward en kubectl. Permite conectarse al servicio y verificar la conexión.

kubectl port-forward service/<nombre del servicio> 3000:80

Aquí:

  • service/<nombre del servicio> — es el nombre del servicio; en nuestro caso es my-service;
  • 3000 — el puerto que se debe abrir en la computadora;
  • 80 — el puerto especificado en el campo port del servicio.

Si se ha podido establecer la conexión, significa que la configuración es correcta.

Si no se ha podido establecer la conexión, entonces el problema está en las etiquetas o los puertos no coinciden.

Conexión entre el Servicio y el Ingress

El siguiente paso para asegurar el acceso a la aplicación está relacionado con la configuración del Ingress. El Ingress debe saber cómo encontrar el servicio, luego localizar los pods y dirigir el tráfico hacia ellos. El Ingress encuentra el servicio correcto por su nombre y puerto abierto.

En la descripción del Ingress y el Servicio deben coincidir dos parámetros:

  1. servicePort en el Ingress debe coincidir con el parámetro port en el Servicio;
  2. serviceName en el Ingress debe coincidir con el campo name en el Servicio.

El siguiente esquema resume la conexión de puertos:

1) Como ya saben, el Servicio escucha un port:

Guía visual para la solución de problemas en Kubernetes

2) El Ingress tiene un parámetro llamado servicePort:

Guía visual para la solución de problemas en Kubernetes

3) Este parámetro (servicePort) siempre debe coincidir con port en la definición del Servicio:

Guía visual para la solución de problemas en Kubernetes

4) Si en el Servicio se establece el puerto 80, entonces es necesario que servicePort también sea igual a 80:

Guía visual para la solución de problemas en Kubernetes

En la práctica, se debe prestar atención a las siguientes líneas:

apiVersion: v1
kind: Service
metadata:
 name: my-service  # <<<
spec:
  ports:
 - port: 80  # <<<
   targetPort: 8080
  selector:
    any-name: my-app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
       serviceName: my-service  # <<<
       servicePort: 80  # <<<
     path: /

¿Cómo verificar si el Ingress está funcionando?

Se puede utilizar el método con kubectl port-forward, pero en lugar del servicio, debe conectarse al controlador de Ingress.

Primero, debe averiguar el nombre del pod con el controlador de Ingress:

kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

Encuentre el pod del Ingress (puede pertenecer a otro espacio de nombres) y ejecute el comando describe, para conocer los números de los puertos:

kubectl describe pod nginx-ingress-controller-6fc5bcc 
--namespace kube-system 
 | grep Ports
Ports:         80/TCP, 443/TCP, 18080/TCP

Finalmente, conéctese al pod:

kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

Ahora, cada vez que envíe una solicitud al puerto 3000 en su computadora, será redirigido al puerto 80 del pod con el controlador de Ingress. Al ir a http://localhost:3000, debería ver la página creada por la aplicación.

Resumen sobre los puertos

Recordemos nuevamente qué puertos y etiquetas deben coincidir:

  1. El selector en la definición del Servicio debe coincidir con la etiqueta del pod;
  2. targetPort la definición del Service debe coincidir con containerPort el contenedor dentro del pod;
  3. port en la definición del Service puede ser cualquier. Diferentes servicios pueden usar el mismo puerto, ya que tienen diferentes direcciones IP;
  4. servicePort el Ingress debe coincidir con port en la definición del Service;
  5. El nombre del servicio debe coincidir con el campo serviceName en el Ingress.

Desafortunadamente, no es suficiente saber cómo estructurar correctamente la configuración YAML.

¿Qué sucede cuando algo sale mal?

Tal vez el pod no se inicia o se cae.

3 pasos para diagnosticar fallos en aplicaciones en Kubernetes

Antes de comenzar a depurar el deployment, es necesario tener una buena comprensión de cómo funciona Kubernetes.

Dado que en cada aplicación desplegada en K8s hay tres componentes, la depuración de estos debe hacerse en un orden específico, comenzando desde el fondo.

  1. Primero hay que asegurarse de que los pods están funcionando, luego...
  2. Verificar si el servicio está proporcionando tráfico a los pods, y después...
  3. Comprobar si el Ingress está configurado correctamente.

Representación visual:

1) Comienza a buscar problemas desde la base. Primero verifica que los pods tengan el estado Listo y En ejecución:

Guía visual para la solución de problemas en Kubernetes

2) Si los pods están listos (Listo), hay que averiguar si el servicio distribuye el tráfico entre los pods:

Guía visual para la solución de problemas en Kubernetes

3) Finalmente, es necesario analizar la conexión entre el servicio y el Ingress:

Guía visual para la solución de problemas en Kubernetes

1. Diagnóstico de pods

En la mayoría de los casos, el problema está relacionado con el pod. Asegúrate de que los pods aparezcan como Listo y En ejecución. Esto se puede verificar con el comando:

kubectl get pods
NOMBRE                    LISTO ESTADO            REINICIOS  EDAD
app1                    0/1   ImagePullBackOff  0         47h
app2                    0/1   Error             0         47h
app3-76f9fcd46b-xbv4k   1/1   En ejecución       1         47h

En la salida del comando anterior, el último pod aparece como En ejecución y Listo, sin embargo, para los otros dos no es así.

¿Cómo entender qué salió mal?

Hay cuatro comandos útiles para diagnosticar pods:

  1. kubectl logs permite extraer registros de los contenedores en el pod;
  2. kubectl describe pod permite ver la lista de eventos relacionados con el pod;
  3. kubectl get pod permite obtener la configuración YAML del pod almacenada en Kubernetes;
  4. kubectl exec -ti bash permite iniciar un shell interactivo en uno de los contenedores del pod.

¿Cuál elegir?

La cuestión es que no hay un comando universal. Deben usarse en combinación.

Problemas típicos de los pods

Existen dos tipos principales de errores en los pods: errores durante el inicio (startup) y errores durante la ejecución (runtime).

Errores de inicio:

  • ImagePullBackoff
  • ImageInspectError
  • ErrImagePull
  • ErrImageNeverPull
  • Registro no disponible
  • Nombre de imagen no válido

Errores de ejecución:

  • CrashLoopBackOff
  • RunContainerError
  • KillContainerError
  • VerifyNonRootError
  • RunInitContainerError
  • CreatePodSandboxError
  • ConfigPodSandboxError
  • KillPodSandboxError
  • SetupNetworkError
  • TeardownNetworkError

Algunos errores ocurren con más frecuencia que otros. Aquí hay algunos de los errores más comunes y cómo solucionarlos.

ImagePullBackOff

Este error aparece cuando Kubernetes no puede obtener la imagen para uno de los contenedores del pod. Aquí están las tres razones más comunes para esto:

  1. El nombre de la imagen está mal especificado, por ejemplo, si cometiste un error o la imagen no existe;
  2. Se especificó una etiqueta inexistente para la imagen;
  3. La imagen está almacenada en un registro privado y Kubernetes no tiene permisos para acceder a ella.

Las dos primeras razones son fáciles de solucionar: solo necesitas corregir el nombre de la imagen y la etiqueta. En el caso de la última, es necesario proporcionar credenciales para el registro privado en Secret y agregar referencias a él en los pods. En la documentación de Kubernetes hay un ejemplo de cómo hacerlo.

CrashLoopBackOff

Kubernetes emite un error CrashLoopBackOff, si no se puede iniciar el contenedor. Esto suele ocurrir cuando:

  1. Hay un error en la aplicación que impide su inicio;
  2. Contenedor está configurado incorrectamente;
  3. La prueba de Liveness ha fallado demasiadas veces.

Es necesario intentar acceder a los registros del contenedor para averiguar la causa de su fallo. Si acceder a los registros es difícil, ya que el contenedor se reinicia demasiado rápido, puedes usar el siguiente comando:

kubectl logs  --previous

Este comando muestra los mensajes de error de la reincarnación anterior del contenedor.

RunContainerError

Este error ocurre cuando el contenedor no puede iniciarse. Se refiere al momento antes de que la aplicación inicie. Normalmente, su causa es una configuración incorrecta, como:

  • intentar montar un volumen inexistente, como ConfigMap o Secrets;
  • intentar montar un volumen de tipo de solo lectura como de lectura-escritura.

Para analizar este tipo de errores, el siguiente comando es útil kubectl describe pod.

Los pods están en estado Pending

Después de crear, el pod permanece en estado Pending.

¿Por qué ocurre esto?

Aquí están las posibles razones (asumiendo que el programador está funcionando correctamente):

  1. No hay suficientes recursos en el clúster, como potencia de cómputo y memoria, para iniciar el pod.
  2. En el espacio de nombres correspondiente, se ha establecido un objeto ResourceQuota La creación del pod llevará a que el espacio de nombres supere la cuota.
  3. Pod está en estado Pendiente PersistentVolumeClaim.

En este caso, se recomienda utilizar el comando kubectl describe y verificar la sección Eventos:

kubectl describe pod

En caso de errores relacionados con ResourceQuotas, se recomienda revisar los registros del clúster utilizando el comando

kubectl get events --sort-by=.metadata.creationTimestamp

Los pods no están en estado Ready

Si el pod aparece como En ejecución, pero no está en estado Listo, significa que la verificación de su disponibilidad (readiness probe) está fallando.

Cuando esto sucede, el pod no se conecta al servicio y no recibe tráfico. La falla en la prueba de disponibilidad es causada por problemas en la aplicación. En este caso, para encontrar el error, es necesario analizar la sección Eventos en la salida del comando kubectl describe.

2. Diagnóstico de servicios

Si los pods aparecen como En ejecución y Listo, pero aún no hay respuesta de la aplicación, se deben verificar las configuraciones del servicio.

Los servicios se encargan de enrutar el tráfico a los pods según sus etiquetas. Por lo tanto, lo primero que hay que hacer es verificar cuántos pods están trabajando con el servicio. Para ello, se pueden verificar los puntos finales en el servicio:

kubectl describe service  | grep Endpoints

El punto final es un par de valores de la forma <IP-адрес:порт>, y en la salida debe haber al menos un par de este tipo (es decir, al menos un pod está trabajando con el servicio).

Si la sección Endpoints está vacía, pueden haber dos opciones:

  1. no hay ningún pod con la etiqueta correcta (sugerencia: verifica si se eligió correctamente el namespace);
  2. hay un error en las etiquetas del servicio en el selector.

Si ves una lista de puntos finales, pero aún no puedes acceder a la aplicación, lo más probable es que haya un error en targetPort la descripción del servicio.

¿Cómo verificar la funcionalidad del servicio?

Independientemente del tipo de servicio, se puede utilizar el comando kubectl port-forward para conectarse a él:

kubectl port-forward service/ 3000:80

Aquí:

  • <service-name> — nombre del servicio;
  • 3000 — puerto que abres en tu computadora;
  • 80 — puerto en el lado del servicio.

3. Diagnóstico de Ingress

Si has llegado hasta aquí, entonces:

  • los pods aparecen como En ejecución y Listo;
  • el servicio distribuye correctamente el tráfico entre los pods.

Sin embargo, todavía no puedes "alcanzar" la aplicación.

Esto significa que es probable que el controlador Ingress esté configurado incorrectamente. Dado que el controlador Ingress es un componente externo en el clúster, existen varios métodos de depuración dependiendo de su tipo.

Pero antes de recurrir a herramientas especiales para configurar Ingress, se puede hacer algo bastante simple. Ingress utiliza serviceName y servicePort para conectarse al servicio. Es necesario verificar si están correctamente configurados. Esto se puede hacer con el comando:

kubectl describe ingress

Si la columna Backend está vacía, es muy probable que haya un error en la configuración. Si los backends están presentes pero aún no se puede acceder a la aplicación, el problema puede estar relacionado con:

  • la configuración de accesibilidad del Ingress desde Internet público;
  • la configuración de accesibilidad del clúster desde Internet público.

Identificar problemas con la infraestructura es posible conectándose directamente al pod del Ingress. Para ello, primero localice el pod del controlador Ingress (puede estar en otro espacio de nombres):

kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

Utilice el comando describe, para establecer el puerto:

kubectl describe pod nginx-ingress-controller-6fc5bcc
--namespace kube-system 
 | grep Ports

Finalmente, conéctese al pod:

kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

Ahora todas las solicitudes al puerto 3000 en la computadora se redirigirán al puerto 80 del pod.

¿Está funcionando ahora?

  • Si es así, el problema está en la infraestructura. Es necesario averiguar cómo se está realizando el enrutamiento del tráfico en el clúster.
  • Si no, el problema está en el controlador Ingress.

Si no se puede hacer funcionar el controlador Ingress, será necesario depurarlo.

Existen muchas variedades de controladores Ingress. Los más populares son Nginx, HAProxy, Traefik, entre otros. (para más detalles sobre las soluciones existentes, consulte nuestro resumen — nota del traductor) Se debe utilizar la guía de solución de problemas en la documentación del controlador correspondiente. Dado que Ingress Nginx es el controlador Ingress más popular, hemos incluido en el artículo algunos consejos para resolver problemas relacionados.

Depuración del controlador Ingress Nginx

El proyecto Ingress-nginx tiene un oficial complemento para kubectl. El comando kubectl ingress-nginx se puede usar para:

  • analizar registros, backends, certificados, etc.;
  • conectarse a Ingress;
  • estudiar la configuración actual.

Las siguientes tres comandos te ayudarán:

  • kubectl ingress-nginx lint — verifica nginx.conf;
  • kubectl ingress-nginx backend — explora el backend (de manera similar a kubectl describe ingress);
  • kubectl ingress-nginx logs — verifica los registros.

Ten en cuenta: en algunos casos puede ser necesario especificar el espacio de nombres correcto para el controlador Ingress utilizando la opción --namespace.

Currículum

La diagnóstico en Kubernetes puede ser una tarea complicada si no sabes por dónde comenzar. Siempre se debe abordar el problema desde el enfoque 'de abajo hacia arriba': comienza con los pods y luego avanza al servicio y al Ingress. Los métodos de depuración descritos en este artículo también pueden aplicarse a otros objetos, tales como:

  • jobs y cronjobs que no funcionan;
  • StatefulSets y DaemonSets.

Agradezco Gergely Risko, Daniel Weibel y Charles Christyraj por sus valiosos comentarios y adiciones.

P.D. del traductor

También puedes leer en nuestro blog:

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster