Init Containers: How to Automate Bootstrapping Resources for Kubernetes Applications

Init containers are a powerful tool in Kubernetes that can be used to improve the security and reliability of your applications. But what are they, and why should you use them?

Posted by Jose Roche on August 17, 2023

Imagine the following:

Once upon a time a team of platform engineers created a Cloud stack for an application development team to deploy their WordPress application. The stack consisted of a Kubernetes cluster deployed to AWS using EKS and other necessary supporting infrastructure such as Virtual Private Cloud (VPC), a MySQL database using Amazon Aurora, etc. 

The platform team followed all good practices like describing the infrastructure using Infrastructure as Code (IaC) and separation of concerns by providing the application team the database cluster endpoint and required WordPress user and password to connect to the database to run the WordPress application and a CI/CD pipeline to create and destroy the infrastructure as needed. 

In a static environment, meaning an environment that rarely changes once deployed, the platform team could manually create the WordPress database and give the WordPress user the right privileges to the database and call it a day. When the environment is not static, meaning the infrastructure could be continuously deleted and recreated, when testing or developing features for example, or when multiple copies of the environment need to be created, it becomes a challenge for the platform team to continuously create the database and user rights. 

There are many ways to solve this, but today I will explore solving it by using Kubernetes Init containers.

What are Init containers?

Init containers are a special type of container that runs before the main application container in a pod. They are used to perform tasks that need to be completed before the main container starts, such as:

  • Downloading and installing dependencies
  • Creating directories and files
  • Running scripts
  • Bootstrapping resource the application needs

Why use Init containers?

Some reasons to use Init containers are:

  • To improve the security and management of your applications using separation of concerns
  • To reduce the size of your application container images
  • To improve the reliability of your applications
  • To perform tasks that need to be completed before the main container starts, such as downloading and installing dependencies.
  • To delay the start of the main container until a set of preconditions are met.

Init containers can be used to isolate tasks that could make the main container image less secure. For our example, we will use an Init container to connect to a brand new database and create the needed database and user privileges in a separate container from the main application container. This would prevent a sensitive process from being included in the application container image, which would make it smaller, more secure and have a clear separation of concerns. The Init container image is owned and managed by the platform team, and the application team will just need to include the Init container in their Pod manifest.

Init containers could also be used to delay the start of the main container until a set of preconditions are met. For example, you could use an Init container to wait for a database to be ready before your application container starts.

How do Init containers work?

Init containers are defined in the same way as application containers, but they are placed in the initContainer section of the pod spec. When a pod is created, the Init containers are started in order, and they must all complete successfully before the main application container is started.

Example of using Init containers:

As mentioned, we will use an Init container to bootstrap our database and have it ready for the WordPress application. In order to use our Init containers there are a few Kubernetes resources we need to create:

  • Secrets to hold the Database endpoint, and admin user and password
  • ConfigMap that contains the initialization script our Init container will use
  • Deployment that contains the Volumes, Volume Mounts, main container and Init container specifications

Using Kustomize you can create a Kubernetes Secrets using secretGenerator as such:

secrets.yaml


secretGenerator:
- name: mysql-pass
  namespace: wordpress
  options:
    disableNameSuffixHash: true
  literals:
  - dbpassword=
  - dbhost=
  - dbuser=
  - dbase=

resources:
  - namespace.yaml

configmap.yaml


kind: ConfigMap
apiVersion: v1
metadata:
  name: mysql-init
  namespace: wordpress
data:
  mysqlInit.sh: |
     #!/bin/bash
     mysql --host=$WORDPRESS_DB_HOST --user=$WORDPRESS_DB_USER --password=$WORDPRESS_DB_PASSWORD -e "CREATE DATABASE IF NOT EXISTS wordpress;USE wordpress;GRANT ALL PRIVILEGES ON wordpress TO 'admin'@'%';"

The ConfigMap holds a bash script that will call the MySQL client passing the database host, user and password stored in the Secret and passed to the Init container as environment variables, then it will create the database and provide the admin user privileges to the database.

wordpress.yaml


apiVersion: v1
kind: Service
metadata:
  name: wordpress
  namespace: wordpress
  labels:
    app: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
    tier: frontend
  type: LoadBalancer

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  namespace: wordpress
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: frontend
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: frontend
    spec:
      containers: 
      - image: wordpress:6.2.1-apache
        name: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: dbhost
        - name: WORDPRESS_DB_USER
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: dbuser
        - name: WORDPRESS_DB_NAME
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: dbase
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: dbpassword
        ports:
        - containerPort: 80
          name: wordpress
        volumeMounts:
        - name: wordpress-persistent-storage
          mountPath: /var/www/html
      initContainers:
      - name: mysql-initializer
        image: mysql:5.7
        volumeMounts:
        - name: mysql-init
          mountPath: /opt
        env:
        - name: WORDPRESS_DB_HOST
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: dbhost
        - name: WORDPRESS_DB_USER
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: dbuser
        - name: WORDPRESS_DB_NAME
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: dbase
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: dbpassword
        command: ["sh", "-c", "./opt/mysqlInit.sh"]
      volumes:
      - name: wordpress-persistent-storage
        persistentVolumeClaim:
          claimName: wordpress-storage-claim
      - name: mysql-init
        configMap: 
          name: mysql-init
          defaultMode: 0770

In the wordpress.yaml, we create a pair of Kubernetes resources. A Service to expose the WordPress the application behind an AWS Load Balancer and a deployment containing the the main container, the Init container and volumes required for each. You can see in the initContainers section where I set the environment variables, the volume mounts and call the init script. 

Conclusion

Init containers are a powerful tool that can be used to improve the security and reliability of your Kubernetes applications. By understanding how Init containers work and when to use them, you can make your applications more efficient by automating processes the application needs without adding unnecessary business logic, thus creating a clean separation of concern .

Here are some additional tips for using Init containers:

  • Use Init containers to perform tasks that are specific to your application.
  • Don't use Init containers to perform tasks that could be done in the main container application logic.
  • When deciding when to use Init containers, think about separation of concerns.

I hope this blog post has helped you to learn more about Init containers. If you have any questions, please leave a comment or a yes below.