2.1 · Escribir tu primer Dockerfile

Nivel 2 · Dockerfile y build

2.1Escribir tu primer Dockerfile

Objetivo: Escribir un Dockerfile mínimo que empaquete una aplicación y la ejecute.
Tiempo estimado: 14 min · lectura autónoma · Si solo tienes 5 min: lee «Lo esencial en 30s» + el esquema.

Lo esencial en 30 segundos

  • 5 instrucciones = 1 imagen ejecutable : FROM, WORKDIR, COPY, RUN, CMD — cada una tiene un rol distinto.
  • El build cocina, el run ejecuta : el build fija las capas, el run arranca el contenedor — lee la última etapa en caso de fallo.
  • RUN construye, CMD arranca : instalar en el build, lanzar en el run — nunca el servidor en RUN.

Mini-caso hilado — Idir y la app que no viajaba

Idir, devops junior en Argel, debe entregar la pequeña app Node de su equipo en otra máquina : « funciona en mi equipo » ya no basta. Escribe su primer Dockerfile en la raíz del proyecto : base oficial, directorio de trabajo, depósito del código, instalación en el build, comando de arranque. Compila en mon-app:1.0 y luego lanza con el puerto publicado : la app responde en localhost:3000, en cualquier máquina. Esta app « empaquetada por Idir » vuelve en 2.2 (build más rápido e imagen más ligera), 2.3 (etiqueta y publicación), 2.4 (puerto configurable) y 2.5 (versión reforzada non-root).

Hilo conductor del nivel 2 : la app de Idir vuelve en el curso, el esquema, el script y el desafío 48h — y continúa en 2.2, 2.3, 2.4 y 2.5.

1. Las 5 instrucciones vitales

FROM elige la base (por ejemplo node:20-slim, una imagen oficial ligera), WORKDIR fija el directorio de trabajo interno (creado si falta), COPY deposita el código desde el contexto, RUN instala durante el build (cada RUN fija una capa), CMD define el comando de arranque del contenedor. Cinco líneas bastan para un primer servicio funcional. Idir las escribe en este orden : base, directorio, código, instalación, lanzamiento.

2. Build y luego run : los dos tiempos

docker build -t mon-app:1.0 . cocina la imagen : el punto final designa el contexto, es decir la carpeta enviada al daemon para el build. docker run la ejecuta : el contenedor arranca y publica sus puertos. Si el build falla, se lee la última etapa mostrada : esa es la culpable (dependencia faltante, errata, red cortada). Idir nombra su imagen nombre:versión desde el build para orientarse.

3. CMD frente a RUN : no confundirlos más

RUN se ejecuta durante el build (instalar, compilar) y fija una capa en la imagen. CMD se ejecuta al arrancar el contenedor (lanzar el servidor), en cada run. Poner el lanzamiento en RUN fija un build que nunca arranca : el error clásico del principiante. Se escribe el CMD en forma JSON (["node", "server.js"]), un único comando de arranque. Idir instala con RUN y arranca con CMD : construir y luego lanzar, nunca al revés.

Error frecuente de interpretación: « RUN y CMD son lo mismo : ambos lanzan comandos ». No. RUN se ejecuta durante el build y fija una capa (instalar, compilar) ; CMD se ejecuta al arrancar el contenedor (lanzar el servidor). La regla de Idir : todo lo que prepara la imagen → RUN ; el comando que da vida al contenedor → CMD, en forma JSON.
Del código a la app en marcha Base, código, instalación en el build, lanzamiento en el run FROM + COPY Base oficial ydepósito del códigoRUN (en el build) Instalar,fijar una capaCMD (en el run) Lanzar elservidor al arrancar
Base, código, instalación en el build, lanzamiento en el run
Versión en texto del esquema (SEO / lectores de pantalla / traducción): 1) Idir COLOCA la base : FROM sobre una imagen oficial ligera y WORKDIR como directorio de trabajo, luego COPY para depositar el código desde el contexto. 2) COCINA : RUN instala las dependencias durante el build (capa fijada), docker build -t mon-app:1.0 fija la imagen. 3) LANZA : CMD en forma JSON arranca el servidor con docker run, puerto publicado. Lección : RUN construye, CMD arranca — dos tiempos, dos roles.

🔎 Ejemplo concreto — 🔎 Ejemplo concreto — Una app Node empaquetada

La app de Idir, del código a la imagen ejecutable :

FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

docker build -t mon-app:1.0 .
docker run -d --name app -p 3000:3000 mon-app:1.0

Lectura : las dependencias se instalan en el build (RUN), el servidor arranca en el run (CMD), el puerto se declara (EXPOSE) y se publica (-p).

Lección : copiar package*.json primero ya prepara la optimización de la caché (2.2).

Vocabulario de la sección

FROM
La imagen base del Dockerfile, punto de partida de todas las capas (preferiblemente una imagen oficial ligera).

Ejemplo sencillo: Idir parte de node:20-slim para su app Node.

Lo que no es: el comando que lanza el contenedor.

Reflejo: base oficial ligera primero.

WORKDIR
El directorio de trabajo interno del contenedor, creado automáticamente si falta ; todas las instrucciones siguientes se refieren a él.

Ejemplo sencillo: Idir fija WORKDIR /app antes de copiar su código.

Lo que no es: un volumen que persiste los datos.

Reflejo: fijar el directorio antes de copiar.

RUN
Ejecutar un comando DURANTE el build (instalar, compilar) ; cada RUN fija una capa en la imagen.

Ejemplo sencillo: Idir instala sus dependencias con RUN npm ci.

Lo que no es: el comando de arranque del contenedor.

Reflejo: preparar la imagen = RUN.

CMD
El comando de ARRANQUE del contenedor, ejecutado en cada docker run ; uno solo, preferiblemente en forma JSON.

Ejemplo sencillo: Idir arranca con CMD ["node", "server.js"].

Lo que no es: una instalación fijada en el build.

Reflejo: dar vida al contenedor = CMD.

En la práctica — En la práctica — Empaquetar una mini-app

  1. Crea una carpeta con un server.js que responda « OK » en el puerto 3000.
  2. Escribe el Dockerfile (FROM, WORKDIR, COPY, RUN, EXPOSE, CMD) en la raíz del proyecto.
  3. Compila en mon-app:1.0 y lee cada etapa del build.
  4. Lanza con -p 3000:3000 y abre localhost:3000.
Conviertes cualquier código en una imagen ejecutable : build aquí, run en todas partes.

Script listo para decir — explicar el Dockerfile a un colega (30 s)

« Este Dockerfile hace cinco cosas : parte de una base oficial, fija el directorio de trabajo, copia el código, instala en el build con RUN y arranca con CMD. Se cocina con docker build, se lanza con docker run : la app funciona igual en todas las máquinas. RUN construye, CMD arranca — es la única distinción que hay que recordar. »

Para decir mostrando el Dockerfile de Idir y luego el build etapa por etapa.

Modelo copiar-pegar — primer Dockerfile Node

FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

App Python

Idir empaqueta un script Python : FROM python:3.12-slim, COPY de requirements y luego RUN pip install, CMD en forma JSON.

Página estática

Un sitio escaparate : Idir parte de una base de servidor ligera, copia los archivos, expone el puerto, CMD lanza el servidor.

Contexto enorme

Build lento : Idir añade un .dockerignore (node_modules, .git) y el contexto vuelve a ser ligero.

RUN con trampa

Un colega lanza el servidor en RUN : el build se fija y nunca arranca — Idir mueve el lanzamiento a CMD.

✓ Buenas prácticas

  • Un único comando de arranque en CMD (forma JSON).
  • Nombrar la imagen nombre:versión desde el build.
  • Leer el build etapa por etapa en caso de fallo.

✗ Errores frecuentes

  • Lanzar el servidor en RUN en lugar de CMD.
  • Olvidar el .dockerignore (contexto enorme).
  • Copiar node_modules local en la imagen.

Puntos clave

  • FROM, WORKDIR, COPY, RUN, CMD : la base de todo Dockerfile.
  • El build cocina la imagen, el run la ejecuta (el « . » es el contexto enviado al daemon).
  • RUN = build (fija una capa), CMD = arranque (forma JSON).
  • Nombrar nombre:versión desde el build ; leer la última etapa en caso de fallo.

Desafío 48h — aplica mañana

Empaqueta una mini-app real (la tuya o la de Idir) : Dockerfile en 7 líneas (FROM, WORKDIR, COPY, RUN, EXPOSE, CMD), build etiquetado 1.0 leído etapa por etapa, y luego run con puerto publicado verificado en el navegador.

Lista de salida de la sección
  • Puedo escribir un Dockerfile mínimo (FROM, WORKDIR, COPY, RUN, EXPOSE, CMD).
  • Sé compilar (contexto incluido) y leer un fallo en la última etapa.
  • Distingo RUN (build) y CMD (arranque) sin dudar.
Autoevaluación 1-5 : ¿coloco cada instrucción en el tiempo correcto (build frente a run) ?¿mi imagen se ejecuta sin retoques en otra máquina ?

Preguntas frecuentes

¿Dónde escribir el Dockerfile ?

En la raíz del proyecto, llamado exactamente « Dockerfile ». El contexto es la carpeta enviada al daemon : quédate en la raíz para COPY . . y aligéralo con un .dockerignore (node_modules, .git) para no enviar cientos de MB en cada build.

¿Por qué el build es tan lento ?

A menudo por un contexto enorme (node_modules, .git) enviado cada vez. El .dockerignore y el orden de las capas (dependencias primero, código después — ver 2.2) resuelven el 80 % de los casos antes que cualquier otra optimización.

Comprueba tu comprensión

En un Dockerfile, ¿qué hace RUN frente a CMD ?

Recursos adicionales (opcional, para ir más allá)

Examen del nivel 2Preguntas inéditas y más difíciles para validar tus conocimientos