📝 Docker_Kubernetes_SaltStack
← Volver

🐳 DOCKER, KUBERNETES Y SALTSTACK

Guía completa de contenedores, orquestación y gestión de infraestructura

🎓 Cómo usar este documento: Cada sección explica el concepto desde cero, con ejemplos prácticos, comparativas y comandos clave. Ideal para entender el ecosistema completo de infraestructura moderna.


BLOQUE 0 — EL ECOSISTEMA: ¿QUÉ ES Y POR QUÉ EXISTE TODO ESTO?

El problema que intentan resolver

Imagina que desarrollas una aplicación en tu portátil con Python 3.11, una base de datos PostgreSQL 15 y una versión específica de una librería. Funciona perfectamente. Subes el código al servidor de producción y... no funciona. El servidor tiene Python 3.8, otra versión de PostgreSQL y las librerías difieren.

Este problema clásico se llama "en mi máquina funciona" (works on my machine). Las tecnologías de contenedores nacieron para resolverlo.

La evolución fue así:

ANTES (años 90-2000)          HOY
┌─────────────────┐           ┌─────────────────────────────────────┐
│  Servidor físico │           │  Cluster de orquestación            │
│  ├─ App A        │           │  ├─ Contenedor App A (réplica x3)   │
│  ├─ App B        │   ──▶     │  ├─ Contenedor App B (réplica x2)   │
│  └─ App C        │           │  ├─ Contenedor BD                   │
│  (todo mezclado) │           │  └─ Autoescalado, balanceo, etc.    │
└─────────────────┘           └─────────────────────────────────────┘

BLOQUE 1 — VIRTUALIZACIÓN VS CONTENEDORES

Máquinas Virtuales (VMs)

Una máquina virtual emula hardware completo. Cada VM incluye su propio sistema operativo completo (kernel incluido), lo que la hace muy aislada pero también muy pesada.

┌────────────────────────────────────────────┐
│            SERVIDOR FÍSICO                  │
│  ┌──────────────────────────────────────┐  │
│  │         HYPERVISOR (VMware/KVM)      │  │
│  ├──────────────┬──────────────────────┤  │
│  │    VM 1      │        VM 2           │  │
│  │ ┌──────────┐ │  ┌──────────────────┐│  │
│  │ │Guest OS  │ │  │    Guest OS      ││  │
│  │ │ (Linux)  │ │  │   (Windows)      ││  │
│  │ ├──────────┤ │  ├──────────────────┤│  │
│  │ │  App A   │ │  │  App B + App C   ││  │
│  │ └──────────┘ │  └──────────────────┘│  │
│  └──────────────┴──────────────────────┘  │
└────────────────────────────────────────────┘

Características de las VMs:
- Arranque lento (minutos)
- Tamaño grande (GBs por VM)
- Aislamiento total (cada una tiene su propio kernel)
- Ideal para aislar sistemas operativos completos


Contenedores

Un contenedor no emula hardware ni incluye un sistema operativo completo. Comparte el kernel del host pero aísla el proceso y su sistema de ficheros mediante tecnologías del propio Linux (namespaces y cgroups).

┌─────────────────────────────────────────────────────┐
│                  SERVIDOR FÍSICO                     │
│  ┌───────────────────────────────────────────────┐  │
│  │              SISTEMA OPERATIVO HOST            │  │
│  │              (Kernel compartido)               │  │
│  ├───────────┬───────────┬───────────────────────┤  │
│  │           │  DOCKER   │                        │  │
│  │ Container1│ Container2│   Container 3          │  │
│  │ ┌───────┐ │ ┌───────┐ │  ┌──────────────────┐ │  │
│  │ │Libs A │ │ │Libs B │ │  │ Libs C           │ │  │
│  │ ├───────┤ │ ├───────┤ │  ├──────────────────┤ │  │
│  │ │ App A │ │ │ App B │ │  │   App C          │ │  │
│  │ └───────┘ │ └───────┘ │  └──────────────────┘ │  │
│  └───────────┴───────────┴───────────────────────┘  │
└─────────────────────────────────────────────────────┘

Características de los contenedores:
- Arranque rapidísimo (segundos o menos)
- Tamaño pequeño (MBs)
- Aislamiento a nivel de proceso (comparten kernel)
- Portables: el mismo contenedor funciona en cualquier máquina con Docker


Comparativa VM vs Contenedor

Característica Máquina Virtual Contenedor
Arranque Minutos Milisegundos–segundos
Tamaño en disco GBs MBs
Uso de RAM Alto (SO completo) Bajo (solo la app)
Aislamiento Total (kernel propio) Proceso (kernel compartido)
Portabilidad Media Muy alta
Caso de uso SO diferente, aislamiento duro Microservicios, apps
Tecnologías VMware, VirtualBox, KVM, Hyper-V Docker, Podman, containerd

💡 En la práctica: No se excluyen. Muchas empresas corren contenedores dentro de VMs para obtener lo mejor de ambos mundos: aislamiento de VM + ligereza de contenedor.


BLOQUE 2 — DOCKER

¿Qué es Docker?

Docker es la plataforma más popular para crear, distribuir y ejecutar contenedores. Lanzado en 2013, democratizó el uso de contenedores que antes eran muy complejos de manejar.

Docker tiene tres conceptos clave:

IMAGEN                    CONTENEDOR              REGISTRO
(plantilla/blueprint)     (instancia en ejecución) (almacén de imágenes)

┌──────────────────┐      ┌──────────────────┐    ┌────────────────┐
│   Dockerfile     │      │  Imagen en RAM   │    │  Docker Hub    │
│                  │ ───▶ │  + proceso vivo  │    │  (nube pública)│
│  FROM ubuntu     │      │                  │    ├────────────────┤
│  RUN apt install │      │  puerto: 8080    │    │ Registry propio│
│  COPY app/ .     │      │  red: bridge     │    │  (empresa)     │
│  CMD ["node",..] │      │  volumen: /data  │    └────────────────┘
└──────────────────┘      └──────────────────┘
        │                         │
     docker build              docker run

Dockerfile — Cómo se construye una imagen

Un Dockerfile es el recetario para construir una imagen. Ejemplo real de una app Node.js:

# Imagen base (sistema operativo + runtime)
FROM node:18-alpine

# Directorio de trabajo dentro del contenedor
WORKDIR /app

# Copiar dependencias primero (aprovecha la caché de capas)
COPY package*.json ./
RUN npm install --production

# Copiar el resto del código
COPY . .

# Exponer el puerto
EXPOSE 3000

# Comando que se ejecuta al arrancar el contenedor
CMD ["node", "server.js"]

El sistema de capas (layers): Cada instrucción en el Dockerfile crea una capa inmutable. Docker reutiliza capas que no han cambiado, lo que hace las builds muy rápidas.

Capa 5: CMD ["node", "server.js"]
Capa 4: COPY . .
Capa 3: RUN npm install          ← si solo cambia el código, esta capa se reutiliza
Capa 2: COPY package*.json ./
Capa 1: FROM node:18-alpine      ← base siempre reutilizada si no cambia

Ciclo de vida de un contenedor

docker create  ──▶  CREADO  ──▶  docker start  ──▶  EN EJECUCIÓN
                                                           │
                    docker run (crea + inicia de una vez)  │
                    ────────────────────────────────────▶  │
                                                           │
                                          docker stop ◀────┤
                                                           │
                                          docker pause ◀───┤  (congela)
                                          docker unpause ──▶
                                                           │
                                          docker kill ◀────┘  (SIGKILL)
                                                │
                                          docker rm  ──▶  ELIMINADO

Comandos Docker — Contenedores

1.1 Ciclo de vida

# Crear sin iniciar
docker create nginx

# Crear e iniciar en una sola orden (el más usado)
docker run nginx

# Crear con opciones comunes
docker run -d \                    # background (detached)
           -p 8080:80 \            # puerto host:puerto_contenedor
           -v /datos:/app/datos \  # volumen host:contenedor
           --name mi-nginx \       # nombre del contenedor
           --rm \                  # borrarse al terminar
           nginx:latest

# Gestión básica
docker start mi-nginx
docker stop mi-nginx
docker restart mi-nginx
docker pause mi-nginx
docker unpause mi-nginx
docker kill mi-nginx               # SIGKILL (fuerza)
docker rm mi-nginx                 # eliminar (debe estar parado)
docker rm -f mi-nginx              # forzar eliminación aunque esté corriendo
docker rename mi-nginx nginx-prod
docker update --memory 512m mi-nginx   # actualizar recursos

1.2 Información y monitorización

docker ps                          # contenedores en ejecución
docker ps -a                       # todos (incluidos parados)
docker logs mi-nginx               # logs del contenedor
docker logs -f mi-nginx            # logs en tiempo real (follow)
docker logs --tail 100 mi-nginx    # últimas 100 líneas
docker inspect mi-nginx            # toda la información (JSON)
docker inspect mi-nginx | grep IPAddress   # obtener IP
docker stats                       # uso de recursos en tiempo real
docker top mi-nginx                # procesos dentro del contenedor
docker events                      # eventos en tiempo real
docker port mi-nginx               # puertos expuestos
docker diff mi-nginx               # ficheros modificados en el FS

1.3 Ejecutar comandos dentro

# Ejecutar un comando en contenedor en marcha
docker exec mi-nginx ls /etc/nginx

# Abrir una shell interactiva
docker exec -it mi-nginx bash
docker exec -it mi-nginx sh        # si no tiene bash

# Como root aunque el contenedor use otro usuario
docker exec -it -u root mi-nginx bash

1.4 Restricciones de recursos

# Limitar CPU (0.5 = 50% de un core)
docker run --cpus="0.5" nginx

# Limitar memoria
docker run -m 300M nginx
docker run --memory=512m --memory-swap=1g nginx

# Usar cores específicos
docker run --cpuset-cpus="0,2" nginx      # solo cores 0 y 2

# Shares de CPU (relativo, 1024 = 100%)
docker run --cpu-shares=512 nginx         # 50% de CPU

1.5 Importar / Exportar

# Copiar ficheros entre host y contenedor
docker cp mi-nginx:/etc/nginx/nginx.conf ./nginx.conf
docker cp ./mi-config.conf mi-nginx:/etc/nginx/nginx.conf

# Exportar sistema de ficheros del contenedor a tar
docker export mi-nginx > contenedor.tar

Comandos Docker — Imágenes

2.1 Ciclo de vida

docker images                      # listar imágenes locales
docker pull nginx:latest           # descargar imagen
docker pull nginx:1.25             # versión específica
docker build -t mi-app:1.0 .       # construir desde Dockerfile en .
docker build -t mi-app:1.0 -f Dockerfile.prod .   # dockerfile específico
docker rmi nginx                   # eliminar imagen
docker rmi -f nginx                # forzar eliminación
docker tag nginx mi-registro/nginx:v1  # etiquetar imagen

2.2 Guardar y cargar imágenes

# Guardar imagen a fichero tar (incluye capas)
docker save nginx:latest | gzip > nginx.tar.gz
docker save -o nginx.tar nginx:latest

# Cargar imagen desde fichero
docker load < nginx.tar.gz
docker load -i nginx.tar

# Importar contenedor como imagen
cat contenedor.tar | docker import - mi-imagen:tag

# Ver historial de capas
docker history nginx

2.3 Limpiar imágenes

docker rmi $(docker images -q)                    # eliminar todas las imágenes
docker rmi $(docker images -q -f dangling=true)   # eliminar dangling (sin tag)
docker image prune                                 # limpiar imágenes sin usar
docker system prune                                # limpiar TODO (contenedores, imágenes, redes, volúmenes)
docker system prune -a                             # limpiar TODO incluso imágenes no usadas

Redes en Docker

Docker gestiona redes virtuales. Los contenedores en la misma red pueden comunicarse por nombre.

# Tipos de red
docker network ls
# bridge (por defecto)  → contenedores en la misma máquina
# host                  → el contenedor usa directamente la red del host
# none                  → sin red
# overlay               → para Docker Swarm / multi-host

docker network create mi-red
docker network rm mi-red
docker network connect mi-red mi-nginx
docker network disconnect mi-red mi-nginx
docker network inspect mi-red

# Ejecutar en una red específica
docker run --network mi-red nginx

# Exponer puertos
docker run -p 8080:80 nginx               # host:contenedor
docker run -p 127.0.0.1:8080:80 nginx    # solo localhost
docker run -P nginx                       # puertos aleatorios del host

Volúmenes en Docker

Los contenedores son efímeros: cuando se borran, pierden sus datos. Los volúmenes persisten los datos fuera del contenedor.

# Tipos de almacenamiento
# 1. Volumen gestionado por Docker (recomendado)
docker volume create mis-datos
docker run -v mis-datos:/app/datos nginx

# 2. Bind mount (directorio del host)
docker run -v /host/ruta:/contenedor/ruta nginx
docker run -v $(pwd)/config:/etc/nginx/conf.d nginx   # directorio actual

# 3. tmpfs (solo en RAM, efímero)
docker run --tmpfs /app/tmp nginx

# Gestión de volúmenes
docker volume ls
docker volume inspect mis-datos
docker volume rm mis-datos
docker volume prune                       # eliminar volúmenes sin usar

Docker Compose

Docker Compose permite definir y ejecutar aplicaciones multi-contenedor con un fichero YAML.

# docker-compose.yml — Ejemplo: app web + base de datos + cache
version: '3.8'

services:
  web:
    build: .                          # construir desde Dockerfile local
    ports:
      - "8080:3000"
    environment:
      - DATABASE_URL=postgresql://postgres:secret@db:5432/miapp
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache
    volumes:
      - ./src:/app/src                # hot-reload en desarrollo
    restart: unless-stopped

  db:
    image: postgres:15
    environment:
      POSTGRES_DB: miapp
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret
    volumes:
      - postgres_data:/var/lib/postgresql/data   # persistencia

  cache:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:
docker compose up -d          # levantar todo en background
docker compose down           # parar y eliminar contenedores
docker compose down -v        # también eliminar volúmenes
docker compose logs -f        # logs de todos los servicios
docker compose ps             # estado de los servicios
docker compose exec web bash  # shell en el servicio web
docker compose build          # reconstruir imágenes
docker compose pull           # descargar imágenes actualizadas

Registro de imágenes (Registry)

Un registry es donde se almacenan y distribuyen las imágenes.

# Docker Hub (público)
docker login
docker tag mi-app usuario/mi-app:v1.0
docker push usuario/mi-app:v1.0
docker pull usuario/mi-app:v1.0

# Registry privado (self-hosted)
docker run -d -p 5000:5000 --name registry registry:2
docker tag mi-app localhost:5000/mi-app:v1.0
docker push localhost:5000/mi-app:v1.0

# Registries populares en la nube
# AWS ECR:    123456789.dkr.ecr.eu-west-1.amazonaws.com/mi-app
# GCP GCR:   gcr.io/mi-proyecto/mi-app
# Azure ACR: miregistro.azurecr.io/mi-app
# GitLab:    registry.gitlab.com/grupo/proyecto/mi-app

Tips y trucos Docker

# Obtener IP de un contenedor
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mi-nginx

# Obtener mapeo de puertos
docker inspect -f '{{range $p, $conf := .NetworkSettings.Ports}}{{$p}} -> {{(index $conf 0).HostPort}}{{"\n"}}{{end}}' mi-nginx

# Matar todos los contenedores en ejecución
docker kill $(docker ps -q)

# Eliminar todos los contenedores parados
docker rm $(docker ps -a -q -f status=exited)

# Eliminar imágenes "dangling" (sin nombre/tag)
docker rmi $(docker images -q -f dangling=true)

# Eliminar volúmenes dangling
docker volume rm $(docker volume ls -q -f dangling=true)

# Ver espacio usado por Docker
docker system df

# Buscar contenedores por expresión regular
for i in $(docker ps -a | grep "PATRÓN" | cut -f1 -d " "); do echo $i; done

BLOQUE 3 — ORQUESTADORES DE CONTENEDORES

¿Por qué necesitamos un orquestador?

Docker resuelve el problema de ejecutar un contenedor en una máquina. Pero en producción real surgen problemas nuevos:

Un orquestador resuelve todos estos problemas. Es como un "gerente de contenedores" que gestiona un clúster de máquinas.

Sin orquestador:                  Con orquestador (Kubernetes):
┌────────┐ ┌────────┐            ┌─────────────────────────────────┐
│ Host 1 │ │ Host 2 │            │        KUBERNETES CLUSTER        │
│        │ │        │            │                                  │
│  App   │ │  App   │            │  Tu declaras: "quiero 5 réplicas │
│  (tú   │ │  (tú   │  ──▶       │  de mi app, con 1GB RAM cada una │
│  lo    │ │  lo    │            │  y que se actualicen sin corte"  │
│  gesti-│ │  gesti-│            │                                  │
│  onas) │ │  onas) │            │  K8s se encarga del resto ✓      │
└────────┘ └────────┘            └─────────────────────────────────┘

Principales orquestadores

1. Kubernetes (K8s) — El estándar de la industria

Creado por Google en 2014, donado a la CNCF. Es el orquestador más usado en el mundo.

Conceptos clave de Kubernetes:

CLUSTER
└─ NODOS (máquinas físicas o VMs)
   ├─ Control Plane (el "cerebro")
   │   ├─ API Server      → punto de entrada de todo
   │   ├─ etcd            → base de datos del estado del cluster
   │   ├─ Scheduler       → decide en qué nodo va cada Pod
   │   └─ Controller Mgr  → mantiene el estado deseado
   └─ Worker Nodes (donde corren las apps)
       ├─ kubelet          → agente que habla con el Control Plane
       ├─ kube-proxy       → gestiona el networking
       └─ Container Runtime (containerd, CRI-O)

Objetos principales de Kubernetes:

Objeto Qué es Ejemplo de uso
Pod Uno o más contenedores que comparten red y almacenamiento La unidad mínima desplegable
Deployment Gestiona réplicas de Pods con rolling updates Tu app web con 3 réplicas
Service Expone Pods con una IP estable y balanceo de carga Punto de acceso a tu app
Ingress Reglas HTTP/HTTPS para enrutar tráfico externo dominio.com/api → servicio-api
ConfigMap Configuración no sensible URL de la base de datos
Secret Datos sensibles cifrados Contraseñas, tokens, certificados
PersistentVolume Almacenamiento persistente Disco para la base de datos
Namespace Entornos virtuales dentro del cluster produccion, staging, dev
DaemonSet Un Pod en CADA nodo del cluster Agente de logs, monitorización
StatefulSet Pods con identidad estable y orden de arranque Bases de datos, ZooKeeper

2. Docker Swarm — El orquestador nativo de Docker

Integrado en Docker, mucho más sencillo que Kubernetes pero menos potente.

# Crear un Swarm
docker swarm init --advertise-addr 192.168.1.10

# Añadir un nodo worker
docker swarm join --token SWMTKN-1-xxx 192.168.1.10:2377

# Desplegar un servicio
docker service create --replicas 3 --name mi-web -p 80:80 nginx

# Escalar
docker service scale mi-web=5

# Ver estado
docker service ls
docker service ps mi-web

3. Nomad (HashiCorp)

Más simple que Kubernetes, soporta no solo contenedores sino también apps binarias y VMs. Popular en empresas que ya usan Terraform/Vault.

4. OpenShift (Red Hat)

Kubernetes con añadidos empresariales: CI/CD integrado, gestión de usuarios, seguridad reforzada. Muy usado en grandes corporaciones.


Comparativa de orquestadores

Kubernetes Docker Swarm Nomad OpenShift
Curva aprendizaje Alta Baja Media Alta
Escalabilidad Muy alta Media Alta Muy alta
Comunidad Enorme Pequeña Media Media
Apps no-contenedor No No No
Gestión Compleja Simple Media Compleja
Ideal para Grandes producciones Pequeños proyectos Multi-workload Empresas grandes
Coste operativo Alto Bajo Medio Muy alto

BLOQUE 4 — KUBERNETES EN PROFUNDIDAD

Comandos kubectl — Visualizar información

Nodos

kubectl get nodes                                    # listar nodos
kubectl get nodes -o wide                            # con más info (IP, OS, etc.)
kubectl describe node [nombre_nodo]                  # detalle completo del nodo
kubectl get nodes -o yaml                            # en formato YAML
kubectl top node [nombre_nodo]                       # uso de CPU/RAM del nodo
kubectl get node --selector=[label]                  # filtrar por etiqueta

Pods

kubectl get pods                                     # pods en el namespace actual
kubectl get pods -o wide                             # con nodo y IP
kubectl get pods --all-namespaces                    # todos los namespaces
kubectl get pods -n kube-system                      # namespace específico
kubectl describe pod [nombre_pod]                    # detalle completo
kubectl get pod [nombre_pod] -o yaml                 # YAML del pod
kubectl get pods -l app=nginx                        # filtrar por label
kubectl get pods --field-selector status.phase=Running   # filtrar por estado

Logs

kubectl logs [pod_name]                              # logs del pod
kubectl logs [pod_name] -f                           # logs en tiempo real
kubectl logs --since=1h [pod_name]                   # última hora
kubectl logs --tail=50 [pod_name]                    # últimas 50 líneas
kubectl logs -f -c [container_name] [pod_name]       # logs de contenedor específico
kubectl logs [pod_name] > pod.log                    # guardar en fichero

Deployments, Services, etc.

# Deployments
kubectl get deploy
kubectl describe deploy [nombre]
kubectl get deploy -o wide
kubectl get deploy -o yaml

# Services
kubectl get svc
kubectl describe svc [nombre]
kubectl get svc --show-labels

# Namespaces
kubectl get ns
kubectl describe ns [nombre]

# Múltiples recursos a la vez
kubectl get svc,po
kubectl get deploy,no
kubectl get all
kubectl get all --all-namespaces

# DaemonSets, ReplicaSets, etc.
kubectl get ds
kubectl get rs
kubectl get cm                                       # ConfigMaps
kubectl get secrets
kubectl get pv                                       # PersistentVolumes
kubectl get pvc                                      # PersistentVolumeClaims
kubectl get ing                                      # Ingress
kubectl get sa                                       # ServiceAccounts
kubectl get roles --all-namespaces

Comandos kubectl — Modificar recursos

Cambiar atributos

# Etiquetas (labels)
kubectl label node [nodo] disktype=ssd
kubectl label pod [pod] env=prod

# Anotaciones
kubectl annotate pod [pod] [anotacion]
kubectl annotate node [nodo] descripcion="nodo GPU"

# Taints (restricciones en nodos)
kubectl taint node [nodo] [taint_name]
kubectl taint node [nodo] key=value:NoSchedule       # no programar pods aquí

# Cordon/Uncordon (impedir/permitir nuevos pods en un nodo)
kubectl cordon [nodo]                                # no nuevos pods
kubectl uncordon [nodo]                              # volver a aceptar pods

# Drain (evacuar nodo para mantenimiento)
kubectl drain [nodo]                                 # mover todos los pods
kubectl drain [nodo] --ignore-daemonsets --delete-emptydir-data

Editar, escalar y eliminar

# Editar en vivo (abre editor)
kubectl edit deploy [nombre]
kubectl edit svc [nombre]
kubectl edit node [nombre]
kubectl edit ns [nombre]
kubectl edit ds [nombre] -n kube-system

# Eliminar recursos
kubectl delete node [nodo]
kubectl delete pod [pod]
kubectl delete svc [svc]
kubectl delete ds [ds]
kubectl delete sa [sa]
kubectl delete ns [ns]                              # elimina TODOS los recursos dentro

# Escalar
kubectl scale deploy [nombre] --replicas=5
kubectl scale deploy [nombre] --replicas=0          # apagar sin eliminar

# Exponer
kubectl expose deploy [nombre] --port=80 --type=NodePort

Comandos kubectl — Crear recursos

Crear Pods y Deployments

# Desde un fichero YAML (la forma recomendada)
kubectl create -f mi-deployment.yaml
kubectl apply -f mi-deployment.yaml                  # create o update
kubectl apply -f ./directorio/                       # aplicar todos los YAML

# Crear directamente (imperativo, para pruebas)
kubectl run mi-pod --image=nginx --restart=Never
kubectl create deploy mi-deploy --image=nginx
kubectl create deploy mi-deploy --image=nginx --replicas=3

# Pod interactivo temporal (muy útil para debugging)
kubectl run debug --image=busybox --rm -it --restart=Never -- sh

# Crear servicio
kubectl create svc nodeport mi-svc --tcp=8080:80

Generar YAML (sin aplicar)

# Ver el YAML que generaría un comando
kubectl create deploy mi-app --image=nginx --dry-run=client -o yaml > deploy.yaml
kubectl run mi-pod --image=nginx --dry-run=client -o yaml > pod.yaml

# Exportar YAML de un recurso existente
kubectl get deploy [nombre] -o yaml > deploy.yaml
kubectl get pod [nombre] -o yaml --export > pod.yaml

Ejemplo completo: desplegar una app en Kubernetes

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-app
  labels:
    app: mi-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mi-app
  template:
    metadata:
      labels:
        app: mi-app
    spec:
      containers:
      - name: mi-app
        image: nginx:1.25
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mi-app-svc
spec:
  selector:
    app: mi-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer
kubectl apply -f deployment.yaml
kubectl get pods -w                                  # watch: ver cómo arrancan
kubectl get svc mi-app-svc                          # ver IP externa asignada
kubectl rollout status deploy/mi-app                # ver progreso del despliegue
kubectl rollout history deploy/mi-app               # historial de versiones
kubectl rollout undo deploy/mi-app                  # rollback a versión anterior

Cluster info y API

kubectl config                                      # configuración del cluster
kubectl cluster-info                                # info del cluster
kubectl get componentstatuses                       # estado de componentes
kubectl get --raw /apis/metrics.k8s.io/            # API call directa
kubectl api-resources                               # todos los tipos de recursos disponibles
kubectl explain deploy.spec                         # documentación del campo spec de Deployment
kubectl -h                                          # ayuda general
kubectl create -h                                   # ayuda sobre create

BLOQUE 5 — SALTSTACK

¿Qué es SaltStack?

SaltStack (ahora simplemente Salt) es una herramienta de gestión de configuración y automatización de infraestructura. Si Docker/Kubernetes gestionan contenedores, Salt gestiona servidores (físicos, VMs, instancias cloud).

Analogía: Imagina 500 servidores en producción. Salt es el "mando a distancia universal" que te permite instalar software, cambiar configuraciones, reiniciar servicios o ejecutar cualquier comando en todos ellos a la vez desde un único punto.


Arquitectura Master / Minion

                    SALT MASTER
                 (servidor central)
                 puerto 4505 (pub)
                 puerto 4506 (rtn)
                       │
          ┌────────────┼────────────┐
          │            │            │
     MINION 1      MINION 2     MINION N
   (servidor 1)  (servidor 2)  (servidor N)

   Comunicación: ZeroMQ (muy rápido, asíncrono)
   Autenticación: clave pública RSA

Flujo de comunicación:
1. Minion arranca y busca el Master (por DNS salt o configuración)
2. Minion genera par de claves RSA y envía la pública al Master
3. Administrador acepta la clave en el Master: salt-key -A
4. A partir de ahí, Master puede enviar comandos a los Minions


Instalación y arranque

# Master
systemctl start salt-master          # systemd (moderno)
service salt-master start            # upstart (Ubuntu antiguo)
/etc/init.d/salt-master start        # SysV init
salt-master -d                       # en background
salt-master -l debug                 # en foreground con debug

# Minion
salt-minion -d                       # en background
salt-minion -l debug                 # en foreground con debug

# Configuración del minion: /etc/salt/minion
master: saltmaster.example.com       # apuntar al master
id: servidor-web-01                  # nombre del minion (por defecto: hostname)

Gestión de claves

salt-key -L                          # listar todas las claves (accepted/denied/rejected/unaccepted)
salt-key -A                          # aceptar TODAS las claves pendientes
salt-key -a servidor-web-01          # aceptar una clave específica
salt-key -d servidor-web-01          # eliminar una clave
salt-key -r servidor-web-01          # rechazar una clave
salt-key -F master                   # ver fingerprint de la clave del master
salt-key -f servidor-web-01          # ver fingerprint de un minion

Comandos Salt básicos

La sintaxis general es: salt '[TARGET]' [MÓDULO].[FUNCIÓN] [ARGUMENTOS]

# Ejecutar en TODOS los minions
salt '*' test.ping

# Ejecutar en un minion específico
salt 'servidor-web-01' test.ping

# Ejecutar en varios con glob
salt 'web*' cmd.run 'uptime'                       # todos los que empiezan por "web"
salt '*prod*' cmd.run 'df -h'                      # todos los que contienen "prod"

# Ejemplos de módulos básicos
salt '*' test.version                              # versión de Salt instalada
salt '*' disk.usage                                # uso de discos
salt '*' network.interfaces                        # interfaces de red
salt '*' cmd.run 'ls -l /etc'                      # ejecutar comando bash
salt '*' cmd.run_all 'systemctl status nginx'      # stdout + stderr + retcode
salt '*' pkg.install nginx                         # instalar paquete
salt '*' pkg.remove nginx                          # eliminar paquete
salt '*' pkg.upgrade                               # actualizar todos los paquetes
salt '*' service.start nginx                       # iniciar servicio
salt '*' service.stop nginx                        # detener servicio
salt '*' service.restart nginx                     # reiniciar servicio
salt '*' service.status nginx                      # estado del servicio
salt '*' service.get_all                           # listar todos los servicios

Targeting (selección de minions)

# Glob (por nombre)
salt 'web*' test.ping
salt '*prod*' test.ping

# Expresiones regulares (-E)
salt -E 'web[0-9]+' test.ping

# Lista de minions (-L)
salt -L 'web01,web02,db01' test.ping

# Por Grain (-G) — datos del sistema del minion
salt -G 'os:Ubuntu' pkg.upgrade
salt -G 'roles:webserver' service.restart nginx
salt -G 'datacenter:madrid' cmd.run 'hostname'

# Por Pillar (-I)
salt -I 'environment:production' test.ping

# Por IP o subred
salt -S '192.168.1.0/24' test.ping

# Compound (combinado)
salt -C 'G@os:Ubuntu and web*' test.ping
salt -C 'G@os:CentOS or G@os:RedHat' pkg.upgrade

# Node Groups (grupos definidos en el master)
salt -N webservers test.ping

# Batch mode (por lotes, para evitar sobrecargar)
salt --batch-size 10 '*' state.apply
salt -b 25% '*' pkg.upgrade

Módulos principales

Módulo cmd — Ejecución de comandos

salt '*' cmd.run 'ls -la /var/log'
salt '*' cmd.run_all 'systemctl status nginx'      # stdout + stderr + retcode
salt '*' cmd.run_stderr 'comando_con_error'        # solo stderr
salt '*' cmd.run_stdout 'echo hola'                # solo stdout
salt '*' cmd.retcode 'test -f /etc/nginx.conf'     # código de retorno
salt '*' cmd.has_exec nginx                        # ¿existe el binario?
salt '*' cmd.shell 'for i in 1 2 3; do echo $i; done' shell=/bin/bash

Módulo pkg — Paquetes

salt '*' pkg.install nginx
salt '*' pkg.install 'nginx,curl,vim'              # varios paquetes
salt '*' pkg.remove nginx
salt '*' pkg.latest nginx                          # actualizar a la última versión
salt '*' pkg.upgrade                               # actualizar todo el sistema
salt '*' pkg.list_pkgs                             # listar paquetes instalados
salt '*' pkg.version nginx                         # versión instalada

Módulo service — Servicios

salt '*' service.start nginx
salt '*' service.stop nginx
salt '*' service.restart nginx
salt '*' service.reload nginx                      # reload sin reiniciar
salt '*' service.status nginx                      # running/stopped
salt '*' service.get_all                           # todos los servicios
salt '*' service.enable nginx                      # habilitar al arranque
salt '*' service.disable nginx                     # deshabilitar al arranque

Módulo file — Ficheros

salt '*' file.exists /etc/nginx/nginx.conf         # ¿existe?
salt '*' file.remove /tmp/fichero.txt              # eliminar
salt '*' file.mkdir /opt/mi-directorio             # crear directorio
salt '*' file.replace /etc/nginx/nginx.conf 'worker_processes 1' 'worker_processes 4'

Módulo user y group

salt '*' user.add deploy                           # crear usuario
salt '*' user.delete deploy                        # eliminar usuario
salt '*' user.info deploy                          # info del usuario
salt '*' user.chpasswd deploy 'nueva_contraseña'
salt '*' group.add developers                      # crear grupo

Módulo grains

salt '*' grains.items                              # todos los grains
salt '*' grains.get os                             # SO del minion
salt '*' grains.get id                             # ID del minion
salt '*' grains.get fqdn                           # FQDN
salt '*' grains.get ipv4                           # IPs del minion
salt '*' grains.setval role webserver              # establecer grain personalizado

Módulo pillar

salt '*' pillar.items                              # todos los pillar data
salt '*' pillar.get environment                    # valor específico
salt '*' pillar.refresh_pillar                     # refrescar datos de pillar

Salt States (Gestión de configuración declarativa)

Los States son la forma de declarar cómo debe estar un sistema. Se escriben en YAML en ficheros .sls bajo /srv/salt/.

# /srv/salt/nginx.sls
# Asegurar que nginx está instalado, configurado y corriendo

nginx:
  pkg.installed: []                                # paquete instalado

nginx_config:
  file.managed:
    - name: /etc/nginx/nginx.conf
    - source: salt://nginx/nginx.conf              # fichero en el salt master
    - user: root
    - group: root
    - mode: 644
    - require:
      - pkg: nginx                                 # requiere que nginx esté instalado

nginx_service:
  service.running:
    - name: nginx
    - enable: True
    - watch:
      - file: nginx_config                         # reinicia si cambia la config
# Aplicar un state a todos los minions
salt '*' state.apply nginx

# Aplicar varios states
salt '*' state.apply nginx,php,mysql

# Highstate: aplicar todos los states asignados al minion
salt '*' state.apply
# equivalente a:
salt '*' state.highstate

# Verificar sin aplicar (dry-run)
salt '*' state.apply nginx test=True

# Ver qué states tiene asignados un minion
salt-call state.show_top

# Verificar un fichero SLS (sintaxis)
salt '*' state.show_sls nginx

Grains, Pillars y la diferencia entre ellos

GRAINS                              PILLAR
────────────────────────────────    ────────────────────────────────
Datos DEL minion                    Datos PARA el minion
Generados automáticamente           Definidos por el administrador
Datos del sistema: OS, IP, CPU...   Datos de negocio: passwords, env
Públicos (visibles desde master)    Privados (solo el minion los ve)
No sensibles                        Sensibles (contraseñas, tokens)

Ejemplo: os=Ubuntu                  Ejemplo: db_password=s3cr3t0
         ipv4=[192.168.1.10]                 environment=production
         cpu_count=4                         deploy_user=deploy

SaltStack vs otras herramientas de gestión

SaltStack Ansible Puppet Chef
Arquitectura Master/Minion Agentless (SSH) Master/Agent Master/Agent
Protocolo ZeroMQ SSH HTTPS HTTPS
Velocidad ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐ ⭐⭐⭐
Escala Miles de nodos Cientos Miles Miles
Lenguaje config YAML + Jinja2 YAML Puppet DSL Ruby
Curva aprendizaje Media-Alta Baja Alta Alta
Tiempo real ✅ (event bus)
Sin agente Opcional (SSH) Por defecto No No
Orquestación Limitada Limitada
Comunidad Media Muy grande Grande Media

BLOQUE 6 — EL ECOSISTEMA COMPLETO: CÓMO ENCAJA TODO

¿Qué hace cada herramienta?

INFRAESTRUCTURA          CONFIGURACIÓN          CONTENEDORES         ORQUESTACIÓN
DE SERVIDORES            DE SERVIDORES          (packaging)          DE CONTENEDORES

  Terraform      ──▶      SaltStack      ──▶      Docker       ──▶    Kubernetes
  (Ansible IaC)           (Ansible)               Podman               Docker Swarm
  Pulumi                  Puppet                  Buildah              Nomad
                          Chef

"Crea la VM"         "Configura la VM"       "Empaqueta la app"   "Gestiona los contenedores"

Ejemplo de stack tecnológico real (empresa mediana)

Desarrollador hace push a Git
         │
         ▼
  CI/CD (GitLab CI / GitHub Actions)
  ├─ Tests automáticos
  ├─ docker build → imagen
  └─ docker push → registro (ECR, Harbor...)
         │
         ▼
  Kubernetes (EKS / GKE / AKS / on-premise)
  ├─ Deployment actualizado con nueva imagen
  ├─ Rolling update (sin downtime)
  └─ Monitorización (Prometheus + Grafana)
         │
         ▼
  SaltStack / Ansible (para los servidores subyacentes)
  ├─ Configurar los nodos del cluster
  ├─ Actualizar el OS de los workers
  └─ Gestionar certificados y secrets

Comparativa final por caso de uso

Necesito... Usa...
Empaquetar mi app con sus dependencias Docker
Desarrollar con varias apps relacionadas (app + DB + cache) Docker Compose
Desplegar mi app en producción con alta disponibilidad Kubernetes
Gestionar configuración de 50 servidores SaltStack / Ansible
Provisionar infraestructura en la nube (VMs, redes, etc.) Terraform
Orquestación sencilla sin Kubernetes Docker Swarm
Entorno de desarrollo reproducible con VMs Vagrant
Clúster Kubernetes gestionado sin operar el control plane EKS / GKE / AKS

📊 TABLA FLASH FINAL — Comandos más importantes

Docker

Acción Comando
Crear e iniciar contenedor docker run -d -p 8080:80 nginx
Ver contenedores en marcha docker ps
Shell en contenedor docker exec -it NOMBRE bash
Ver logs docker logs -f NOMBRE
Parar contenedor docker stop NOMBRE
Eliminar contenedor docker rm NOMBRE
Construir imagen docker build -t nombre:tag .
Listar imágenes docker images
Eliminar imagen docker rmi IMAGEN
Subir imagen docker push usuario/imagen:tag
Limpiar todo docker system prune -a
Levantar compose docker compose up -d
Bajar compose docker compose down

kubectl (Kubernetes)

Acción Comando
Ver pods kubectl get pods
Ver todo kubectl get all
Detalle de un pod kubectl describe pod NOMBRE
Logs kubectl logs -f NOMBRE
Shell en pod kubectl exec -it NOMBRE -- bash
Aplicar YAML kubectl apply -f fichero.yaml
Escalar kubectl scale deploy NOMBRE --replicas=5
Eliminar kubectl delete pod NOMBRE
Editar en vivo kubectl edit deploy NOMBRE
Rollback kubectl rollout undo deploy/NOMBRE
Ver namespaces kubectl get ns
Info del cluster kubectl cluster-info

Salt

Acción Comando
Listar claves salt-key -L
Aceptar claves salt-key -A
Ping a todos salt '*' test.ping
Ejecutar comando salt '*' cmd.run 'uptime'
Instalar paquete salt '*' pkg.install nginx
Reiniciar servicio salt '*' service.restart nginx
Aplicar state salt '*' state.apply NOMBRE
Aplicar highstate salt '*' state.apply
Por sistema operativo salt -G 'os:Ubuntu' cmd.run 'uname -a'
Por lotes salt --batch-size 10 '*' state.apply