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.

diagrama de Pod

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?

  1. 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.
  2. 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.
  3. Facilidad de uso. Los usuarios no necesitan ejecutar sus propios administradores de procesos, para propagación de señales, códigos de salida, etc.
  4. 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:

  1. El usuario envía un comando para eliminar Pod, con un período de gracia predeterminado (30s)
  2. 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.
  3. El Pod aparece como "Terminando" cuando aparece en los comandos del cliente
  4. (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.
    1. 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).
    2. 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.
  5. (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.
  6. Cuando expira el período de gracia, todos los procesos que todavía se ejecutan en el Pod se eliminan con SIGKILL.
  7. 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 - Pod Preset

FEATURE STATE: Kubernetes v1.6 [alpha]

Esta página provee una descripción general de los PodPresets, los cuales son los objetos que se utilizan para inyectar cierta información en los Pods en el momento de la creación. Esta información puede incluir secretos, volúmenes, montajes de volúmenes y variables de entorno.

Entendiendo los Pod Presets

Un PodPreset es un recurso de la API utilizado para poder inyectar requerimientos adicionales de tiempo de ejecución en un Pod en el momento de la creación. Se utilizan los selectores de etiquetas para especificar los Pods a los que se aplica un PodPreset determinado.

El uso de un PodPreset permite a los autores de plantillas de Pods no tener que proporcionar explícitamente toda la información de cada Pod. De esta manera, los autores de plantillas de Pods que consuman un determinado servicio no tendrán que conocer todos los detalles de ese servicio.

Habilitando un PodPreset en su clúster

Con el fin de utilizar los Pod Presets en un clúster debe asegurarse de lo siguiente:

  1. Que se ha configurado el tipo de API settings.k8s.io/v1alpha1/podpreset. Esto se puede hacer, por ejemplo, incluyendo settings.k8s.io/v1alpha1=true como valor de la opción --runtime-config en el servidor API. En minikube se debe añadir el flag --extra-config=apiserver.runtime-config=settings.k8s.io/v1alpha1=true cuando el clúster se está iniciando.

  2. Que se ha habilitado el controlador de admisión PodPreset. Una forma de hacer esto es incluir PodPreset como valor de la opción --enable-admission-plugins especificada para el servidor API. En minikube se debe añadir el flag

    --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodPreset
    

    cuando el clúster se está iniciando.

Cómo funciona

Kubernetes provee un controlador de admisión (PodPreset) que, cuando está habilitado, aplica los Pod Presets a las peticiones de creación de Pods entrantes. Cuando se realiza una solicitud de creación de Pods, el sistema hace lo siguiente:

  1. Obtiene todos los PodPresets disponibles para usar.
  2. Verifica si los selectores de etiquetas de cualquier PodPreset correspondan con las etiquetas del Pod que se está creando.
  3. Intenta fusionar los diversos recursos definidos por el PodPreset dentro del Pod que se está creando.
  4. Si se llegase a producir un error al intentar fusionar los recursos dentro del Pod, lanza un evento que documente este error, luego crea el Pod sin ningún recurso que se inyecte desde el PodPreset.
  5. Escribe una nota descriptiva de la especificación de Pod modificada resultante para indicar que ha sido modificada por un PodPreset. La nota descriptiva presenta la forma podpreset.admission.kubernetes.io/podpreset-<pod-preset name>: "<resource version>".

Cada Pod puede ser correspondido por cero o más Pod Presets; y cada PodPreset puede ser aplicado a cero o más Pods. Cuando se aplica un PodPreset a una o más Pods, Kubernetes modifica la especificación del Pod. Para los cambios a env, envFrom, y volumeMounts, Kubernetes modifica la especificación del Container para todos los Containers en el Pod; para los cambios a volumes, Kubernetes modifica la especificación del Pod.

Nota:

Un Pod Preset es capaz de modificar los siguientes campos en las especificaciones de un Pod en caso de ser necesario:

  • El campo .spec.containers.
  • El campo .spec.initContainers

Deshabilitar un Pod Preset para un Pod específico

Puede haber casos en los que se desee que un Pod no se vea alterado por ninguna posible modificación del Pod Preset. En estos casos, se puede añadir una observación en el Pod .spec de la siguiente forma: podpreset.admission.kubernetes.io/exclude: "true".

Siguientes pasos

Ver Inyectando datos en un Pod usando PodPreset

Para más información sobre los detalles de los trasfondos, consulte la propuesta de diseño de PodPreset.

3 - Containers Efímeros

FEATURE STATE: Kubernetes v1.22 [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.22/#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 los EphemeralContainers 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