La primera vez que ejecuté docker run funcionó. Pero cuando algo falló, no tenía idea de dónde buscar. No entendía quién hacía qué, cómo se comunicaban las piezas ni por qué a veces el daemon parecía tener vida propia. Este artículo es lo que me hubiera gustado leer antes de ese momento.
Docker no es un solo programa: es un sistema de piezas
Uno de los errores conceptuales más comunes cuando arrancás con Docker es pensarlo como «el comando que corre contenedores». En realidad, Docker es una arquitectura cliente-servidor compuesta por varios componentes que trabajan juntos. Entenderlos hace que todo lo demás tenga sentido.
El flujo completo en un diagrama
┌─────────────────────────────────────────────────────────────┐
│ TU TERMINAL │
│ │
│ $ docker run nginx ← Docker Client (CLI) │
└──────────────────┬──────────────────────────────────────────┘
│ REST API (Unix socket o TCP)
▼
┌─────────────────────────────────────────────────────────────┐
│ DOCKER DAEMON (dockerd) │
│ │
│ • Escucha comandos del cliente │
│ • Administra imágenes, contenedores, redes, volúmenes │
│ • Delega la ejecución a containerd │
└──────────┬─────────────────────────┬────────────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────────────────────┐
│ containerd │ │ Docker Registry │
│ │ │ (Docker Hub / privado) │
│ • Gestiona │ │ │
│ ciclo de vida │ │ • Almacena imágenes │
│ del contenedor│ │ • docker pull baja de acá │
│ • Usa runc para │ │ • docker push sube acá │
│ crear procesos│ └──────────────────────────────────┘
└──────────────────┘
Docker Engine: el corazón del sistema
El Docker Engine es el conjunto completo: client + daemon + la API REST que los conecta. Cuando instalás Docker en un servidor, lo que instalás es el Engine. En mis nodos SUSE Linux, el daemon corre como servicio systemd y arranca automáticamente con el sistema.
# Ver estado del daemon
sudo systemctl status docker
# Ver logs del daemon en tiempo real
sudo journalctl -u docker -f
# Información completa del sistema Docker
docker info
Docker Client: lo que escribís en la terminal
El cliente es simplemente la CLI: el binario docker que usás en la terminal. Su único trabajo es traducir tus comandos a llamadas a la API REST del daemon. Lo que importa saber: el cliente y el daemon pueden estar en máquinas diferentes. Puedo controlar el daemon de un servidor remoto desde mi notebook sin ningún problema.
# Conectar el cliente a un daemon remoto
export DOCKER_HOST=tcp://192.168.1.100:2376
docker ps # Lista contenedores del servidor remoto
# O con contextos (la forma moderna)
docker context create servidor-prod --docker "host=ssh://mbernal@192.168.1.100"
docker context use servidor-prod
docker ps # Ahora habla con el servidor remoto
Docker Daemon (dockerd): quien realmente hace el trabajo
El daemon es el proceso que corre en background y gestiona todo: imágenes, contenedores, redes y volúmenes. Cuando ejecutás docker run nginx, es el daemon quien:
- Recibe el comando del cliente
- Verifica si la imagen
nginxexiste localmente - Si no existe, la descarga del registry
- Crea el contenedor usando
containerdyrunc - Configura la red y el sistema de archivos
- Arranca el proceso principal del contenedor
Imágenes Docker: plantillas inmutables
Una imagen es una plantilla de solo lectura que define el sistema de archivos y la configuración inicial de un contenedor. Está compuesta por capas (layers), donde cada instrucción del Dockerfile agrega una capa nueva. Esta arquitectura por capas es brillante: si dos imágenes comparten las mismas capas base, se almacenan una sola vez en disco.
# Ver imágenes locales
docker images
# Ver las capas de una imagen
docker history nginx:latest
# Inspeccionar metadatos completos
docker inspect nginx:latest
Contenedores: instancias en ejecución de una imagen
Un contenedor es una imagen en ejecución. La diferencia clave: la imagen es inmutable (solo lectura), mientras que el contenedor agrega una capa de escritura encima donde los procesos pueden crear y modificar archivos. Cuando el contenedor se destruye, esa capa desaparece. Por eso los datos importantes van en volúmenes — pero eso lo vemos en otro artículo.
# Relación imagen → contenedor
docker images ls # ver imágenes (plantillas)
docker ps -a # ver contenedores (instancias)
# Crear contenedor sin arrancarlo
docker create --name mi-nginx nginx
# Arrancarlo
docker start mi-nginx
# O directamente: crear + arrancar
docker run -d --name mi-nginx -p 80:80 nginx
Docker Registry: el repositorio de imágenes
El registry es donde viven las imágenes. Docker Hub es el registry público por defecto, pero en producción muchas empresas usan registries privados. En mi entorno on-premise uso un registry privado para no depender de internet en los deploys.
# Levantar un registry privado local
docker run -d -p 5000:5000 --name registry-privado -v /data/registry:/var/lib/registry registry:2
# Tagear imagen para el registry privado
docker tag mi-api:latest localhost:5000/mi-api:latest
# Subir al registry privado
docker push localhost:5000/mi-api:latest
# Bajar desde el registry privado
docker pull localhost:5000/mi-api:latest
Docker Compose: orquestación local
Docker Compose es la herramienta para definir y ejecutar aplicaciones multi-contenedor. En lugar de ejecutar múltiples docker run, definís todos los servicios en un archivo YAML y los gestionás con un solo comando. Lo veremos en profundidad más adelante — te adelanto que cambia completamente la forma de trabajar.
El flujo completo: qué pasa cuando ejecutás docker pull nginx
# Esto es lo que pasa internamente:
$ docker pull nginx
# 1. Docker Client envía petición al daemon via /var/run/docker.sock
# 2. Daemon consulta: ¿tengo nginx:latest localmente?
# 3. Si no → contacta Docker Hub (registry.hub.docker.com)
# 4. Autentica (si la imagen es privada)
# 5. Descarga cada capa (layer) que no tenga en cache
# 6. Verifica integridad con el digest SHA256
# 7. Almacena las capas en /var/lib/docker/overlay2/
Using default tag: latest
latest: Pulling from library/nginx
a803e7c4b030: Pull complete ← cada línea es una capa
8b625c47d697: Pull complete
4d3239651a63: Pull complete
Digest: sha256:bc5eac5eafc581aeda3008b4b1f07ebba230de2f27d47767129a6a905c84f470
Status: Downloaded newer image for nginx:latest
Por qué me importa entender esto
El día que tuve un contenedor que no arrancaba y no sabía por dónde empezar a debuggear, entender la arquitectura me salvó. Saber que el daemon escribe en /var/lib/docker/, que los logs del daemon están en journalctl, que el socket Unix es /var/run/docker.sock — esos detalles marcan la diferencia entre resolver el problema en 5 minutos o perder una hora.
# Cuando algo falla, estos son mis primeros comandos:
sudo journalctl -u docker --since "1 hour ago"
docker info
docker system df # ver uso de disco
docker system events # stream de eventos del daemon
← Artículo anterior: Cómo Docker cambió la forma en que trabajo | Serie Docker Completo | Próximo: Mi guía para escribir Dockerfiles →
← Artículo anterior: Cómo Docker cambió la forma en que trabajo (y por qué tardé en entenderlo) | Serie Docker Completo | Próximo: Mi guía para escribir Dockerfiles que no me den vergüenza →
Deja una respuesta