Kubernetes abbreviated as k8s or Kube is an effective container orchestration tool initially developed by Google, but currently a community project. This tool is used to automate and scale container deployment. In recent days, the popularity of Kubernetes and its ecosystem has grown immensely with its ability to design patterns, workload types, and behavior.
One of the jewels in the crown is the init containers feature. This feature provides many advantages in the initialization of workloads.
What is an init Container?
An init container can be defined as a container with modified operational rules and behavior. They normally contain utilities and setup scripts that are not present in the app image. The most preeminent feature for init containers is that they start and terminate before the application containers.
In a single pod, there might be multiple containers running with one or more init containers as well. Init containers have similar features and fields to app containers. These features include volumes, security settings, and resource limits though resource limits in init containers are handled differently.
The main difference between init containers and regular containers are:
- Init containers always run to completion.
- Each init container must complete successfully before the next one starts.
In addition to the above, init containers do not support livenessProbe, readinessProbe, startupProbe or lifecycle since they must run to completion for the pod to be ready. Normally, when multiple init containers are specified in a pod, the kubelet runs them sequentially. Each container has to succeed before the next runs.
When an init pod fails, the Kubelet will restart it repeatedly until it succeeds. However, if the pod has a restartPolicy of Never, and the init Pod fails to run successfully, then the overall pod will be treated as a failed pod.
Init containers are used in the following areas:
- Fetch encrypted secrets from a vault and write them to a file system
- A blockchain app that needs to register itself amongst its peers
- Database migrations/applying seeds – Dynamic data fetched from a database and cached for the app to run after startup
- An app that must fetch an access token from an identity provider
- Fetching assets from a git repository or cloud storage that requires credentials.
- Generating configuration files using runtime properties, for example, pod IP, assigned external IP, hostname, external ingress IP, etc.
This guide provides an overview of how to create and use init containers in Kubernetes.
Before you Begin
I assume that you already have a Kubernetes cluster set up. If not, you can use the below guides to set up a Kubernetes cluster.
- Install Kubernetes Cluster on Rocky Linux 8 with Kubeadm & CRI-O
- Deploy Kubernetes Cluster on Linux With k0s
- Install Kubernetes Cluster on Ubuntu using K3s
- Run Kubernetes on Debian with Minikube
Once the cluster is configured, you can use the
kubectl utility to manage it. Install it as below:
curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" chmod +x kubectl sudo mv kubectl /usr/local/bin
Export the admin conf to be able to manage the cluster.
# For k0s export KUBECONFIG=/var/lib/k0s/pki/admin.conf
Creating and Using Init Containers in Kubernetes
There are several ways in which Init containers can be used. Some of them can be:
- Waiting for a service to start using a shell command:
for i in 1..100; do sleep 1; if dig myservice; then exit 0; fi; done; exit 1
- Waiting for some time before the app container is started.
- Register this Pod with a remote server from the downward API with a command:
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(
This guide provides a more practical example by creating a simple pod with two Init containers. Normally, init containers are defined in the pod.spec.initContainers array while the app containers are defined in the pod. spec.containers array.
Here, we will have the first init container that waits for service and the other waiting for database.
Create the configuration file.
The file will contain the below lines:
apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: myapp-container image: busybox:1.28 command: ['sh', '-c', 'echo The app is running! && sleep 3600'] initContainers: - name: init-service image: busybox:1.28 command: ['sh', '-c', "until nslookup service.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for service; sleep 2; done"] - name: init-db image: busybox:1.28 command: ['sh', '-c', "until nslookup database.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for database; sleep 2; done"]
Apply the manifest.
$ kubectl apply -f application.yaml pod/myapp-pod created
Check the status of the pod.
$ kubectl get -f application.yaml NAME READY STATUS RESTARTS AGE myapp-pod 0/1 Init:0/2 0 23s
$ kubectl get pod myapp-pod NAME READY STATUS RESTARTS AGE myapp-pod 0/1 Init:0/2 0 65s
From the output, we have two init containers. You can view more details by describing the pod:
$ kubectl describe -f application.yaml Name: myapp-pod Namespace: default Priority: 0 Node: debian/192.168.205.4 Start Time: Sun, 24 Apr 2022 10:29:34 +0000 Labels: app=myapp Annotations: kubernetes.io/psp: 00-k0s-privileged Status: Pending IP: 10.244.1.3 IPs: IP: 10.244.1.3 Init Containers: init-service: Container ID: containerd://7ca9c41dbf829d3be660a68eb5dce4ebbe8902b6ae0562bbbd723453898ef9ce Image: busybox:1.28 Image ID: docker.io/library/[email protected]:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47 Port:
Host Port: Command: sh -c until nslookup service.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for service; sleep 2; done State: Running Started: Sun, 24 Apr 2022 10:29:38 +0000 Ready: False Restart Count: 0 Environment: Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-l29rv (ro) init-db: Container ID: Image: busybox:1.28 Image ID: Port: Host Port: Command: sh -c until nslookup database.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for database; sleep 2; done State: Waiting Reason: PodInitializing Ready: False Restart Count: 0 Environment: .......
Inspect logs for the init containers in the pod(myapp) with the command:
# Inspect the first init container kubectl logs myapp-pod -c init-service # Inspect the second init container kubectl logs myapp-pod -c init-db
The output suggests that the Init containers are waiting for the services
service for them to run.
So we will create the two services as below:
Add the lines below to it.
--- apiVersion: v1 kind: Service metadata: name: service spec: ports: - protocol: TCP port: 80 targetPort: 9376 --- apiVersion: v1 kind: Service metadata: name: database spec: ports: - protocol: TCP port: 80 targetPort: 9377
Create the services with the command:
$ kubectl apply -f service.yaml service/service created service/database created
Now check the status of the pod.
$ kubectl get -f application.yaml myapp-pod 1/1 Running 0 4m52s
The two Init containers have started and terminated successfully, which has allowed the app container( myapp-pod) to start as well. That brings this guide on how to create and use init containers in Kubernetes to the finale. You can now use the gathered knowledge to create your own Init containers in Kubernetes.