El Dockerfile a fondo

Aprende Docker → Lección 4

Un Dockerfile es una receta: cada línea produce una capa de la imagen. Son una docena de instrucciones en total y con cinco se construye casi todo. La que más confusión genera, con diferencia, es la diferencia entre CMD y ENTRYPOINT, así que esta lección se detiene ahí.

Las cinco que se usan siempre

InstrucciónQué hace
FROMLa imagen de partida. Siempre es la primera línea
WORKDIRLa carpeta de trabajo dentro de la imagen; la crea si no existe
COPYLleva archivos de tu máquina a la imagen
RUNEjecuta una orden al construir y guarda el resultado en una capa
CMDQué ejecutar al arrancar el contenedor

La distinción entre «al construir» y «al arrancar» es la que ordena todo lo demás. RUN ocurre una vez, cuando se crea la imagen, y lo que deje queda grabado. CMD no se ejecuta nunca durante la construcción: solo describe qué debe pasar cuando alguien levante un contenedor.

CMD y ENTRYPOINT: la confusión clásica

Las dos dicen qué ejecutar al arrancar, y por eso se confunden. La diferencia está en qué pasa cuando quien ejecuta el contenedor pasa argumentos propios.

Solo con CMD: los argumentos lo sustituyen

FROM alpine:3.22
CMD ["echo", "soy el CMD por defecto"]
docker run --rm mi-demo:cmdsoy el CMD por defecto
docker run --rm mi-demo:cmd echo otra cosaotra cosa

El CMD desapareció por completo: lo que se pasa en la línea de órdenes lo reemplaza. Sirve para una orden por defecto que el usuario puede cambiar entera.

Con ENTRYPOINT: los argumentos se añaden

FROM alpine:3.22
ENTRYPOINT ["echo", "siempre se ejecuta:"]
CMD ["sin argumentos"]
docker run --rm mi-demo:entrysiempre se ejecuta: sin argumentos
docker run --rm mi-demo:entry hola mundosiempre se ejecuta: hola mundo

Aquí el ENTRYPOINT se ejecuta siempre y lo que se pasa sustituye únicamente al CMD, que actúa como argumentos por defecto. Es el patrón para una imagen que es una herramienta: el programa está fijado y el usuario solo elige sus parámetros.

La regla práctica. Si tu imagen ejecuta una aplicación concreta y quieres que nadie la cambie por accidente, usa ENTRYPOINT para el programa y CMD para los argumentos por defecto. Si la imagen es un entorno de propósito general donde conviene poder lanzar cualquier cosa, usa solo CMD. Para un servicio en producción, lo primero.

La forma de lista y la forma de texto

Las dos instrucciones admiten dos escrituras y no son equivalentes:

FormaQué ocurre
CMD ["programa", "arg"]Se ejecuta directamente. Recomendada
CMD programa argSe ejecuta a través de /bin/sh -c
Por qué importa, y mucho, en producción. Con la forma de texto, el proceso principal del contenedor es el intérprete de órdenes, no tu programa. Cuando Docker manda la señal de parada al detener el contenedor, la recibe el intérprete y no la reenvía: tu aplicación nunca se entera de que debe cerrarse y acaba terminada a la fuerza a los diez segundos, dejando conexiones y transacciones a medias. Con la forma de lista, tu programa es el proceso principal y recibe la señal directamente. Usa siempre la forma de lista salvo que necesites expresamente variables o tuberías del intérprete.

Las demás instrucciones

InstrucciónPara qué
ENVVariables de entorno con valor por defecto
ARGVariables solo durante la construcción; no quedan en la imagen
EXPOSEDocumenta qué puerto usa. No publica nada por sí solo
USERCon qué usuario se ejecuta. Por defecto es root, y no debería
VOLUMEMarca una ruta cuyos datos deben persistir
HEALTHCHECKCómo comprobar que el contenedor está sano
ADDComo COPY, pero además descomprime y descarga URL
Dos aclaraciones que ahorran disgustos. EXPOSE no abre ningún puerto: es documentación para quien lea el Dockerfile. Para que el puerto sea accesible hay que publicarlo al arrancar, con -p. Y entre ADD y COPY, usa COPY: ADD tiene comportamientos implícitos, como descomprimir un tar automáticamente, que sorprenden cuando no los esperas.