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
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
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
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.
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