Capítulo 1: Preparación para Producción
Introducción
Antes de desplegar, debe preparar su aplicación. Este capítulo cubre el empaquetado, los perfiles y la gestión de configuraciones.
🎯 Objetivos del Capítulo
Al final de este capítulo, usted sabrá:
- ✅ Crear un JAR ejecutable
- ✅ Configurar los perfiles de Spring
- ✅ Externalizar la configuración
- ✅ Gestionar los secretos de manera segura
- ✅ Optimizar para producción
1. Empaquetado: JAR vs WAR
JAR Ejecutable (Recomendado)
Spring Boot crea un fat JAR que contiene todo:
# Build
mvn clean package
# Ejecutar
java -jar target/myapp-1.0.0.jar
Ventajas:
- ✅ Servidor Tomcat embebido
- ✅ Sencillo de desplegar
- ✅ Portable
- ✅ Perfecto para Docker
WAR (Servidor externo)
Para desplegar en Tomcat/JBoss existente:
<!-- pom.xml -->
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
// Modificar la clase principal
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Recomendación
JAR para:
- Docker / Kubernetes
- Cloud (Heroku, Railway, AWS)
- Microservicios
WAR para:
- Infraestructura legacy
- Servidores de aplicaciones existentes
2. Perfiles de Spring
Definir Perfiles
src/main/resources/
├── application.properties # Configuración común
├── application-dev.properties # Desarrollo
├── application-prod.properties # Producción
└── application-test.properties # Pruebas
application.properties (común)
spring.application.name=myapp
server.port=8080
# Perfil por defecto
spring.profiles.active=dev
application-dev.properties
# Base de datos H2 en memoria
spring.datasource.url=jdbc:h2:mem:devdb
spring.datasource.driver-class-name=org.h2.Driver
spring.h2.console.enabled=true
# JPA
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# Logging verbose
logging.level.com.myapp=DEBUG
logging.level.org.hibernate.SQL=DEBUG
# Actuator: todo expuesto en dev
management.endpoints.web.exposure.include=*
application-prod.properties
# PostgreSQL en producción
spring.datasource.url=${DATABASE_URL}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
# JPA
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false
# Logging mínimo
logging.level.root=WARN
logging.level.com.myapp=INFO
# Actuator: seguro
management.endpoints.web.exposure.include=health,info,prometheus
management.endpoint.health.show-details=when_authorized
# Seguridad
server.error.include-stacktrace=never
Activar un Perfil
# Vía argumento JVM
java -jar myapp.jar --spring.profiles.active=prod
# Vía variable de entorno
export SPRING_PROFILES_ACTIVE=prod
java -jar myapp.jar
# Vía Docker
docker run -e SPRING_PROFILES_ACTIVE=prod myapp
3. Configuración Externalizada
Orden de Prioridad
Spring Boot carga la configuración en este orden (prioridad creciente):
1. application.properties (dentro del JAR)
2. application-{profile}.properties (dentro del JAR)
3. application.properties (fuera del JAR)
4. application-{profile}.properties (fuera del JAR)
5. Variables de entorno
6. Argumentos de línea de comandos
Variables de Entorno
# application.properties
spring.datasource.url=${DATABASE_URL:jdbc:h2:mem:default}
spring.datasource.username=${DB_USER:sa}
spring.datasource.password=${DB_PASS:}
# Definir las variables
export DATABASE_URL=jdbc:postgresql://localhost:5432/mydb
export DB_USER=myuser
export DB_PASS=secret
java -jar myapp.jar
Configuración YAML (alternativa)
# application.yml
spring:
application:
name: myapp
profiles:
active: dev
---
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:h2:mem:devdb
h2:
console:
enabled: true
---
spring:
config:
activate:
on-profile: prod
datasource:
url: ${DATABASE_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
4. Gestión de Secretos
❌ Nunca Hacer
# ¡NUNCA en el código fuente!
spring.datasource.password=MySecretPassword123
api.key=sk-1234567890abcdef
✅ Buenas Prácticas
Opción 1: Variables de Entorno
export DB_PASSWORD=secret
export API_KEY=sk-xxx
Opción 2: Archivo .env (solo local)
# .env (¡agregar a .gitignore!)
DATABASE_URL=jdbc:postgresql://localhost:5432/mydb
DB_PASSWORD=secret
API_KEY=sk-xxx
Opción 3: Secret Managers
# Kubernetes Secret
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
data:
db-password: c2VjcmV0 # base64
Opción 4: Spring Cloud Config
# Para los secretos centralizados
spring:
cloud:
config:
uri: http://config-server:8888
.gitignore
# Secretos
.env
*.pem
*.key
application-local.properties
secrets/
5. Optimizaciones para Producción
Configuración JVM
# Production JVM settings
java -Xms512m -Xmx1024m \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-Djava.security.egd=file:/dev/./urandom \
-jar myapp.jar
application-prod.properties
# Compresión de respuestas
server.compression.enabled=true
server.compression.mime-types=application/json,text/html,text/css
server.compression.min-response-size=1024
# Connection pool optimizado
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=300000
# Caché de templates
spring.thymeleaf.cache=true
# Desactivar features de desarrollo
spring.devtools.restart.enabled=false
spring.h2.console.enabled=false
Graceful Shutdown
# Esperar a que terminen las solicitudes en curso
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s
6. Build para Producción
Maven
# Build con perfil prod
mvn clean package -Pprod -DskipTests
# O con el perfil de Spring
mvn clean package -Dspring.profiles.active=prod
pom.xml - Perfiles Maven
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<spring.profiles.active>dev</spring.profiles.active>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<spring.profiles.active>prod</spring.profiles.active>
</properties>
</profile>
</profiles>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
Verificar el JAR
# Contenido del JAR
jar tf target/myapp-1.0.0.jar
# Probar localmente
java -jar target/myapp-1.0.0.jar --spring.profiles.active=prod
7. Resumen
Checklist Producción
□ JAR ejecutable creado
□ Perfiles configurados (dev, prod, test)
□ Secretos externalizados (no en el código)
□ Variables de entorno documentadas
□ Logging configurado (no DEBUG en prod)
□ Actuator seguro
□ Compresión activada
□ Connection pool configurado
□ Graceful shutdown activado
Archivos de Configuración
application.properties → Configuración común
application-dev.properties → H2, DEBUG, todo expuesto
application-prod.properties → PostgreSQL, WARN, seguro
application-test.properties → H2, para pruebas
Variables Esenciales
SPRING_PROFILES_ACTIVE=prod
DATABASE_URL=jdbc:postgresql://...
DB_USERNAME=myuser
DB_PASSWORD=secret
SERVER_PORT=8080
🎯 Próximo Paso
La aplicación está lista. ¡Ahora, contenedoricémosla con Docker!
➡️ Capítulo 2: Contenerización Docker