Nota perev. : Este pequeño (¡pero espacioso!) Artículo escrito por Michael Hausenblas del equipo OpenShift de Red Hat nos gustó tanto que fue agregado a nuestra base de conocimiento interno de Kubernetes casi inmediatamente después de ser descubierto. Y dado que la información presentada en él obviamente será útil para la comunidad de TI de habla rusa en general, nos complace publicar su traducción.
Como habrás adivinado, el título de esta publicación es una referencia a la caricatura de Pixar de 1998 "A Bug's Life"
(en la taquilla rusa se llamaba "Adventures of Flick" o "The Life of an Insect" - aprox. Transl. ) , Y de hecho: entre la hormiga Kubernetes tiene mucho en común con los trabajadores y hogares. Examinaremos cuidadosamente el ciclo de vida completo del hogar desde un punto de vista práctico, en particular, las formas en que puede influir en el comportamiento al inicio y apagado, así como los enfoques correctos para verificar el estado de la aplicación.
Independientemente de si creó bajo usted mismo o, mejor, a través de un controlador como
Deployment ,
DaemonSet o
StatefulSet , under puede estar en una de las siguientes fases:
- Pendiente : el Servidor API creó un recurso de pod y lo guardó en etcd, pero aún no estaba planeado, y las imágenes de sus contenedores no se recibieron del registro;
- Running (funcionamiento): under fue asignado al nodo y kubelet creó todos los contenedores;
- Con éxito (completado con éxito): la operación de todos los contenedores de hogar se ha completado con éxito y no se reiniciarán;
- Falló : todos los contenedores en el hogar dejaron de funcionar y al menos uno de los contenedores falló;
- Desconocido : API Server no pudo consultar el estado del hogar, generalmente debido a un error al interactuar con kubelet .
Al ejecutar
kubectl get pod
, tenga en cuenta que la columna
STATUS
puede mostrar otros mensajes (excepto estos cinco), por ejemplo,
Init:0/1
o
CrashLoopBackOff
. Esto se debe a que la fase es solo una parte del estado general del hogar. Una buena manera de averiguar qué sucedió exactamente es ejecutar
kubectl describe pod/$PODNAME
y mirar la entrada del
kubectl describe pod/$PODNAME
Events:
continuación. Muestra una lista de acciones relevantes: que se recibió la imagen del contenedor, se planeó, que el contenedor se encuentra en un estado "
no saludable" .
Ahora eche un vistazo a un ejemplo específico del ciclo de vida de un hogar de principio a fin, como se muestra en el siguiente diagrama:

Que paso aqui Los pasos son los siguientes:
- Esto no se muestra en el diagrama, pero al principio se lanza un infracontenedor especial que configura los espacios de nombres a los que se unen los contenedores restantes.
- El primer contenedor definido por el usuario que se inicia es el contenedor init ; Se puede utilizar para tareas de inicialización.
- A continuación, el contenedor principal y el gancho posterior al inicio se lanzan simultáneamente; En nuestro caso, esto sucede después de 4 segundos. Los ganchos se definen para cada contenedor.
- Luego, en el séptimo segundo, entran en juego las pruebas de vida y preparación, nuevamente para cada contenedor.
- En el undécimo segundo, cuando se mata debajo, se activa un gancho previo a la parada y se mata el contenedor principal después de un período de gracia . Tenga en cuenta que, en realidad, el proceso de finalización del pod es algo más complicado.
¿Cómo llegué a la secuencia anterior y su sincronización? Para hacer esto, utilizamos la siguiente
Implementación , creada específicamente para rastrear el orden de los eventos (no es muy útil en sí misma):
kind: Deployment apiVersion: apps/v1beta1 metadata: name: loap spec: replicas: 1 template: metadata: labels: app: loap spec: initContainers: - name: init image: busybox command: ['sh', '-c', 'echo $(date +%s): INIT >> /loap/timing'] volumeMounts: - mountPath: /loap name: timing containers: - name: main image: busybox command: ['sh', '-c', 'echo $(date +%s): START >> /loap/timing; sleep 10; echo $(date +%s): END >> /loap/timing;'] volumeMounts: - mountPath: /loap name: timing livenessProbe: exec: command: ['sh', '-c', 'echo $(date +%s): LIVENESS >> /loap/timing'] readinessProbe: exec: command: ['sh', '-c', 'echo $(date +%s): READINESS >> /loap/timing'] lifecycle: postStart: exec: command: ['sh', '-c', 'echo $(date +%s): POST-START >> /loap/timing'] preStop: exec: command: ['sh', '-c', 'echo $(date +%s): PRE-HOOK >> /loap/timing'] volumes: - name: timing hostPath: path: /tmp/loap
Tenga en cuenta que para cerrar el pod con fuerza cuando el contenedor principal funcionaba, ejecuté el siguiente comando:
$ kubectl scale deployment loap --replicas=0
Observamos una secuencia específica de eventos en acción y ahora estamos listos para pasar a prácticas en el campo de la gestión del ciclo de vida del hogar. Son los siguientes:
- Use contenedores init para preparar el hogar para el funcionamiento normal. Por ejemplo, para obtener datos externos, crear tablas en la base de datos o esperar la disponibilidad del servicio del que depende. Si es necesario, puede crear muchos contenedores de inicio, y todos deben completarse con éxito antes de que se inicien los contenedores normales.
- Siempre agregue
livenessProbe
y readinessProbe
. El primero lo usa kubelet 'ohm para comprender si se debe reiniciar el contenedor y cuándo, y el despliegue ' ohm para decidir si la actualización continua fue exitosa. El segundo lo utiliza el servicio para decidir la dirección del tráfico hacia el sub. Si estas muestras no están definidas, kubelet para ambos supone que se completaron con éxito. Esto lleva a dos consecuencias: a) la política de reinicio no se puede aplicar, b) los contenedores en el hogar reciben instantáneamente el tráfico del servicio al que se enfrentan, e incluso si todavía están ocupados con el proceso de inicio. - Use ganchos para inicializar correctamente el contenedor y destruirlo por completo. Por ejemplo, esto es útil en el caso del funcionamiento de una aplicación cuyo código fuente no tiene acceso o no puede modificar, pero que requiere cierta inicialización o preparación para su finalización, por ejemplo, borrar las conexiones de la base de datos. Tenga en cuenta que al usar el servicio , apagar el Servidor API, el controlador de punto final y el proxy kube puede llevar algo de tiempo (por ejemplo, eliminar las entradas correspondientes de iptables). Por lo tanto, finalizar su trabajo puede afectar las solicitudes de solicitud. A menudo, para resolver este problema, un simple gancho con una llamada de suspensión es suficiente.
- Para las necesidades de depuración y para comprender en general por qué dejó de funcionar, la aplicación puede escribir en
/dev/termination-log
, y puede ver los mensajes usando kubectl describe pod …
Esta configuración predeterminada se cambia a través de terminationMessagePath
y / o usando terminationMessagePolicy
en la subespecificación; consulte la referencia de la API para obtener más detalles.
Esta publicación no trata sobre los
inicializadores (algunos detalles sobre ellos se pueden encontrar al final de este material - aprox. Transl. ) . Este es un concepto completamente nuevo introducido en Kubernetes 1.7. Los inicializadores funcionan dentro del plano de control (Servidor API) en lugar de estar en el contexto de
kubelet , y pueden usarse para enriquecer los hogares, por ejemplo, con contenedores de sidecar o aplicando políticas de seguridad. Además, no se consideraron
PodPresets , que en el futuro se pueden reemplazar por un concepto más flexible de inicializadores.
PD del traductor
Lea también en nuestro blog: