Dockerfile en Profundidad y Buenas Prácticas
Un Dockerfile es el manifiesto de texto declarativo que contiene las instrucciones secuenciales que el motor de Docker ejecuta para construir una imagen de contenedor.
Escribir un Dockerfile eficiente no solo acelera los tiempos de compilación (Build Time), sino que reduce el consumo de almacenamiento y minimiza la superficie de ataque frente a vulnerabilidades de seguridad.
1. Directivas Esenciales de un Dockerfile #
| Directiva | Propósito | Ejemplo / Buena Práctica |
|---|---|---|
FROM | Define la imagen base sobre la que se construye. | FROM node:20-alpine (usar versiones específicas, evitar latest). |
WORKDIR | Establece el directorio de trabajo dentro del contenedor (lo crea si no existe). | WORKDIR /app (nunca trabajar en la raíz /). |
COPY vs ADD | COPY transfiere archivos locales del host. ADD descomprime además tarballs y descarga URLs remotas. | Usar siempre COPY por predictibilidad y seguridad. |
RUN | Ejecuta comandos durante la fase de compilación de la imagen. | Encadenar comandos (RUN apt update && apt install -y ... && rm -rf /var/lib/apt/lists/*) para crear una sola capa. |
ENV vs ARG | ARG define variables solo disponibles durante el build; ENV persiste variables de entorno dentro del contenedor en ejecución. | ARG NODE_ENV=production / ENV PORT=3000. |
USER | Especifica el usuario no privilegiado con el que se ejecutará el proceso. | USER node o USER 1001 (evitar correr como root). |
EXPOSE | Documenta los puertos TCP/UDP en los que la aplicación escucha tráfico. | EXPOSE 8080 (es solo informativo; requiere -p al ejecutar). |
HEALTHCHECK | Permite a Docker comprobar si la aplicación interna sigue viva y respondiendo. | HEALTHCHECK --interval=30s CMD curl -f http://localhost:8080/health || exit 1. |
2. Diferencia Crucial: ENTRYPOINT vs CMD #
Ambas directivas definen qué comando se ejecutará cuando arranque el contenedor, pero tienen funciones muy distintas:
1# Forma EXEC (Recomendada con corchetes JSON) 2ENTRYPOINT ["node", "server.js"] 3CMD ["--port", "3000"]
ENTRYPOINT: Define el ejecutable principal inmutable del contenedor.CMD: Proporciona los argumentos predeterminados para elENTRYPOINT.- Si el usuario ejecuta
docker run mi-app, se ejecutará:node server.js --port 3000. - Si el usuario ejecuta
docker run mi-app --port 8080, el argumento personalizado sobreescribe elCMD, ejecutando:node server.js --port 8080.
- Si el usuario ejecuta
3. Seguridad: Ejecutar siempre como Usuario No-Root #
Por defecto, los procesos dentro de un contenedor se ejecutan como el usuario root (UID 0). Si un atacante consigue explotar una vulnerabilidad en la aplicación y escapar del contenedor, tendrá privilegios de administrador en el sistema operativo anfitrión.
1FROM node:20-alpine 2WORKDIR /usr/src/app 3 4# Crear usuario y grupo del sistema sin privilegios 5RUN addgroup -S appgroup && adduser -S appuser -G appgroup 6 7COPY package*.json ./ 8RUN npm ci --only=production 9 10COPY . . 11 12# Cambiar al usuario no-root antes de arrancar 13USER appuser 14 15EXPOSE 3000 16CMD ["node", "index.js"]
4. El Archivo .dockerignore #
Al igual que .gitignore, el archivo .dockerignore ubicado junto al Dockerfile excluye archivos y carpetas locales para que no se envíen al contexto de compilación del demonio de Docker (Docker Build Context):
1# .dockerignore 2node_modules 3.git 4.env* 5npm-debug.log 6dist 7Dockerfile 8README.md
Resumen del tema
Conceptos clave #
- Caché de Capas: coloca las instrucciones que cambian con poca frecuencia (
COPY package.jsonyRUN npm install) antes de las instrucciones que cambian frecuentemente (COPY . .). - Uso de
COPY: prefiere siempreCOPYfrente aADD. - Principio de Menor Privilegio: define siempre un
USERno root para mitigar vulnerabilidades de seguridad. .dockerignore: acelera la compilación evitando enviar carpetas pesadas o credenciales sensibles al demonio de Docker.
Qué debes recordar #
Estructura tus Dockerfiles agrupando comandos en una sola capa RUN y ejecuta tus aplicaciones con usuarios no privilegiados para crear imágenes ligeras y seguras.