Nota perev. : Este artículo es parte de los materiales disponibles gratuitamente del proyecto learnk8s , que enseña a las empresas y administradores individuales cómo trabajar con Kubernetes. En él, Daniele Polencic, el gerente del proyecto, comparte una instrucción clara sobre qué pasos tomar en caso de problemas generales para las aplicaciones que se ejecutan en el clúster K8.
TL; DR: aquí hay un diagrama que lo ayudará a depurar la implementación en Kubernetes:
Diagrama de flujo para encontrar y corregir errores en un clúster. En el original (en inglés) está disponible en PDF y como imagen .Al implementar una aplicación en Kubernetes, generalmente necesita definir tres componentes:
- La implementación es una receta para crear copias de una aplicación llamada pods;
- Servicio : un equilibrador de carga interno que distribuye el tráfico entre los pods;
- Ingreso : una descripción de cómo fluirá el tráfico del mundo exterior al Servicio.
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 equilibradores de carga: internos y externos.

2) El equilibrador interno se llama Servicio, el externo - Ingreso.

3) La implementación crea pods y los monitorea (no se crean manualmente).

Supongamos que desea implementar una aplicación simple a la
Hello World . La configuración de YAML para esto se verá así:
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 acerca de cómo se relacionan los componentes entre sí.
Por ejemplo:
- ¿Cuándo debería usar el puerto 80 y cuándo - 8080?
- ¿Debo crear un nuevo puerto para cada servicio para que no entren en conflicto?
- ¿Importan los nombres de las etiquetas? ¿Deberían ser iguales en todas partes?
Antes de centrarnos en la depuración, recordemos cómo los tres componentes están relacionados entre sí. Comencemos con la implementación y el servicio.
Implementación de conexión'a y Servicio'a
Se sorprenderá, pero las implementaciones y el servicio no están conectados de ninguna manera. En cambio, el Servicio apunta directamente a Pods sin pasar por la Implementación.
Por lo tanto, estamos interesados en cómo se relacionan los Pods y los Servicios entre sí. Tres cosas para recordar:
- Un
selector
servicio debe coincidir con al menos una etiqueta Pod. targetPort
debe coincidir con el containerPort
contenedor dentro del Pod.port
servicio port
puede ser cualquier cosa. Diferentes servicios pueden usar el mismo puerto porque tienen diferentes direcciones IP.
El siguiente diagrama representa todo lo anterior en forma gráfica:
1) Imagine que el servicio dirige el tráfico a un determinado pod:

2) Al crear un pod, debe especificar
containerPort
para cada contenedor en los pods:

3) Al crear el servicio, debe especificar
port
y
targetPort
.
¿Pero por cuál se conecta al contenedor?
4) A través de
targetPort
. Debe coincidir con
containerPort
.

5) Digamos que el puerto 3000 está abierto en el contenedor, entonces el valor
targetPort
debería ser el mismo.

En el archivo YAML, las etiquetas y los
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 track: canary
en la parte superior de la sección Implementación? ¿Debería coincidir?Esta etiqueta se refiere a la implementación y el servicio no la utiliza para enrutar el tráfico. En otras palabras, se puede eliminar o asignar un valor diferente.
¿Qué pasa con el selector de matchLabels
?Siempre debe coincidir con las etiquetas de Pod , ya que Deployment lo utiliza para rastrear pods.
Supongamos que hiciste las ediciones correctas. ¿Cómo verificarlos?Puede verificar la etiqueta del pod 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
es la etiqueta
any-name: my-app
.
¿Hay alguna dificultad?¡Puedes conectarte al pod! Para hacer esto, use el comando
port-forward
en kubectl. Le permite conectarse al servicio y verificar la conexión.
kubectl port-forward service/<service name> 3000:80
Aquí:
service/<service name>
- nombre del servicio; en nuestro caso es my-service
;- 3000: el puerto que desea abrir en la computadora;
- 80: puerto especificado en el campo de
port
del servicio.
Si pudo establecer una conexión, la configuración es correcta.
Si no se pudo establecer la conexión, entonces hay un problema con las etiquetas o los puertos no coinciden.
Conexión de servicio e ingreso
El siguiente paso para proporcionar acceso a la aplicación está relacionado con la configuración de Ingress. Ingress debe saber cómo encontrar el servicio, luego encontrar los pods y dirigir el tráfico hacia ellos. Ingress encuentra el servicio deseado por nombre y puerto abierto.
En la descripción de Ingress y Service, deben coincidir dos parámetros:
servicePort
in Ingress debe coincidir con el parámetro de port
en Service;serviceName
in Ingress debe coincidir con el campo de name
en Service.
El siguiente diagrama resume la conexión de los puertos:
1) Como ya sabe, el Servicio escucha en un determinado
port
:

2) Ingress tiene un parámetro llamado
servicePort
:

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

4) Si el puerto 80 se especifica en Servicio,
servicePort
también debe ser 80:

En la práctica, 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 Ingress está funcionando?Puede usar el método con
kubectl port-forward
, pero en lugar del servicio necesita conectarse al controlador Ingress.
Primero debe encontrar el nombre del pod con el controlador 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
Localice el pod de Ingress (puede referirse a un espacio de nombres diferente) y ejecute el comando
describe
para averiguar los números de puerto:
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 la computadora, será redirigida al puerto 80 del pod con el controlador Ingress. Al ir a
http: // localhost: 3000 , debería ver la página creada por la aplicación.
Resumen de puerto
Recordemos nuevamente qué puertos y etiquetas deben coincidir:
- El selector en la definición del Servicio debe coincidir con la etiqueta del pod;
targetPort
en la definición del Servicio debe coincidir con el containerPort
contenedor dentro del pod;port
en la definición de Servicio puede ser cualquier cosa. Diferentes servicios pueden usar el mismo puerto porque tienen diferentes direcciones IP;servicePort
Ingress debe coincidir con el port
en la definición del Servicio;- El nombre del servicio debe coincidir con el campo
serviceName
en Ingress.
Por desgracia, no es suficiente saber cómo estructurar adecuadamente su configuración YAML.
¿Qué pasa cuando algo sale mal?Quizás la cápsula no se inicia o se bloquea.
3 pasos para solucionar fallas de aplicaciones en Kubernetes
Antes de depurar una implementación, debe tener una buena comprensión de cómo funciona Kubernetes.
Dado que hay tres componentes en cada aplicación descargada a K8, se deben depurar en un cierto orden, comenzando desde abajo.
- Primero debes asegurarte de que las vainas funcionan, luego ...
- Compruebe si el servicio entrega tráfico a los pods y luego ...
- Compruebe si Ingress está configurado correctamente.
Presentación visual:
1) Iniciar la búsqueda de problemas debe ser desde abajo. Primero verifique que las vainas tengan estados de
Ready
y En
Running
:

2) Si los pods están
Ready
, debe averiguar si el servicio distribuye el tráfico entre los pods:

3) Finalmente, debe analizar la conexión entre el servicio e Ingress:

1. Diagnóstico de vainas
En la mayoría de los casos, el problema es con la cápsula. Asegúrese de que las vainas estén
Ready
y
Running
. Puede verificar esto usando el comando:
kubectl get pods NAME READY STATUS RESTARTS AGE app1 0/1 ImagePullBackOff 0 47h app2 0/1 Error 0 47h app3-76f9fcd46b-xbv4k 1/1 Running 1 47h
En el resultado del comando anterior, el último pod aparece como
Running
y
Ready
, pero para los otros dos no lo está.
¿Cómo entender lo que salió mal?Hay cuatro comandos útiles para diagnosticar pods:
kubectl logs < pod'>
permite extraer registros de contenedores en el pod;kubectl describe pod < pod'>
permite ver una lista de eventos asociados con el pod;kubectl get pod < pod'>
permite obtener la configuración YAML del kubectl get pod < pod'>
almacenado en Kubernetes;kubectl exec -ti < pod'> bash
permite ejecutar un shell de comando interactivo en uno de los contenedores de pod
¿Cuál elegir?El hecho es que no hay un equipo universal. Se debe usar una combinación de estos.
Problemas comunes de la cápsula
Hay dos tipos principales de errores de pod: errores de inicio y errores de tiempo de ejecución.
Errores de inicio:
ImagePullBackoff
ImageInspectError
ErrImagePull
ErrImageNeverPull
RegistryUnavailable
InvalidImageName
Errores de tiempo de ejecución:
CrashLoopBackOff
RunContainerError
KillContainerError
VerifyNonRootError
RunInitContainerError
CreatePodSandboxError
ConfigPodSandboxError
KillPodSandboxError
SetupNetworkError
TeardownNetworkError
Algunos errores son más comunes que otros. Aquí hay algunos errores comunes y cómo solucionarlos.
ImagePullBackOff
Este error aparece cuando Kubernetes no puede obtener una imagen para uno de los contenedores de pod. Estas son las tres razones más comunes para esto:
- El nombre de la imagen está incorrectamente especificado; por ejemplo, cometió un error o la imagen no existe;
- Se especifica una etiqueta inexistente para la imagen;
- La imagen se almacena en un registro privado y Kubernetes no tiene autoridad para acceder a ella.
Las dos primeras razones son fáciles de eliminar: solo arregle el nombre y la etiqueta de la imagen. En el caso de este último, debe ingresar las credenciales para el registro privado en Secret y agregar enlaces a él en pods. La documentación de Kubernetes
tiene un ejemplo de cómo se puede hacer esto.
CrashLoopBackOff
Kubenetes arrojará un error CrashLoopBackOff si el contenedor no puede iniciarse. Esto generalmente ocurre cuando:
- Hay un error en la aplicación que impide que se inicie;
- El contenedor está configurado incorrectamente ;
- La prueba de vitalidad falló demasiadas veces.
Debe intentar llegar a los registros desde el contenedor para averiguar el motivo de su falla. Si el acceso a los registros es difícil, porque el contenedor se reinicia demasiado rápido, puede usar el siguiente comando:
kubectl logs <pod-name> --previous
Muestra mensajes de error de una reencarnación de contenedor anterior.
RunContainerError
Este error ocurre cuando el contenedor no puede iniciarse. Corresponde al momento anterior al lanzamiento de la aplicación. Por lo general, su causa es una configuración incorrecta, por ejemplo:
- Intentar montar un volumen inexistente, como ConfigMap o Secrets;
- Intente montar un volumen de solo lectura como lectura-escritura.
El
kubectl describe pod <pod-name>
es muy adecuado para analizar tales errores.
Vainas pendientes
Después de la creación, la cápsula permanece en el estado
Pending
.
¿Por qué está pasando esto?Estas son las posibles razones (supongo que el planificador funciona bien):
- El clúster no tiene suficientes recursos, como potencia de procesamiento y memoria, para ejecutar el pod.
- El objeto
ResourceQuota
se instala en el espacio de nombres correspondiente y crear un pod hará que el espacio de nombres vaya más allá de la cuota. - Pod está vinculado a Pending
PersistentVolumeClaim
pendiente.
En este caso, se recomienda utilizar el comando
kubectl describe
y verificar la sección
Events
:
kubectl describe pod <pod name>
En caso de errores relacionados con
ResourceQuotas
, se recomienda ver los registros del clúster con el comando
kubectl get events --sort-by=.metadata.creationTimestamp
Vainas no listas
Si el pod aparece como En
Running
, pero no está en estado
Ready
, la
sonda de preparación no tiene éxito.
Cuando esto sucede, el pod no se conecta al servicio y el tráfico no fluye hacia él. La prueba de preparación falló debido a problemas de aplicación. En este caso, para encontrar el error, debe analizar la sección
Events
en la salida del comando
kubectl describe
.
2. Diagnóstico de los servicios.
Si los pods están listados como
Running
y
Ready
, pero aún no hay respuesta de la aplicación, debe verificar la configuración del servicio.
Los servicios están involucrados en el enrutamiento del tráfico a los pods según sus etiquetas. Por lo tanto, lo primero que debe hacer es verificar cuántas unidades funcionan con el servicio. Para hacer esto, puede verificar los puntos finales en el servicio:
kubectl describe service <service-name> | grep Endpoints
El punto final es un par de valores de la forma
<IP-:>
, y al menos uno de estos pares debe estar presente en la salida (es decir, al menos un pod funciona con el servicio).
Si la sección
Endpoins
vacía, hay dos opciones posibles:
- no hay pods con la etiqueta correcta (pista: compruebe si el espacio de nombres está seleccionado correctamente);
- Hay un error en las etiquetas de servicio en el selector.
Si ve una lista de puntos finales, pero aún no puede acceder a la aplicación, entonces el probable culpable es el error en
targetPort
en la descripción del servicio.
¿Cómo verificar la capacidad de servicio del servicio?Independientemente del tipo de servicio, puede usar el
kubectl port-forward
para conectarse a él:
kubectl port-forward service/<service-name> 3000:80
Aquí:
<service-name>
- el nombre del servicio;- 3000 - el puerto que abres en la computadora;
- 80 - puerto en el lado de servicio.
3. Diagnóstico de ingreso
Si lees hasta este lugar, entonces:
- los pods se enumeran como
Running
y Ready
; - El servicio distribuye con éxito el tráfico entre los pods.
Sin embargo, aún no puede "comunicarse" con la aplicación.
Esto significa que, muy probablemente, el controlador Ingress está configurado incorrectamente. Dado que el controlador Ingress es un componente de terceros en el clúster, existen varios métodos de depuración según su tipo.
Pero antes de recurrir a herramientas especiales para configurar Ingress, puede hacer algo muy simple. Ingress usa
serviceName
y
servicePort
para conectarse al servicio. Debe verificar que estén configurados correctamente. Puedes hacer esto usando el comando:
kubectl describe ingress <ingress-name>
Si la columna
Backend
está vacía, existe una alta probabilidad de un error de configuración. Si los backends están en su lugar, pero todavía no hay acceso a la aplicación, entonces el problema puede estar relacionado con:
- Configuración de accesibilidad de entrada desde Internet público;
- configuración de accesibilidad del clúster desde Internet público.
Puede identificar problemas de infraestructura conectándose directamente al pod de Ingress. Para hacer esto, primero encuentre el pod del controlador Ingress (puede estar en un espacio de nombres diferente):
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
Use el comando
describe
para configurar 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 para el puerto 3000 en la computadora serán redirigidas al puerto 80 pod.
¿Funciona ahora?- Si es así, entonces el problema es con la infraestructura. Es necesario averiguar exactamente cómo se enruta el tráfico al clúster.
- De lo contrario, el problema está en el controlador de Ingress.
Si no puede hacer que el controlador Ingress funcione, deberá depurarlo.
Hay muchas variedades de controladores de entrada. Los más populares son Nginx, HAProxy, Traefik, etc.
(para obtener más información sobre las soluciones existentes, consulte nuestra revisión , aprox. Transl.) Debe usar la guía de solución de problemas en la documentación del controlador correspondiente. Dado que
Ingress Nginx es el controlador de Ingress más popular, hemos incluido algunos consejos para resolver problemas relacionados en este artículo.
Depuración de un controlador Nginx de Ingress
El proyecto Ingress-nginx tiene un
complemento oficial
para kubectl . El
kubectl ingress-nginx
se puede usar para:
- análisis de registros, backends, certificados, etc.
- conexión a Ingress;
- estudiando la configuración actual.
Los siguientes tres equipos te ayudarán con esto:
kubectl ingress-nginx lint
- comprueba nginx.conf
;kubectl ingress-nginx backend
: examina el backend (similar a kubectl describe ingress <ingress-name>
);kubectl ingress-nginx logs
: comprueba los registros.
Tenga en cuenta que en algunos casos puede ser necesario especificar el espacio de nombres correcto para el controlador Ingress usando el
--namespace <name>
.
Resumen
Diagnosticar Kubernetes puede ser una tarea desalentadora si no sabes por dónde empezar. El problema siempre debe abordarse de acuerdo con el principio de abajo hacia arriba: comience con pods y luego vaya al servicio e Ingress. Los métodos de depuración descritos en el artículo se pueden aplicar a otros objetos, como:
- empleos inactivos y CronJobs;
- StatefulSets y DaemonSets.
Gracias a
Gergely Risko ,
Daniel Weibel y
Charles Christyraj por sus valiosos comentarios y adiciones.
PD del traductor
Lea también en nuestro blog: