En Dockerfile existen muchas instrucciones, pero, como suele pasar, en el día a día con solo 4 o 5 de ellas construirás el 90% de tus imágenes.
En este artículo vamos a ver las instrucciones y comandos más habituales en Dockerfiles. los “verbos” principales como FROM, WORKDIR, COPY, RUN y CMD (y un par más al final).
| Comando | Descripción |
|---|---|
FROM | Define la imagen base de partida para el contenedor. |
WORKDIR | Establece el directorio de trabajo para las siguientes instrucciones. |
COPY | Copia archivos y carpetas locales hacia dentro de la imagen. |
RUN | Ejecuta comandos durante la construcción de la imagen. |
CMD | Define el proceso por defecto que se ejecutará al iniciar el contenedor. |
FROM
Define sobre qué sistema operativo o entorno vamos a trabajar. La instrucción FROM siempre es la primera línea de un Dockerfile (salvo excepciones muy muy avanzadas).
FROM node:18-alpine
- ¿Quieres hacer una app en Python?
FROM python:3.9. - ¿Quieres control total?
FROM ubuntu:22.04. - ¿Quieres algo minúsculo?
FROM alpine.
Docker intentará descargar esta imagen de Docker Hub si no la tienes en local.
WORKDIR
Por defecto, Docker empieza en la raíz / del sistema de archivos ❗❗❗❗. Si empiezas a copiar cosas ahí, acabarás mezclando tu código con las carpetas del sistema (/bin, /etc…). Un desastre.
Para cambiar el directorio de trabajo usamos WORKDIR
WORKDIR /app
WORKDIR hace dos cosas:
- Crea la carpeta si no existe.
- Hace
cda esa carpeta. Todas las instrucciones siguientes se ejecutarán allí.
Úsalo siempre después del FROM. Mantén tu código ordenado en /app, /usr/src/app o similar.
COPY
Ya tenemos base y carpeta. Ahora hay que meter nuestro código.
La sintaxis es
COPY <origen_en_tu_pc> <destino_en_imagen>
- El origen es relativo a donde tienes el Dockerfile (Build Context).
- El destino puede ser relativo al
WORKDIR(por eso usamos el punto .) o absoluto.
Por ejemplo,
COPY package.json .
COPY src/ ./src/
A veces verás tutoriales viejos usando ADD. Es el hermano mayor de COPY. Hace lo mismo, pero además permite descargar URLs y descomprimir .tar.gz automáticamente.
Usa siempre COPY, es más explícito. Solo usa ADD si realmente necesitas descomprimir un archivo automáticamente.
RUN
RUN ejecuta comandos durante la construcción de la imagen. Se usa principalmente para:
- Instalar dependencias (
apt-get install,npm install,pip install). - Configurar el entorno.
- Compilar código.
RUN apt-get update && apt-get install -y git
RUN npm install
RUN se ejecuta una sola vez cuando haces el docker build. El resultado se “congela” en la imagen. No se ejecuta cuando arrancas el contenedor.
Cada instrucción RUN crea una nueva capa en la imagen. Por eso, a menudo verás comandos encadenados con && para que todo se haga en una sola capa y la imagen ocupe menos:
# Bien hecho (1 capa)
RUN apt-get update && apt-get install -y \
python3 \
git \
&& rm -rf /var/lib/apt/lists/*
CMD
Ya tenemos la imagen lista (build). Ahora, ¿qué debe hacer el contenedor cuando alguien haga docker run?
CMD (Command) define el proceso por defecto que se ejecutará al arrancar.
CMD ["node", "app.js"]
Solo puede haber un CMD en el Dockerfile (si pones varios, solo vale el último).
Bonus comandos adicionales
Como habiamos dicho, os pongo 3 comandos más no tan importantes como los que acabamos de ver, pero también muy frecuentes.
| Comando | Descripción |
|---|---|
ENV | Define variables de entorno que vivirán dentro del contenedor. |
EXPOSE | Documenta el puerto en el que la aplicación estará escuchando. |
ENTRYPOINT | Convierte tu contenedor en un comando ejecutable estricto. |
Las aplicaciones necesitan configuración (conexiones a bases de datos, claves, entornos de producción o desarrollo). ENV te permite inyectar estas variables para que tu aplicación las lea al arrancar.
ENV NODE_ENV=production
ENV PUERTO=8080
Lo bueno de usar ENV es que estableces un valor por defecto en la imagen, pero el usuario puede cambiarlo fácilmente al arrancar el contenedor usando la bandera -e (ej: docker run -e PUERTO=9000 mi-app).
Este es el comando peor entendido de todo Docker (y con razón, porque básicamente no hacer nada)
EXPOSE 8080
Poner EXPOSE 8080 en tu Dockerfile NO hace que tu contenedor sea accesible desde internet. No abre ningún puerto físico ni modifica el firewall.
Entonces, ¿para qué sirve? Es puramente informativo y de documentación. Es la forma que tienes de decirle al próximo programador que lea tu código: “Oye, la aplicación que está dentro de este contenedor ha sido programada para emitir datos por el puerto 8080”.
Para abrir el puerto de verdad y conectarlo con el mundo exterior, siempre tendrás que usar la bandera -p al ejecutar el contenedor (docker run -p 80:8080).
Mientras que CMD define un comando por defecto, ENTRYPOINT sirve para decirle a Docker que este contenedor no es un sistema genérico, sino una herramienta de propósito único.
Convierte tu contenedor en un comando ejecutable estricto que no puede ser alterado fácilmente.
ENTRYPOINT ["python3", "script_procesamiento.py"]
Si pones esto, tu contenedor siempre, bajo cualquier circunstancia, ejecutará ese script de Python al arrancar.
