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ón Qué hace
FROM La imagen de partida. Siempre es la primera línea
WORKDIR La carpeta de trabajo dentro de la imagen; la crea si no existe
COPY Lleva archivos de tu máquina a la imagen
RUN Ejecuta una orden al construir y guarda el resultado en una capa
CMD Qué 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:

Forma Qué ocurre
CMD ["programa", "arg"] Se ejecuta directamente. Recomendada
CMD programa arg Se 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ón Para qué
ENV Variables de entorno con valor por defecto
ARG Variables solo durante la construcción; no quedan en la imagen
EXPOSE Documenta qué puerto usa. No publica nada por sí solo
USER Con qué usuario se ejecuta. Por defecto es root, y no debería
VOLUME Marca una ruta cuyos datos deben persistir
HEALTHCHECK Cómo comprobar que el contenedor está sano
ADD Como 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.