ALL THINGS KUBERNETES

Introduction to Init Containers in Kubernetes

You may be familiar with a concept of Init scripts — programs that configure runtime, environment, dependencies, and other prerequisites for the applications to run. Kubernetes implements similar functionality with Init containers that run before application containers are started. In order for the main app to start, all commands and requirements specified in the Init container should be successfully met. Otherwise, a pod will be restarted, terminated or stay in the pending state until the Init container completes.

In this article, we discuss the peculiarities of Init containers compared to regular containers, look into their basic use cases, and walk you through a simple tutorial to create your own Init containers in Kubernetes. Let’s start!

How Are Init Containers Different?

In a nutshell, Init containers are much like regular Kubernetes containers. They have all of the fields of an app container and are defined within the same pod spec. However, there are several design differences you should know before you build your Init containers. These are the most important:

  • Init containers always run to completion. In contrast, regular application containers can be either always running (e.g., Nginx or Apache HTTP server) or running to completion.
  • If multiple Init containers are specified, they run sequentially. That is, each of the Init containers must successfully complete before the next one is started.
  • Init containers cannot have readiness probes that are designed to watch whether application containers are prepared to accept the traffic. The Init container, by definition, exists because the pod isn’t yet ready and needs an extra container to help. This means that Init containers should never have a readiness probe because having one would mean the pod could be declared ready (based on the probe’s response from the Init container) before the main container was running, which would be a false positive.
  • Resource requests and limits for Init containers are managed differently than for regular containers. In particular, the following resource usage rules apply:
    • The effective init request/limit is the highest resource request and limit among all Init containers scheduled for running.
    • Because scheduling is done using an effective requests/limits (see the bullet point above), Init containers can reserve resources not used during the pod lifecycle.

How Can You Use Init Containers?

There are a number of use cases for Init containers that can be leveraged in your applications:

  • Init containers can run utilities and/or command that you don’t want to include in the app container image for security reasons.
  • Init containers can contain the Init or setup code that is not present in the app image. Instead of making an image FROM another image to include this code or utilities, you can simply use it in the Init container. In this way, you can make your app containers more lightweight and modular. For example, you can use tools like awk  or python  in your Init containers to set up the environment for your app containers.
  • Similarly to app containers, Init containers use Linux namespaces. Because these namespaces are different from the namespaces of app containers, Init containers end up with their unique filesystem views. You can leverage these filesystem views to give Init containers access to secrets that app containers cannot access.
  • Since Init container run sequentially, you can easily use them to block and delay the startup of app containers until some prerequisites are met.

We illustrate some of these use cases for Init containers in the tutorial, so let’s start!

Tutorial

To complete examples in this tutorial, you’ll need the following prerequisites:

  • A running Kubernetes cluster. See Supergiant GitHub wiki for more information about deploying a Kubernetes cluster with Supergiant. As an alternative, you can install a single-node Kubernetes cluster on a local system using Minikube.
  • A kubectl command line tool installed and configured to communicate with the cluster. See how to install kubectl here.

Example #1: Using Init Container to Clone a GitHub Repository into a Volume

You can use Init containers as a convenient alternative to the gitRepo volume type (that is in the deprecation stage) for cloning a GitHub repository into a volume to make the repo accessible to app containers. Unlike gitRepo  volumes that use kubelet , in this approach the git  command is run directly within the Init container. Also, cloning GitHub repos from the Init container avoids the kubelet  shelling out to git  on the host, which may raise security issues on the untrusted repos. To illustrate how this works, let’s first define a pod spec with our init container:

Note: replace the directory name in the spec.initContainers[0].args  , spec.initContainers[0].volumeMounts , and spec.volumes.hostPath.path  with your own user directory without root permissions. The path to this directory should be /home/<user-name>/repo  on Linux and /Users/<user-name>/repo  on Mac. Also, note that the directory with this name must not exist or must be empty.

The spec above will create a pod with two containers. The first one is the application container running BusyBox container image. The sh  command of this container echoes a random greeting message to stdout . The second one is the Init container that clones a GitHub repository and saves it to the hostPath  Volume. Let’s describe the most important parameters of this spec in more detail:

spec.initContainers[0].image  — the container image pulled into the Init container. We use alpine/git  container image from the Docker Hub repository, but any image containing git  command will do.

spec.initContainers[0].args  — arguments for the container’s command. The default command (EntryPoint) of the alpine/git  container is git . Remember that when we do not specify a command for a container and just define the args  field, the arguments we specify will be used with the default EntryPoint replacing Cmd (i.e., default arguments) of the image. Since we don’t specify a command, we expect that the args  will run with the default git  command used in the image. The arguments that we defined tell git  to clone the Supergiant’s GitHub repository and save it to our /repo  directory. As simple as that!

spec.initContainers[0].volumeMounts  — we are mounting hostPath  volume specified in spec.volumes  at the /repo  mountPath  of our Init container. This will be a directory to save our cloned GitHub repo to.

Let’s check how this setup works. Save the spec in the init-demo.yaml , and create the pod running the following command:

Let’s see the pod’s status immediately upon the creation:

As you see, the Status  field indicates that the Init container has not yet completed, and, hence, our main application pod is not Ready yet.

You can see more details by running kubectl describe pod init-demo :

Scroll down to pod events to see the pod’s history. As you might have noticed, first, the kubelet  created and started your Init container. Only after the container was completed did it create and start the main application container.

To verify what the Init container was doing, let’s check its logs:

As you see, the container was cloning the Supergiant repo into our /Users/<user-name>/repo  directory. Let’s verify that the cloning was successful by checking the host directory created on your node:

Great! The init container has successfully cloned Supergiant repo to the /repo  folder on your host.

Example #2: Creating Init Containers that Wait for the Service to be Created

In the second example, we’ll look into a scenario when the Init container waits for a service to be created. Until the service is created, the main application container stays in the pending state. Once the service is created, the Init container completes and kubelet  starts the application container. This setup might be useful when your pod is dependent on certain service/s to run. Let’s define a spec:

This spec creates a pod with two containers. As in the first example, the first one runs BusyBox shell command that echoes a custom message from the container. The second container is the Init container that uses the nslookup  command from the BusyBox container to periodically look for your service. This command will be waiting for the service until it is defined and created and becomes accessible by DNS. Afterward, the Init container exits/completes.

Let’s save this spec in the init-pod.yaml  and create the Pod as usual:

Now, let’s check the status of this pod:

As you see, the pod is not ready yet because the Init container was not completed. In fact, the main application container will never run unless we create a service pinged by the nslookup  command.

Let’s see a detailed description of the pod status:

You see that the status of the pod is pending. Our container is still running because the service was not created and the app container is not ready.

Now, let’s create a simple service that our Init container is waiting for:

Save this spec in the init-service.yaml  and create it with the following command:

Now, if you check our pod, you’ll see that its status has changed to running. That is because our nslookup  command finally found the init-service  .

If you look into the logs of init-service  container, you’ll find what our init container was doing:

As you see, the nslookup  command could not resolve the service until it was finally created and assigned the following DNS sub-domain: init-service.default.svc.cluster.local . Once the service was found, our Init container successfully completed and terminated.

Cleaning Up

Our tutorial is over, so let’s clean up after ourselves.

Delete the pods:

Delete /repo  directory:

Delete our Init service:

Finally, delete all files with the specs we created if you don’t need them anymore.

Conclusion

That’s it! You have learned how Init containers can be easily used to set up or download necessary prerequisites for your Kubernetes application. You can use Init containers to download repositories or files required by your application containers or block the pod initialization process until certain requirements are met. Instead of integrating your Init scripts into applications, you can opt for Init containers to ensure separation of concerns (SoC) and modularity of your deployment code. That’s the most powerful advantage of Init containers! Stay tuned for new content about managing Kubernetes containers to find out more.

If you enjoyed this article you might be interested in watching the following webinar!

Free Kubernetes Training - How to Deploy a Cluster from Scratch link to webinar

Subscribe to our newsletter