Gestión de Etiquetas (Labels) en Recursos
Las etiquetas son pares clave-valor esenciales para organizar y seleccionar recursos en Kubernetes. A continuación, se detallan las operaciones fundamentales para manipular etiquetas tanto en Pods como en Nodos.
Inspección y Modificación de Etiquetas en Pods
Para visualizar las etiquetas asignadas a los Pods en un namespace específico, se utiliza el flag --show-labels o se especifican las claves deseadas con -L.
$ kubectl get pods -L app,role
NAME READY STATUS RESTARTS AGE APP ROLE
db-primary-01 1/1 Running 0 12m postgres primary
cache-redis-02 1/1 Running 0 45m redis replica
Para añadir una nueva etiqueta a un Pod existente, se emplea el comando label. Si la etiqueta ya existe y se desea modificar su valor, es obligatorio incluir el flag --overwrite.
$ kubectl label pod db-primary-01 environment=production
pod/db-primary-01 labeled
$ kubectl label pod db-primary-01 environment=staging --overwrite
pod/db-primary-01 labeled
Etiquetado de Nodos
El mismo principio se aplica a los nodos del clúster, lo cual es crucial para estrategias de programación basadas en hardware o zonas de disponibilidad.
$ kubectl label node worker-node-1 hardware=gpu
node/worker-node-1 labeled
$ kubectl get nodes -l hardware=gpu --show-labels
NAME STATUS ROLES AGE VERSION LABELS
worker-node-1 Ready <none> 15d v1.25.0 hardware=gpu,kubernetes.io/os=linux
Selectores de Nodos y Programación
Kubernetes ofrece dos mecanismos principales para restringir la programación de Pods a nodos específicos:
- nodeName: Una asignación directa y forzada al nombre de un nodo específico. Omite al planificador (scheduler) estándar.
- nodeSelector: Un mapa de pares clave-valor que permite al planificador seleccionar dinámicamente un nodo que coincida con todas las etiquetas especificadas.
Ejemplo Práctico con nodeSelector
En este escenario, desplegaremos una base de datos postgres asegurando que se ejecute únicamente en nodos etiquetados con almacenamiento de estado sólido (disktype=ssd).
$ kubectl label node worker-node-1 disktype=ssd
Definición del manifiesto de despliegue:
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-backend
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:14-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
value: "securepassword123"
nodeSelector:
disktype: ssd
Al aplicar el manifiesto, el planificador evaluará las restricciones y asignará el Pod al nodo correspondiente:
$ kubectl apply -f postgres-deployment.yaml
deployment.apps/postgres-backend created
$ kubectl describe pod -l app=postgres | grep -i "Node:"
Node: worker-node-1/10.0.0.5
Anotaciones (Annotations) y Metadatos
A diferencia de las etiquetas, las anotaciones no se utilizan para identificar o seleccionar objetos. Su propósito es almacenar metadatos arbitrarios y no identificativos que pueden ser consumidos por herramientas externas, librerías de clientes o controladores.
Al explorar la estructura de metadatos de un Pod mediante kubectl explain, se destacan los siguientes campos críticos:
- annotations: Mapa de clave-valor para metadatos no estructurados (ej. configuraciones de herramientas de CI/CD, hashes de configuración).
- labels: Mapa de clave-valor para identificación y selección.
- finalizers: Lista de identificadores que bloquean la eliminación del recurso hasta que los controladores responsables realicen sus tareas de limpieza (recolección de basura).
- ownerReferences: Referencias a objetos que poseen y gestionan el ciclo de vida de este recurso (ej. un ReplicaSet poseyendo un Pod).
- resourceVersion: Valor opaco utilizado para el control de concurrencia optimista y detección de cambios en el servidor API.
Ciclo de Vida y Flujo de Creación de Pods
El ciclo de vida de un Pod se define por sus fases (Phases), las cuales representan el estado macroscópico del recurso en el clúster:
- Pending: El Pod ha sido aceptado por el clúster, pero uno o más contenedorse no han sido creados o están en proceso de descarga de imágenes.
- Running: El Pod ha sido vinculado a un nodo y todos sus contenedores han sido creados. Al menos uno está en ejecución o en proceso de inicio/reinicio.
- Succeeded: Todos los contenedores han terminado de ejecutarse voluntariamente (código de salida 0) y no se reiniciarán.
- Failed: Todos los contenedores han terminado, y al menos uno ha fallado (código de salida distinto de 0 o terminación por el sistema).
- Unknown: El estado no puede obtenerse, generalmente debido a un error en la comunicación con el nodo donde reside el Pod.
Flujo de Creación Internos
La materialización de un Pod sigue una secuencia estritca a través de los componentes del plano de control y los nodos:
- El usuario envía la petición de creación al API Server.
- El API Server valida la petición y persiste el estado inicial del Pod en etcd.
- El Scheduler detecta el Pod sin nodo asignado, evalúa las restricciones (taints, tolerations, nodeSelectors) y selecciona el nodo óptimo.
- El resultado de la programación se actualiza en etcd.
- El Kubelet del nodo objetivo, que está monitoreando el API Server, detecta la asignación.
- El Kubelet invoca al runtime de contenedores (ej. containerd, CRI-O) para crear los contenedores según el manifiesto y reporta el estado final al API Server.
Exploración de Contenedores y Políticas de Reinicio
Dentro de un Pod, el Kubelet ejecuta sondas (probes) para determinar el estado de salud de los contenedores individuales:
- livenessProbe (Sondeo de supervivencia): Indica si el contenedor está en ejecución. Si falla, el Kubelet mata el contenedor y aplica la política de reinicio. Es útil para recuperar aplicaciones de estados de bloqueo (deadlocks).
- readinessProbe (Sondeo de preparación): Indica si el contenedor está listo para atender peticiones de red. Si falla, el controlador de endpoints elimina la IP del Pod de todos los Servicios coincidentes, sin reiniciar el contenedor.
Finalmente, el comportamiento ante fallos se rige por la restartPolicy a nivel de Pod, la cual aplica a todos los contenedores del mismo. Las opciones válidas son:
- Always: Reinicia el contenedor indefinidamente si falla (predeterminada).
- OnFailure: Reinicia el contenedor solo si termina con un código de error distinto de cero.
- Never: Nunca reinicia el contenedor, independientemente del resultado de su ejecución.