Contenedores y servicios de Docker: entender Docker con hamburguesas

Qué significa realmente “un menú de hamburguesa, por favor”

La vez pasada usamos un Dockerfile para crear nuestra propia “imagen de Docker (Image)”. Es como una “receta que nunca cambia”, donde está escrito en qué orden se pone la carne sobre el pan y luego la lechuga encima.

Ahora toca usar esa receta para empezar el negocio real, es decir, el servicio. Pero cuando empiezas a usar Docker aparecen términos que confunden. Container, Service, Stack… todos parecen parecidos, así que ¿qué diferencia hay exactamente?

Si no entiendes bien estos conceptos, más adelante, al escribir scripts de despliegue, acabarás dando vueltas con preguntas como “¿por qué levanté el contenedor y el servicio no sube?”. Para ordenar hoy este árbol genealógico, traje el lenguaje universal de todo el mundo: la analogía de una hamburguesería.

Preparas una hamburguesa, el contenedor, siguiendo la receta, la imagen, gestionas el mismo menú como servicio y lo vendes agrupado como combo, el stack.

1. Contenedor (Container): la hamburguesa hecha tras recibir el pedido

  • Definición: es la “entidad real” que se obtiene al ejecutar una imagen y cargarla en memoria.
  • Característica: al cliente no le puedes dar el papel de la receta (la imagen). Tienes que darle una “hamburguesa (contenedor)” recién hecha y calentita, preparada según la receta.
  • Comando: docker run my-burger

Si levanto una instancia de servidor, eso significa que he ejecutado “un contenedor”.

2. Servicio (Service): gestionar el stock del mismo menú

Llega la hora del almuerzo y entran los clientes de golpe. Con la velocidad de cocinar una hamburguesa cada vez ya no alcanza. Así que decidimos aumentar el personal de cocina y dejar preparados tres “cheeseburgers” por adelantado. Desde ese momento ya no gestiono “una cheeseburger”, sino toda la “línea de cheeseburgers”, es decir, el Service.

  • Definición: un “conjunto de contenedores” creados a partir de la misma imagen.
  • Característica: se le puede asignar una “política” como “mantén siempre 3 unidades de este menú en stock (Replicas=3)”. Si se cae una hamburguesa (error), prepara otra nueva por sí solo.
  • Objetivo: “escalado horizontal (Scale-out)”. Distribución del tráfico y estabilidad.

3. Stack: el menú combo, es decir, el paquete completo

Comer solo una hamburguesa se queda corto. También necesitas patatas fritas, la DB, y una cola, Redis. Solo cuando todo eso se junta aparece por fin “una comida completa”, es decir, la aplicación.

  • Definición: el “grupo de nivel superior” que agrupa servicios distintos (web, DB, caché).
  • Característica: la hamburguesa y la cola se comen más cómodamente si están en la misma bandeja (conexión de red). Al agruparlos en un stack, se crea un espacio exclusivo (Network) en el que pueden comunicarse entre ellos.

El problema: me voy a quedar sin aire solo de hacer el pedido

La idea se entiende, pero cuando intentas operarlo de verdad empieza el dolor de cabeza. El servicio web, es decir, el stack, que queremos construir suele necesitar componentes como los siguientes.

  1. Spring Boot (Burger): 2 platos principales (redundancia)
  2. PostgreSQL (Fries): 1 acompañamiento
  3. Redis (Coke): 1 bebida

Levantar todo eso con comandos puros de Docker, docker run, es como hacer un pedido en el mostrador de esta manera.

“Disculpe, quiero una ‘cheeseburger (Spring)’, pero cocine la carne en término medio bien hecho, sin pepinillos y con mucha salsa. Ah, y también deme ‘patatas fritas (DB)’, pero sin sal y con tres sobres de ketchup. Por último, la ‘cola (Redis)’ que sea cola cero y con solo la mitad de hielo. Y por favor ponga estas tres cosas en ‘una sola bandeja (Network)’.”

# En la realidad hay que escribir estos comandos cada vez
$ docker run -d --name my-web -e PATTY=medium -e PICKLE=no -p 8080:8080 ...
$ docker run -d --name my-db -e SALT=no ...
$ docker run -d --name my-redis -e ICE=half ...

¿De verdad podrías recitar este pedido larguísimo sin un solo error cada vez que despliegas? ¿Y si te equivocas en el orden y las patatas salen después de la hamburguesa? El cliente, es decir, el usuario, se enfadará.

El salvador: Docker Compose – “Lo de siempre, por favor”

Por eso los desarrolladores pensaron: “¿y si escribimos este pedido complicado por adelantado en un ‘menú, documento’ y simplemente podemos decir ‘deme el combo número 1’?”

Así nació “Docker Compose”. Si definimos nuestro propio “menú combo personalizado” en un archivo llamado docker-compose.yml, Docker se encarga de mandar el pedido a la cocina y de colocar todo ordenadamente en la bandeja.

Incluso un pedido personalizado complejo se resuelve con una sola frase, “quiero este”, si ya está escrito en el menú.

[Action] Crear tu propio menú combo

No nos quedemos solo en palabras; levantemos de verdad, de una sola vez, el servidor Spring y la DB que están en local. Todo lo que tienes que hacer es crear un archivo docker-compose.yml en la raíz del proyecto y pegar el contenido de abajo.

version: '3.8'

services:
  # 1. Patatas fritas (BD)
  my-database:
    image: postgres:14
    environment:
      POSTGRES_PASSWORD: mysecretpassword
    ports:
      - "5432:5432"

  # 2. Hamburguesa con queso (Web)
  my-web-server:
    image: my-spring-app:latest
    ports:
      - "8080:8080"
    depends_on:
      - my-database # "Las patatas se frien antes de que salga la hamburguesa" (orden garantizado)
    environment:
      DB_URL: jdbc:postgresql://my-database:5432/mydb

Ahora pronuncia el hechizo mágico en la terminal.

# "Un combo 1 (docker-compose.yml), por favor"
$ docker-compose up -d

Resultado: Docker lee ese archivo de menú, levanta primero la DB y, cuando la DB está lista, arranca el servidor Spring. Este es justamente el inicio del concepto de IaC, Infrastructure as Code, es decir, gestionar la infraestructura con código. A partir de ahora, incluso una configuración compleja de servidores se resuelve compartiendo un solo “archivo de menú”.

Para cerrar: Works on My Machine? Works on ANY Machine!

La excusa con la que empezó esta serie, “pero en mi máquina funciona…”, ya no sirve. Entendimos Linux, el OS, aislamos entornos con Docker, el Container, y definimos servicios complejos en un solo archivo con Docker Compose.

Ahora, con solo tener el archivo docker-compose.yml que escribí, puedo preparar exactamente el mismo menú de hamburguesa en mi ordenador, en el del jefe del equipo o en un servidor de AWS en solo un minuto.

Ya no somos “simples programadores”. Nos hemos convertido en “ingenieros” capaces de diseñar no solo el código que escribimos, sino también el mundo, el entorno, donde ese código va a ejecutarse. Con esto termina la serie [Works on My Machine].

A partir de la próxima serie, nos adentraremos en el mundo de [The Pipeline: automatización CI/CD], donde dejamos el tedioso proceso de despliegue en manos de las máquinas sobre esta infraestructura sólida.

Deja un comentario