DEV Community

Joseph D. Marhee
Joseph D. Marhee

Posted on

Getting Started with Kubernetes InitContainers and Volume pre-population

The InitContainer resource in Kubernetes is an interesting, and very useful resource; in many cases, you'll see it used to do things like prepopulate data in a volume before creating the containers in a Pod deployment, so upon spinning up the containers, the volume data is already initialized.

In my case, I have a simple web frontend with a single static page, and it boots using the standard nginx Docker image to serve a manifest file:

FROM nginx

COPY index.html /usr/share/nginx/html/index.html
COPY smartos.ipxe /usr/share/nginx/html/smartos.ipxe

This image builds and downloads very quickly, which is awesome, but partly because it's stateless; the the smartos.ipxe file, for example, makes reference to release data that, upon spinning up the app, needs to be available or else those references will not work as intended (abstracted as a 404 HTTP response):

#!ipxe
dhcp
set base-url http://sdc-ipxe.east.gourmet.yoga
kernel ${base-url}/smartos/smartos/platform/i86pc/kernel/amd64/unix -B smartos=true,console=ttyb,ttyb-mode="115200,8,n,1,-"
module ${base-url}/smartos/smartos/platform/i86pc/amd64/boot_archive type=rootfs name=ramdisk
boot

These files, however, are not part of the app image because, for example, it updates frequently, so each time we rollout a new version, we'd like the latest release available in the volume, and since we don't need to maintain these files in the image, or else it would be very large and costly to store in our registry, we can mount a Volume to the containers in the Pod to provide them.

So, basically, we need a way to pre-populate a volume we're mounting to /usr/share/nginx/html/smartos.

Using the InitContainer resource, we can specify a command to be run, and like any other container in a Pod, we can allocate a volume to be mounted, so let's start with a Kubernetes manifest like this:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sdc-ipxe-deployment
  labels:
    app: sdc-ipxe
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sdc-ipxe
  template:
    metadata:
      labels:
        app: sdc-ipxe
    spec:
      initContainers:
      - name: config-data
        image: ubuntu:xenial
        command: ["/bin/sh","-c"]
        args: ["apt update; apt install -y wget tar; wget https://us-east.manta.joyent.com/Joyent_Dev/public/SmartOS/platform-latest.tgz; tar xvf platform-latest.tgz -C /data; mkdir /data/smartos; mv /data/platform* /data/smartos/platform"]
        volumeMounts:
        - mountPath: /data
          name: sdc-data
      volumes:
      - name: sdc-data
        hostPath:
          path: /mnt/kube-data/sdc-ipxe/
          type: DirectoryOrCreate

So, at this point, we're provisioning the volume sdc-data, mounting it to an initContainer called config-data at /data, and running:

"apt update; apt install -y wget tar; wget https://us-east.manta.joyent.com/Joyent_Dev/public/SmartOS/platform-latest.tgz; tar xvf platform-latest.tgz -C /data; mkdir /data/smartos; mv /data/platform* /data/smartos/platform"

to download and extract the data to the volume. Now, we we add a containers: key to the manifest, and attach that Volume again, the prepopulated data will be availabnle:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sdc-ipxe-deployment
  labels:
    app: sdc-ipxe
...
      containers:
      - name: sdc-ipxe
        image: coolregistryusa.bix/jmarhee/sdc-ipxe:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/share/nginx/html/smartos
          name: sdc-data
...

with the volume mounted where the app in the container expects, /usr/share/nginx/html/smartos using the same sdc-data volume.

Other examples of this pattern being useful can be if your application relies on configurations with variable requirements; maybe you're issued a token, or an address is fairly dynamic, and this needs to be passed through files on disk, rather than the environment (i.e. a loadbalancer, or webserver, or database client with a configuration file, not easily handled through a Secret or ConfigMap because of how often they change), this provides a somewhat easily programmable interface for pre-populating or completing the templating of data handed to a container.

Further Reading

Using InitContainers to pre-populate Volume data in Kubernetes

Top comments (0)