sebastiandaschner blog
Inject Kubernetes ConfigMap values with Java EE & WildFly
wednesday, april 26, 2017The Kubernetes ConfigMap concept is used to configure applications from the orchestration environment. The configured values can be made accessible within the Java EE container. This blog posts shows the integration of Kubernetes ConfigMaps, WildFly and Java EE vendor-agnostic configuration.
Kubernetes can inject configured values to the running POD, e.g. as properties files. By defining custom modules in WildFly, these files — that are not shipped with the deployment artifact — can be made accessible from the classpath.
First define a Kubernetes ConfigMap via YAML file:
kind: ConfigMap
apiVersion: v1
metadata:
name: hello-config
data:
application.properties: |
hello.greeting=Hi
hello.name=Kubernetes
Alternatively, you can also define ConfigMaps directly from properties files: kubectl create configmap hello-config --from-file=application.properties
The properties entries of this ConfigMap are injected into the running POD in different ways, for instance via mounted volume, which results in a properties file being created, containing all the configured values.
Consider following container definition in a deployment resource using WildFly as application server and referring to the ConfigMap.
kind: Deployment
apiVersion: apps/v1beta1
metadata:
name: cloud-native-jee
spec:
replicas: 1
template:
metadata:
labels:
app: cloud-native-jee
spec:
containers:
- name: cloud-native-jee
image: cloud-native-jee:123
volumeMounts:
- name: config-volume
mountPath: /opt/wildfly/modules/com/sebastian-daschner/configuration/main/properties
volumes:
- name: config-volume
configMap:
name: hello-config
restartPolicy: Always
The base image containing the WildFly application server has to configure an additional module.
This is done by placing a module.xml
file into a module directory <wildfly_home>/modules/com/sebastian-daschner/configuration/main
:
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.3" name="com.sebastian-daschner.configuration">
<resources>
<resource-root path="properties"/>
</resources>
</module>
Also declare the module in the standalone.xml
configuration:
<subsystem xmlns="urn:jboss:domain:ee:4.0">
...
<global-modules>
<module name="com.sebastian-daschner.configuration" slot="main" />
</global-modules>
</subsystem>
This will access the new module and make all files residing under …/configuration/main/properties
accessible from the classpath.
Now the running container can access the application.properties
file.
To make the development process more convenient, we define a custom CDI producer for the properties:
@ApplicationScoped
public class Configurator {
private final Properties properties = new Properties();
@PostConstruct
private void initProperties() {
try (final InputStream inputStream = Configurator.class.getResourceAsStream("/application.properties")) {
properties.load(inputStream);
} catch (IOException e) {
throw new RuntimeException("Could not init configuration", e);
}
}
@Produces
@Config("")
public String exposeConfig(InjectionPoint injectionPoint) {
final Config config = injectionPoint.getAnnotated().getAnnotation(Config.class);
if (config != null)
return properties.getProperty(config.value());
return null;
}
}
The @Config
annotation is defined in our application to select the specific keys:
@Qualifier
@Retention(RUNTIME)
@Documented
public @interface Config {
@Nonbinding
String value();
}
Now we can @Inject
values from anywhere in our application:
@Path("hello")
public class HelloResource {
@Inject
@Config("hello.greeting")
String greeting;
@Inject
@Config("hello.name")
String name;
@GET
public String hello() {
return greeting + ", " + name + "!";
}
}
Happy Kubernetes configuring!
Found the post useful? Subscribe to my newsletter for more free content, tips and tricks on IT & Java: