Photo by Ryan Moulton on Unsplash
A ConfigMap is a set of named bits of data. They allow Kubernetes operators to supply additional runtime configuration to the images they are spinning. They can be the source of environment variables. They can even be the source of files on-disk.
Consider the nginx image on Docker Hub. This is an excellent example of a generic container, ready to be plumbed into a wider deployment. The image itself runs the nginx master process and as many workers as you configure. Out of the box, it ships with a sane configuration that serves static files from /usr/share/nginx/html
.
We can use a ConfigMap to put files in that directory, for nginx to serve up!
---apiVersion: v1
kind: ConfigMap
metadata:
name: static-files
data:
index.html: |
<html><head><title>End of the Internet</title></head>
<body><h1>The End of the Internet</h1>
<p>You have reached the end of the Internet.
Feel free to go outside now.</p>
</body></html>
---
apiVersion: v1
kind: Pod
metadata:
name: website
spec:
volumes:
- name: static-files
configMap:
name: static-files
containers:
- name: nginx
image: nginx
volumeMounts:
- name: static-files
mountPath: /usr/share/nginx/html
Once that’s applied, you can port-forward your way into nginx, without much fuss:
$ kubectl port-forward website 8008:80
Forwarding from 127.0.0.1:8008 -> 80
Forwarding from [::1]:8008 -> 80
Head on over to http://localhost:8008, and you should see the following:
So, ConfigMaps can let you inject arbitrary files into a container.
In UNIX, everything is a file, including the programs we run.
That means we should be able to inject arbitrary files into a container, using a ConfigMap…
---
apiVersion: v1
kind: ConfigMap
metadata:
name: bins
data:
hello: |
#!/bin/sh
echo "Hello, ConfigMap!"
exec sleep 86400
---
apiVersion: v1
kind: Pod
metadata:
name: hiya
spec:
volumes:
- name: bins
configMap:
name: bins
containers:
- name: hiya
image: alpine
volumeMounts:
- name: bins
mountPath: /usr/local/bin
command:
- /usr/local/bin/hello
However, if you run that, the hiya
pod will fail to start. Somewhere in the kubectl describe
output, you should see an error like this:
starting container process caused "exec: \"/usr/local/bin/hello\": permission denied": unknown
By default, ConfigMaps mount in as non-executable. That makes sense, given that they are normally used for things like configuration files, data files, etc. What we’re doing is a bit unusual, but we have some options.
The first option is to change the command on the hiya
container from this:
command:
- /usr/local/bin/hello
to this:
command:
- /bin/sh
- /usr/local/bin/hello
That passes the script to /bin/sh
as an argument, skipping the part where Linux tries to figure out how to run the script via the #!/...
shebang at the top.
Note: this also means that whatever you do put in the shebang is ignored. This script will always run under the Bourne shell, /bin/sh
. The shebang is now a comment.
Another way to fix this, that lets us avoid committing to an executor in the command:
section is to mount the ConfigMap in with a different defaultMode.
volumes:
- name: bins
configMap:
name: bins
defaultMode: 0755
This new defaultMode: 0755
bit is set on the bins
volume, and instructs Kubernetes to change the permissions on all of the files (but not the directories!) to have the executable bit turned on for all parties.
Next Steps
Using ConfigMaps to insert arbitrary executable scripts (safely!) into upstream container images can be super useful in a variety of circumstances, like:
- Not having to chain a bunch of commands together with
&&
- Not having to build a custom image with a few shell scripts added
- Using a single (possibly bespoke) container and augmenting behavior with plugins