ConfigMaps and volumeMounts in Kubernetes : the right way

Arfat Bin Kileb
4 min readJan 31, 2021

In almost every case, we want our configuration to be separate than the application code itself. This will enable us to change and tweak the behavior of application without modifying the code itself. There is nothing wrong in modifying code, but is it worth it to go through the long process of compile, build, create image , push image to repository and the deploy again?

In this era of Cloud-native / micro-services (whatever you like to call it), it makes sense to extract the configuration out and provide it after the fact.

If we follow 12 factor apps methodology, https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology

it says

III |  Config |  Configuration that varies between deployments should be stored in the environment.

Well in case of Kubernetes , it is extremely easy using configMaps and volumeMounts.

In this installment lets look at externalizing configuration and using configMaps.

[Note] By all means, it is not in-depth tutorial about any of these concepts.

Scenario: we have simple spring boot app that exposes a single REST endpoint /hello . This end point will return String response Hello from <configured server name>

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/")
public String greet(@Value("${server.name:Test Server}")
String serverName) {
return "Hello from " + serverName;
}
}

Now there are multiple ways we can inject this <configured servier name>

Spring boot makes that absolutely easy. we can use ENV or usual appliacion.properties file.

server.name=Dev Server
...

For example, if you run the app, and access http://localhost:8080, you should get

Response

Perfect. We have externalized our properties(in this case just 1 :D ). now we want to inject, somehow, this properties file into our spring container. we haven’t talked about spring boot to container image, have we?

FROM openjdk:15-jdk-alpine
EXPOSE 8080
WORKDIR application
ADD target/demo-0.0.1-SNAPSHOT.jar ./app.jar
ENTRYPOINT ["java","-jar","app.jar"]

Follow this guide to create optimal container images from spring boot app.

Now lets deploy our awesome app into Kubernetes.

apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: <your.registry/>spring-hello-world
ports:
- containerPort: 8080
env:
- name: server.name
valueFrom:
fieldRef:
fieldPath: metadata.name

notice env , this will set pod name as server.name

while this is useful but we want to mount configuration as file/s.

---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config #1
namespace: default
data:
application.properties: | #2
server.name=My Awesome App
another-file.txt: |
test
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: registry.remit.in/spring-hello-world
ports:
- containerPort: 8080
volumeMounts:
- name: config #3
mountPath: "/application/application.properties"
subPath: "application.properties"
readOnly: true # true/false
volumes:
# You set volumes at the Pod level,
# then mount them into containers inside that Pod
- name: config #3
configMap:
# Provide the name of the ConfigMap you want to mount.
name: app-config #1
# An array of keys from the ConfigMap to create as files
items:
- key: "application.properties" #2
#this should match with the key in ConfigMap
path: "application.properties" # mount path
#notice we are only mounting 1 file out of 2 in ConfigMap

If we accessed app now, it should return something like so:

port forward to pod

Which is exactly what we injected using ConfigMap.

Notes:

  • In this approach we are injecting just one file, So we are using subPath. If we skip that (shown in below code block), all the files in volumes[].configMap.items will be mounted in mountPath
  • We can mount different files in different locations as well. as you can see volumeMounts is an array
  • If your pod contains multiple containers, you can mount volumes in same way. Notice the volumes is mounted in pod level. So by default all containers can share those volumes
  • You must create a ConfigMap before you can use it.
  • A container using a ConfigMap as a subPath volume mount will not receive ConfigMap updates. More recommended approach will be to have a separate config directory and this directory should not contain any files. Because kubernetes will remove any files present in mountPath
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config #1
namespace: default
data:
application.properties: | #2
server.name=My Awesome App
another-file.txt: |
test
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: registry.remit.in/spring-hello-world
ports:
- containerPort: 8080
volumeMounts:
- name: config #3
mountPath: "/config"
readOnly: true # true/false
volumes:
# You set volumes at the Pod level,
# then mount them into containers inside that Pod
- name: config #3
configMap:
# Provide the name of the ConfigMap you want to mount.
name: app-config #1
# An array of keys from the ConfigMap to create as files
items:
- key: "application.properties" #2
#this should match with the key in ConfigMap
path: "application.properties" # mount path
#notice we are only mounting 1 file out of 2 in ConfigMap

And there you have it.

Thanks

--

--