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:

  1. Docker cachea cada capa (instrucción) individualmente
  2. Solo se reconstruyen capas cuando sus dependencias cambian
  3. Al separar package.json y yarn.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