We can still live with configuration management tools like Puppet, Chef etc. But there are issues with typical configuration management:
- Deployment oriented: Need to kick-off deployments even for small or temporary changes.
- Push-based is usually not dynamic enough: Won't work in cloud environment, as we need to keep track of all the newly spun up instances and push to those as well.
- Pull-based adds latency with temporal polling: Usually works on polling mechanism, which adds time-interval latency.
Configuration Server can solve our problems!
Application Configuration Server
- Dedicated, dynamic and centralized key/value store (maybe distributed).
- Authoritative source.
- Auditing.
- Versioning.
- Cryptography support.
Options available for configuration management backend:
- Spring Cloud Consul
- Spring Cloud Zookeeper
- Spring Cloud Config
Integration with Spring Applications
Config Client:
- Embedded in application
- Spring
Environmentabstraction:
@Inject
Environment environment;
Config Server:
- Standalone (can be embedded)
- Spring
PropertySourceabstraction
classpath:file.properties
Spring Cloud Config: Config Server
- HTTP REST Access
- Output formats
- JSON (default)
- Properties
- YAML
- Backend Stores
- Git (default)
- SVN
- File system
- Configuration scopes
Using Spring Cloud Config Server
pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden-SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
application.properties
server.port=8888
spring.cloud.config.server.git.uri=<uri_to_git_repo>
OR application.yml
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: <uri_to_git_repo>
Application.java
@SpringBootApplication
@EnableConfigServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
TIP: Add Eureka client dependencies, service-url configuration and @EnableDiscoveryClient to make the config server discoverable.
Configuring backend store:
- Create a folder to store configuration.
- (optional) Add a
propertiesorymlfile with nameapplication. - Add
propertiesorymlfile with name{application}-{profile}. git init.git addandgit commit.- (optional) Setup remote git repository and
git push.
Additional Notes:
- Git is the default mechanism/backend.
- Global configs goes into
application.yml/application.propertiesand application specific config go into the named files as above.
NOTE: Don't forget to secure your Config Server!
- Easy to configure Spring Security.
REST Endpoint Parameters
- {application} - maps to
spring.application.nameon client. - {profile} - maps to
spring.profiles.activeon client. - {label} - server side feature to refer to set of config files by name.
GET /{application}/{profile}[/{label}]
/myapp/dev/master/myapp/prod/v2/myapp/default
GET /{application}-{profile}.(yml | properties)
/myapp-dev.yml/myapp-prod.properties/myapp-default.properties
GET /{label}/{application}-{profile}.{yml | properties}
/master/myapp-dev.yml/v2/myapp-prod.properties/master/myapp-default.properties
Spring Cloud Config: Config Client
Spring Config Client
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
Config First
AI-generated
Specify the location of the Config Server directly. With Spring Boot 2.4 and
later, Config Data import is the default approach and a bootstrap file is not
required.
application.properties
spring.application.name=<your_application_name>
spring.config.import=optional:configserver:http://localhost:8888
Remove optional: when startup must fail if the Config Server is unavailable.
Discovery First
AI-generated
Discover the Config Server through a configured discovery client instead of hard-coding its URL.
application.properties
spring.application.name=<your_application_name>
spring.config.import=optional:configserver:
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=configserver
Add and configure the selected discovery-client implementation. Discovery-first adds a registry lookup before configuration can be fetched, so use it only when that extra dependency is worthwhile.
Refreshing configuration properties
#1: Configuration changes
- Edit and save configuration file(s).
- Commit and/or push changes to a VCS.
#2: Notify Application(s) to refresh configuration
/refreshwithspring-boot-actuator.- Explicit refresh.
/bus/refreshwithspring-cloud-bus.- Dynamic push refresh.
- Automatic via Spring Cloud Bus broadcasting.
- From RabbitMQ or Kafka?
- VCS +
/monitorwithspring-cloud-config-monitorandspring-cloud-bus. - Automatic and Smart refresh.
- Via post commit hooks, Spring Cloud Config Monitor and Spring Cloud Config Bus broadcasting.
Refreshing Config: What's covered and what's not
- [x]
@ConfigurationProperties - [x] All logging levels defined by
logging.level.*are updated. - [ ] Any
@Beanor@Valuethat only gets its configuration upon initialization.
@RefreshScope
To refresh a @Bean or a @Value that only gets its configuration upon initialization.
@Configuration
public class SomeConfiguration {
@Bean
@RefreshScope
public FooService fooService(FooProperties properties) {
return new FooService(properties.getConfigValue());
}
}
Additional Features Supported
- Encrypted configuration at rest and/or in-flight.
- An
/encryptendpoint to encrypt configuration. - A
/decryptendpoint to decrypt configuration. - Encrypting and decrypting with symmetric or asymmetric keys.
What does encrypted configuration look like?
application.properties
my.datasource.username=foobar
my.datasource.password={cipher}AASFDSFJSRFAFESECCSE
OR application.yml
my:
datasource:
username: foobar
password: '{cipher}AASFDSFJSRFAFESECCSE'
At what point the configuration is decrypted?
- Upon request at the server (default).
- Locally at the client on response.
Configure the Config Server with: spring.cloud.config.server.encrypt.enabled=false
Encryption setup example
AI-generated
The Config Server can use a symmetric key for a small local setup:
export ENCRYPT_KEY='replace-with-a-secret-from-a-secret-manager'
curl --silent --header 'Content-Type: text/plain' \
--data 'database-password' http://localhost:8888/encrypt
Store the returned value with the {cipher} prefix. Secure the /encrypt and
/decrypt endpoints, never commit the encryption key, and use TLS because server-
side decryption sends plaintext configuration to the client over the connection.
For asymmetric encryption, configure a keystore so encryption can use a public
key while decryption remains limited to the server holding the private key.
Further reading: Spring Cloud Config encryption and decryption.