"Configuration management é a base de DevOps. Um Config Service centralizado elimina 'works on my machine' e habilita GitOps completo." — #30DiasJava DevOps Notes
🎯 Objetivo do Day 16
Com múltiplos serviços rodando, gerenciar configurações em cada um era um pesadelo. O Day 16 do #30DiasJava entregou um Config Service centralizado com backend Git, encryption de secrets, refresh dinâmico e suporte a múltiplos ambientes.
🛠️ O que foi implementado
✅ Config Service Centralizado
- Spring Cloud Config Server: Servidor centralizado de configuração
- Git backend: Configurações versionadas em Git
- File-based config: Suporte a YAML, Properties, JSON
- Environment profiles: Configs por ambiente (dev, staging, prod)
- Label/tag support: Configs por versão/tag do Git
✅ Encryption de Secrets
- Symmetric encryption: AES-256 para secrets sensíveis
- Key rotation: Rotação automática de chaves de encryption
- Environment variables: Secrets via env vars (mais seguro)
- Vault integration: Suporte para HashiCorp Vault (opcional)
✅ Dynamic Configuration Refresh
- @RefreshScope: Beans que podem ser atualizados sem restart
- Actuator endpoints:
/actuator/refresh,/actuator/bus-refresh - Webhooks: Refresh automático via webhook do Git
- Selective refresh: Refresh apenas de serviços específicos
✅ Multi-Environment Support
- Profile-based config:
application-dev.yml,application-prod.yml - Environment variables override: Env vars sobrescrevem configs
- Default values: Fallbacks quando configs não existem
- Environment-specific secrets: Secrets diferentes por ambiente
✅ Observabilidade
- Config change events: Eventos quando configs mudam
- Config audit log: Histórico de mudanças de configuração
- Health checks: Verificação de conectividade com Git backend
- Metrics:
config.requests.total,config.refresh.total
📊 Arquitetura
┌──────────────┐
│ Git Repo │
│ (Configs) │
└──────┬───────┘
│
▼
┌─────────────────┐
│ Config Server │
│ (Port 8888) │
└──────┬──────────┘
│
├──────────────┬──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Backend │ │ Payment │ │ Search │
│ Service │ │ Service │ │ Service │
└──────────┘ └──────────┘ └──────────┘
🔧 Implementação Técnica
1. Config Server (pom.xml)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
2. Config Server Application
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
3. Config Server Configuration
spring:
cloud:
config:
server:
git:
uri: https://github.com/adelmonsouza/30dias-java-configs
default-label: main
search-paths: '{application}'
encryption:
enabled: true
key-store:
location: classpath:config-server-keystore.jks
password: ${KEYSTORE_PASSWORD}
alias: config-server-key
4. Client Configuration
spring:
config:
import: configserver:http://config-server:8888
cloud:
config:
name: backend-service
profile: ${SPRING_PROFILES_ACTIVE:prod}
label: ${CONFIG_LABEL:main}
5. Encrypted Secrets
# application-prod.yml
database:
password: '{cipher}AQA...encrypted...'
jwt:
secret: '{cipher}AQA...encrypted...'
6. Dynamic Refresh
@RestController
@RefreshScope
public class ConfigController {
@Value("${feature.new-checkout:false}")
private boolean newCheckout;
@GetMapping("/config/feature/new-checkout")
public Map<String, Boolean> getFeature() {
return Map.of("enabled", newCheckout);
}
}
💡 Padrões de Configuração
Hierarquia de Configs
- Defaults (application.yml)
- Profile (application-prod.yml)
- Environment Variables (highest priority)
Config Organization
config-repo/
├── application.yml # Shared configs
├── application-dev.yml # Dev overrides
├── application-prod.yml # Prod overrides
├── backend-service/
│ ├── application.yml
│ └── application-prod.yml
└── payment-service/
├── application.yml
└── application-prod.yml
💡 Lições do dia
- Centralização elimina duplicação: Um único source of truth
- Versionamento é essencial: Git permite rollback de configs
- Encryption protege secrets: Nunca commitar secrets em texto plano
- Refresh dinâmico reduz downtime: Atualiza configs sem restart
- Multi-environment é obrigatório: Dev, staging, prod precisam isolamento
📈 Observabilidade
Config Health Check
@Component
public class ConfigHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// Check Git connectivity
// Check encryption key availability
return Health.up()
.withDetail("git", "connected")
.withDetail("encryption", "enabled")
.build();
}
}
Config Change Events
@EventListener
public void handleConfigChange(EnvironmentChangeEvent event) {
log.info("Configuration changed: {}", event.getKeys());
metrics.increment("config.changes.total", "keys", event.getKeys().size());
}
🔗 Recursos
- Repositório: https://github.com/adelmonsouza/30DiasJava-Day16-ConfigService
- Spring Cloud Config: https://spring.io/projects/spring-cloud-config
- Encryption Guide: https://cloud.spring.io/spring-cloud-config/reference/html/#_encryption_and_decryption
- Projeto pessoal: Este é um projeto do desafio #30DiasJava, mantido independentemente para fins educacionais.
Next episode → Day 17/30 — Resilience Patterns & Circuit Breakers (em breve)