Implementar aplicaciones en Kubernetes

KubernetesBeginner
Practicar Ahora

Introducción

En la primera sección del curso exploraste un clúster de Kubernetes sin modificarlo. Aprendiste que kubectl envía solicitudes al servidor de API y que los controladores de Kubernetes comparan continuamente el estado deseado con el estado real.

Ahora realizarás tus primeras solicitudes sobre una aplicación. En lugar de indicar a Kubernetes cada acción de bajo nivel que debe ejecutar, describirás en archivos YAML el resultado que quieres obtener. Estos archivos se denominan manifiestos. Kubernetes almacena las definiciones de esos objetos y trabaja para hacerlas realidad.

Comenzarás con un Pod individual para que sea fácil observar la estructura básica de un manifiesto. Después definirás un Deployment que administre dos Pods. Al comparar el Pod independiente con los Pods administrados por un Deployment, entenderás por qué normalmente se prefieren los controladores de nivel superior para las aplicaciones.

Este laboratorio se centra deliberadamente en la creación de cargas de trabajo. Más adelante aprenderás a exponer aplicaciones mediante Services, cuando ya te resulten familiares los Pods, las etiquetas y los Deployments.

Comprender los objetos declarativos de Kubernetes

Inicio del entorno: Este laboratorio inicia un clúster de Kubernetes completo. La configuración del plano de control, el nodo y los componentes de red suele tardar 2–3 minutos. Espera pacientemente a que el entorno termine de cargarse antes de comenzar.

En este paso relacionarás el concepto de estado deseado del laboratorio anterior con los manifiestos de Kubernetes y prepararás un espacio de trabajo para tus primeras definiciones de aplicaciones.

De los comandos al estado deseado

Kubernetes admite dos estilos generales de administración:

  • Con un comando imperativo, solicitas directamente una acción, como «crear un Pod llamado first-nginx».
  • Con un manifiesto declarativo, guardas en un archivo la configuración deseada del objeto y le pides a Kubernetes que ajuste el clúster para que coincida con ella.

Los archivos declarativos son útiles porque puedes leerlos antes de aplicar un cambio, volver a aplicarlos, revisar diferencias y almacenarlos en un sistema de control de versiones. En este curso se dará prioridad al estilo declarativo.

Todos los objetos de Kubernetes que devuelve la API tienen varios campos importantes de nivel superior:

  • apiVersion selecciona el grupo y la versión de la API de Kubernetes.
  • kind identifica el tipo de objeto, como Pod o Deployment.
  • metadata proporciona la identidad del objeto, incluido su nombre y sus etiquetas.
  • spec describe el estado deseado del objeto.
  • status informa del estado observado. Kubernetes normalmente lo completa después de crear el objeto; no se escribe en el manifiesto.

Esto forma un ciclo importante:

manifest spec -> API server stores desired state -> controllers act -> object status reports actual state

Preparar el directorio de manifiestos

Ve al directorio preparado para este laboratorio:

cd /home/labex/project/k8s-manifests

Confirma tu ubicación con pwd, que significa print working directory:

pwd
/home/labex/project/k8s-manifests

Crea un archivo breve de notas con los cuatro campos de manifiesto que utilizarás. El comando printf escribe cada cadena entre comillas en una línea independiente y > redirige esa salida a un archivo, reemplazándolo si ya existe.

printf '%s\n' apiVersion kind metadata spec > manifest-fields.txt

Muestra el archivo para verificarlo:

cat manifest-fields.txt
apiVersion
kind
metadata
spec

Este archivo de notas es una pequeña comprobación de aprendizaje: los cuatro campos aparecerán en los dos manifiestos que crearás a continuación.

Escribir y validar un manifiesto de Pod

En este paso escribirás un manifiesto YAML para un Pod y validarás su estructura antes de enviarlo al clúster.

Conocer el Pod

Un Pod es el objeto desplegable más pequeño de Kubernetes. Proporciona a uno o varios contenedores estrechamente relacionados una identidad de red y un contexto de almacenamiento compartidos. En la mayoría de los ejemplos iniciales se utiliza un contenedor por Pod.

El Pod de este laboratorio ejecutará NGINX, un servidor web ligero. La imagen está fijada en nginx:1.27-alpine. Fijar una versión hace que el resultado sea más reproducible que utilizar la etiqueta cambiante latest. La imagen ya se ha almacenado en la caché del clúster, por lo que el trabajo no depende de una descarga desde Internet.

Crear el archivo YAML

Asegúrate de estar en el directorio de manifiestos:

cd /home/labex/project/k8s-manifests

Utilizarás un documento aquí para crear el archivo. El shell redirige todas las líneas comprendidas entre <<'EOF' y el EOF de cierre a first-pod.yaml. Las comillas del primer EOF impiden que el shell expanda los caracteres especiales dentro del YAML.

cat <<'EOF' > first-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: first-nginx
  namespace: default
  labels:
    app: first-nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.27-alpine
      imagePullPolicy: IfNotPresent
      ports:
        - name: http
          containerPort: 80
          protocol: TCP
EOF

YAML representa la jerarquía mediante la indentación. Usa espacios de forma coherente; las tabulaciones pueden invalidar el YAML. Un guion, como en - name: nginx, inicia un elemento de una lista.

Lee el objeto de arriba abajo:

  • apiVersion: v1 selecciona la API principal utilizada por los Pods.
  • kind: Pod declara el tipo de recurso.
  • metadata.name asigna al Pod el nombre estable first-nginx.
  • metadata.namespace: default lo coloca en el espacio de nombres habitual para aplicaciones del curso, en lugar de un espacio de nombres del sistema.
  • metadata.labels añade app=first-nginx, que posteriormente puede utilizarse para seleccionar este Pod.
  • spec.containers es una lista de los contenedores que debe ejecutar el Pod.
  • imagePullPolicy: IfNotPresent utiliza la imagen almacenada en la caché cuando está disponible.
  • El puerto http utiliza containerPort: 80 y el protocolo TCP. Documenta dónde escucha NGINX dentro del contenedor; no expone el Pod fuera del clúster.

Validar antes de crear

Usa una ejecución de prueba del lado del cliente para analizar el archivo sin crear el Pod. La opción -f significa file, y --dry-run=client mantiene la solicitud en el equipo local:

kubectl apply --dry-run=client -f first-pod.yaml
pod/first-nginx created (dry run)

Las palabras dry run son fundamentales: la sintaxis es válida, pero el clúster todavía no ha cambiado.

Pide a kubectl que muestre el objeto normalizado como YAML:

La opción de salida -o significa output format. Al indicar yaml, solicitas que kubectl represente el objeto analizado como YAML en lugar de imprimir únicamente una línea de resultado:

kubectl apply --dry-run=client -f first-pod.yaml -o yaml

Verás tus campos y también valores predeterminados añadidos por el cliente. Esto resulta útil para detectar errores de indentación, nombres de campos y tipos antes de realizar una aplicación real.

Crear y examinar tu primer Pod

En este paso aplicarás el manifiesto validado, observarás cómo Kubernetes lleva el Pod hacia su estado deseado y examinarás el objeto resultante.

Aplicar el manifiesto

Si es necesario, ve al directorio de manifiestos:

cd /home/labex/project/k8s-manifests

Aplica el archivo sin la opción de ejecución de prueba:

kubectl apply -f first-pod.yaml
pod/first-nginx created

kubectl apply envía el objeto al servidor de API. El servidor de API almacena la especificación deseada del Pod, y el planificador junto con kubelet colaboran para ejecutarlo en el nodo.

Esperar a que esté listo

La creación de un Pod es asíncrona: kubectl apply puede devolver el resultado antes de que el contenedor esté listo. Usa kubectl wait para esperar a que el Pod cumpla la condición Ready. El comando termina correctamente cuando la condición se vuelve verdadera o falla después de 60 segundos:

kubectl wait --for=condition=Ready pod/first-nginx --timeout=60s
pod/first-nginx condition met

Ahora enumera el Pod. Este laboratorio repite -o wide porque cada laboratorio debe poder utilizarse de forma independiente: -o selecciona un formato de salida y wide añade campos como la IP del Pod y el nombre del nodo:

kubectl get pod first-nginx -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP           NODE
first-nginx   1/1     Running   ...        ...   ...          labex-v135

READY=1/1 significa que el único contenedor está listo, mientras que STATUS=Running indica la fase del Pod. La vista ampliada también muestra la IP del Pod y el nodo asignado. Las IP de los Pods y sus edades son valores generados, por lo que pueden variar.

Examinar las etiquetas y la propiedad

Muestra las etiquetas del Pod:

kubectl get pod first-nginx --show-labels

Busca app=first-nginx. Las etiquetas se almacenan junto con el objeto y serán importantes cuando los Deployments y los Services seleccionen Pods.

Pregunta a Kubernetes si otro objeto controla este Pod. -o jsonpath='...' extrae campos concretos en lugar de mostrar el objeto completo. La expresión recorre metadata.ownerReferences; {"\n"} añade un salto de línea final para que el indicador del shell aparezca en la línea siguiente:

kubectl get pod first-nginx -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}{"\n"}'
Owner:

El propietario aparece vacío porque creaste directamente este Pod independiente. Si se elimina, ningún controlador de nivel superior sabrá que debe reemplazarlo. En los pasos siguientes lo compararás con Pods administrados.

Comprobación del paso

Convertiste un archivo local de estado deseado en un objeto de Kubernetes en ejecución. La API aceptó el Pod, el planificador lo asignó y kubelet preparó su contenedor. El Pod existe, pero ningún controlador administra su ciclo de vida.

Definir un Deployment

En este paso definirás un Deployment que solicitará a Kubernetes mantener dos copias de un Pod NGINX.

¿Por qué utilizar un Deployment?

Un Pod independiente es útil para aprender, pero las aplicaciones normalmente necesitan un controlador. Un Deployment proporciona un número deseado de réplicas y una plantilla de Pod. Crea un ReplicaSet, y el ReplicaSet mantiene los Pods solicitados.

La cadena de propiedad es:

Deployment -> ReplicaSet -> Pods -> containers

Si desaparece un Pod administrado, el ReplicaSet detecta que el número real de réplicas es inferior al deseado y crea un reemplazo. En laboratorios posteriores utilizarás Deployments para escalar y realizar actualizaciones progresivas.

Crear el manifiesto del Deployment

Vuelve al directorio de manifiestos:

cd /home/labex/project/k8s-manifests

Crea course-web-deployment.yaml mediante un documento aquí:

cat <<'EOF' > course-web-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: course-web
  labels:
    app: course-web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: course-web
  template:
    metadata:
      labels:
        app: course-web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
EOF

El Deployment utiliza apps/v1, la API estable para Deployments. Su spec introduce tres campos importantes:

  • replicas: 2 es el número deseado de Pods.
  • selector.matchLabels identifica los Pods que administra el Deployment.
  • template es el modelo utilizado para crear cada Pod.

El selector y template.metadata.labels utilizan ambos app: course-web. Deben coincidir; de lo contrario, el Deployment no podría identificar los Pods creados a partir de su propia plantilla.

Validar el Deployment

Analiza el manifiesto sin modificar el clúster:

kubectl apply --dry-run=client -f course-web-deployment.yaml
deployment.apps/course-web created (dry run)

Usa kubectl diff para comparar el manifiesto con el estado activo. Un código de salida distinto de cero simplemente significa que el objeto cambiaría; || true evita que el shell interprete esa diferencia esperada como un error:

kubectl diff -f course-web-deployment.yaml || true

Como course-web todavía no existe, la salida muestra el objeto completo como una adición, con líneas que comienzan por +. A diferencia de apply, diff no modifica el clúster.

Implementar y examinar la aplicación administrada

En este paso aplicarás el Deployment, esperarás a que sus dos réplicas estén listas y examinarás los recursos relacionados seleccionados mediante su etiqueta compartida.

Aplicar y esperar al Deployment

Primero ve al directorio que contiene el manifiesto. Después aplica el estado deseado guardado; -f indica a kubectl que debe leerlo desde ese archivo:

cd /home/labex/project/k8s-manifests
kubectl apply -f course-web-deployment.yaml
deployment.apps/course-web created

Espera a que finalice el despliegue progresivo del Deployment. Un despliegue progresivo es el proceso de llevar los Pods del Deployment hasta la plantilla y el número de réplicas deseados:

kubectl rollout status deployment/course-web --timeout=60s
deployment "course-web" successfully rolled out

Enumera el Deployment:

kubectl get deployment course-web
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
course-web   2/2     2            2           ...

READY=2/2 significa que las dos réplicas deseadas están listas. UP-TO-DATE=2 significa que ambas utilizan la plantilla actual del Pod, y AVAILABLE=2 indica que las dos están disponibles.

Examinar los recursos relacionados

Usa el selector de etiquetas -l app=course-web para enumerar los recursos relacionados:

kubectl get deployment,replicaset,pods -l app=course-web

La salida contiene un Deployment, un ReplicaSet y dos Pods. Los sufijos generados del ReplicaSet y de los Pods pueden variar:

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/course-web   2/2     2            2           ...

NAME                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/course-web-...          2         2         2       ...

NAME                              READY   STATUS    RESTARTS   AGE
pod/course-web-...-...            1/1     Running   ...        ...
pod/course-web-...-...            1/1     Running   ...        ...

Esta vista demuestra que las dos réplicas deseadas del Deployment se han convertido en dos Pods listos. En el paso siguiente seguirás las conexiones de propiedad entre estos recursos.

Comprobación del paso

Aplicaste un manifiesto de Deployment y esperaste a que alcanzara su estado deseado. Kubernetes creó un ReplicaSet y dos Pods, y la etiqueta compartida app=course-web te permitió enumerarlos como un único grupo de aplicaciones.

Seguir la propiedad entre controladores

En este paso seguirás las referencias de propietario de Kubernetes desde un Pod administrado hasta su ReplicaSet y, después, hasta el Deployment. También volverás a aplicar el manifiesto para observar la idempotencia declarativa.

Examinar el propietario de un Pod

Guarda el nombre de uno de los Pods generados en una variable del shell. La sintaxis NAME=$(command) es una sustitución de comandos: el shell ejecuta el comando y guarda su salida en NAME. Aquí, -l app=course-web selecciona los Pods coincidentes y JSONPath extrae el nombre generado del primer Pod:

POD_NAME=$(kubectl get pods -l app=course-web -o jsonpath='{.items[0].metadata.name}')

Imprímelo para saber qué Pod se seleccionó:

echo "$POD_NAME"

Ahora examina su propietario directo. Las comillas alrededor de "$POD_NAME" pasan el nombre almacenado como un único argumento seguro del comando:

kubectl get pod "$POD_NAME" -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
Owner: ReplicaSet/course-web-...

A diferencia del Pod independiente first-nginx, un Pod administrado por un Deployment tiene como propietario un ReplicaSet. El ReplicaSet, a su vez, pertenece al Deployment.

Muestra el propietario del ReplicaSet. El primer comando repite el mismo patrón de sustitución de comandos, pero esta vez guarda el nombre de un ReplicaSet en RS_NAME:

RS_NAME=$(kubectl get replicaset -l app=course-web -o jsonpath='{.items[0].metadata.name}')
kubectl get replicaset "$RS_NAME" -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
Owner: Deployment/course-web

Volver a aplicar el estado deseado

Aplica de nuevo el mismo manifiesto:

kubectl apply -f course-web-deployment.yaml
deployment.apps/course-web unchanged

unchanged demuestra una propiedad declarativa importante: volver a aplicar repetidamente el mismo estado deseado es seguro. Kubernetes solo necesita actuar cuando la configuración deseada y la real son diferentes.

Comprobación del paso

Ahora tienes un Pod independiente y una aplicación administrada por un Deployment. Ambos ejecutan contenedores, pero el Deployment añade una jerarquía de controladores que mantiene dos réplicas y proporciona una base para futuras operaciones de escalado y actualizaciones progresivas.

Resumen

Pasaste de explorar el clúster en modo de solo lectura a administrar aplicaciones de forma declarativa. Aprendiste las funciones de apiVersion, kind, metadata y spec; validaste manifiestos mediante ejecuciones de prueba del lado del cliente; creaste y examinaste un Pod independiente; e implementaste dos réplicas administradas mediante un Deployment.

Lo más importante es que observaste la cadena de propiedad de los controladores, desde el Deployment hasta el ReplicaSet y los Pods. Esta base de estado deseado te prepara para diagnosticar aplicaciones, exponerlas mediante Services, escalar réplicas y realizar actualizaciones progresivas en las siguientes secciones del curso.