# Docker — Ejemplos y Configuraciones Completas

Referencia extendida de la skill `contenedores-docker`. Contiene Dockerfiles completos,
docker-compose, networking, volúmenes, security scanning y optimización de caché.

---

## Dockerfile — Node.js + Angular (Multi-stage)

```dockerfile
# Etapa 1: Dependencias npm
FROM node:20-alpine AS npm-deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production

# Etapa 2: Build Angular
FROM node:20-alpine AS angular-builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build -- --configuration=production --output-path=dist

# Etapa 3: Nginx para servir el SPA
FROM nginx:1.25-alpine AS production

COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=angular-builder /app/dist /usr/share/nginx/html

RUN chown -R nginx:nginx /usr/share/nginx/html && \
    chown -R nginx:nginx /var/cache/nginx && \
    touch /var/run/nginx.pid && \
    chown nginx:nginx /var/run/nginx.pid

USER nginx

EXPOSE 80

HEALTHCHECK --interval=30s --timeout=5s \
    CMD curl -f http://localhost/health || exit 1
```

---

## .dockerignore completo

```dockerignore
# Control de versiones
.git
.gitignore

# Dependencias locales
node_modules
__pycache__
*.pyc
*.pyo
.venv
venv
env

# Tests y documentacion
tests/
docs/
*.md
!README.md

# Archivos de configuracion de desarrollo
.env
.env.*
*.env
docker-compose.override.yml
.editorconfig
.eslintrc*
.prettierrc*

# IDE
.vscode
.idea
*.swp

# Build outputs locales
dist/
build/
.angular/
coverage/
.pytest_cache/

# CI/CD
.github/
.gitlab-ci.yml
Dockerfile*
docker-compose*.yml
```

---

## docker-compose — Entorno de Desarrollo Completo

```yaml
version: '3.9'

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
      target: deps
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
    volumes:
      - .:/app
      - /app/.venv
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/appdb
      - REDIS_URL=redis://redis:6379/0
      - DEBUG=true
    env_file:
      - .env.local
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - app-network
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d appdb"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --requirepass "${REDIS_PASSWORD:-dev_password}"
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
    networks:
      - app-network

  nginx:
    image: nginx:1.25-alpine
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - api
    networks:
      - app-network

volumes:
  postgres_data:
    driver: local
  redis_data:
    driver: local

networks:
  app-network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16
```

---

## Networking en Docker

```bash
# Crear red personalizada
docker network create --driver bridge --subnet 172.21.0.0/16 mi-red

# Inspeccionar red
docker network inspect mi-red

# MAL: usar --link (obsoleto)
docker run --link db:database api

# BIEN: red compartida
docker run --network mi-red --name api mi-imagen-api
docker run --network mi-red --name db postgres:15
```

---

## Volúmenes — Persistencia Correcta

```yaml
# 1. Named volumes (RECOMENDADO para datos de BD)
volumes:
  postgres_data:

# 2. Bind mounts (SOLO para desarrollo — hot reload)
volumes:
  - ./src:/app/src

# 3. tmpfs (datos efimeros)
tmpfs:
  - /tmp
  - /run

# MAL: montar directorio completo en produccion
volumes:
  - .:/app                # Incluye .git, node_modules, .env!

# BIEN: montar solo lo necesario
volumes:
  - ./config:/app/config:ro
  - ./logs:/app/logs
```

---

## Variables de Entorno y Secrets

```bash
# MAL: secrets en variables de entorno del Dockerfile
ENV DATABASE_PASSWORD=supersecreta123

# MAL: pasar secrets en build args
docker build --build-arg DB_PASS=secreto .

# BIEN: runtime environment variables
docker run -e DATABASE_URL="postgresql://..." mi-imagen

# BIEN: Docker Secrets (en Swarm)
docker secret create db_password ./db_password.txt

# BIEN: En docker-compose con .env
env_file:
  - .env
```

---

## Security Scanning — Pipeline de CI

```yaml
# .github/workflows/security.yml
name: Security Scan

on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build imagen
        run: docker build -t mi-app:${{ github.sha }} .

      - name: Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: mi-app:${{ github.sha }}
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'

      - name: Hadolint
        uses: hadolint/hadolint-action@v3.1.0
        with:
          dockerfile: Dockerfile

      - name: Dockle
        run: |
          docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
            goodwithtech/dockle:latest --exit-code 1 mi-app:${{ github.sha }}
```

---

## Optimización de Build Cache

```dockerfile
# ORDEN CRITICO para maximizar cache:
# 1. Lo que cambia MENOS al inicio
# 2. Lo que cambia MAS al final

# MAL: copiar todo antes de instalar dependencias
COPY . .
RUN pip install -r requirements.txt

# BIEN: dependencias antes que codigo
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

# BuildKit — cache de montaje
# syntax=docker/dockerfile:1
FROM python:3.12-slim

RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

RUN --mount=type=cache,target=/var/cache/apt \
    apt-get update && apt-get install -y libpq-dev
```
