¡Redoble de tambores, por favor, redoble de tambores que suene como Matador de Los Fabulosos Cadillacs, redoble aquí! 🥁
Dónde estás Matador, el YAML te está buscando
Ya llegamos a la parte que realmente nos gusta, bueno, la que a mí me gusta, manos a la obra. En los dos posts anteriores (las bases y qué es realmente un Pod) vimos mucha teoría, y créeme que no fue en vano, pero hoy vamos a crear nuestro primer Pod y realmente usar K8s. Y de paso lo vamos a romper a propósito MUAJAJAJA, porque así es como se aprende de verdad 😈
Antes de empezar, lo que necesitas 🧰
Pero antes, para crear algo en K8s necesitamos dos herramientas:
- kubectl, el cliente de línea de comandos. Es el que habla con el API server del master, ese que vimos en el primer post como la “aduana” por la que pasa todo. Tú le dices qué quieres, él se lo pasa al cluster. Recuerda los posts pasados, porfa
- minikube, que nos levanta un cluster de K8s completo en nuestra propia máquina (un master y un nodo metidos en uno solo, normalmente dentro de un contenedor o una VM). Es nuestro mar de juguete 🌊, uno donde puedes hundir barcos sin que nadie te cobre por ello, sí, te estoy hablando a ti, AWS y Google Cloud.
Si no los tienes instalados, aquí están las guías oficiales de kubectl y de minikube, si no, ve unos tutoriales de YT o TK 👻. Cuando los tengas instalados, levantamos el cluster:
minikube start
Tarda un ratito la primera vez porque tiene que descargar cosas, ve por un café ☕ o un mate 🧉

Nuestro primer Pod 🫛
Vamos a usar el comando kubectl run, que es la forma más rápida de crear un Pod. Corre esto en tu terminal:
kubectl run ourfirstpod --image=nginx:alpine
Y vas a ver que la salida de la terminal es similar a esta:
pod/ourfirstpod created
Okay, ¿pero qué hace exactamente este comando? Si vamos a la documentación, la sintaxis es algo así:
kubectl run NAME --image=image [--env="key=value"] [--port=port] [--dry-run=server|client] [--overrides=inline-json] [--command] -- [COMMAND] [args...]
Nosotros solo le pasamos dos cosas: el nombre del Pod (ourfirstpod) y la imagen que queremos correr (nginx:alpine, que es nginx sobre Alpine Linux, una imagen bien chiquita y minimalista de sistemas Unix, googlea para más info). Todo lo demás es opcional.
Un detalle que debemos tener en cuenta, si nos fijamos, escribimos el nombre todo en minúscula. No es capricho mío, K8s sigue el RFC 1123 para los nombres, que solo permite minúsculas, números, guiones y puntos. Si intentas crear un Pod llamado ourfirstpod-Nginx, K8s te lo rechaza en la cara, sin anestesia. A mí me pasó más de una vez, por si te sirve de consuelo 😅, soy team #CamelCase, pero no puedo aquí 🙄.
¿Está corriendo?
Para ver nuestros Pods usamos:
kubectl get pods
NAME READY STATUS RESTARTS AGE
ourfirstpod 1/1 Running 0 2m43s
Nice, ¡todo va bien! Está en Running. Pero esta salida tiene más información de la que parece, vamos columna por columna a ver qué significa cada cosa:
- READY: cuántos contenedores del Pod están listos sobre el total que tiene.
1/1es un contenedor listo de un contenedor,1/2significaría que algo anda mal con uno de los dos (spoiler alert! ⚠️, más adelante lo vamos a ver en vivo). - STATUS: en qué fase de su vida está el Pod. Puede ser
Pending(esperando que lo asignen a un nodo o que baje la imagen),Running,SucceededoFailed(los dos últimos para cosas que corren y terminan), y también aparecen razones de error comoErrImagePulloCrashLoopBackOff, que ya conoceremos. - RESTARTS: cuántas veces K8s ha tenido que reiniciar un contenedor del Pod. Si este número sube y sube, tenemos un problema.
- AGE: cuánto tiempo lleva vivo.

Ver un Pod a detalle 🔍
Bien, ya sabemos cómo crear un Pod y ver si corre, pero eso es apenas la superficie. Como buenos devops tenemos que ir más allá siempre, y para eso nada mejor que un caso hipotético: ¿qué pasa si le pedimos a K8s una imagen que no existe? Qué se yo, nginx:rivaldito 😌
kubectl run oursecondpod --image=nginx:rivaldito
pod/oursecondpod created
Si miras las salidas de K8s, nos dice que lo creó sin problema. Y es lógico, ya que K8s acepta tu petición (el Pod “existe” como objeto en el cluster) y recién después intenta bajar la imagen. Es como pedir un taxi a una dirección que no existe, el taxi sale igual y recién cuando llega se da cuenta.
Ahora veamos qué tal está nuestro Pod realmente corriendo:
kubectl get pods
NAME READY STATUS RESTARTS AGE
ourfirstpod 1/1 Running 0 45m
oursecondpod 0/1 ErrImagePull 0 9s
ErrImagePull. Okay parece que no todo está bien como pensábamos, K8s ya nos está dando una pista, pero no nos cuenta toda la historia. Pues para estas situaciones existe el comando describe y si lo corremos:
kubectl describe pod oursecondpod
La salida es larguísima (la recorto un poco para no alargar este post, pero te invito a correrla tú y ver toda la salida de la terminal):
Name: oursecondpod
Namespace: default
Node: minikube/192.168.49.2
Labels: run=oursecondpod
Status: Pending
IP: 10.244.0.6
Containers:
oursecondpod:
Image: nginx:rivaldito
State: Waiting
Reason: ImagePullBackOff
Ready: False
Restart Count: 0
...
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready False
ContainersReady False
PodScheduled True
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m56s default-scheduler Successfully assigned default/oursecondpod to minikube
Normal Pulling 83s (x4 over 2m55s) kubelet Pulling image "nginx:rivaldito"
Warning Failed 82s (x4 over 2m53s) kubelet Failed to pull image "nginx:rivaldito": ... manifest for nginx:rivaldito not found: manifest unknown
Warning Failed 82s (x4 over 2m53s) kubelet Error: ErrImagePull
Normal BackOff 4s (x11 over 2m53s) kubelet Back-off pulling image "nginx:rivaldito"
Warning Failed 4s (x11 over 2m53s) kubelet Error: ImagePullBackOff
Aquí hay muchísimas cosas que iremos viendo en futuros posts (las Conditions, el QoS, las Tolerations, los Volumes…), pero la parte que nos interesa en este momento es la última, Events, que es básicamente la bitácora del capi K8s sobre el Pod, si la leemos vemos que tenemos esto:
Scheduled: el scheduler (te acuerdas de él del primer post, el que decide en qué nodo cae cada Pod) asignó nuestro Pod al nodominikube. Hasta aquí todo bien.Pulling: el kubelet de ese nodo (el capataz local) intenta descargar la imagennginx:rivaldito.Failed: la descarga falla, y el mensaje es clarísimo,manifest unknown. El registro de imágenes nos dice “ese tag no existe, hermano”.ErrImagePulleImagePullBackOff: el kubelet reintenta, pero no a lo loco. Entre cada intento espera más tiempo (el famoso back-off, que va creciendo hasta un tope de unos 5 minutos) para no bombardear el registro. Por eso ves losx4 over 2m55s, son los intentos que van acumulados.
Y ojo con esto, ErrImagePull es el error en el momento del fallo, e ImagePullBackOff es el estado de “estoy esperando para volver a intentarlo”. Son dos caras de lo mismo en el ciclo de K8s, y verás que el STATUS cambia entre una y otra.

Tarea para la casa: Sí, me puse como profe, pero así se aprende, ahora haz un describe de nuestro primer Pod, el que no falló, y compara los eventos. Verás la historia de un Pod que sí salió bien: asignado, imagen descargada, contenedor creado, contenedor iniciado.
Eliminar Pods 🗑️
Ya sabemos crear, ya sabemos ver si corre e incluso leer su historial de vida, pero todo tiene su final, así que toca aprender a borrar. Es facilísimo:
kubectl delete pod oursecondpod
pod "oursecondpod" deleted from default namespace
Se pueden pasar varios nombres separados por espacio (kubectl delete pod uno dos tres). Un dato curioso: si fuiste rápido y borraste un Pod que estaba sano, vas a notar que tarda unos 30 segundos en desaparecer. No se colgó, es que K8s le avisa amablemente al contenedor que se va a apagar (una señal SIGTERM), le da 30 segundos de gracia para cerrar sus cosas, y si no obedece, lo apaga a la fuerza (SIGKILL). Educado pero firme, así es K8s 🫡
¿Y dónde está el contenedor? 🕵️
Hagamos un ejercicio que va a resultar muy obvio, pero a mí no me gusta dejar nada a la suposición. Voy a asumir que usas Docker como runtime, ya que es el “común” en la industria. Con nuestro ourfirstpod corriendo, ejecuta:
docker ps
Si usaste minikube con el driver de Docker (lo más común), vas a ver un único contenedor llamado minikube. ¿Y nginx? Pues está adentro, porque ese contenedor es nuestro nodo de K8s. Entremos a mirar:
minikube ssh
docker ps
Ahora sí, ahí está nuestro nginx:alpine, con un nombre medio raro tipo k8s_ourfirstpod_ourfirstpod_default_.... Y si te fijas bien, también aparece otro con un nombre que empieza por k8s_POD_... que corre una imagen llamada pause. ¿Te suena? Es el contenedor invisible del post anterior que sostiene los namespaces del Pod. 😎
(Si tu minikube usa containerd en vez de Docker, el comando equivalente dentro del nodo es sudo crictl ps.)
Ahora, el punto del ejercicio, ya que me desvié un poco. El contenedor existe, sí, pero en K8s nosotros manejamos Pods, no contenedores. Si te metes a apagar o modificar ese contenedor directamente con Docker, K8s no se entera de lo que hiciste (o peor, se entera y lo reinicia, o se confunde con el estado) y te puedes armar un problema considerable. Los contenedores son un detalle de implementación del nodo, el cluster los administra, tú no, como regla, te deberías olvidar de Docker al usar K8s en despliegues a menos que sea muy necesario.
Entonces, ¿cómo entro a un contenedor?
Muy lógico que te preguntes eso “si no puedo usar Docker, ¿cómo C3$%#$%@ entro a la consola de un contenedor?”. K8s tiene su propio equivalente, kubectl exec:
kubectl exec -ti ourfirstpod -- sh
El -ti te da una terminal interactiva, y el -- separa los flags de kubectl del comando que quieres ejecutar adentro (en este caso sh). Como nuestro Pod tiene un solo contenedor, K8s sabe a cuál entrar. Cuando tengamos varios en un mismo Pod usaremos -c nombre-del-contenedor para elegir, y de hecho lo vamos a necesitar más abajo.
Ver los logs 📜
Igual de importante, o más: los logs. Cuando algo falla en un Pod, 9 de cada 10 veces la respuesta está ahí.
kubectl logs ourfirstpod -f
El -f (de follow) deja la terminal “pegada” mostrando los logs en vivo, como el tail -f de toda la vida. Sin él, solo te muestra lo que hay hasta ahora y se acaba. Entre describe (qué le pasó al Pod desde afuera) y logs (qué dijo la aplicación desde adentro) tienes el 90% del debugging de K8s cubierto.

Los manifiestos YAML 📜
Hasta ahora hemos creado cosas lanzando comandos, que es genial para aprender y para pruebas rápidas. Pero en la vida real casi nadie crea infraestructura así. Lo que se usa son los manifiestos, archivos YAML que describen lo que queremos que exista en el cluster.
Antes de explicar qué es un manifiesto, podemos verlo con nuestros propios ojos. Pídele a K8s el YAML de nuestro primer Pod:
kubectl get pod ourfirstpod -o yaml
¡Pruébalo! Verás una salida gigantesca. Pues eso es, básicamente, el objeto Pod tal cual K8s lo guarda internamente. Cuando corriste kubectl run, kubectl armó por ti una descripción así (bastante más chica, la parte gorda la rellenó K8s con valores por defecto y con el estado actual) y se la mandó al API server. O sea que ese YAML siempre estuvo ahí, tras bambalinas, y kubectl run era solo un atajo para no escribirlo 🎭
¿Por qué YAML y no comandos?
Imaginemos que tenemos que desplegar un servicio con cinco contenedores, tres réplicas, ciertos puertos, variables de entorno, límites de memoria y un montón de cosas más… Con comandos sueltos tendrías que acordarte de cada flag y repetirlo cada vez que algo falle o que quieras montarlo en otro cluster. Esto realmente es inviable, y propenso a errores humanos.
Detrás de esto hay una idea importante, la diferencia entre dos estilos:
- Imperativo: le dices a K8s qué hacer, paso a paso. “Crea este Pod.” “Borra este otro.” Es lo que hicimos con
kubectl runykubectl delete. - Declarativo: le dices a K8s cómo quieres que se vea el mundo y que se encargue él de llegar ahí. “Quiero que existan estos dos Pods con esta configuración.”
Los manifiestos son el estilo declarativo, y es el corazón de K8s. Lo que buscamos hacer siempre es describir el estado deseado, K8s compara con el estado actual, y si no coinciden, actúa hasta igualarlos. Si te acuerdas del primer post, ese vaivén es justamente el trabajo del master (el API server guarda lo que quieres, y los controllers se pasan la vida comparando). Una analogía del mar, no le decimos al capitán “gira el timón 15 grados a babor”, le dices “llévame al puerto de Cartagena” y él decide cómo, es esto lo que nosotros buscamos.
Y hay un bonus enorme en todo esto, el que a mí más me convenció, un archivo YAML es texto, así que lo puedes meter en Git. Tienes historial de cada cambio, quién lo hizo, cuándo, y puedes volver atrás si algo explota. Con comandos sueltos en la terminal, eso es muchísimo más difícil, porque tu “documentación” es tu historial de bash y la memoria de quien estuviera de guardia. Esto, por cierto, tiene nombre (Infrastructure as Code), y llevado un paso más allá (que el cluster se sincronice solo con lo que hay en Git) se llama GitOps. Solo dejo la semillita 🌱 para que lo investigues tú!

Nuestro primer manifiesto
Abre una terminal y crea un archivo llamado pod.yaml. (Yo monté un repositorio en GitHub con todo lo que hicimos en este post, por si prefieres descargarlo en vez de copiar y pegar.)
¿De dónde sacamos el contenido? Hay varias formas:
- Copiar la salida de
kubectl get pod ourfirstpod -o yamly limpiarla (muy educativo, pero es un montón de ruido). - La didáctica: buscar en Google “kubernetes pod” y entrar a la documentación oficial de Pods, que trae un ejemplo sencillo.
- La tercera (jajaja): preguntarle a una IA. Funciona bien, pero te pone las cosas demasiado fáciles, y cuando aprendes algo conviene sufrir un poquito.
Hay una cuarta que me gusta mucho, y es pedirle a kubectl que genere el YAML por ti sin crear nada:
kubectl run nginx --image=nginx:1.14.2 --dry-run=client -o yaml
El --dry-run=client significa “simula, no crees nada de verdad”, y -o yaml imprime lo que habría enviado. Es un truco de todos los días, porque te da un esqueleto limpio para editar.
Pero en este caso usemos el de la documentación, que es este:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Cópialo en tu pod.yaml y vamos a desglosarlo línea por línea.

apiVersion
Le dice a K8s con qué versión de su API estamos hablando. Para los Pods es v1. ¿Y cómo sabemos qué valor poner? Preguntándole al cluster:
kubectl api-versions
admissionregistration.k8s.io/v1
apiextensions.k8s.io/v1
apiregistration.k8s.io/v1
apps/v1
authentication.k8s.io/v1
authorization.k8s.io/v1
autoscaling/v1
autoscaling/v2
batch/v1
certificates.k8s.io/v1
coordination.k8s.io/v1
discovery.k8s.io/v1
events.k8s.io/v1
flowcontrol.apiserver.k8s.io/v1
networking.k8s.io/v1
node.k8s.io/v1
policy/v1
rbac.authorization.k8s.io/v1
resource.k8s.io/v1
scheduling.k8s.io/v1
storage.k8s.io/v1
v1
Es la lista de todas las versiones de la API que nuestro cluster entiende. Fíjate que tienen la forma grupo/versión, y que la última, v1, no tiene grupo. Esa es el “grupo core”, el de los recursos más básicos de K8s (Pods, Services, Namespaces…). Los demás se fueron organizando por grupos con el tiempo, como apps/v1 (donde viven los Deployments, que veremos pronto) o batch/v1 (para los Jobs). Por eso un Pod lleva v1 y no batch/v1, ojo con confundirse.
kind
Qué tipo de recurso queremos crear. K8s es un orquestador, y como todo buen director de orquesta dirige una infinidad de instrumentos 🎻. Para ver todos los que sabe manejar:
kubectl api-resources
No pego la salida porque es larguísima, míralo tú mismo, go aheaaaad 😏. Verás una tabla con el nombre, el grupo de API y el KIND de cada recurso, que es justo el valor que va en este campo. Los nombres son sensibles a mayúsculas: Pod sí, pod no.
metadata
La “cédula” del objeto: su nombre y, opcionalmente, labels y otros datos. Por ahora solo nos importa name, que es el nombre que tendrá el Pod. Los labels los dejamos para más abajo, ya verás.
spec
La especificación, o sea, qué queremos que contenga y cómo debe comportarse el recurso. Cada kind tiene su propio spec. En el caso de un Pod, lo principal es la lista containers, y cada contenedor lleva su name, su image y, opcionalmente, los puertos. Fíjate en el guion (-) antes de name: en YAML eso significa “elemento de una lista”, y es la forma de decir que aquí podríamos poner más contenedores.
Sobre containerPort: es informativo. Documenta en qué puerto escucha la aplicación, pero no abre ni bloquea nada. Si pones 8080 y nginx escucha en el 80, nginx seguirá escuchando en el 80 y K8s ni se inmuta. Ten esto presente, que nos va a servir en un rato.
Si alguna vez te pierdes con un campo, K8s trae su propio manual en la terminal:
kubectl explain pod.spec.containers
Funciona con cualquier campo, y es mi salvación cuando no tengo internet o me da pereza abrir la documentación.
Et voilà, ¡nuestro primer manifiesto! Para aplicarlo:
kubectl apply -f pod.yaml
Y si lo quieres borrar:
kubectl delete -f pod.yaml
La gracia de apply es que es idempotente: lo puedes correr diez veces y el resultado es el mismo. Si el Pod no existe, lo crea. Si ya existe y cambiaste algo del YAML, lo actualiza. Si no cambió nada, no hace nada. Esa es la magia declarativa, tú describes, K8s se entiende 🧙.
Es por eso el título de este tercer capítulo “El manifiesto como un estilo de vida”
Varios Pods en un solo archivo
Ahora, lo bueno: si queremos crear muchos Pods, ya no hace falta correr kubectl run mil veces. Podemos meter varios recursos en un solo archivo separándolos con tres guiones (---):
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: nginx2
spec:
containers:
- name: nginx2
image: nginx:1.14.2
ports:
- containerPort: 8080
Un solo kubectl apply -f pod.yaml y nacen los dos. Y como dijimos, tenemos además el historial de cómo se creó y se gestionó todo, algo que con solo la línea de comandos es bastante más difícil de tener.
Varios contenedores en un Pod 🫛🫛
Recuerda lo que vimos en el post anterior: un Pod puede tener varios contenedores que comparten red. Esto es útil cuando un servicio necesita que otro lo acompañe de cerca (el patrón sidecar). Vamos a probarlo con un experimento.
Usaremos la imagen python:3.12-alpine3.24 y una función muy práctica de Python, http.server, que levanta un servidor web con una sola línea. Cada contenedor va a escribir su nombre en un index.html y servirlo en el puerto 8080:
apiVersion: v1
kind: Pod
metadata:
name: pythonservers
spec:
containers:
- name: pythonserver1
image: python:3.12-alpine3.24
command: ['sh', '-c', 'echo cont1 > index.html && python -m http.server 8080']
- name: pythonserver2
image: python:3.12-alpine3.24
command: ['sh', '-c', 'echo cont2 > index.html && python -m http.server 8080']
Fíjate en el campo command, que sobreescribe lo que la imagen ejecutaría por defecto. Lo creamos con kubectl apply -f y miramos qué pasa…
kubectl get pods
NAME READY STATUS RESTARTS AGE
pythonservers 1/2 CrashLoopBackOff 5 (2m30s ago) 5m22s
Mmmm. Se creó, pero algo huele mal. READY 1/2 (uno de los dos contenedores está caído) y un estado nuevo y temible: CrashLoopBackOff, que significa que el contenedor arranca, se muere, K8s lo reinicia, se vuelve a morir, y así en bucle, esperando cada vez más tiempo entre reinicios (¿te suena el back-off?). La columna RESTARTS lo confirma: ya van 5 reinicios.
A estas alturas ya tienes todas las herramientas para resolverlo tú solo, así que pausa el post e inténtalo antes de seguir leyendo. Te espero 🧘
…
¿Ya? Vamos juntos. Primero describe:
kubectl describe pod pythonservers
Events:
...
Warning BackOff 65s (x12 over 7m59s) kubelet Back-off restarting failed container pythonserver2 in pod pythonservers_default(913ee761-...)
Nos dice que pythonserver2 está fallando, pero no nos dice por qué. Los eventos te cuentan lo que K8s intentó, no lo que la aplicación dijo antes de morir. Para eso están los logs, y ahora tenemos dos contenedores, así que hay que decirle a cuál con -c:
kubectl logs pythonservers -c pythonserver1
La salida está vacía, y eso es buena señal: el servidor 1 está corriendo tranquilo (el servidor de Python solo escribe cuando alguien le hace una petición). Ahora el segundo:
kubectl logs pythonservers -c pythonserver2
Traceback (most recent call last):
...
OSError: [Errno 98] Address already in use
¡Ahí está el culpable! Address already in use, el puerto 8080 ya está ocupado. (Y de paso, un error mío que seguro tú también cometerás: yo primero probé con kubectl logs pythonserver2 y me salió pods "pythonserver2" not found, porque ese es el nombre del contenedor, no del Pod. El Pod se llama pythonservers, el contenedor va con -c.)
Comprobémoslo desde adentro
No me creas a mí, veámoslo con nuestros propios ojos. Entremos al contenedor que sí está vivo:
kubectl exec -ti pythonservers -c pythonserver1 -- sh
Una vez adentro, pidámosle algo al puerto 8080 de localhost. Alpine trae wget de fábrica (vía busybox), pero si prefieres curl lo puedes instalar con el gestor de paquetes de Alpine, apk:
apk add curl
curl localhost:8080
cont1
Responde cont1. Y aquí viene lo interesante: nosotros le hablamos a localhost, y nos contestó el servidor del contenedor 1, pero el contenedor 2 también intentó usar localhost:8080 y se estrelló. ¿Por qué? Porque ya sabemos qué comparten los contenedores de un Pod: el namespace de red. Para ambos contenedores, localhost es el mismo, tienen la misma IP y la misma tabla de puertos. Es exactamente igual a lo que pasa en tu computador cuando intentas levantar dos programas en el mismo puerto, el segundo que llega se queda sin silla 🪑
(Y esto confirma algo que dijimos en el post anterior: el Mount no se comparte. Los dos contenedores escribieron su propio index.html, pero cada uno en su propio sistema de archivos, sin pisarse. Los puertos sí se pisan, los archivos no.)
La solución es sencilla: que cada contenedor use un puerto distinto, por ejemplo 8080 para uno y 8081 para el otro. Pruébalo, cambia el manifiesto, vuelve a aplicarlo, y mira cómo pasa a 2/2. (Un detalle: los Pods son bastante inmutables, así que si apply te protesta por intentar cambiar ciertos campos, borra el Pod con kubectl delete -f y créalo de nuevo.)

Los labels 🏷️
Cambiando de tema, hablemos de los labels (etiquetas). Son, básicamente, metadata en forma de pares clave-valor que le pegas a un recurso para poder identificarlo y agruparlo. ¿Para qué? Supón que tienes tres Pods casi idénticos, pero uno es de producción y dos son de staging. ¿Cómo los distingues? Con labels.
Los labels son arbitrarios, puedes inventar los que quieras, pero hay algunos que son casi universales en la comunidad, como app (a qué aplicación pertenece) y env (en qué ambiente corre). Usémoslos:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: frontend
env: development
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: nginx2
labels:
app: backend
env: production
spec:
containers:
- name: nginx2
image: nginx:1.14.2
ports:
- containerPort: 8080
Los aplicamos con kubectl apply -f, y ahora, si quiero filtrar solo ciertos Pods, uso el flag -l (de label) en el get:
kubectl get pods -l app=frontend
Y solo me aparece el Pod que cumple. También puedes combinar filtros separados por coma (-l app=backend,env=production) y ver los labels de todos con kubectl get pods --show-labels.

Esto parece un detalle menor ahora, pero los labels son el pegamento de K8s. Más adelante verás que los Services, los Deployments y casi todo lo demás encuentran “sus” Pods precisamente buscando labels. Guárdate esta idea, que es de las importantes.
El problema de los Pods sueltos 😬
Y ahora la parte donde te quito la ilusión. Todo lo que hicimos hoy fue didáctico, pero tiene un problemón gigante: un Pod creado así es huérfano.
Piénsalo. Si creo un solo Pod y alguien lo borra (o el nodo se cae, o se queda sin memoria), no hay garantía de que se levante otra vez. Nadie lo está vigilando. Y si yo quisiera tener siempre, como mínimo, dos réplicas de mi Pod corriendo, ¿cómo lo garantizo? Los Pods por sí solos no se replican ni se reviven. Si se mueren, se mueren, y hasta ahí llegó su historia 🪦

Este es probablemente el problema más grande de trabajar con Pods directamente, y K8s tiene una solución elegante para él. Pero eso ya es tema del próximo post, donde vamos a conocer a los controladores que vigilan y mantienen vivos a nuestros Pods.
La canción del post
Estos últimos días, con la salida de La bola negra, he estado escuchando bastante a Guitarricadelafuente y tengo La nieve, la canción de la peli, metida en la cabeza, en loop.
Mira qué tontería 🥲