"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

  1. Defaults (application.yml)
  2. Profile (application-prod.yml)
  3. 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


Next episode → Day 17/30 — Resilience Patterns & Circuit Breakers (em breve)