Versión imprimible multipagina. Haga click aquí para imprimir.
Pods
- 1: Pods
- 2: Ciclo de vida de un Pod
- 3: Contenedores de Inicialización
- 4: Interrupciones
- 5: Containers Efímeros
1 - Pods
Los Pods son las unidades de computación desplegables más pequeñas que se pueden crear y gestionar en Kubernetes.
¿Qué és un Pod?
Un Pod (como en una vaina de ballenas o vaina de guisantes) es un grupo de uno o más contenedores (como contenedores Docker), con almacenamiento/red compartidos, y unas especificaciones de cómo ejecutar los contenedores. Los contenidos de un Pod son siempre coubicados, coprogramados y ejecutados en un contexto compartido. Un Pod modela un "host lógico" específico de la aplicación: contiene uno o más contenedores de aplicaciones relativamente entrelazados. Antes de la llegada de los contenedores, ejecutarse en la misma máquina física o virtual significaba ser ejecutado en el mismo host lógico.
Mientras que Kubernetes soporta más runtimes de contenedores a parte de Docker, este último es el más conocido y ayuda a describir Pods en términos de Docker.
El contexto compartido de un Pod es un conjunto de namespaces de Linux, cgroups y, potencialmente, otras facetas de aislamiento, las mismas cosas que aíslan un contenedor Docker. Dentro del contexto de un Pod, las aplicaciones individuales pueden tener más subaislamientos aplicados.
Los contenedores dentro de un Pod comparten dirección IP y puerto, y pueden encontrarse a través de localhost
. También pueden comunicarse entre sí mediante comunicaciones estándar entre procesos, como semáforos de SystemV o la memoria compartida POSIX. Los contenedores en diferentes Pods tienen direcciones IP distintas y no pueden comunicarse por IPC sin configuración especial.
Estos contenedores normalmente se comunican entre sí a través de las direcciones IP del Pod.
Las aplicaciones dentro de un Pod también tienen acceso a volúmenes compartidos, que se definen como parte de un Pod y están disponibles para ser montados en el sistema de archivos de cada aplicación.
En términos de Docker, un Pod se modela como un grupo de contenedores de Docker con namespaces y volúmenes de sistemas de archivos compartidos.
Al igual que los contenedores de aplicaciones individuales, los Pods se consideran entidades relativamente efímeras (en lugar de duraderas). Como se explica en ciclo de vida del pod, los Pods se crean, se les asigna un identificador único (UID) y se planifican en nodos donde permanecen hasta su finalización (según la política de reinicio) o supresión. Si un nodo muere, los Pods programados para ese nodo se programan para su eliminación después de un período de tiempo de espera. Un Pod dado (definido por su UID) no se "replanifica" a un nuevo nodo; en su lugar, puede reemplazarse por un Pod idéntico, con incluso el mismo nombre si lo desea, pero con un nuevo UID (consulte controlador de replicación para obtener más detalles).
Cuando se dice que algo tiene la misma vida útil que un Pod, como un volumen, significa que existe mientras exista ese Pod (con ese UID). Si ese Pod se elimina por cualquier motivo, incluso si se crea un reemplazo idéntico, el recurso relacionado (por ejemplo, el volumen) también se destruye y se crea de nuevo.
Un Pod de múltiples contenedores que contiene un extractor de archivos y un servidor web que utiliza un volumen persistente para el almacenamiento compartido entre los contenedores.
Motivación para los Pods
Gestión
Los Pods son un modelo del patrón de múltiples procesos de cooperación que forman una unidad de servicio cohesiva. Simplifican la implementación y la administración de las aplicaciones proporcionando una abstracción de mayor nivel que el conjunto de las aplicaciones que lo constituyen. Los Pods sirven como unidad de despliegue, escalado horizontal y replicación. La colocación (coprogramación), el destino compartido (por ejemplo, la finalización), la replicación coordinada, el uso compartido de recursos y la gestión de dependencias se controlan automáticamente para los contenedores en un Pod.
Recursos compartidos y comunicación
Los Pods permiten el intercambio de datos y la comunicación entre los contenedores que lo constituyen.
Todas las aplicaciones en un Pod utilizan el mismo namespace de red (la misma IP y puerto) y, por lo tanto, pueden "encontrarse" entre sí y comunicarse utilizando localhost
.
Debido a esto, las aplicaciones en un Pod deben coordinar su uso de puertos. Cada Pod tiene una dirección IP en un espacio de red compartido que tiene comunicación completa con otros servidores físicos y Pods a través de la red.
Los contenedores dentro del Pod ven que el hostname del sistema es el mismo que el nombre
configurado para el Pod. Hay más información sobre esto en la sección networking.
Además de definir los contenedores de aplicaciones que se ejecutan en el Pod, el Pod especifica un conjunto de volúmenes de almacenamiento compartido. Los volúmenes permiten que los datos sobrevivan a reinicios de contenedores y se compartan entre las aplicaciones dentro del Pod.
Usos de Pods
Los Pods pueden ser usados para alojar pilas de aplicaciones integradas (por ejemplo, LAMP), pero su objetivo principal es apoyar los programas de ayuda coubicados y coadministrados, como:
- sistemas de gestión de contenido, loaders de datos y archivos, gestores de caché locales, etc.
- copia de seguridad de registro y punto de control, compresión, rotación, captura de imágenes, etc.
- observadores de cambio de datos, adaptadores de registro y monitoreo, publicadores de eventos, etc.
- proxies, bridges y adaptadores.
- controladores, configuradores y actualizadores.
Los Pods individuales no están diseñados para ejecutar varias instancias de la misma aplicación, en general.
Para una explicación más detallada, ver El sistema distribuido ToolKit: Patrones para Contenedores multiaplicación.
Alternativas
¿Por qué simplemente no ejecutar múltiples programas en un solo contenedor de Docker?
- Transparencia. Hacer visibles los contenedores dentro del Pod a la infraestructura permite que esta brinde servicios, como gestión de procesos y monitoreo de recursos, a los contenedores, facilitando una serie de comodidades a los usuarios.
- Desacople de dependencias de software. Los contenedores individuales pueden ser versionados, reconstruidos y redistribuidos independientemente. Kubernetes podría incluso apoyar actualizaciones en vivo de contenedores individuales en un futuro.
- Facilidad de uso. Los usuarios no necesitan ejecutar sus propios administradores de procesos, para propagación de señales, códigos de salida, etc.
- Eficiencia. Debido a que la infraestructura asume más responsabilidad, los contenedores pueden ser más livianos.
¿Por qué no admitir la planificación conjunta de contenedores por afinidad?
Ese enfoque proporcionaría la ubicación conjunta, pero no la mayor parte de beneficios de los Pods, como compartir recursos, IPC, compartir el destino garantizado y gestión simplificada.
Durabilidad de pods (o su ausencia)
Los Pods no están destinados a ser tratados como entidades duraderas. No sobrevivirán a errores de planificación, caídas de nodo u otros desalojos, ya sea por falta de recursos o en el caso de mantenimiento de nodos.
En general, los usuarios no deberían necesitar crear Pods directamente, deberían usar siempre controladores incluso para Pods individuales, como por ejemplo, los Deployments. Los controladores proporcionan autorecuperación con un alcance de clúster, así como replicación y gestión de despliegue. Otros controladores como los StatefulSet pueden tambien proporcionar soporte para Pods que necesiten persistir el estado.
El uso de API colectivas como la principal primitiva de cara al usuario es relativamente común entre los sistemas de planificación de clúster, incluyendo Borg, Marathon, Aurora, y Tupperware.
El Pod se expone como primitiva para facilitar:
- planificación y capacidad de conexión del controlador
- soporte para operaciones a nivel de Pod sin la necesidad de "proxy" a través de las API del controlador
- desacople de la vida útil del Pod de la vida útil del controlador, como para el arranque
- desacople de controladores y servicios, el endpoint del controlador solo mira Pods
- composición limpia de funcionalidad a nivel de Kubelet con funcionalidad a nivel de clúster, Kubelet es efectivamente el "controlador de Pod"
- aplicaciones en alta disponibilidad, que esperan que los Pods sean reemplazados antes de su finalización y ciertamente antes de su eliminación, como en el caso de desalojos planificados o descarga previa de imágenes.
Finalización de Pods
Debido a que los Pods representan procesos en ejecución en los nodos del clúster, es importante permitir que esos procesos finalicen de forma correcta cuando ya no se necesiten (en lugar de ser detenidos bruscamente con una señal de KILL). Los usuarios deben poder solicitar la eliminación y saber cuándo finalizan los procesos, pero también deben poder asegurarse de que las eliminaciones finalmente se completen. Cuando un usuario solicita la eliminación de un Pod, el sistema registra el período de gracia previsto antes de que el Pod pueda ser eliminado de forma forzada, y se envía una señal TERM al proceso principal en cada contenedor. Una vez que el período de gracia ha expirado, la señal KILL se envía a esos procesos y el Pod se elimina del servidor API. Si se reinicia Kubelet o el administrador de contenedores mientras se espera que finalicen los procesos, la terminación se volverá a intentar con el período de gracia completo.
Un ejemplo del ciclo de terminación de un Pod:
- El usuario envía un comando para eliminar Pod, con un período de gracia predeterminado (30s)
- El Pod en el servidor API se actualiza con el tiempo a partir del cual el Pod se considera "muerto" junto con el período de gracia.
- El Pod aparece como "Terminando" cuando aparece en los comandos del cliente
- (simultáneo con 3) Cuando el Kubelet ve que un Pod se ha marcado como terminado porque se ha configurado el tiempo en 2, comienza el proceso de apagado del Pod.
- Si uno de los contenedores del Pod ha definido un preStop hook, se invoca dentro del contenedor. Si el hook
preStop
todavía se está ejecutando después de que expire el período de gracia, el paso 2 se invoca con un pequeño período de gracia extendido (2s). - El contenedor recibe la señal TERM. Tenga en cuenta que no todos los contenedores en el Pod recibirán la señal TERM al mismo tiempo y cada uno puede requerir un hook
preStop
si el orden en el que se cierra es importante.
- Si uno de los contenedores del Pod ha definido un preStop hook, se invoca dentro del contenedor. Si el hook
- (simultáneo con 3) Pod se elimina de la lista de endponts del servicio, y ya no se considera parte del conjunto de Pods en ejecución para controladores de replicación. Los Pods que se apagan lentamente no pueden continuar sirviendo el tráfico ya que los balanceadores de carga (como el proxy de servicio) los eliminan de sus rotaciones.
- Cuando expira el período de gracia, todos los procesos que todavía se ejecutan en el Pod se eliminan con SIGKILL.
- El Kubelet terminará de eliminar el Pod en el servidor API configurando el período de gracia 0 (eliminación inmediata). El Pod desaparece de la API y ya no es visible desde el cliente.
Por defecto, todas las eliminaciones se realizan correctamente en 30 segundos. El comando kubectl delete
admite la opción--grace-period = <seconds>
que permite al usuario anular el valor predeterminado y especificar su propio valor. El valor 0
forzar eliminación del Pod.
Debe especificar un indicador adicional --force
junto con --grace-period = 0
para realizar eliminaciones forzadas.
Forzar destrucción de Pods
La eliminación forzada de un Pod se define como la eliminación de un Pod del estado del clúster y etcd inmediatamente. Cuando se realiza una eliminación forzada, el apiserver no espera la confirmación del kubelet de que el Pod ha finalizado en el nodo en el que se estaba ejecutando. Elimina el Pod en la API inmediatamente para que se pueda crear un nuevo Pod con el mismo nombre. En el nodo, los Pods que están configurados para terminar de inmediato recibirán un pequeño período de gracia antes de ser forzadas a matar.
Estas eliminaciones pueden ser potencialmente peligrosas para algunos Pods y deben realizarse con precaución. En el caso de Pods de StatefulSets, consulte la documentación de la tarea para eliminando Pods de un StatefulSet.
Modo privilegiado para Pods
Cualquier contenedor en un Pod puede habilitar el modo privilegiado, utilizando el indicador privilegiado
en el contexto de seguridad de la especificación del contenedor. Esto es útil para contenedores que desean usar capacidades de Linux como manipular la pila de red y acceder a dispositivos. Los procesos dentro del contenedor obtienen casi los mismos privilegios que están disponibles para los procesos fuera de un contenedor. Con el modo privilegiado, debería ser más fácil escribir complementos de red y volumen como Pods separados que no necesitan compilarse en el kubelet.
Nota:
El runtime de contenedores debe admitir el concepto de un contenedor privilegiado para que esta configuración sea relevante.API
Pod es un recurso de nivel superior en la API REST de Kubernetes. La definición de objeto de API Pod describe el objeto en detalle.
2 - Ciclo de vida de un Pod
Esta página describe el ciclo de vida de un Pod.
Los Pods siguen un ciclo de vida definido, comenzando en la fase Pending
,
y luego pasando a "en ejecución" Running
si al menos uno de sus contenedores primarios se
inicia correctamente,
y luego pasando a "exitoso" (Succeeded
) o "fallido" (Failed
) si uno de los contenedores de un Pod
termina en error.
Al igual que contenedores de aplicaciones, los Pods se consideran entidades relativamente efímeras. Los Pods se crean y se les asigna un identificador único (UID), y se programan para ejecutarse en nodos donde se mantienen hasta que se terminan (de acuerdo con las políticas de reinicio) o se eliminan.
Si un nodo muere, los Pods programados para ejecutarse en ese Nodo se programan para eliminarse. El plano de control marca los Pods para ser eliminados luego de un periodo de tiempo.
Ciclo de vida de un Pod
Mientras un Pod se está ejecutando, el kubelet puede reiniciar contenedores para manejar algunos tipos de fallos. Dentro de un Pod, Kubernetes rastrea distintos estados del contenedor y determina qué acción realizar para que el Pod esté sano nuevamente.
En la API de Kubernetes, los Pods tienen una especificación y un estatus actual. El estatus de un objeto Pod consiste en un conjunto de condiciones del Pod. También puedes inyectar información de readiness personalizada a los datos de condición de un Pod, si es útil para tu aplicación.
Los Pods solo se programan una vez en su ciclo de vida; asignar un Pod a un nodo específico se llama vincular (binding, en inglés), y el proceso de seleccionar cuál Pod usar se llama programar. Una vez que un Pod está vinculado a un nodo, Kubernetes intenta ejecutar el Pod en ese nodo. El Pod se ejecuta en ese nodo hasta que termina, o hasta que es terminado; si Kubernetes no es capaz de iniciar el Pod en el nodo seleccionado (por ejemplo, si el nodo falla antes que el Pod inicie), entonces ese Pod en particular nunca inicia.
Puedes usar readiness de programación del Pod para retrasar la programación de un Pod hasta que todas sus puertas de programación sean removidas. Por ejemplo, podrías querer definir un conjunto de Pods, pero solo lanzar la programación una vez que todos los Pods hayan sido creados.
Recuperación de fallos en los Pods
Si falla uno de los contenedores en el Pod, Kubernetes puede intentar reiniciar ese contenedor en específico. Para saber más, lea cómo los Pods manejan los errores del contenedor.
Sin embargo, los Pods pueden fallar de una manera que el clúster no puede recuperar, y en ese caso Kubernetes no intenta más sanar el Pod; en su lugar, Kubernetes elimina el Pod y confía en otros componentes para proporcionar una curación automática.
Si un Pod está programado para un nodo y ese nodo luego falla, el Pod se trata como no saludable y Kubernetes eventualmente elimina el Pod. Un Pod no sobrevivirá a una evacuación debido a la falta de recursos o al mantenimiento del Nodo.
Kubernetes utiliza una abstracción de nivel superior, llamada controlador, que maneja el trabajo de gestionar las instancias de Pods relativamente desechables.
Un Pod dado (como se define por un UID) nunca es "reprogramado" a un nodo diferente; en su lugar,
ese Pod puede ser reemplazado por un nuevo Pod casi idéntico.
Si hace un Pod de reemplazo, incluso puede
tener el mismo nombre (como en .metadata.name
) que tenía el Pod antiguo, pero el reemplazo
tendría un .metadata.uid
diferente del Pod antiguo.
Kubernetes no garantiza que un reemplazo de un Pod existente sea programado en el mismo nodo en el que el antiguo Pod estaba siendo reemplazado.
Ciclo de vida asociados
Cuando se dice que algo tiene la misma vida útil que un Pod, como un volúmen, eso significa que el objeto existe mientras ese Pod específico (con ese UID exacto) exista. Si ese Pod se elimina por cualquier razón, e incluso si se crea un reemplazo idéntico, el objeto relacionado (un volumen, en este ejemplo) también se destruye y se crea nuevamente.
Fase del Pod
El campo status
de un Pod es un objeto
PodStatus
de Kubernetes que tiene un campo phase
.
La fase de un Pod es un resumen simple y de alto nivel de dónde se encuentra el Pod en su ciclo de vida. La fase no pretende ser un resumen completo de observaciones del estado del contenedor o Pod, ni tampoco pretende ser una máquina de estado completa.
El número y los significados de los valores de fase de un Pod están
estrechamente guardados.
Aparte de lo que se documenta aquí, no se debe asumir nada acerca de los Pods
que tienen un valor de phase
determinado.
Aquí están los posibles valores de phase
:
Valor | Descripción |
---|---|
Pending |
El clúster de Kubernetes aceptó el Pod, pero uno o más contenedores no se configuraron ni prepararon para ejecutarse. Esto incluye el tiempo que pasa un Pod esperando ser programado, así como el tiempo dedicado a descargar imágenes de contenedores a través de la red. |
Running |
El Pod se vinculó a un nodo y se crearon todos los contenedores. Al menos un contenedor todavía se está ejecutando o está en proceso de iniciarse o reiniciarse. |
Succeeded |
Todos los contenedores del Pod finalizaron con éxito y no se reiniciarán. |
Failed |
Todos los contenedores del Pod han finalizado y al menos un contenedor ha finalizado con error. Es decir, el contenedor salió con un estado distinto de cero o el sistema lo canceló. |
Unknown |
Por alguna razón no se pudo obtener el estado del Pod. Esta fase generalmente ocurre debido a un error en la comunicación con el nodo donde debería ejecutarse el Pod. |
Nota:
Cuando se está borrando un Pod, se muestra comoTerminating
por algunos
comandos de kubectl.
Este estado Terminating
no es una de las fases del Pod.
A un Pod se le garantiza un tiempo para terminar con gracia,
cuyo valor por defecto es 30 segundos.
Puedes utilizar el flag --force
para terminar un Pod por la fuerza.A partir de la versión 1.27 de Kubernetes, el kubelet aplica una transición de
los Pods borrados, excepto
por Pods estáticos
y Pods borrados por la fuerza sin un finalizador, a
una fase terminal
(Failed
o Succeeded
dependiendo de los códigos de salida de los contenedores
del Pod) antes de su eliminación del servidor API.
Si un Nodo muere o se desconecta del resto del clúster,
Kubernetes aplica una política para establecer la phase
de todos los Pods
en Failed
.
Estados del contenedor
Así como la fase del Pod en general, Kubernetes rastrea el estado de cada contenedor dentro de un Pod. Puedes usar hooks del ciclo de vida de un contenedor para lanzar eventos en ciertos puntos en el ciclo de vida de un contenedor.
Una vez que el programador
asigna un Pod a un Nodo,
el kubelet inicia creando los contenedores para ese Pod usando un
runtime del contenedor.
Hay 3 estados posibles para un contenedor: Waiting
(esperando), Running
(en ejecución), y Terminated
(terminado).
Para revisar el estado de los contenedores de un Pod,
puedes usar kubectl describe pod <name-of-pod>
.
La salida muestra el estado de cada contenedor dentro del Pod.
Cada estado tiene un significado específico:
Waiting
Si un contenedor no está en el estado Running
o Terminated
, está Waiting
.
Un contenedor en el estado Waiting
aún está ejecutando las operaciones que
requiere para completar su arranque:
por ejemplo,
descargar la imagen del contenedor de un registro de imágenes de un contenedor,
o aplicando datos secretos.
Running
El estado Running
indica que el contenedor se está ejecutando sin problemas.
Si hay un hook postStart
configurado, ya se ha ejecutado y finalizado.
Cuando utilizas el comando kubectl
para consultar un Pod con un contenedor que
está Running
,
también puedes ver información sobre cuando el contenedor entró en
estado Running
.
Terminated
Un contenedor en el estado Terminated
comenzó su ejecución y luego se terminó
con éxito o falló por alguna razón.
Cuando usas kubectl
para consultar un Pod con un contenedor que
está Terminated
, puedes ver un motivo, y un código de salida, y la hora de
inicio y de finalización del contenedor.
Si un contenedor tiene un hook preStop
configurado, el hook se ejecuta antes
de que el contenedor entre en estado Terminated
.
Cómo los Pods manejan los problemas con los contenedores
Kubernetes maneja los fallos de los contenedores dentro de los Pods usando una política de reinicio, restartPolicy
en inglés definida en la especificación spec
del Pod.
Esta política determina cómo reacciona Kubernetes cuando los contenedores salen debido a errores u otras razones, que sigue la siguiente secuencia:
-
Fallo inicial: Kubernetes intenta un reinicio inmediato basado en la
restartPolicy
del Pod. -
Fallos repetidos: Después del fallo inicial, Kubernetes aplica un retraso exponencial para los reinicios subsiguientes, descrito en restartPolicy. Esto evita que los intentos de reinicio rápidos y repetidos sobrecarguen el sistema.
-
Estado de CrashLoopBackOff: Esto indica que el mecanismo de retraso exponencial está actualmente en efecto para un contenedor dado que está en un bucle de fallos, fallando y reiniciando repetidamente.
-
Reinicio del retraso: Si un contenedor funciona correctamente durante un cierto período (por ejemplo, 10 minutos), Kubernetes reinicia el retraso, tratando cualquier nuevo fallo como el primero.
-
En la práctica, un
CrashLoopBackOff
es una condición o evento que podría verse como salida del comandokubectl
, al describir o listar Pods, cuando un contenedor en el Pod no arranca correctamente y luego intenta y falla continuamente en un bucle.
En otras palabras, cuando un contenedor entra en el bucle de fallos, Kubernetes aplica el retraso exponencial mencionado en la Política de reinicio del contenedor. Este mecanismo evita que un contenedor defectuoso sobrecargue el sistema con intentos de inicio fallidos continuos.
El CrashLoopBackOff
puede ser causado por problemas como los siguientes:
- Errores de la aplicación que hacen que el contenedor salga.
- Errores de configuración, como variables de entorno incorrectas o archivos de configuración faltantes.
- Restricciones de recursos, donde el contenedor puede no tener suficiente memoria o CPU para arrancar correctamente.
- Fallos en los chequeos de salud si la aplicación no comienza a servir dentro del tiempo esperado.
- Las sondas de liveness o de arranque del contenedor devuelven un resultado de
Failure
como se menciona en la sección de sondas. Para investigar la causa raíz de un problema deCrashLoopBackOff
, un usuario puede:
- Revisar los registros: Use
kubectl logs <nombre-del-pod>
para revisar los registros del contenedor. Esta es a menudo la forma más directa de diagnosticar el problema que causa los fallos. - Inspeccionar eventos: Use
kubectl describe pod <nombre-del-pod>
para ver eventos para el Pod, lo que puede proporcionar pistas sobre problemas de configuración o recursos. - Revisar la configuración: Asegúrese de que la configuración del Pod, incluidas las variables de entorno y los volúmenes montados, sea correcta y que todos los recursos externos necesarios estén disponibles.
- Verificar los límites de recursos: Asegúrese de que el contenedor tenga suficiente CPU y memoria asignada. A veces, aumentar los recursos en la definición del Pod puede resolver el problema.
- Depurar la aplicación: Pueden existir errores o configuraciones incorrectas en el código de la aplicación. Ejecutar esta imagen de contenedor localmente o en un entorno de desarrollo puede ayudar a diagnosticar problemas específicos de la aplicación.
Política de reinicio del contenedor
La especificación (spec
en inglés) de un Pod tiene un campo restartPolicy
con los posibles
valores Always
, OnFailure
, y Never
.
El valor por defecto es Always
.
La política de reinicio (restartPolicy
en inglés) para un Pod aplica a
contenedores de apps en el Pod
para contenedores de inicialización regulares.
Los contenedores sidecar
ignoran el campo restartPolicy
: en Kubernetes, un sidecar se define como una
entrada dentro de initContainers
que tiene su restartPolicy
a nivel del contenedor
establecido en Always
. Para contenedores de inicio que finalizan con un error, el kubelet reinicia el
contenedor de inicio si el nivel del Pod restartPolicy
es OnFailure
o Always
:
Always
: Automáticamente reinicia el contenedor luego de alguna terminación.OnFailure
: Solo reinicia el contenedor si finaliza con un error (estado de salida distinto de cero).Never
: No reinicia el contenedor automáticamente.
Cuando el kubelet está manejando el contenedor, se reinicia de acuerdo con la política de reinicio configurada, que solo se aplica a los reinicios que realizan contenedores de
reemplazo dentro del
mismo Pod y ejecutándose en el mismo nodo.
Después de que los contenedores en un Pod terminan, el kubelet
los reinicia con un retraso de retroceso exponencial (10s, 20s, 40s,...), que
está limitado a
cinco minutos. Una vez que un contenedor se ha ejecutado durante 10 minutos sin
ningún problema, el
kubelet restablece el temporizador de reinicio para ese contenedor.
Ciclo de vida de contenedores Sidecar y el Pod
explica el comportamiento de init containers
cuando
especifica una restartPolicy
.
Condiciones del Pod
Un Pod tiene un PodStatus
, que tiene un listado de
PodConditions
a través de los cuales el Pod ha pasado o no.
El kubelet administra las siguientes condiciones del Pod:
PodScheduled
: El Pod está programado para un nodo.PodReadyToStartContainers
: (característica beta; habilitada por defecto) La zona de pruebas del Pod se creó correctamente y se configuró la red.ContainersReady
: todos los contenedores en el Pod están listos.Initialized
: todos los contenedores de inicio han terminado exitosamente.Ready
: el Pod es capaz de recibir peticiones y debería ser agregado a los grupos de equilibrio de carga de todos los Services que coincidan.
Nombre del campo | Descripción |
---|---|
type |
Nombre de esta condición del Pod. |
status |
Indica si la condición es aplicable, con valores posibles "True ", "False ", ó "Unknown ". |
lastProbeTime |
Marca de tiempo de cuando se probó por última vez la condición del Pod. |
lastTransitionTime |
Marca de tiempo de cuando el Pod hizo transición de un estado a otro. |
reason |
Texto legible por máquina que indica el motivo de la última transición de la condición. |
message |
Mensaje legible por humanos indicando detalles acerca de la última transición de estado. |
Preparación del Pod
Kubernetes v1.14 [stable]
Tu aplicación puede inyectar retroalimentación adicional o señales
al PodStatus
:
Pod readiness.
Para usar esto, establece readinessGates
en la spec
del Pod para especificar una
lista de condiciones adicionales que el kubelet evalúa para la preparación del
Pod.
Las condiciones de preparación están determinadas por el estado actual de los
campos status.conditions
de un Pod.
Si Kubernetes no puede encontrar una condición en el campo status.conditions
de un Pod, el estado de la condición se establece en "False
".
Aquí hay un ejemplo:
kind: Pod
...
spec:
readinessGates:
- conditionType: "www.example.com/feature-1"
status:
conditions:
- type: Ready # una PodCondition construida
status: "False"
lastProbeTime: null
lastTransitionTime: 2018-01-01T00:00:00Z
- type: "www.example.com/feature-1" # una PodCondition extra
status: "False"
lastProbeTime: null
lastTransitionTime: 2018-01-01T00:00:00Z
containerStatuses:
- containerID: docker://abcd...
ready: true
...
Las condiciones del Pod que incluyas deben tener nombres que sean válidos para los formatos de etiqueta de Kubernetes.
Estado de preparación del Pod
El comando kubectl patch
no admite actualizar el estado del objeto.
Para establecer estas status.conditions
para el Pod, las aplicaciones y
los operadores
deberían utilizar la acción Patch
.
Puedes utilizar una librería cliente de Kubernetes para escribir código que establece condiciones personalizadas de un Pod para su preparación.
Para los Pods que utilizan condiciones personalizadas, ese Pod es evaluado para estar listo solamente cuando ambas afirmaciones aplican:
- Todos los contenedores del Pod están listos.
- Todas las condiciones personalizadas especificadas en
readinessGates
estánTrue
.
Cuando los contenedores de un Pod están listos, pero al menos una condición
personalizada está ausente o False
,
el kubelet establece la condición del Pod
en ContainersReady
.
Preparación de la red del Pod
Kubernetes v1.29 [beta]
Nota:
Durante su desarrollo temprano, esta condición se llamóPodHasNetwork
.Después de que un Pod es programado en un nodo, necesita ser admitido por el kubelet y
tener cualquier volumen de almacenamiento requerido montado.
Una vez que estas fases se completan,
el kubelet trabaja con
un runtime de contenedores (usando Container runtime interface (CRI)) para configurar un
sandbox de runtime y configurar la red para el Pod.
Si la puerta de características
PodReadyToStartContainersCondition
está habilitada
(está habilitada por defecto para Kubernetes 1.31), la
condición PodReadyToStartContainers
se agregará al campo status.conditions
de un Pod.
La condición PodReadyToStartContainers
se establece en False
por el kubelet cuando detecta que un
Pod no tiene un sandbox de runtime con red configurada.
Esto ocurre en los siguientes escenarios:
- Al principio del ciclo de vida del Pod, cuando el kubelet aún no ha comenzado a configurar un sandbox para el Pod usando el runtime de contenedores.
- Más adelante en el ciclo de vida del Pod, cuando el sandbox del Pod ha sido destruido debido a:
- el nodo reiniciándose, sin que el Pod sea desalojado.
- para runtimes de contenedores que usan máquinas virtuales para aislamiento, la máquina virtual del sandbox del Pod reiniciándose, lo que luego requiere crear un nuevo sandbox y una nueva configuración de red para el contenedor.
La condición PodReadyToStartContainers
se establece en True
por el kubelet después de la
finalización exitosa de la creación del sandbox y la configuración de la red para el Pod
por el plugin de runtime. El kubelet puede comenzar a extraer imágenes de contenedores y crear
contenedores después de que la condición PodReadyToStartContainers
se haya establecido en True.
Para un Pod con contenedores de inicialización, el kubelet establece la condición Initialized
en
True
después de que los contenedores de inicialización se hayan completado exitosamente (lo que ocurre
después de la creación exitosa del sandbox y la configuración de la red por el plugin de runtime).
Para un Pod sin contenedores de inicialización, el kubelet establece la condición Initialized
en True
antes de que comience la creación del sandbox y la configuración de la red.
Sondeos del contenedor
Una sonda es un diagnóstico realizado periódicamente por el kubelet en un contenedor. Para ejecutar este diagnóstico, el kubelet ejecuta código dentro del contenedor o realiza una solicitud de red.
Mecanismos de revisión
Existen cuatro maneras diferentes de revisar un contenedor usando una sonda. Cada sonda debe definir exactamente una de estas cuatro maneras:
exec
: Ejecuta un comando especificado dentro del contenedor.
El diagnóstico se considera exitoso si el comando termina con un código de
estado 0.
grpc
- Realiza una llamada de procedimiento remoto usando gRPC.
El destino debe
implementar revisión de estado de gRPC.
El diagnóstico se considera exitoso si el
status
de la respuesta esSERVING
. httpGet
- Realiza una petición HTTP
GET
contra la dirección IP en la ruta y puerto especificado. El diagnóstico se considera exitoso si la respuesta tiene un código de estado mayor o igual que 200 y menor que 400. tcpSocket
- Realiza una revisión TCP contra la dirección IP del Pod en un puerto específico. El diagnóstico se considera exitoso si el puerto está abierto. Si el sistema remoto (el contenedor) cierra la conexión inmediatamente después de abrir la conexión, el diagnóstico se considera exitoso.
Precaución:
A diferencia de otros mecanismos, la implementación de la sonda exec
involucra
la creación/bifurcación de múltiples procesos cada vez que se ejecuta.
Como resultado, en caso de clústers con mayor densidad de Pods, intérvalos más
bajos de initialDelaySeconds
, periodSeconds
, configurando un sondeo
con exec
puede introducir una sobrecarga en el uso de la CPU del nodo.
En tales escenarios, considere la utilización de los mecanismos alternativos de sondeo para evitar la sobrecarga.
Resultados de sondeos
Cada sondeo puede tener uno de tres resultados:
Success
- El contenedor ha pasado el diagnóstico.
Failure
- El contenedor ha fallado el diagnóstico.
Unknown
- El diagnóstico ha fallado (no se debe tomar ninguna acción, y el kubelet hará más revisiones adicionales).
Tipos de sondeo
Opcionalmente, el kubelet puede ejecutar y reaccionar a tres tipos de sondeos en contenedores en ejecución:
livenessProbe
- Indica si el contenedor se está ejecutando.
Si el sondeo falla, el kubelet mata el contenedor,
y el contenedor está sujeto a su política de reinicio.
Si un contenedor no tiene un sondeo de liveness, el estado por defecto
es
Success
. readinessProbe
- Indica si un contenedor está preparado para responder a peticiones.
Si el sondeo falla,
el controlador de endpoints elimina las direcciones IP del Pod de los endpoints
de todos los Services que coinciden con el Pod.
El estado por defecto de readiness antes del retraso inicial es
Failure
. Si un contenedor no tiene un sondeo de readiness, el estado por defecto esSuccess
. startupProbe
- Indica si la aplicación dentro del contenedor ha iniciado. El resto de los
sondeos están deshabilitados si un sondeo de inicio se proporciona, hasta que se
complete. Si el sondeo falla, el kubelet mata el contenedor y el contenedor
está sujeto a su política de reinicio. Si un contenedor no
tiene un sondeo de inicio, el estado por defecto es
Success
.
Para mayor información sobre como configurar un sondeo liveness, readiness o de startup, mira la sección Configurar una sonda Liveness, Readiness y Startup.
¿Cuándo debería utilizar un sondeo liveness?
Si el proceso en tu contenedor es capaz de terminar por sí mismo cuando
encuentra un error o deja de estar sano, no necesitas un sondeo liveness; el
kubelet automáticamente realizará la acción adecuada de acuerdo con la política
de reinicio restartPolicy
del Pod.
Si te gustaría que tu contenedor fuese destruido y reiniciado si falla un
sondeo, especifica un sondeo liveness y especifica una restartPolicy
de Always
o OnFailure
.
¿Cuándo debería utilizar un sondeo readiness?
Se te gustaría enviar tráfico al Pod solo cuando una sonda sea exitosa, especifica un sondeo readiness. En este caso, el sondeo readiness podría ser el mismo que el liveness, pero la existencia del sondeo readines en la especificación significa que el Pod iniciará sin recibir ningún tráfico y solo iniciará cuando el sondeo readiness sea exitoso.
Si quieres que tu contenedor sea capaz de darse de baja por mantenimiento por sí mismo, puedes especificar un sondeo de readiness que revisa un endpoint específico de readiness que es distinto del sondeo liveness.
Si tu aplicación tiene una dependencia estricta con servicios de trasfondo, puedes implementar ambos sondeos de liveness y readiness. El sondeo de liveness pasa cuando la aplicación por sí misma está sana, pero el sondeo de readiness revisa adicionalmente que cada servicio de trasfondo está disponible. Esto ayuda a evitar enviar a Pods que solo pueden responder con errores.
Si tu contenedor necesita trabajar cargando grandes datos, ficheros de configuración, o migraciones durante el inicio, puedes usar un sondeo de inicio. Sin embargo, si quieres detectar la diferencia entre una aplicación que ha fallado y una aplicación que todavía está procesando datos de inicialización, puedes usar un sondeo de readiness.
Nota:
Si quieres ser capaz de drenar peticiones cuando se elimina el Pod, no necesitas un sondeo readiness; cuando se elimina el Pod, automáticamente se cambia al estadounready
sin importar si existe el sondeo
readiness.
El Pod permanece en el estado unready
mientras espera que los contenedores en
el Pod se paren.¿Cuándo debería utilizar un sondeo de inicialización?
Los sondeos de inicialización son útiles para Pods que tienen contenedores que se toman un largo tiempo para estar en servicio. En lugar de especificar un intérvalo largo de liveness, puedes crear una configuración separada para sondear el contenedor en el inicio, permitiendo un tiempo mayor que el intervalo de liveness.
Si tu contenedor usualmente inicia en más
de initialDelaySeconds + failureThreshold × periodSeconds
, deberías
especificar un sondeo de inicialización que revise el mismo endpoint que la
sonda liveness.
El periodo por defecto periodSeconds
es de 10 segundos.
Deberías especificar el campo failureThreshold
lo suficientemente alto para
permitir al contenedor arrancar, sin cambiar los valores por defecto de la sonda
liveness.
Esto ayuda a proteger contra puntos muertos.
Finalización de Pods
Ya que los Pods representan procesos ejecutándose en nodos de un clúster, es
importante permitir que esos procesos terminen con gracia cuando no se
necesitan (en lugar de detenerse abruptamente con una señal Kill
y sin
oportunidad de limpiarse).
El diseño está orientado a permitir que puedas solicitar la eliminación de un Pod y saber cuándo finalizan los procesos, pero también para asegurar que la eliminación se completa eventualmente. Cuando solicitas la eliminación de un Pod, el clúster registra y rastrea el periodo de gracia antes de que el Pod se elimine por la fuerza. Con este rastreo de detención forzada en marcha, el kubelet intenta pararlo con gracia.
Típicamente, con esta finalización con gracia del Pod, el kubelet hace
peticiones al tiempo de ejecución del contenedor para intentar detener los
contenedores en el Pod, primeramente enviando una señal Term
(ej. SIGTERM),
con un período de tiempo de gracia, al proceso principal de cada contenedor.
Las peticiones para parar los contenedores se procesan de forma asíncrona en el
tiempo de ejecución del contenedor.
No hay garantía del orden de procesamiento de estas peticiones.
Muchos contenedores respetan el valor STOPSIGNAL
definido en la imagen del
contenedor y, si es diferente, envían el valor de STOPSIGNAL
en lugar de
TERM.
Una vez que el período de gracia ha acabado, se envía la señal KILL a cualquier proceso restante, y luego el Pod se elimina del Servidor API. Si el kubelet o el tiempo de ejecución del contenedor del servicio que lo administra se reinicia mientras espera que los procesos terminen, el kubelet reintenta de nuevo el proceso incluyendo el periodo original de gracia.
Un flujo de finalización de un Pod, ilustrado con un ejemplo:
-
Utilizas la herramienta
kubectl
para eliminar manualmente un Pod específico, con un periodo de gracia por defecto (30 segundos). -
El Pod en el servidor API se actualiza con el tiempo más allá del cual el Pod se considera "muerto" junto con el periodo de gracia. Si utilizas
kubectl describe
para revisar el Pod que estás borrando, ese Pod se mostrará comoTerminating
. En el nodo donde se ejecuta el Pod: tan pronto como el kubelet observa que el Pod se ha marcado como terminando (se ha definido una duración de parada con gracia), el kubelet comienza el proceso local de parar el Pod.- Si uno de los contenedores del Pod tiene definido
un hook
preStop
y elterminationGracePeriodSeconds
en la especificación del Pod no está definido en 0, el kubelet ejecuta ese hook dentro del contenedor. ElterminationGracePeriodSeconds
por defecto es 30 segundos.
Si el hook
preStop
todavía se está ejecutando luego de la expiración del período de gracia, el kubelet solicita una extensión 2 segundos del periodo de gracia.Nota:
Si el hookpreStop
necesita más tiempo para completar que el tiempo permitido por defecto, debes modificar elterminationGracePeriodSeconds
para adaptarlo.- El kubelet lanza el tiempo de ejecución del contenedor para enviar una
señal TERM al proceso 1 dentro de cada contenedor.
Nota:
Los contenedores en el Pod reciben la señal TERM en tiempos diferentes y en orden arbitrario. Si el orden de finalización importa, considera utilizar un hookpreStop
para sincronizarlos.
- Si uno de los contenedores del Pod tiene definido
un hook
-
Al mismo tiempo que el kubelet inicia la finalización con gracia del Pod, el panel de control evalúa si quitar este Pod en finalización de los objetos
EndpointSlice
(yEndpoints
), donde aquellos objetos representan un Service con un selector configurado.Los ReplicaSets y otros recursos de carga de trabajo ya no consideran al Pod como réplica válida, en servicio.
Los Pods que finalizan lentamente no servirían tráfico regular y debería iniciar la finalización de procesamiento de conexiones abiertas. Algunas aplicaciones necesitan ir más allá de finalizar las conexiones abiertas y necesitan finalización aún con más gracia, por ejemplo, drenar y completar una sesión.
Cualquier endpoint que representa los Pods en finalización no son removidos inmediatamente de
EndpointSlices
y se expone un estatus indicando el estado de terminación de la API de EndpointSlice (y la API de Endpoint legada). Los endpoints que están terminando siempre tienen su estatusready
comofalse
(para compatibilidad con versiones anteriores a 1.26), por lo que los balanceadores de carga no los usarán para tráfico regular.Si se necesita drenar el tráfico en un Pod que está terminando, el readiness se puede revisar con la condición
serving
. Puedes encontrar más detalles en cómo implementar drenado de conexiones en el tutorial Pods y flujo de terminación de Endpoints
- El kubelet se asegura que el Pod se ha apagado y terminado
- Cuando finaliza el tiempo de gracia, si aún existe algún contenedor ejecutándose en el Pod, el kubelet lanza un apagado forzado.
El runtime del contenedor envía una señal
SIGKILL
a cualquier proceso ejecutándose en cualquier contenedor en el Pod. El kubelet también limpia un contenedorpause
oculto si ese contenedor usa uno. - El kubelet hace la transición del Pod a una fase terminal (
Failed
óSucceeded
dependiendo del estado final de sus contenedores). - El Kubelet lanza la eliminación forzosa de los objetos del Pod del servidor API, estableciendo el periodo de gracia a 0 (detención inmediata).
- El servidor API elimina el objeto API del Pod, que ya no es visible desde ningún cliente.
- Cuando finaliza el tiempo de gracia, si aún existe algún contenedor ejecutándose en el Pod, el kubelet lanza un apagado forzado.
El runtime del contenedor envía una señal
Terminación Forzada del Pod
Precaución:
Eliminaciones forzadas pueden ser potencialmente disruptivas para algunas cargas de trabajo y sus Pods.Por defecto, todas las eliminaciones tienen un tiempo de gracia de 30 segundos.
El comando kubelet delete
soporta la opción --grace-period=<segundos>
que
permite sobreescribir el valor por defecto y especificar tu propio valor.
Establecer el período de gracia a 0
elimina de forma forzada e inmediata el
Pod del servidor API.
Si el Pod aún se está ejecutando en un nodo, esa eliminación forzada hace que
el kubelet inicie una limpieza inmediata.
Usando kubectl, debes especificar una opción adicional --force
junto con --grace-period=0
para realizar eliminaciones forzadas.
Cuando se realiza una eliminación forzada, el servidor API no espera la confirmación del kubelet de que el Pod ha terminado en el nodo en que se está ejecutando. Este elimina el Pod en la API inmediatamente para que se pueda crear un Pod con el mismo nombre. En el nodo, los Pods que están por terminar inmediatamente aún pueden tener un pequeño período de gracia antes de ser eliminados de forma forzada.
Precaución:
La eliminación inmediata no espera la confirmación de que el recurso en ejecución ha terminado. El recurso puede continuar ejecutándose en el clúster de forma indefinida.Si necesitas eliminar Pods por la fuerza y son parte de un StatefulSet
,
mira la documentación
para borrar Pods de un StatefulSet.
Terminación del Pod y contenedores sidecar
Si tus Pods incluyen uno o más contenedores sidecar (contenedores de inicialización con una política de reinicio Always
), el kubelet retrasará el envío de la señal TERM a estos contenedores sidecar hasta que el último contenedor principal se haya terminado completamente.
Los contenedores sidecar serán eliminados en orden inverso al que se han definido en la especificación del Pod.
Esto asegura que los contenedores sidecar continúan sirviendo a los otros contenedores en el Pod hasta que ya no se necesiten.
Esto significa que la terminación lenta de un contenedor principal también retrasará la terminación de los contenedores sidecar.
Si el periodo de gracia expira antes que se complete el proceso de terminación, el Pod podría entrar en terminación forzada. En este caso, todos los contenedores restantes en el Pod serán terminados simultáneamente con un periodo de gracia corto.
De forma similar, si el Pod tiene un hook preStop
que excede el periodo de gracia de finalización, puede ocurrir una terminación de emergencia.
En general, si has usado hooks de preStop
para controlar el orden de terminación sin contenedores sidecar, puedes quitarlos y permitir que el kubelet maneje la terminación de sidecars automáticamente.
Recolección de elementos no utilizados de los Pods
Cuando los Pods fallan, los objetos API permanecen en el clúster hasta que un humano o el proceso de controlador los elimine explícitamente.
El recolector de elementos no utilizados (PodGC en inglés) es un controlador en
el plano de control que elimina los Pods que se han terminado (con una fase
de Succeeded
o Failed
), cuando el número de Pods excede el umbral
configurado (determinado por terminated-pod-gc-threshold
en el controlador de
kube-controller-manager).
Esto evita la fuga de recursos mientras que los Pods se crean y se eliminan en
el tiempo.
Adicionalmente, el PodGC limpia cualquier Pod que satisfaga cualquiera de las siguientes condiciones:
- Pods huérfanos - asociados a un Nodo que ya no existe,
- Pods que están finalizando y no están programados,
- Pods que están finalizando,
asociados a un nodo que no está listo, contaminado
con
node.kubernetes.io/out-of-service
, cuando la condiciónNodeOutOfServiceVolumeDetach
está habilitada.
Cuando la condición PodDisruptionCondition
está habilitada,
además de limpiar los Pods,
el PodGC también los marcará como fallidos si están en una fase no terminal.
También, el PodGC agrega una condición de disrupción del Pod cuando realiza la limpieza de un Pod huérfano.
Mira condiciones de disrupción del Pod para más detalles.
Siguientes pasos
-
Obtén experiencia práctica agregando controladores a los eventos del ciclo de vida del contenedor.
-
Obtén experiencia práctica configurando sondas de Liveness, Readiness y Startup.
-
Aprende más sobre hooks del ciclo de vida del contenedor.
-
Aprende más sobre contenedores sidecar.
-
Para información detallada sobre el estatus del contenedor del Pod en la API, mira la documentación de referencia de la API que cubre el
status
del Pod.
3 - Contenedores de Inicialización
Esta página proporciona una descripción general de los contenedores de inicialización (init containers): contenedores especializados que se ejecutan antes de los contenedores de aplicación en un Pod. Los contenedores de inicialización pueden contener utilidades o scripts de instalación no presentes en una imagen de aplicación.
Tú puedes especificar contenedores de inicialización en la especificación del Pod junto con el arreglo de containers
(el cual describe los contenedores de aplicación).
Entendiendo los contenedores de inicialización
Un Pod puede tener múltiples contenedores ejecutando aplicaciones dentro de él, pero también puede tener uno o más contenedores de inicialización que se ejecutan antes de que se inicien los contenedores de aplicación.
Los contenedores de inicialización son exactamente iguales a los contenedores regulares excepto por:
- Los contenedores de inicialización siempre se ejecutan hasta su finalización.
- Cada contenedor de inicialiación debe completarse correctamente antes de que comience el siguiente.
Si el contenedor de inicialización de un Pod falla, kubelet reinicia repetidamente ese contenedor de inicialización hasta que tenga éxito.
Sin embargo, si el Pod tiene una restartPolicy
de Never
y un contenedor de inicialización falla durante el inicio de ese Pod, Kubernetes trata al Pod en general como fallido.
Para especificar un contenedor de inicialización para un Pod, agrega el campo initContainers
en
la especificación del Pod,
como un arreglo de elementos container
(similar al campo containers
de aplicación y su contenido).
Consulta Container en la
referencia de API para más detalles.
El estado de los contenedores de inicialización se devuelve en el campo .status.initContainerStatuses
como un arreglo de los estados del contenedor (similar al campo .status.containerStatuses
).
Diferencias con los contenedores regulares
Los contenedores de inicialización admiten todos los campos y características de los contenedores de aplicaciones, incluidos los límites de recursos, los volúmenes y la configuración de seguridad. Sin embargo, las solicitudes de recursos y los límites para un contenedor de inicialización se manejan de manera diferente, como se documenta en Recursos.
Además, los contenedores de inicialización no admiten lifecycle
, livenessProbe
, readinessProbe
o
startupProbe
porque deben de ejecutarse hasta su finalización antes de que el Pod pueda estar listo.
Si especificas varios contenedores de inicialización para un Pod, kubelet ejecuta cada contenedor de inicialización secuencialmente. Cada contenedor de inicialización debe tener éxito antes de que se pueda ejecutar el siguiente. Cuando todos los contenedores de inicialización se hayan ejecutado hasta su finalización, kubelet inicializa los contenedores de aplicación para el Pod y los ejecuta como de costumbre.
Usando contenedores de inicialización
Dado que los contenedores de inicialización tienen imágenes separadas de los contenedores de aplicaciones, estos tienen algunas ventajas sobre el código relacionado de inicio:
- Los contenedores de inicialización pueden contener utilidades o código personalizado para la configuración que no están presentes en una
imagen de aplicación. Por ejemplo, no hay necesidad de hacer una imagen
FROM
de otra imagen solo para usar una herramienta comosed
,awk
,python
odig
durante la instalación. - Los roles de constructor e implementador de imágenes de aplicación pueden funcionar de forma independiente sin la necesidad de construir conjuntamente una sola imagen de aplicación.
- Los contenedores de inicialización pueden ejecutarse con una vista diferente al sistema de archivos que los contenedores de aplicaciones en el mismo Pod. En consecuencia, se les puede dar acceso a Secrets a los que los contenedores de aplicaciones no pueden acceder.
- Debido a que los contenedores de inicialización se ejecutan hasta su finalización antes de que se inicien los contenedores de aplicaciones, los contenedores de inicialización ofrecen un mecanismo para bloquear o retrasar el inicio del contenedor de aplicación hasta que se cumplan una serie de condiciones previas. Una vez que las condiciones previas se cumplen, todos los contenedores de aplicaciones de un Pod pueden iniciarse en paralelo.
- Los contenedores de inicialización pueden ejecutar de forma segura utilidades o código personalizado que de otro modo harían a una imagen de aplicación de contenedor menos segura. Si mantiene separadas herramientas innecesarias, puede limitar la superficie de ataque a la imagen del contenedor de aplicación.
Ejemplos
A continuación, se muestran algunas ideas sobre cómo utilizar los contenedores de inicialización:
-
Esperar a que se cree un Service usando una sola linea de comando de shell:
for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
-
Registrar este Pod con un servidor remoto desde la downward API con un comando como:
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
-
Esperar algo de tiempo antes de iniciar el contenedor de aplicación con un comando como:
sleep 60
-
Clonar un repositorio de Git en un Volume
-
Colocar valores en un archivo de configuración y ejecutar una herramienta de plantilla para generar dinámicamente un archivo de configuración para el contenedor de aplicación principal. Por ejemplo, colocar el valor
POD_IP
en una configuración y generar el archivo de configuración de la aplicación principal usando Jinja.
Contenedores de inicialización en uso
Este ejemplo define un simple Pod que tiene dos contenedores de inicialización.
El primero espera por myservice
y el segundo espera por mydb
. Una vez que ambos
contenedores de inicialización se completen, el Pod ejecuta el contenedor de aplicación desde su sección spec
.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo ¡La aplicación se está ejecutando! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo esperando a myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo esperando a mydb; sleep 2; done"]
Puedes iniciar este Pod ejecutando:
kubectl apply -f myapp.yaml
El resultado es similar a esto:
pod/myapp-pod created
Y verificar su estado con:
kubectl get -f myapp.yaml
El resultado es similar a esto:
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
o para más detalles:
kubectl describe -f myapp.yaml
El resultado es similar a esto:
Name: myapp-pod
Namespace: default
[...]
Labels: app.kubernetes.io/name=MyApp
Status: Pending
[...]
Init Containers:
init-myservice:
[...]
State: Running
[...]
init-mydb:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Containers:
myapp-container:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
16s 16s 1 {default-scheduler } Normal Scheduled Successfully assigned myapp-pod to 172.17.4.201
16s 16s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulling pulling image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulled Successfully pulled image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Created Created container with docker id 5ced34a04634; Security:[seccomp=unconfined]
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container with docker id 5ced34a04634
Para ver los logs de los contenedores de inicialización en este Pod ejecuta:
kubectl logs myapp-pod -c init-myservice # Inspecciona el primer contenedor de inicialización
kubectl logs myapp-pod -c init-mydb # Inspecciona el segundo contenedor de inicialización
En este punto, estos contenedores de inicialización estarán esperando para descubrir los Servicios denominados
mydb
y myservice
.
Aquí hay una configuración que puedes usar para que aparezcan esos Servicios:
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
Para crear los servicios de mydb
y myservice
:
kubectl apply -f services.yaml
El resultado es similar a esto:
service/myservice created
service/mydb created
Luego verás que esos contenedores de inicialización se completan y que el Pod myapp-pod
pasa al estado Running
:
kubectl get -f myapp.yaml
El resultado es similar a esto:
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
Este sencillo ejemplo debería servirte de inspiración para crear tus propios contenedores de inicialización. ¿Qué es lo que sigue? contiene un enlace a un ejemplo más detallado.
Comportamiento detallado
Durante el inicio del Pod, kubelet retrasa la ejecución de contenedores de inicialización hasta que la red y el almacenamiento estén listos. Después, kubelet ejecuta los contenedores de inicialización del Pod en el orden que aparecen en la especificación del Pod.
Cada contenedor de inicialización debe salir correctamente antes de que
comience el siguiente contenedor. Si un contenedor falla en iniciar debido al tiempo de ejecución o
sale con una falla, se vuelve a intentar de acuerdo con el restartPolicy
del Pod. Sin embargo,
si el restartPolicy
del Pod se establece en Always
, los contenedores de inicialización usan
el restartPolicy
como OnFailure
.
Un Pod no puede estar Ready
sino hasta que todos los contenedores de inicialización hayan tenido éxito. Los puertos en un
contenedor de inicialización no se agregan a un Servicio. Un Pod que se está inicializando,
está en el estado de Pending
, pero debe tener una condición Initialized
configurada como falsa.
Si el Pod se reinicia o es reiniciado, todos los contenedores de inicialización deben ejecutarse de nuevo.
Los cambios en la especificación del contenedor de inicialización se limitan al campo de la imagen del contenedor. Alterar un campo de la imagen del contenedor de inicialización equivale a reiniciar el Pod.
Debido a que los contenedores de inicialización se pueden reiniciar, reintentar o volverse a ejecutar, el código del contenedor de inicialización
debe ser idempotente. En particular, el código que escribe en archivos en EmptyDirs
debe estar preparado para la posibilidad de que ya exista un archivo de salida.
Los contenedores de inicialización tienen todos los campos de un contenedor de aplicaciones. Sin embargo, Kubernetes
prohíbe el uso de readinessProbe
porque los contenedores de inicialización no pueden
definir el readiness
distinto de la finalización. Esto se aplica durante la validación.
Usa activeDeadlineSeconds
en el Pod para prevenir que los contenedores de inicialización fallen por siempre.
La fecha límite incluye contenedores de inicialización.
Sin embargo, se recomienda utilizar activeDeadlineSeconds
si el usuario implementa su aplicación
como un Job
porque activeDeadlineSeconds
tiene un efecto incluso después de que initContainer
finaliza.
El Pod que ya se está ejecutando correctamente sería eliminado por activeDeadlineSeconds
si lo estableces.
El nombre de cada aplicación y contenedor de inicialización en un Pod debe ser único; un error de validación es arrojado para cualquier contenedor que comparta un nombre con otro.
Recursos
Dado el orden y la ejecución de los contenedores de inicialización, las siguientes reglas para el uso de recursos se aplican:
- La solicitud más alta de cualquier recurso o límite particular definido en todos los contenedores de inicialización es la solicitud/límite de inicialización efectiva. Si algún recurso no tiene un límite de recursos especificado éste se considera como el límite más alto.
- La solicitud/límite efectiva para un recurso es la más alta entre:
- la suma de todas las solicitudes/límites de los contenedores de aplicación, y
- la solicitud/límite de inicialización efectiva para un recurso
- La planificación es hecha con base en las solicitudes/límites efectivos, lo que significa que los contenedores de inicialización pueden reservar recursos para la inicialización que no se utilizan durante la vida del Pod.
- El nivel de
QoS
(calidad de servicio) del nivel deQoS
efectivo del Pod es el nivel deQoS
tanto para los contenedores de inicialización como para los contenedores de aplicación.
La cuota y los límites son aplicados con base en la solicitud y límite efectivos de Pod.
Los grupos de control de nivel de Pod (cgroups) se basan en la solicitud y el límite de Pod efectivos, al igual que el planificador de Kubernetes (kube-scheduler).
Razones de reinicio del Pod
Un Pod puede reiniciarse, provocando la re-ejecución de los contenedores de inicialización por las siguientes razones:
- Se reinicia el contenedor de infraestructura del Pod. Esto es poco común y debería hacerlo alguien con acceso de root a los nodos.
- Todos los contenedores en un Pod son terminados mientras
restartPolicy
esté configurado enAlways
, forzando un reinicio y el registro de finalización del contenedor de inicialización se ha perdido debido a la recolección de basura.
El Pod no se reiniciará cuando se cambie la imagen del contenedor de inicialización o cuando se pierda el registro de finalización del contenedor de inicialización debido a la recolección de basura. Esto se aplica a Kubernetes v1.20 y posteriores. Si estás utilizando una versión anterior de Kubernetes, consulta la documentación de la versión que estás utilizando.
Siguientes pasos
- Lee acerca de creando un Pod que tiene un contenedor de inicialización
- Aprende cómo depurar contenedores de inicialización
4 - Interrupciones
Esta guía es para los dueños de aplicaciones que quieren crear aplicaciones con alta disponibilidad y que necesitan entender qué tipos de interrupciones pueden suceder en los Pods.
También es para los administradores de clústeres que quieren aplicar acciones automatizadas en sus clústeres, como actualizar o autoescalar los clústeres.
Interrupciones voluntarias e involuntarias
Los Pods no desaparecen hasta que algo (una persona o un controlador) los destruye o hay problemas de hardware o software que son inevitables.
Nosotros llamamos a esos casos inevitables interrupciones involuntarias de una aplicación. Algunos ejemplos:
- Una falla en el hardware de la máquina física del nodo.
- Un administrador del clúster borra una VM (instancia) por error.
- El proveedor de la nube o el hipervisor falla y hace desaparecer la VM.
- Un kernel panic.
- El nodo desaparece del clúster por un problema de red que lo separa del clúster.
- Una remoción del Pod porque el nodo no tiene recursos suficientes.
A excepción de la condición sin recursos suficientes, todas estas condiciones deben ser familiares para la mayoría de los usuarios, no son específicas de Kubernetes.
Nosotros llamamos a los otros casos interrupciones voluntarias. Estas incluyen las acciones iniciadas por el dueño de la aplicación y aquellas iniciadas por el Administrador del Clúster. Las acciones típicas de los dueños de la aplicación incluyen:
- borrar el Deployment u otro controlador que maneja el Pod
- actualizar el Deployment del Pod que causa un reinicio
- borrar un Pod (por ejemplo, por accidente)
Las acciones del administrador del clúster incluyen:
- Drenar un nodo para reparar o actualizar.
- Drenar un nodo del clúster para reducir el clúster (aprenda acerca de Autoescalamiento de Clúster).
- Remover un Pod de un nodo para permitir que otra cosa pueda ingresar a ese nodo.
Estas acciones pueden ser realizadas directamente por el administrador del clúster, por tareas automatizadas del administrador del clúster o por el proveedor del clúster.
Consulte al administrador de su clúster, a su proveedor de la nube o a la documentación de su distribución para determinar si alguna de estas interrupciones voluntarias está habilitada en su clúster. Si ninguna se encuentra habilitada, puede omitir la creación del presupuesto de Interrupción de Pods.
Precaución:
No todas las interrupciones voluntarias son consideradas por el presupuesto de interrupción de Pods. Por ejemplo, borrar un Deployment o Pods que evitan el uso del presupuesto.Tratando con las interrupciones
Estas son algunas de las maneras para mitigar las interrupciones involuntarias:
- Asegurarse que el Pod solicite los recursos que necesita.
- Replique su aplicación si usted necesita alta disponibilidad. (Aprenda sobre correr aplicaciones replicadas stateless y stateful
- Incluso, para una alta disponibilidad mayor cuando se corren aplicaciones replicadas, propague las aplicaciones por varios racks (usando anti-affinity) o usando zonas (si usa un clúster multi-zona.)
La frecuencia de las interrupciones voluntarias varía. En un clúster basico de Kubernetes, no hay interrupciones voluntarias automáticas (solo el usuario las genera). Sin embargo, su administrador del clúster o proveedor de alojamiento puede correr algun servicio adicional que pueda causar estas interrupciones voluntarias. Por ejemplo, desplegando una actualización de software en los nodos puede causar interrupciones. También, algunas implementaciones de clústers con autoescalamiento de nodos puede causar interrupciones para defragmentar o compactar los nodos. Su administrador de clúster o proveedor de alojamiento debe tener documentado cuál es el nivel de interrupciones voluntarias esperadas, sí es que las hay. Ciertas opciones de configuración, como ser usar PriorityClasses en las especificaciones de su Pod pueden también causar interrupciones voluntarias (o involuntarias).
Presupuesto de Interrupción de Pods
Kubernetes v1.21 [stable]
Kubernetes ofrece carácteristicas para ayudar a ejecutar aplicaciones con alta disponibliidad, incluso cuando usted introduce interrupciones voluntarias frecuentes.
Como dueño de la aplicación, usted puede crear un presupuesto de interrupción de Pods (PDB por sus siglas en inglés) para cada aplicación. Un PDB limita el numero de Pods de una aplicación replicada, que estan caídos de manera simultánea por interrupciones voluntarias. Por ejemplo, una aplicación basada en quórum puede asegurarse que el número de réplicas corriendo nunca es menor al número necesitado para obtener el quórum. Una web de tipo front end puede querer asegurarse que el número de réplicas atendiendo al tráfico nunca puede caer bajo un cierto porcentaje del total.
Los administradores del clúster y proveedores de hosting pueden usar herramientas que respeten el presupuesto de interrupción de Pods utilizando la API de Desalojo en vez de directamente borrar Pods o Deployments.
Por ejemplo, el subcomando kubectl drain
le permite marcar un nodo a un modo fuera de
servicio. Cuando se ejecuta kubectl drain
, la herramienta trata de quitar a todos los Pods en
el nodo que se esta dejando fuera de servicio. La petición de desalojo que kubectl
solicita en
su nombre puede ser temporalmente denegado, entonces la herramienta periodicamente reintenta todas las
peticiones fallidas hasta que todos los Pods en el nodo afectado son terminados o hasta que el tiempo de espera,
que puede ser configurado, es alcanzado.
Un PDB especifica el número de réplicas que una aplicación puede tolerar, relativo a cuantas
se pretende tener. Por ejemplo, un Deployment que tiene un .spec.replicas: 5
se
supone que tiene 5 Pods en cualquier momento. Si su PDB permite tener 4 a la vez,
entonces la API de Desalojo va a permitir interrupciones voluntarias de uno (pero no de dos) Pod a la vez.
El grupo de Pods que comprende a la aplicación está especificado usando una etiqueta selectora, la misma que es usada por el controlador de aplicación (deployment, stateful-set, etc).
El número de Pods "deseado" es calculado a partir de .spec.replicas
del recurso de Workload
que es manejado para esos Pods. El plano de control descubre el recurso Workload perteneciente al
examinar las .metadata.ownerReferences
del Pod.
Las Interrupciones Involuntarias no pueden ser prevenidas por los PDB; pero si son contabilizadas a partir de este presupuesto.
Los Pods que son borrados o no están disponibles debido a una actualización continua de una aplicación forman parte del presupuesto de interrupciones, pero los recursos Workload (como los Deployments y StatefulSet) no están limitados por los PDBs cuando se hacen actualizaciones continuas. En cambio, la administración de fallas durante la actualización de la aplicación está configurada en la especificación para este recurso Workload específico.
Cuando un Pod es eliminado usando la API de desalojo, este es
terminado correctamente, haciendo honor al
terminationGracePeriodSeconds
configurado en su PodSpec.
Ejemplo de Presupuesto de Interrupción de POD
Considere un clúster con 3 nodos, nodo-1
hasta nodo-3
.
El clúster está ejecutando varias aplicaciones. Uno de ellos tiene 3 replicas, que llamaremos
pod-a
, pod-b
, y pod-c
. Otro Pod no relacionado y sin PDB, llamado pod-x
, también se muestra.
Inicialmente los Pods están distribuidos de esta manera:
nodo-1 | nodo-2 | nodo-3 |
---|---|---|
pod-a available | pod-b available | pod-c available |
pod-x available |
Los 3 Pods son parte de un Deployment, ellos colectivamente tienen un PDB que requiere que por lo menos 2 de los 3 Pods estén disponibles en todo momento.
Por ejemplo, supongamos que el administrador del clúster quiere reiniciar para actualizar el kernel y solucionar un error.
El administrador del clúster primero intenta drenar el nodo-1
usando el comando kubectl drain
.
La herramienta intenta drenar los Pods pod-a
y pod-x
. Esto tiene éxito inmediatamente.
Ambos Pods pasan al estado terminating
al mismo tiempo.
Esto pone al clúster en el siguiente estado:
nodo-1 draining | nodo-2 | nodo-3 |
---|---|---|
pod-a terminating | pod-b available | pod-c available |
pod-x terminating |
El Deployment detecta que uno de los Pods está terminando, entonces crea un reemplazo
llamado pod-d
. Dado que el nodo-1
está bloqueado, el pod se inicia en otro nodo. Además,
se crea el pod pod-y
como reemplazo de pod-x
.
(Nota: para un StatefulSet, pod-a
, que debería llamarse algo como pod-0
, debe terminar completamente antes de su reemplazo, que también se llama pod-0
pero tiene un UID diferente, puede ser creado. De lo contrario, el ejemplo también se aplica a un StatefulSet).
Ahora el clúster está en este estado:
nodo-1 draining | nodo-2 | nodo-3 |
---|---|---|
pod-a terminating | pod-b available | pod-c available |
pod-x terminating | pod-d starting | pod-y starting |
En algún momento, los Pods terminan y el clúster se ve así:
nodo-1 drained | nodo-2 | nodo-3 |
---|---|---|
pod-b available | pod-c available | |
pod-d starting | pod-y starting |
En este estado, si un administrador del clúster impaciente intenta drenar el nodo-2
o el
nodo-3
, el comando de drenado será bloqueado, porque solo hay 2 Pods disponibles para
el Deployment y el PDB requiere al menos 2. Después de un tiempo, pod-d
y pod-y
están disponibles.
El estado del clúster ahora se ve así:
nodo-1 drained | nodo-2 | nodo-3 |
---|---|---|
pod-b available | pod-c available | |
pod-d available | pod-y available |
Ahora, el administrador del clúster drena el nodo-2
.
El comando de drenado intentará drenar los 2 Pods en algún orden, digamos
primero pod-b
y luego pod-d
. Tendrá éxito en eliminar pod-b
.
Pero cuando intente drenar pod-d
, será rechazado porque eso dejará
solo un Pod disponible para el Deployment.
El Deployment crea un reemplazo para pod-b
llamado pod-e
.
Dado que no hay suficientes recursos disponibles en el clúster para programar
pod-e
, el drenado será bloqueado nuevamente. El clúster terminará en este
estado:
nodo-1 drained | nodo-2 | nodo-3 | no node |
---|---|---|---|
pod-b terminating | pod-c available | pod-e pending | |
pod-d available | pod-y available |
Ahora, el administrador del clúster necesita agregar un nuevo nodo en el clúster para continuar con la actualización.
Usted puede ver cómo Kubernetes varía la tasa a la que ocurren las interrupciones, según:
- cuántas réplicas necesita una aplicación
- cuánto tiempo lleva apagar una instancia correctamente
- cuánto tiempo lleva iniciar una nueva instancia
- el tipo de controlador
- la capacidad de recursos del clúster
Separación entre el dueño del Clúster y los roles de dueños de la Aplicación
Muchas veces es útil pensar en el Administrador del Clúster y al dueño de la aplicación como roles separados con conocimiento limitado el uno del otro. Esta separación de responsabilidades puede tener sentido en estos escenarios:
- Cuando hay muchos equipos con aplicaciones compartiendo un clúster de Kubernetes y hay una especialización natural de roles
- Cuando se usa una herramienta de terceros o un servicio para automatizar el control del clúster
El presupuesto de interrupción de Pods respalda esta separación de roles, proporcionando una interfaz entre los roles.
Si no hay tal separación de responsabilidades en la organización, es posible que no sea necesario el Presupuesto de Interrupción de Pods.
Cómo realizar Acciones Disruptivas en el Clúster
Si usted es el Administrador del Clúster y necesitas realizar una acción disruptiva en todos los nodos del clúster, como una actualización de nodo o de software, estas son algunas de las opciones:
- Aceptar el tiempo de inactividad mientras dura la actualización.
- Cambiar a otra réplica completa del clúster.
- No hay tiempo de inactividad, pero puede ser costoso tener duplicados los nodos y también se requiere esfuerzo humano para orquestar dicho cambio.
- Diseñar la tolerancia a fallas en la aplicación y usar PDBs.
- No hay tiempo de inactividad.
- Duplicación mínima de recursos.
- Permite mucha más automatización en la administración del clúster.
- Diseñar aplicaciones para tolerar fallas es complicado, pero el trabajo para tolerar interrupciones involuntarias a menudo vale la pena en comparación con el trabajo de admitir autoescalado y tolerar interrupciones involuntarias.
Siguientes pasos
-
Siga los pasos para proteger su aplicación con configurar el Presupuesto de Interrupciones de Pods.
-
Aprenda más sobre drenar nodos.
-
Aprenda sobre actualizar un Deployment incluyendo los pasos para mantener su disponibilidad durante la actualización.
5 - Containers Efímeros
Kubernetes v1.31 [alpha]
Esta página proporciona una descripción general de los Containers efímeros: un tipo especial de Container que se ejecuta temporalmente en un Pod ya existente para cumplir las acciones iniciadas por el usuario, como por ejemplo, la solución de problemas. En vez de ser utilizadas para crear aplicaciones, los Containers efímeros se utilizan para examinar los servicios.
Advertencia:
Los Containers efímeros se encuentran en una fase alfa inicial y no son aptos para clústers de producción. Es de esperar que esta característica no funcione en algunas situaciones, por ejemplo, al seleccionar los Namespaces de un Container. De acuerdo con la Política de Deprecación de Kubernetes, esta característica alfa puede variar significativamente en el futuro o ser eliminada por completo.Entendiendo los Containers efímeros
Pods son el componente fundamental de las aplicaciones de Kubernetes. Puesto que los Pods están previstos para ser desechables y reemplazables, no se puede añadir un Container a un Pod una vez creado. Sin embargo, por lo general se eliminan y se reemplazan los Pods de manera controlada utilizando Deployments.
En ocasiones es necesario examinar el estado de un Pod existente, como por ejemplo, para poder solucionar un error difícil de reproducir. Puede ejecutar en estos casos un Container efímero en un Pod ya existente para examinar su estado y para ejecutar comandos de manera arbitraria.
Qué es un Container efímero?
Los Containers efímeros se diferencian de otros Containers en que no garantizan ni los recursos ni la ejecución, y en que nunca se reiniciarán automáticamente, de modo que no son aptos para la construcción de aplicaciones. Los Containers efímeros se describen usando la misma ContainerSpec que los Containers regulares, aunque muchos campos son incompatibles y no están habilitados para los Containers efímeros.
- Los Containers efímeros no pueden tener puertos, por lo que campos como
ports
,livenessProbe
,readinessProbe
no están habilitados. - Las asignaciones de recursos del Pod son inmutables, por lo que no esta habilitado configurar "resources".
- Para obtener una lista completa de los campos habilitados, consulte la documentación de referencia [EphemeralContainer] (/docs/reference/generated/kubernetes-api/v1.31/#ephemeralcontainer-v1-core).
En vez de añadirlos de forma directa al pod.spec
, los Containers efímeros se crean usando un
controlador especial de la API, ephemeralcontainers
, por lo tanto no es posible añadir un
Container efímero utilizando kubectl edit
.
Al igual en el caso de los Containers regulares, no se puede modificar o remover un Container efímero después de haberlo agregado a un Pod.
Casos de uso para los Containers efímeros
Los Containers efímeros resultan útiles para la solución interactiva de incidencias cuando
kubectl exec
es insuficiente tanto porque un container se ha caído, como porque la imagen de un
Container no incluye las utilidades de depuración.
En particular, las imágenes distroless
le permiten desplegar imágenes de Containers mínimos que disminuyen la superficie de ataque
y la exposición a errores y vulnerabilidades. Ya que las imágenes distroless no contienen un
shell ni ninguna utilidad de depuración, resulta difícil solucionar los problemas de las imágenes
distroless usando solamente kubectl exec
.
Cuando utilice Containers efímeros, es conveniente habilitar el proceso Namespace de uso compartido para poder ver los procesos en otros containers.
Ejemplos
Nota:
Los ejemplos de esta sección requieren que losEphemeralContainers
feature
gate estén habilitados
y que tanto el cliente como el servidor de Kubernetes tengan la version v1.16 o posterior.En los ejemplos de esta sección muestran la forma en que los Containers efímeros se
presentan en la API. Los usuarios normalmente usarían un plugin kubectl
para la solución
de problemas que automatizaría estos pasos.
Los Containers efímeros son creados utilizando el subrecurso ephemeralcontainers
del Pod,
que puede ser visto utilizando kubectl --raw
. En primer lugar describa el Container
efímero a añadir como una lista de EphemeralContainers
:
{
"apiVersion": "v1",
"kind": "EphemeralContainers",
"metadata": {
"name": "example-pod"
},
"ephemeralContainers": [{
"command": [
"sh"
],
"image": "busybox",
"imagePullPolicy": "IfNotPresent",
"name": "debugger",
"stdin": true,
"tty": true,
"terminationMessagePolicy": "File"
}]
}
Para actualizar los Containers efímeros de los example-pod
en ejecución:
kubectl replace --raw /api/v1/namespaces/default/pods/example-pod/ephemeralcontainers -f ec.json
Esto devolverá una nueva lista de Containers efímeros:
{
"kind":"EphemeralContainers",
"apiVersion":"v1",
"metadata":{
"name":"example-pod",
"namespace":"default",
"selfLink":"/api/v1/namespaces/default/pods/example-pod/ephemeralcontainers",
"uid":"a14a6d9b-62f2-4119-9d8e-e2ed6bc3a47c",
"resourceVersion":"15886",
"creationTimestamp":"2019-08-29T06:41:42Z"
},
"ephemeralContainers":[
{
"name":"debugger",
"image":"busybox",
"command":[
"sh"
],
"resources":{
},
"terminationMessagePolicy":"File",
"imagePullPolicy":"IfNotPresent",
"stdin":true,
"tty":true
}
]
}
Se puede ver el estado del Container efímero creado usando kubectl describe
:
kubectl describe pod example-pod
...
Ephemeral Containers:
debugger:
Container ID: docker://cf81908f149e7e9213d3c3644eda55c72efaff67652a2685c1146f0ce151e80f
Image: busybox
Image ID: docker-pullable://busybox@sha256:9f1003c480699be56815db0f8146ad2e22efea85129b5b5983d0e0fb52d9ab70
Port: <none>
Host Port: <none>
Command:
sh
State: Running
Started: Thu, 29 Aug 2019 06:42:21 +0000
Ready: False
Restart Count: 0
Environment: <none>
Mounts: <none>
...
Se puede conectar al nuevo Container efímero usando kubectl attach
:
kubectl attach -it example-pod -c debugger
Si el proceso Namespace de uso compartido está habilitado, se pueden visualizar los procesos de todos los Containers de ese Pod.
Por ejemplo, después de haber conectado, ejecute ps
en el debugger del container:
ps auxww
La respuesta es semejante a:
PID USER TIME COMMAND
1 root 0:00 /pause
6 root 0:00 nginx: master process nginx -g daemon off;
11 101 0:00 nginx: worker process
12 101 0:00 nginx: worker process
13 101 0:00 nginx: worker process
14 101 0:00 nginx: worker process
15 101 0:00 nginx: worker process
16 101 0:00 nginx: worker process
17 101 0:00 nginx: worker process
18 101 0:00 nginx: worker process
19 root 0:00 /pause
24 root 0:00 sh
29 root 0:00 ps auxww