Exploring the Role of Init Containers in Kubernetes Applications

Neeraj GuptaNeeraj Gupta
4 min read

A pod can have multiple containers, this init container is run before the main app containers.

Init containers are run before the main app containers.

Init containers always run to completion before the pod can be ready and it not continuously running alongside the main containers.

Init containers run to completion sequentially & the main container does not start until all the init containers have been completed.

Each init container must be completed successfully before the next one starts.

Use cases of init containers:

  • Some configurations need to be performed before starting the main application container.

  • Check for some X services or database services before starting the application containers.

Understanding with the help of the following examples:

Use-case 1: Deploy the file index.html & then start the nginx pod.

Here, we will use a common volume mount for both the init container & app container.
The application(Nginx) container only starts if the init container is executed successfully.

spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  initContainers:
  - name: first-init-container
    image: busybox
    command: ['sh', '-c', 'wget -O /usr/share/data/index.html https://minex.hashnode.dev']
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/data
  containers:
  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html

Explanation:
1. First, we have created the empty volume called shared-data.
2. Then, use the initContainer block, specify the command to update the index.html file, and mount it to the /usr/share/data directory (This is the container directory.)
3. Then, have the main container block, where we used the same volume that was created earlier (This volume has the updated index.html file), and mount it to the main container, to reflect the changes.

Use-case 2: Check the databases service & then start the main pod.

Here, we have two init containers, first to check the database service & second to check the application service.
Once the successful completion of both the init containers are sequentially completed, then only the main container gets started.

spec:
  initContainers:
  - name: check-db-service
    image: busybox
    command: ['sh', '-c', 'until nslookup db.default.svc.cluster.local; do echo waiting for db service; sleep 2; done;']
  - name: check-myservice
    image: busybox
    command: ['sh', '-c', 'until nslookup myservice.default.svc.cluster.local; do echo waiting for myservice; sleep 2; done;']
  containers:
  - name: main-container
    image: busybox
    command: ['sleep', '3600']

This service file is for the above pod manifest.

---
apiVersion: v1
kind: Service
metadata:
  name: db
spec:
  selector:
    app: demo1
  ports:
  - protocol: TCP
    port: 3306
    targetPort: 3306
---
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  selector:
    app: demo2
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

Explanation:
1. Here, we have created the kubernetes service kind for the database and myservice services and used the Kubernetes DNS name for a service within a Kubernetes cluster to check the status.
2. Then, it starts the main container.

Using the init container as a sidecar container

About sidecar containers:
The init container runs to completion before starting the main container, whereas the side-car container starts first & continuously runs along with the main container as the supporting container.

Use case of sidecar container:
- Service mesh (proxy server)
- Logging purpose
- Metrics collection

  • To make the normal init container as the sidecar container, we have to use “restartPolicy: Always” in the initContainers block.

Use-case 3: Use a sidecar container to append the memory info to the index.html file every 10 sec.

Here, we will use a common volume mount for both the init container & app container.

spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  initContainers:
  - name: meminfo-container
    image: alpine
    restartPolicy: Always
    command: ['sh', '-c', 'sleep 5; while true; do cat /proc/meminfo > /usr/share/data/index.html; sleep 10; done;']
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/data
  containers:
  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html

Explanation:
1. First, we have created the empty volume called shared-data.
2. Then, use the initContainer block, specify the “restartPolicy: Always” to act as a sidecar container and command to update the index.html file with memory info at every 10 sec of interval and mount it to the /usr/share/data directory (This is the container directory.)
3. Then, have the main container block, where we used the same volume that was created earlier (This volume has the updated index.html file), and mount it to the main container, to reflect the changes.

Note: The sidecar container will remain running during the entire life of the pod.

Refer to the GitHub repository for all the manifest files used in this blog.

2
Subscribe to my newsletter

Read articles from Neeraj Gupta directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Neeraj Gupta
Neeraj Gupta