Capítulo 2: Contenerización con Docker
Introducción
Docker permite empaquetar su aplicación con todas sus dependencias. "Funciona en mi máquina" se convierte en "Funciona en todas partes".
🎯 Objetivos del Capítulo
Al final de este capítulo, usted sabrá:
- ✅ Crear un Dockerfile para Spring Boot
- ✅ Optimizar con multi-stage builds
- ✅ Usar Docker Compose
- ✅ Gestionar las configuraciones
- ✅ Optimizar el tamaño de las imágenes
1. Dockerfile Básico
Versión Simple
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Build y Run
# Compilar la aplicación
mvn clean package -DskipTests
# Construir la imagen
docker build -t myapp:1.0 .
# Ejecutar
docker run -p 8080:8080 myapp:1.0
2. Multi-Stage Build (Recomendado)
Dockerfile Optimizado
# ===== STAGE 1: Build =====
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
# Copiar los archivos de dependencias primero (cache Docker)
COPY pom.xml .
RUN mvn dependency:go-offline -B
# Copiar el código fuente y compilar
COPY src ./src
RUN mvn clean package -DskipTests
# ===== STAGE 2: Runtime =====
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# Crear un usuario no-root
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
# Copiar el JAR de la etapa anterior
COPY --from=builder /app/target/*.jar app.jar
# Configuración
EXPOSE 8080
ENV JAVA_OPTS="-Xms256m -Xmx512m"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Ventajas del Multi-Stage
Imagen simple: ~500 MB (con Maven + JDK)
Imagen multi-stage: ~200 MB (solo JRE)
Imagen Alpine: ~150 MB (JRE Alpine mínimo)
3. Dockerfile con Layers
Spring Boot 2.3+ soporta layers para un cache Docker optimizado:
# ===== STAGE 1: Build =====
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn clean package -DskipTests
# Extraer los layers
RUN java -Djarmode=layertools -jar target/*.jar extract
# ===== STAGE 2: Runtime =====
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
# Copiar los layers (en orden de frecuencia de cambio)
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
Activar los Layers
<!-- pom.xml -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
4. Docker Compose
docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
- DATABASE_URL=jdbc:postgresql://db:5432/mydb
- DB_USERNAME=postgres
- DB_PASSWORD=secret
depends_on:
db:
condition: service_healthy
networks:
- backend
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secret
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- backend
volumes:
postgres_data:
networks:
backend:
driver: bridge
Comandos Docker Compose
# Iniciar todos los servicios
docker-compose up -d
# Ver los logs
docker-compose logs -f app
# Detener
docker-compose down
# Reconstruir
docker-compose up -d --build
5. Perfil Docker
application-docker.properties
# Configuración para Docker
spring.datasource.url=${DATABASE_URL}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
# Flyway
spring.flyway.enabled=true
# Actuator
management.endpoints.web.exposure.include=health,info,prometheus
# Logging
logging.level.root=INFO
6. .dockerignore
# Build
target/
!target/*.jar
# IDE
.idea/
*.iml
.vscode/
# Git
.git/
.gitignore
# Docker
Dockerfile
docker-compose*.yml
.dockerignore
# Logs
*.log
logs/
# Tests
src/test/
7. Optimizaciones
Imagen Alpine (más ligera)
FROM eclipse-temurin:17-jre-alpine
Usuario No-Root
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
Health Check
HEALTHCHECK --interval=30s --timeout=3s --start-period=30s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:8080/actuator/health || exit 1
Límites de Recursos
# docker-compose.yml
services:
app:
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
8. Docker Build con BuildKit
Activar BuildKit
# Variable de entorno
export DOCKER_BUILDKIT=1
docker build -t myapp .
# O en Docker Desktop: Settings > Docker Engine
{
"features": {
"buildkit": true
}
}
Cache de Dependencias Maven
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
# Cache de dependencias con BuildKit
COPY pom.xml .
RUN --mount=type=cache,target=/root/.m2 mvn dependency:go-offline -B
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 mvn clean package -DskipTests
9. Comandos Útiles
Build
# Build simple
docker build -t myapp:latest .
# Build con tag
docker build -t myapp:1.0.0 -t myapp:latest .
# Build sin cache
docker build --no-cache -t myapp .
# Build con argumentos
docker build --build-arg JAR_FILE=target/*.jar -t myapp .
Run
# Run básico
docker run -p 8080:8080 myapp
# Run en segundo plano
docker run -d -p 8080:8080 --name myapp-container myapp
# Run con variables de entorno
docker run -d -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e DATABASE_URL=jdbc:postgresql://host:5432/db \
myapp
# Run con archivo .env
docker run -d -p 8080:8080 --env-file .env myapp
Debug
# Logs
docker logs myapp-container
docker logs -f myapp-container # Follow
# Shell dentro del contenedor
docker exec -it myapp-container sh
# Inspeccionar
docker inspect myapp-container
10. Resumen
Dockerfile Recomendado
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Checklist Docker
□ Multi-stage build
□ Imagen Alpine (ligera)
□ Usuario no-root
□ .dockerignore configurado
□ Variables de entorno para secretos
□ Health check
□ docker-compose para desarrollo local
🎯 Próximo Paso
La imagen Docker está lista. ¡Automaticemos el build y el despliegue con CI/CD!
➡️ Capítulo 3: CI/CD con GitHub Actions