Volver a Spring Boot Intermedio

Contenedores y Despliegue de Aplicaciones Spring Boot

Construir una Imagen Docker

Usando los Buildpacks Integrados de Spring Boot

# Construir imagen OCI sin Dockerfile ./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=miapp:latest

Dockerfile Optimizado

# Build multi-etapa para imagen mínima FROM eclipse-temurin:21-jdk-alpine AS constructor WORKDIR /app COPY mvnw pom.xml ./ COPY .mvn .mvn RUN ./mvnw dependency:go-offline -q COPY src src RUN ./mvnw package -DskipTests -q # Extraer JAR por capas RUN java -Djarmode=layertools -jar target/*.jar extract # Imagen de ejecución FROM eclipse-temurin:21-jre-alpine WORKDIR /app # Copiar capas para mejor caché COPY --from=constructor /app/dependencies ./ COPY --from=constructor /app/spring-boot-loader ./ COPY --from=constructor /app/snapshot-dependencies ./ COPY --from=constructor /app/application ./ # Usuario no-root por seguridad RUN addgroup -S spring && adduser -S spring -G spring USER spring EXPOSE 8080 ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

Docker Compose para Producción

version: '3.8' services: app: image: miapp:latest ports: - "8080:8080" environment: SPRING_PROFILES_ACTIVE: prod SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/mibd SPRING_DATASOURCE_USERNAME: ${DB_USUARIO} SPRING_DATASOURCE_PASSWORD: ${DB_CONTRASENA} JWT_SECRETO: ${JWT_SECRETO} depends_on: db: condition: service_healthy healthcheck: test: ["CMD", "wget", "-q", "-O-", "http://localhost:8080/actuator/health"] interval: 30s timeout: 10s retries: 3 db: image: postgres:15-alpine environment: POSTGRES_DB: mibd POSTGRES_USER: ${DB_USUARIO} POSTGRES_PASSWORD: ${DB_CONTRASENA} volumes: - datos-postgres:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USUARIO}"] interval: 10s volumes: datos-postgres:

Despliegue en Kubernetes

# k8s/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: servicio-productos spec: replicas: 3 selector: matchLabels: app: servicio-productos template: metadata: labels: app: servicio-productos spec: containers: - name: servicio-productos image: miapp:1.0.0 ports: - containerPort: 8080 env: - name: SPRING_PROFILES_ACTIVE value: prod - name: DB_CONTRASENA valueFrom: secretKeyRef: name: secreto-bd key: contrasena resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 10 periodSeconds: 5 livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 30 periodSeconds: 10

Configuración de Probes en Kubernetes

# application.yml — exponer probes de Kubernetes management: endpoint: health: probes: enabled: true health: livenessstate: enabled: true readinessstate: enabled: true

CI/CD con GitHub Actions

# .github/workflows/deploy.yml name: Construir y Desplegar on: push: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Configurar JDK 21 uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin' cache: maven - name: Ejecutar pruebas run: ./mvnw test - name: Construir imagen Docker run: ./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=ghcr.io/${{ github.repository }}:${{ github.sha }} - name: Publicar imagen run: | echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin docker push ghcr.io/${{ github.repository }}:${{ github.sha }} - name: Desplegar en Kubernetes run: | kubectl set image deployment/servicio-productos \ servicio-productos=ghcr.io/${{ github.repository }}:${{ github.sha }}

Imagen Nativa con GraalVM (Opcional)

Para inicio ultra-rápido y bajo consumo de memoria:

<plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> </plugin>
./mvnw native:compile -Pnative ./target/demo # inicia en ~50ms, usa ~80MB RAM