Kubernetes ejecuta pods, que son grupos de contenedores que siempre se ejecutan juntos en el mismo nodo y en el mismo espacio de nombres.

Introducción

De la misma forma que Docker - Compose nos permite ejecutar un grupo de contenedores estrechamente relacionas, kubernetes utiliza pods.

La mayoría de pods sólo tienen un contenedor, pero un contenedor siempre ha de ejecutarse dentro de un pod.

Minikube

Minikube es una herramienta que te permite usar Kubernetes en un único nodo para que realizar pruebas o como entorno de desarrollo.

Crea una máquina Linux e instala Docker

connect-wsl k8s -new
install-docker

Instala la última versión estable de minikube usando el paquete Debian :

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
sudo dpkg -i minikube_latest_amd64.deb

Configura minikube para que tenga más "potencia":

$ minikube config set cpus 8
$ minikube config set memory 8096
$ minikube config view
- cpus: 8
- memory: 8096

Inicia el cluster:

$ minikube start
😄  minikube v1.35.0 on Ubuntu 24.04 (amd64)
✨  Automatically selected the docker driver. Other choices: none, ssh

❌  Exiting due to MK_USAGE: Docker has only 7777MB memory but you specified 8096MB

Como te has excedido con la memoria (quizás no 😅), modifica la configuración correspondiente:

~$ minikube config set memory 6144
❗  These changes will take effect upon a minikube delete and then a minikube start

Vamos 🚀:

$ minikube start
😄  minikube v1.35.0 on Ubuntu 24.04 (amd64)
✨  Using the docker driver based on existing profile
👍  Starting "minikube" primary control-plane node in "minikube" cluster
...
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

Al principio, es posible que algunos servicios, como el aprovisionador de almacenamiento, aún no estén en estado de ejecución. Esta es una situación normal durante la activación del clúster y se resolverá por sí sola en unos momentos.

Comprueba el estado del clúster:

$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

Puedes comprobar que estamos utilizando el driver docker:

$ minikube profile list
|----------|-----------|---------|--------------|------|---------|--------|-------|----------------|--------------------|
| Profile  | VM Driver | Runtime |      IP      | Port | Version | Status | Nodes | Active Profile | Active Kubecontext |
|----------|-----------|---------|--------------|------|---------|--------|-------|----------------|--------------------|
| minikube | docker    | docker  | 192.168.49.2 | 8443 | v1.32.0 | OK     |     1 | *              | *                  |
|----------|-----------|---------|--------------|------|---------|--------|-------|----------------|--------------------|

Puedes detener el clúster con el comando stop:

$ minikube stop
✋  Stopping node "minikube"  ...
🛑  Powering off "minikube" via SSH ...
🛑  1 node stopped.

Y también, puedes borrar el cluster con el comando delete (no hace falta que lo pruebes 🙄):

$ minikube delete
🔥  Deleting "minikube" in docker ...
🔥  Deleting container "minikube" ...
🔥  Removing /home/box/.minikube/machines/minikube ...
💀  Removed all traces of the "minikube" cluster.

Y si eres un poco curioso, deberías serlo 🧐, puedes entrar dentro de la màquina virtual Minikube:

$ minikube ssh

En este caso es "docker", pero podria ser una màquina virtual de verdad .

$ minikube ssh
docker@minikube:~$ exit
logout

Como administrador debes dominar el cluster desde el shell, pero a veces es útil un entorno gráfico.

Abre un nuevo terminal, y ejecuta:

$ minikube dashboard
🤔  Verifying dashboard health ...
🚀  Launching proxy ...
🤔  Verifying proxy health ...
🎉  Opening http://127.0.0.1:42919/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
👉  http://127.0.0.1:42919/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/

Haz clic en el enlace que te indica 👉.

Importante El puerto es dinámico por lo que haz clic en el enlace de tu shell, no en el de la documentación ! 🫡

kubectl

Instala kubectl:

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

Habilita el autocompletado de kubectl (te permite utilizar la tecla tab para completar los comandos):

echo 'source <(kubectl completion bash)' >>~/.bashrc
source ~/.bashrc

Con kubectl puedes controlar un cluster kubernetes mediante peticiones REST al servidor API que se ejecuta en el nodo "master".

Por ejemplo, puedes obtener la información del cluster:

$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:32781
CoreDNS is running at https://127.0.0.1:32781/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

Puedes ver que el custer está activo y diferentes URLs de varios componentes de kubernnets.

A continuación mira cuantos nodos forman parte del cluster:

$ kubectl get nodes
NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   28m   v1.32.0

Como era de esperar sólo hay un nodo, que es "minikube".

Con el comando kubectl get pudes obtener una lista de diferentes tipos de objetos "kubernetes" ( en nuestro ejemplo has pedido una lista de todos los objetos de tipo "node").

Si quieres obtener detalles adicionales de un objeto puedes utilizar el comando describe con el tipo de objeto y nombre del objeto.

Por ejemplo, obtenén los detalles del objecto "minikube" de tipo "node":

$ kubectl describe node minikube
Name:               minikube
Roles:              control-plane
...

La descripción muestra el estado del nodo, los datos de CPU y memoria, información del sistema, los contenedores que se están ejecutando dentro del nodo, y mucha más información.

Pod

La forma más habitual y senzilla es ejectuar un pod con un solo contenedor.

A continuación ejecuta un pod con un único contenedor apache:

$ kubectl run apache --image=httpd
pod/apache created

Con el comando get puedes ver todos los pods que estan desplegados en el clúster:

$ kubectl get pods
NAME     READY   STATUS    RESTARTS   AGE
apache   1/1     Running   0          17s

Si quieres puedes obtener una descrición de un contenedor con el comando describe:

$ kubectl describe pod apache 
...
IP:               10.244.0.13
IPs:
  IP:  10.244.0.13
Containers:
  apache:
    Container ID:   docker://ce53a65a3e96949e84ca92e7019eab5cbfdeff2ca0cfa7dff25c8718477b70b2
    Image:          httpd
    Image ID:       docker-pullable://httpd@sha256:437b9f7d469dd606fa6d2a5f9a3be55fe3af7e0c66e0329da8c14b291ae0d31c
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sat, 01 Feb 2025 22:19:29 +0100
    Ready:          True
...

Puedes ver que el pod tiene la IP 10.244.0.13 i un contenedor "apache".

También puedes ejecutar un comando en un contenedor de la misma forma que lo haces con docker:

$ kubectl exec apache -- ls htdocs
index.html

Como el pod sólo tiene un contenedor no hay que especificar el contenedor, pero si no fuese así tienes que especificar el contendor con el argumento -c:

$ kubectl exec apache -c apache -- ls htdocs
index.html

Ejecuta bash en el contenedor "apache" en un terminal interactivo:

$ kubectl exec -it apache -- bash
root@apache:/usr/local/apache2# 

IP

La IP de un pod es interna al clúster, la comparten todos los contenedores del pod, y cambia cada vez que se crea el pod.

Elimina el pod "apache" y vuelve a crear el pod:

$ kubectl delete pod apache
pod "apache" deleted
$ kubectl run apache --image=httpd
pod/apache created

Mira la dirección IP del pod con el flag -o wide:

$ kubectl get pods -o wide
NAME     READY   STATUS    RESTARTS   AGE     IP            NODE       NOMINATED NODE   READINESS GATES
apache   1/1     Running   0          5m28s   10.244.0.14   minikube   <none>           <none>

Si sólo quieres la dirección IP, puedes utilizar una "plantilla Go" para formatear la información del Pod y devolver solo la dirección IP del Pod.

$ kubectl get pod nginx-w29j2 -o=jsonpath='{.status.podIP}'
10.244.0.14

Si ejecutas curl puedes ver que no tienes acceso a esa IP:

$ curl -m 2 10.244.0.14
curl: (28) Connection timed out after 2037 milliseconds

Pero si entras dentro del cluster, tienes acceso al servidor "apache":

$ minikube ssh
docker@minikube:~$ curl 10.244.0.14
<html><body><h1>It works!</h1></body></html>
docker@minikube:~$ exit
logout

Como hemos explicado al principio, la IP es interna del cluster, lo que quiere decir que cualquier pod puede utilizar esa IP porque compartenla misma red.

Crea un pod "nginx" y crea una session interativa:

$ kubectl run nginx --image=nginx
pod/nginx created
box@k8s:~$ kubectl exec -it nginx -- bash
root@nginx:/#

Verifica que desde el contenedor "nginx" tienes acceso al contenedor "apache":

$ curl 10.244.0.14
<html><body><h1>It works!</h1></body></html>

Otra manera de conectar-se a un pod para hacer pruebas, o para "debugging", es mediante un port forwarding:

$ kubectl port-forward apache 8000:80
Forwarding from 127.0.0.1:8000 -> 80
Forwarding from [::1]:8000 -> 80

Ahora te puedes conectar directamente desde el navegador: http://localhost:8000/.

En Servicio aprenderás como exponer un pod al exterior mediante la configuración de servicios.

Descriptor

De la misma forma que en Docker - Compose utilizas un fichero yaml para definir una composición de contenedores, con kuberntes también puede utilizar un fichero yaml para definir un pod.

A continuación mira la definicón del pod que está en ejecución en formato yaml:

$ kubectl get pod apache -o yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2025-02-01T21:19:23Z"
  labels:
    run: apache
  name: apache
  namespace: default
  resourceVersion: "3554"
  uid: c58a72ec-83c7-4d4e-b49e-3413a72a2a2c
spec:
  containers:
  - image: httpd
    imagePullPolicy: Always
    name: apache
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-gjqjw
      readOnly: true
...

Puede parecer muy complicado al principio, pero a medida que aprendas los conceptos básicos y aprendas a distinguir las partes importatnes de lo que son detalles menores verás que es muy fácil de entender.

Además, para definir un pod no necesitas escribir todo esto.

A continuación vas a desplegar un pod con MongoDB

Crea un fichero mongodb.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: mongodb
spec:
  containers:
  - image: mongo
    name: mongodb
    ports:
    - containerPort: 27017
      protocol: TCP

Puedes ver que en la definición de un pod:

  • metadata incluye el nombre, el espacio de nombres, etiquetas y otra información acerca del pod.
  • spec incluye la descripción de los contenidos del pod, como son los contenedores, volumenes y otros datso.
  • status incluye la información acerca de la ejecución del pod, por lo que no forma parte del fichero de definición.

Puedes consultar para que sirve cada entrada del fichero con el comando explain.

Importante!. El puerto es sólo descriptivo, para que tu o cualquier otro sepa que puertos utiliza el contenedor.

Por ejemplo:

$ kubectl explain pod.spec
KIND:       Pod
VERSION:    v1

FIELD: spec <PodSpec>
...

Utiliza el comando create para crear un pod a partir de un fichero yaml:

$ kubectl create -f mongodb.yaml 
pod/mongodb created

Puedes ver que ahora tines dos pods:

$ kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
apache    1/1     Running   0          41m
mongodb   1/1     Running   0          54s

Conectacte a la base de datos mongodb desde el exterior con mongosh:

Abre otro terminal y haz un port forward:

$ kubectl port-forward mongodb 27017:27017
Forwarding from 127.0.0.1:27017 -> 27017
Forwarding from [::1]:27017 -> 27017

Conéctate com mongosh:

$ docker run --rm -it --network host mongo mongosh
Current Mongosh Log ID: 679ea6b415ca11579ae94969
Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.3.4
Using MongoDB:          8.0.4
Using Mongosh:          2.3.4
...
test>

Logs

Las aplicaciones que se ejectucan en contenedores normalmente no escriben los "logs" en ficheros, sinó que los envía a la salida estàndard, de tal forma que los usuarios pueden acceder a esa información de una manera simple y estàndar.

De esta forma, Docker redirige esos "streams" a ficheros que te permiten acceder a los "logs" de los contenedores a través de Docker de forma homogenea.

Puedes utilizar kubectl para acceder a esos logs, por ejemplo los de apache:

$ kubectl logs apache
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.244.0.4. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.244.0.4. Set the 'ServerName' directive globally to suppress this message
[Sat Feb 01 21:19:29.784649 2025] [mpm_event:notice] [pid 1:tid 1] AH00489: Apache/2.4.63 (Unix) configured -- resuming normal operations
[Sat Feb 01 21:19:29.785020 2025] [core:notice] [pid 1:tid 1] AH00094: Command line: 'httpd -D FOREGROUND'
10.244.0.1 - - [01/Feb/2025:21:22:34 +0000] "GET / HTTP/1.1" 200 45
127.0.0.1 - - [01/Feb/2025:21:34:38 +0000] "GET / HTTP/1.1" 200 45
127.0.0.1 - - [01/Feb/2025:21:34:38 +0000] "GET /favicon.ico HTTP/1.1" 404 196

Ten en cuenta que cuando borras un pod ya no tienes acceso a los logs de los contenedores de ese pod.

$ kubectl delete pod apache
pod "apache" deleted
$ kubectl logs apache
error: error from server (NotFound): pods "apache" not found in namespace "default"

Si quieres conservar los logs de los pods incluso después de que sean eliminados, tienes que implementar un registro centralizado que guarde los logs de todo el clúster.

Etiquetas

Un clúster kubernetes está pensado para desplegar centenares y miles de pods.

Por suerte, puedes poner etiquetas a los pods para organizar y poner orden.

De echo, puedes etiquetar cualquier tipo de objeto kubernetes.

Crea un pod con un contenedor nginx:

$ kubectl run nginx --image nginx
pod/nginx created

A continuación vamos a etiquetar los tres pods con una etiqueta type:

$ kubectl label pod apache type=web
pod/apache labeled
$ ...

Por defecto el comando kubectl get pods no muestro las etiquetas, pero puedes hacer que se muestren con el paràmetro --show-labels:

$ kubectl get pods --show-labels
NAME      READY   STATUS    RESTARTS   AGE    LABELS
apache    1/1     Running   0          17m    run=apache,type=web
mongodb   1/1     Running   0          5m3s   type=db
nginx     1/1     Running   0          8h     run=nginx,type=web

Puede ver que además de nuestra etiqueta, los pods apache y nginx tienen la etiqueta run porque no los has creado a partir de un fichero sino directamente con kubectl run.

Además de la etiqueta type, añade la etiqueta env para indicar si un pod se ejectuda en entorno de producció o depuración:

$ kubectl label pod mongodb env=prod
pod/mongodb labeled
...

En lugar de ver todas las etiquetas con --show-labels, podemos seleccionar las etiquetas que queremos ver con el flafg -L, de tal manera que cada etiqueta se despliega en su propia columna:

$ kubectl get pods -L type,env
NAME      READY   STATUS    RESTARTS   AGE   TYPE   ENV
apache    1/1     Running   0          29m   web    prod
mongodb   1/1     Running   0          16m   db     prod
nginx     1/1     Running   0          8h    web    debug

Para modificar una etiqueta necesitas utilitzar el flag --overwrite, para evitar una modificación accidental:

$ kubectl label pod nginx env=test
error: 'env' already has a value (debug), and --overwrite is false

$ kubectl label pod nginx env=test --overwrite
pod/nginx labeled

Puedes ver que es fácil añadir etiquetas a los recursos y modificarlas.

En Replicación verás una de las utilidades más importantes que tienen.

De momento nos limitaremos a filtrar recursos mediante un selector de etiquetas.

Para ver todos los pods que estan en entorno de producción (etiquetados con env=prod), haz lo siguiente:

$ kubectl get pods -l env=prod
NAME      READY   STATUS    RESTARTS   AGE
apache    1/1     Running   0          41m
mongodb   1/1     Running   0          28m

Para tener una lista de todos los pods que tiene la etiqueta run, sin importar el valor de la etiqueta:

$ kubectl get pods -l run
NAME     READY   STATUS    RESTARTS   AGE
apache   1/1     Running   0          43m
nginx    1/1     Running   0          8h

I aquellos que no tienen la etiqueta run:

$ kubectl get pods -l '!run'
NAME      READY   STATUS    RESTARTS   AGE
mongodb   1/1     Running   0          31m

Observación. Tienes que utilizar comillas simple alrededor de !run para que el shell no evalue el signo de exclamación.

También puedes incluir diversos criterios separados por coma:

$ kubectl get pods -l env=prod,type=web
NAME     READY   STATUS    RESTARTS   AGE
apache   1/1     Running   0          47m

Espacio de nombres

Kubernetes utiliza diferentes espacios de nombres para agrupar y separar recursos.

En un espacio de nombres el nombre del recurso debe ser único, pero puede haber recursos con el mismo nombre en diferentes espacios de nombres.

Mira los espacios de nombre que hay en tu cluster:

$ kubectl get namespaces 
NAME              STATUS   AGE
default           Active   21h
kube-node-lease   Active   21h
kube-public       Active   21h
kube-system       Active   21h

Hasta ahora has utilizado el espacio de nombres default, que es el que se utiliza por defecto con kubectl.

Mira los pods que hay en el espacio de nombres kube-system:

$ kubectl get pods --namespace kube-system
NAME                               READY   STATUS    RESTARTS     AGE
coredns-668d6bf9bc-qbhmr           1/1     Running   1 (9h ago)   21h
etcd-minikube                      1/1     Running   1 (9h ago)   21h
kube-apiserver-minikube            1/1     Running   1 (9h ago)   21h
kube-controller-manager-minikube   1/1     Running   1 (9h ago)   21h
kube-proxy-x7ghr                   1/1     Running   1 (9h ago)   21h
kube-scheduler-minikube            1/1     Running   1 (9h ago)   21h
storage-provisioner                1/1     Running   3 (9h ago)   21h

Tip. Puedes utilizar -n en lugar de --namespace.

En otras actividades aprenderás acerca de estos pods, pero a partir del nombre kube-system puedes intuir que se trata de pods relacions con el sistema de ejecución de kubernetes.

Los espacios de nombre permiten aislar recursos y permitir que sólo determinados usuarios puedan acceder a dichos recursos o incluso limitar la capacidad de los recursos de usuarios concretos.

Un espacio de nombres es como cualquier otro recurso, y lo podemos crear con un fichero yaml:

apiVersion: v1
kind: Namespace
metadata:
    name: xtec

Utiliza kubectl para enviar el fichero al servidor API kubernetes:

$ kubectl create -f namespace.yaml 
namespace/xtec created

Como puedes haver adivinado también lo puedes crear directamente por linea de comandos:

$ kubectl create namespace keko
namespace/keko created

A continuación crea un servidor caddy con el nombre "caddy" en el espacio de nombres xtec:

$ kubectl run caddy --image=caddy -n xtec
pod/caddy created

Puedes ver que no aparece en el espacio de nombres default:

$ kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
apache    1/1     Running   0          72m
mongodb   1/1     Running   0          59m
nginx     1/1     Running   0          9h

Pero si en el espacio de nombres xtec:

$ kubectl get pods -n xtec
NAME    READY   STATUS    RESTARTS   AGE
caddy   1/1     Running   0          76s 

Haz un "port fowrard":

$ kubectl port-forward caddy 3000:80 -n xtec
Forwarding from 127.0.0.1:3000 -> 80
Forwarding from [::1]:3000 -> 80
Handling connection for 3000

Puedes ver el servidor caddy funcionando: http://localhost:3000/.

Aunque los pods se ejecuten en espacios separados, se pueden comunicar entre ellos en función del tipo de solución de red se haya desplegado en el cluster.

Nuestro "minikube" utiliza una solució simple como puedes ver a continuación:

$ kubectl get pod -o wide | grep apache
apache    1/1     Running   0          84m   10.244.0.16   minikube   <none>           <none>

$ kubectl exec -it caddy -n xtec -- sh
/srv $ apk add curl
OK: 13 MiB in 30 packages
/srv $ curl 10.244.0.16
<html><body><h1>It works!</h1></body></html>

Gestionar pods

A diferencia de docker, con kubernetes no puedes parar contenedores, sólo los puedes crear o eliminar pods.

Puedes elimar un pod por su nombre:

$ kubectl delete pod mongodb
pod "mongodb" deleted

O eliminar un conjunto de pods mediante un selector de etiquetas:

$ kubectl delete pod -l type=web
pod "apache" deleted
pod "nginx" deleted

Incluso puedes eliminar todo un espacio de nombres (incluyendo todos los recursos):

$ kubectl delete ns xtec
namespace "xtec" deleted