Docker y Containerización - Eziuz/Proyecto-POLI-Generador-de-Claves GitHub Wiki
🐳 Docker y Containerización
Esta guía analiza en detalle el Dockerfile utilizado en el proyecto POLI-Generador-de-Claves, explicando las estrategias de optimización implementadas y las mejores prácticas de containerización.
🎯 Visión General
El Dockerfile implementa un enfoque multi-stage para:
- 🔄 Optimizar el tamaño de la imagen final
- 🛡️ Mejorar la seguridad con usuario no-root
- 🚀 Acelerar los tiempos de build con caching eficiente
- 📦 Preparar la aplicación para producción
📋 Análisis del Dockerfile
# Usar Node.js 20 LTS con Alpine como base
FROM node:20-alpine AS base
# Instalar dependencias solo cuando sea necesario
FROM base AS deps
# Instalar solo lo necesario para compilar
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Habilitar corepack para Yarn 4
RUN corepack enable
# Copiar archivos de configuración de Yarn
COPY .yarnrc.yml ./
COPY .yarn/releases/ ./.yarn/releases/
COPY package.json yarn.lock* ./
# Instalar dependencias con Yarn 4
RUN yarn install --immutable
# Construir la aplicación
FROM base AS builder
WORKDIR /app
# Habilitar corepack para Yarn 4
RUN corepack enable
# Copiar dependencias y archivos de configuración
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/.yarn/ ./.yarn/
COPY --from=deps /app/.yarnrc.yml ./
COPY . .
# Deshabilitar telemetría de Next.js
ENV NEXT_TELEMETRY_DISABLED=1
# Construir la aplicación
RUN yarn build
# Imagen de producción, usar una imagen aún más ligera
FROM node:20-alpine AS runner
WORKDIR /app
# Configurar entorno de producción
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Crear un usuario no-root para seguridad
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Copiar solo los archivos necesarios
COPY --from=builder /app/public ./public
COPY --from=builder /app/next.config.mjs ./
# Configurar permisos para el directorio .next
RUN mkdir .next && \
chown nextjs:nodejs .next
# Copiar el build optimizado
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Cambiar al usuario no-root
USER nextjs
# Exponer el puerto
EXPOSE 3000
# Configurar variables de entorno para el servidor
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1
# Comando para iniciar la aplicación
CMD ["node", "server.js"]
🏗️ Análisis por Etapas (Multi-stage Build)
1️⃣ Etapa Base
FROM node:20-alpine AS base
Decisión | Justificación | Beneficio |
---|---|---|
Node.js 20 LTS | Versión con soporte a largo plazo | Estabilidad y seguridad |
Alpine | Distribución Linux ligera | Imagen base más pequeña (~80MB vs ~1GB) |
AS base | Etapa nombrada para referencia | Reutilización en etapas posteriores |
Ventajas:
- 🏋️♀️ Tamaño reducido: Alpine es ~10x más pequeño que Debian/Ubuntu
- 🔒 Superficie de ataque menor: Menos paquetes = menos vulnerabilidades
- 🚀 Arranque rápido: Imágenes más pequeñas inician más rápido
2️⃣ Etapa de Dependencias
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Habilitar corepack para Yarn 4
RUN corepack enable
# Copiar archivos de configuración de Yarn
COPY .yarnrc.yml ./
COPY .yarn/releases/ ./.yarn/releases/
COPY package.json yarn.lock* ./
# Instalar dependencias con Yarn 4
RUN yarn install --immutable
Decisión | Justificación | Beneficio |
---|---|---|
Etapa separada | Aislar instalación de dependencias | Mejor caching de Docker |
libc6-compat | Compatibilidad con bibliotecas nativas | Evita errores de compilación |
Copiar solo config | Minimizar invalidación de cache | Builds más rápidos |
--immutable | Garantiza instalación determinista | Consistencia entre entornos |
Estrategia de caching:
- Docker cachea cada capa (instrucción) individualmente
- Solo se reconstruyen capas cuando sus dependencias cambian
- Al separar
package.json
yyarn.lock
, las dependencias solo se reinstalan cuando estos archivos cambian
Copiar package.json/yarn.lock
↓
¿Cambiaron?
┌─────┬─────┐
│ No │ Sí │
└─────┴─────┘
↓ ↓
Cache Reinstalar
↓ ↓
Continuar build
3️⃣ Etapa de Build
FROM base AS builder
WORKDIR /app
# Habilitar corepack para Yarn 4
RUN corepack enable
# Copiar dependencias y archivos de configuración
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/.yarn/ ./.yarn/
COPY --from=deps /app/.yarnrc.yml ./
COPY . .
# Deshabilitar telemetría de Next.js
ENV NEXT_TELEMETRY_DISABLED=1
# Construir la aplicación
RUN yarn build
Decisión | Justificación | Beneficio |
---|---|---|
Nueva etapa limpia | Separar build de dependencias | Evitar artefactos innecesarios |
COPY --from=deps | Reutilizar dependencias instaladas | No reinstalar dependencias |
COPY . . | Copiar todo el código fuente | Necesario para build |
Deshabilitar telemetría | Privacidad y rendimiento | Sin llamadas externas |
Optimizaciones:
- 🔄 Reutilización: Aprovecha dependencias ya instaladas
- 🧹 Limpieza: Etapa separada evita artefactos temporales
- 🚫 Sin telemetría: Mejora privacidad y rendimiento
4️⃣ Etapa de Producción
FROM node:20-alpine AS runner
WORKDIR /app
# Configurar entorno de producción
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Crear un usuario no-root para seguridad
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Copiar solo los archivos necesarios
COPY --from=builder /app/public ./public
COPY --from=builder /app/next.config.mjs ./
# Configurar permisos para el directorio .next
RUN mkdir .next && \
chown nextjs:nodejs .next
# Copiar el build optimizado
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Cambiar al usuario no-root
USER nextjs
# Exponer el puerto
EXPOSE 3000
# Configurar variables de entorno para el servidor
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1
# Comando para iniciar la aplicación
CMD ["node", "server.js"]
Decisión | Justificación | Beneficio |
---|---|---|
Nueva imagen base | Imagen limpia para producción | Tamaño mínimo final |
Usuario no-root | Seguridad | Mitigación de vulnerabilidades |
Copiar solo lo necesario | Minimizar tamaño | Imagen final más pequeña |
--chown | Permisos correctos | Evita problemas de acceso |
HEALTHCHECK | Monitoreo de salud | Detección automática de problemas |
Mejores prácticas de seguridad:
- 🛡️ Usuario no-root: Limita el impacto de vulnerabilidades
- 📁 Permisos explícitos: Solo los necesarios para la aplicación
- 🔍 Health check: Monitoreo automático de la aplicación
📊 Comparativa de Tamaños
Enfoque | Tamaño Aproximado | Notas |
---|---|---|
Node.js en Debian | ~1.2 GB | Imagen base estándar |
Node.js en Alpine | ~120 MB | Reducción del 90% |
Single-stage build | ~500 MB | Con node_modules y archivos temporales |
Multi-stage build | ~150 MB | Solo archivos necesarios |
Multi-stage + Alpine | ~150 MB | Implementación actual |
🔧 Optimizaciones Implementadas
1. 📦 Estrategia de Capas
Base: Alpine + Node.js
↓
Deps: Solo dependencias
↓
Builder: Código + Build
↓
Runner: Solo artefactos
Beneficios:
- ⚡ Builds más rápidos: Reutilización de capas no modificadas
- 🔄 Desarrollo eficiente: Reconstruye solo lo necesario
- 📉 Menor tamaño: Elimina archivos intermedios
2. 🛡️ Seguridad Mejorada
Medida | Implementación | Propósito |
---|---|---|
Usuario no-root | adduser --system --uid 1001 nextjs |
Limitar privilegios |
Permisos específicos | --chown=nextjs:nodejs |
Principio de mínimo privilegio |
Imagen base mínima | Alpine | Reducir superficie de ataque |
Health check | HEALTHCHECK |
Detección temprana de problemas |
3. 🚀 Optimización de Rendimiento
Técnica | Implementación | Beneficio |
---|---|---|
NODE_ENV=production | Variable de entorno | Optimizaciones automáticas de Node.js |
Standalone output | Next.js output: 'standalone' | Elimina dependencias innecesarias |
Hostname 0.0.0.0 | ENV HOSTNAME="0.0.0.0" |
Accesible desde cualquier interfaz |
Caching de dependencias | Etapa separada | Reutilización entre builds |
🔍 Análisis de Seguridad
Vulnerabilidades Mitigadas
Vulnerabilidad | Mitigación | Implementación |
---|---|---|
Escalada de privilegios | Usuario no-root | USER nextjs |
Inyección de comandos | Comando fijo | CMD ["node", "server.js"] |
Exposición de secretos | Multi-stage build | Secretos no persisten en imagen final |
Paquetes vulnerables | Alpine minimalista | Menos paquetes = menos vulnerabilidades |
Denegación de servicio | Health check | Detección y recuperación automática |
Escaneo de Seguridad
Para escanear la imagen en busca de vulnerabilidades:
# Usando Trivy (herramienta de escaneo de seguridad)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image usuario/generador-claves:latest
🚀 Uso de la Imagen
Ejecutar en Desarrollo
# Construir la imagen
docker build -t generador-claves:dev .
# Ejecutar con montaje de volumen para desarrollo
docker run -p 3000:3000 -v $(pwd):/app \
-e NODE_ENV=development \
--name dev-generador \
generador-claves:dev
Ejecutar en Producción
# Descargar la imagen oficial
docker pull usuario/generador-claves:latest
# Ejecutar en producción
docker run -d -p 3000:3000 \
--restart unless-stopped \
--name generador-claves \
usuario/generador-claves:latest
Docker Compose
version: '3.8'
services:
app:
image: usuario/generador-claves:latest
ports:
- "3000:3000"
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/"]
interval: 30s
timeout: 3s
retries: 3
start_period: 5s
🔧 Personalización y Extensión
Variables de Entorno
Variable | Propósito | Valor por defecto |
---|---|---|
PORT |
Puerto de la aplicación | 3000 |
HOSTNAME |
Interfaz de escucha | 0.0.0.0 |
NODE_ENV |
Entorno de ejecución | production |
Añadir Funcionalidades
Para extender la imagen con funcionalidades adicionales:
FROM usuario/generador-claves:latest
# Instalar herramientas adicionales
USER root
RUN apk add --no-cache curl
# Copiar configuración personalizada
COPY ./custom-config.json /app/config.json
# Volver al usuario no-root
USER nextjs
🚨 Troubleshooting
Error: "standard_init_linux.go:228: exec user process caused: no such file or directory"
Causa: Problemas con los finales de línea (CRLF vs LF)
Solución:
# Convertir finales de línea a LF
git config --global core.autocrlf input
# Clonar de nuevo el repositorio o convertir los archivos
dos2unix entrypoint.sh
Error: "Error response from daemon: OCI runtime create failed"
Causa: Problemas de permisos o incompatibilidad de arquitectura
Solución:
# Verificar arquitectura
docker buildx build --platform linux/amd64,linux/arm64 -t usuario/generador-claves:latest .
Error: "EACCES: permission denied" dentro del contenedor
Causa: Problemas de permisos con archivos o directorios
Solución:
# Ajustar permisos en el Dockerfile
RUN mkdir -p /app/data && chown -R nextjs:nodejs /app/data
📚 Referencias y Recursos
Documentación Oficial
Herramientas Útiles
- 🔍 Dive - Herramienta para explorar capas de Docker
- 🛡️ Trivy - Escáner de vulnerabilidades
- 📊 Docker Stats - Monitoreo de recursos