google-site-verification: google61fe8ba583a51912.html
Spring Cloud Config Server in Java

Spring Cloud Config Server provides centralized external configuration management distributed across multiple applications and environments. This comprehensive guide covers setting up, configuring, and using Spring Cloud Config Server in Java applications.

Basic Config Server Setup

1. Config Server Dependencies

Maven Configuration:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.0</version> <relativePath/> </parent> <properties> <java.version>17</java.version> <spring-cloud.version>2022.0.3</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>

2. Basic Config Server Application

@SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }

3. Config Server Configuration

application.yml:

server: port: 8888 spring: application: name: config-server profiles: active: native, git security: user: name: config-user password: config-password cloud: config: server: # Git backend configuration git: uri: https://github.com/my-organization/config-repo default-label: main search-paths: - "applications/{application}" - "environments/{profile}" timeout: 5 force-pull: true # Native file system backend native: search-locations: - "classpath:/config/" - "file:./config/" - "file:/etc/config/" # Composite configuration with multiple backends composite: - type: git uri: https://github.com/my-org/config-repo - type: vault host: 127.0.0.1 port: 8200 kv-version: 2 # Encryption configuration encrypt: key-store: location: classpath:keystore.jks password: storepass alias: configkey secret: keypass # Management endpoints management: endpoints: web: exposure: include: health,info,metrics,refresh,bus-refresh endpoint: health: show-details: always

Git Backend Configuration

4. Advanced Git Configuration

spring: cloud: config: server: git: uri: https://github.com/my-organization/config-repo default-label: main search-paths: - "applications/{application}" - "services/{application}" - "shared/{profile}" repos: # Application-specific repositories my-special-app: pattern: my-special-app* uri: https://github.com/my-organization/special-config-repo search-paths: - "config" # Development team repositories team-a: pattern: team-a-* uri: https://github.com/team-a/config-repo team-b: pattern: team-b-* uri: https://github.com/team-b/config-repo # Git authentication username: ${GIT_USERNAME} password: ${GIT_PASSWORD} passphrase: ${GIT_PASSPHRASE} private-key: ${GIT_PRIVATE_KEY} # Git configuration timeout: 10 basedir: /tmp/config-repo force-pull: true delete-untracked-branches: true clone-on-start: true

5. File System Backend

spring: profiles: active: native cloud: config: server: native: search-locations: - "classpath:/config/" - "file:./config-repo/" - "file:/etc/application-config/" - "optional:configserver:${user.home}/config-repo/"

Security Configuration

6. Security Configurations

@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authz -> authz .requestMatchers("/actuator/health").permitAll() .requestMatchers("/actuator/info").permitAll() .anyRequest().authenticated() ) .httpBasic(withDefaults()) .csrf(csrf -> csrf.ignoringRequestMatchers("/actuator/bus-refresh")); return http.build(); } @Bean public UserDetailsService userDetailsService() { UserDetails user = User.withUsername("config-user") .password("{bcrypt}$2a$10$dXJ3SW6G7P.XBLBvanJYc.ZO.8bS5ZL.8L.8.8.8.8.8.8.8.8.8.8.8") .roles("CONFIG") .build(); return new InMemoryUserDetailsManager(user); } }

7. JWT Security with Config Server

@Configuration public class JwtSecurityConfig { @Value("${jwt.secret:defaultSecret}") private String jwtSecret; @Bean public JwtDecoder jwtDecoder() { return NimbusJwtDecoder.withSecretKey( new SecretKeySpec(jwtSecret.getBytes(), "HmacSHA256") ).build(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authz -> authz .requestMatchers("/actuator/**").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); } }

Client Configuration

8. Config Client Setup

Client Dependencies:

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

bootstrap.yml (Client):

spring: application: name: my-client-app profiles: active: dev cloud: config: uri: http://localhost:8888 username: config-user password: config-password fail-fast: true retry: initial-interval: 1000 max-interval: 2000 max-attempts: 6 multiplier: 1.1 request-connect-timeout: 5000 request-read-timeout: 5000 # Label for Git branch/tag label: main # Profile-specific configuration profile: dev,eu-west # Discovery first approach discovery: enabled: true service-id: config-server # Enable refresh endpoint management: endpoints: web: exposure: include: health,info,refresh endpoint: refresh: enabled: true

9. Client Application with Refresh Scope

@SpringBootApplication public class ClientApplication { public static void main(String[] args) { SpringApplication.run(ClientApplication.class, args); } } @RestController @RefreshScope public class ConfigClientController { @Value("${app.message:Hello default}") private String message; @Value("${app.feature.enabled:false}") private boolean featureEnabled; @Autowired private AppConfig appConfig; @GetMapping("/message") public String getMessage() { return message; } @GetMapping("/config") public AppConfig getConfig() { return appConfig; } @PostMapping("/refresh") public void refresh() { // Configuration will be refreshed automatically } } @Component @ConfigurationProperties(prefix = "app") @RefreshScope public class AppConfig { private String name; private int version; private DatabaseConfig database; private List<String> servers = new ArrayList<>(); public static class DatabaseConfig { private String url; private String username; private String password; private int poolSize; // Getters and setters public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getPoolSize() { return poolSize; } public void setPoolSize(int poolSize) { this.poolSize = poolSize; } } // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } public DatabaseConfig getDatabase() { return database; } public void setDatabase(DatabaseConfig database) { this.database = database; } public List<String> getServers() { return servers; } public void setServers(List<String> servers) { this.servers = servers; } }

Configuration Files Structure

10. Git Repository Structure

config-repo/ ├── applications/ │ ├── my-client-app/ │ │ ├── application.yml │ │ ├── application-dev.yml │ │ ├── application-prod.yml │ │ └── application-uat.yml │ └── another-app/ │ ├── application.yml │ └── application-prod.yml ├── shared/ │ ├── application-common.yml │ ├── database-common.yml │ └── security-common.yml └── environments/ ├── dev/ │ └── application.yml ├── prod/ │ └── application.yml └── uat/ └── application.yml

11. Configuration File Examples

application.yml (Shared):

# Common configuration for all applications app: version: 1.0.0 company: "My Company" logging: level: root: INFO com.example: DEBUG org.springframework.cloud: INFO management: endpoints: web: exposure: include: health,info,metrics,refresh endpoint: health: show-details: always

my-client-app.yml:

app: name: "My Client Application" message: "Hello from Config Server" feature: enabled: true name: "New Dashboard" server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: ${DB_USERNAME:appuser} password: ${DB_PASSWORD:apppass} hikari: maximum-pool-size: 20 minimum-idle: 5 jpa: hibernate: ddl-auto: validate show-sql: false custom: cache: ttl: 300s max-size: 1000 retry: max-attempts: 3 backoff: 1000ms

my-client-app-dev.yml:

app: message: "Hello from Development Environment" feature: enabled: true spring: datasource: url: jdbc:h2:mem:testdb jpa: show-sql: true hibernate: ddl-auto: create-drop logging: level: com.example: DEBUG

my-client-app-prod.yml:

app: message: "Hello from Production Environment" feature: enabled: false spring: datasource: url: jdbc:mysql://prod-db:3306/mydb jpa: show-sql: false hibernate: ddl-auto: validate management: endpoints: web: exposure: include: health,info

Encryption and Decryption

12. Encryption Setup

@Configuration public class EncryptionConfig { @Bean public TextEncryptor textEncryptor( @Value("${encrypt.key:defaultKey}") String key) { return Encryptors.text(key, "deadbeef"); } @Bean @ConditionalOnProperty(name = "encrypt.key-store.location") public TextEncryptor keyStoreTextEncryptor( @Value("${encrypt.key-store.location}") Resource location, @Value("${encrypt.key-store.password}") String storePass, @Value("${encrypt.key-store.alias}") String alias, @Value("${encrypt.key-store.secret}") String secret) { return Encryptors.text(storePass, secret); } }

Using Encryption:

# Encrypted values in configuration spring: datasource: password: '{cipher}FKSAJDFGYOS8F7GHAKJFGHLKERHG0943Y598HKLJDFHGLKJDHFGLKJDHFG' app: api-key: '{cipher}AQICAHh...long-encrypted-string...'

13. Encryption Endpoints

@RestController @RequestMapping("/encrypt") public class EncryptionController { @Autowired private TextEncryptor textEncryptor; @PostMapping public String encrypt(@RequestBody String plainText) { return textEncryptor.encrypt(plainText); } @PostMapping("/decrypt") public String decrypt(@RequestBody String encryptedText) { return textEncryptor.decrypt(encryptedText); } }

Service Discovery Integration

14. Config Server with Eureka

# Config Server with Service Discovery spring: cloud: config: server: git: uri: https://github.com/my-org/config-repo discovery: enabled: true eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true # Client configuration with discovery spring: cloud: config: discovery: enabled: true service-id: config-server fail-fast: true

Monitoring and Management

15. Health Check and Monitoring

@Component public class ConfigServerHealthIndicator implements HealthIndicator { @Autowired private EnvironmentRepository environmentRepository; @Override public Health health() { try { // Test configuration retrieval environmentRepository.findOne("config-server", "default", null); return Health.up() .withDetail("repository", "accessible") .build(); } catch (Exception e) { return Health.down(e) .withDetail("repository", "unreachable") .build(); } } }

Custom Actuator Endpoints:

@RestController @RequestMapping("/actuator/config") public class ConfigActuatorEndpoint { @Autowired private EnvironmentRepository environmentRepository; @GetMapping("/applications") public List<String> getApplications() { // Return list of configured applications return Arrays.asList("app1", "app2", "app3"); } @GetMapping("/{application}/{profile}") public Map<String, Object> getConfiguration( @PathVariable String application, @PathVariable String profile) { Environment env = environmentRepository.findOne(application, profile, null); Map<String, Object> config = new HashMap<>(); config.put("name", env.getName()); config.put("profiles", env.getProfiles()); config.put("propertySources", env.getPropertySources()); return config; } }

High Availability Setup

16. Config Server Cluster

# Multiple Config Server instances with load balancing spring: cloud: config: server: git: uri: https://github.com/my-org/config-repo # Client configuration for multiple servers uri: - http://config-server-1:8888 - http://config-server-2:8888 - http://config-server-3:8888 # With service discovery eureka: instance: instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}} client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-2:8761/eureka/

Testing Configuration

17. Test Configurations

@SpringBootTest @ActiveProfiles("test") public class ConfigServerTest { @Autowired private TestRestTemplate restTemplate; @Test public void testConfigServerEndpoints() { ResponseEntity<String> response = restTemplate .withBasicAuth("config-user", "config-password") .getForEntity("/my-client-app/default", String.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); } @Test public void testEncryption() { String plainText = "secret-value"; ResponseEntity<String> encrypted = restTemplate .postForEntity("/encrypt", plainText, String.class); assertThat(encrypted.getStatusCode()).isEqualTo(HttpStatus.OK); } } @SpringBootTest public class ConfigClientTest { @Autowired private AppConfig appConfig; @Test public void testConfigurationProperties() { assertThat(appConfig.getName()).isEqualTo("My Client Application"); assertThat(appConfig.getVersion()).isEqualTo(1); } @TestConfiguration @EnableConfigurationProperties(AppConfig.class) static class TestConfig { } }

Spring Cloud Bus for Configuration Refresh

18. Config Server with Bus

Dependencies:

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>

Configuration:

spring: rabbitmq: host: localhost port: 5672 username: guest password: guest management: endpoints: web: exposure: include: bus-refresh

Refresh all clients:

curl -X POST http://config-server:8888/actuator/bus-refresh

Refresh specific client:

curl -X POST http://config-server:8888/actuator/bus-refresh/my-client-app:8080

Best Practices

19. Configuration Management Tips

  1. Use profiles for environment-specific configurations
  2. Externalize sensitive data using encryption
  3. Version control your configuration files
  4. Use shared configurations for common settings
  5. Implement proper security with authentication
  6. Monitor configuration changes and their impact
  7. Use fail-fast in clients to detect configuration issues early
  8. Implement retry mechanisms for configuration loading
  9. Use service discovery for dynamic config server location
  10. Regularly backup your configuration repository

Spring Cloud Config Server provides a robust solution for centralized configuration management in microservices architectures, enabling dynamic configuration updates, environment-specific settings, and secure credential management across your distributed systems.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper