DevOps Day 61: Pre-requisite Tasks with Kubernetes Init Containers¶
Today's task was a deep dive into a powerful and elegant Kubernetes pattern: the Init Container. My objective was to create a Pod where a special "setup" container would run and prepare a file before the main application container started. This main container would then use the file created by the init container.
This was a fantastic lesson in managing application dependencies and pre-requisite tasks in a clean, decoupled way. I learned how to define an initContainers block in my Deployment manifest and how it interacts with shared volumes to prepare an environment for the main application. This document is my very detailed, first-person guide to that entire process, written from the perspective of a Kubernetes beginner.
Table of Contents¶
- DevOps Day 61: Pre-requisite Tasks with Kubernetes Init Containers
- Table of Contents
- The Task
- My Step-by-Step Solution
- Phase 1: Writing the Deployment Manifest
- Phase 2: Applying the Manifest and Verifying
- Why Did I Do This? (The "What \& Why" for a K8s Beginner)
- Deep Dive: A Line-by-Line Explanation of My Deployment YAML File
- Common Pitfalls for Beginners
- Exploring the Essential
kubectlCommands
The Task¶
My objective was to create a Kubernetes Deployment that used an init container. The specific requirements were:
1. Create a Deployment named ic-deploy-devops with 1 replica.
2. The Pods created must have the label app: ic-devops.
3. The Pod must have an Init Container named ic-msg-devops using the debian:latest image. This container must run a command to write the string "Init Done - Welcome to xFusionCorp Industries" to a file at /ic/beta.
4. The Pod must have a main container named ic-main-devops using the debian:latest image. This container must run a continuous loop that reads and prints the content of the /ic/beta file.
5. Both containers must share an emptyDir volume named ic-volume-devops, mounted at /ic.
My Step-by-Step Solution¶
The professional way to create this complex Pod is with a single YAML manifest file for the Deployment.
Phase 1: Writing the Deployment Manifest¶
- I connected to the jump host.
- I created a new file named
ic-deployment.yamlusingvi. - Inside the editor, I wrote the following YAML code, which defines the shared volume, the init container that writes to it, and the main container that reads from it.
apiVersion: apps/v1 kind: Deployment metadata: name: ic-deploy-devops labels: app: ic-devops spec: replicas: 1 selector: matchLabels: app: ic-devops template: metadata: labels: app: ic-devops spec: initContainers: - name: ic-msg-devops image: debian:latest command: ["/bin/bash", "-c", "echo Init Done - Welcome to xFusionCorp Industries > /ic/beta"] volumeMounts: - name: ic-volume-devops mountPath: /ic containers: - name: ic-main-devops image: debian:latest command: ["/bin/bash", "-c", "while true; do cat /ic/beta; sleep 5; done"] volumeMounts: - name: ic-volume-devops mountPath: /ic volumes: - name: ic-volume-devops emptyDir: {} - I saved and quit the file.
Phase 2: Applying the Manifest and Verifying¶
- I used
kubectlto create the Deployment from my manifest.kubectl apply -f ic-deployment.yaml - Verification: The final part of the task was to prove that the init container ran first and successfully prepared the file for the main container.
- First, I watched the Pod startup process.
I saw the status change from
kubectl get pods -wPending->Init:0/1->PodInitializing->Running. TheInit:0/1status was the key, showing my init container was running. - For the definitive proof, I checked the logs of the main container. I first had to get the full name of the pod created by the deployment.
The output showed the exact string
POD_NAME=$(kubectl get pods -l app=ic-devops -o jsonpath='{.items[0].metadata.name}') kubectl logs $POD_NAME -c ic-main-devopsInit Done - Welcome to xFusionCorp Industries. This was the final proof that the init container had run to completion, and then the main container had started and was successfully reading the file it created.
- First, I watched the Pod startup process.
Why Did I Do This? (The "What & Why" for a K8s Beginner)¶
- The Problem of Prerequisites: Many applications can't start until some pre-flight checks or setup tasks are completed. For example, a web app might need to wait for a database to be available, or it might need a specific configuration file to be in place before it launches.
- Init Containers (The Solution): An Init Container is a special type of container in a Pod that is designed to solve this problem. It's a container that runs and must complete successfully before the main application containers are started.
- The Lifecycle:
1. The Pod is scheduled to a Node.
2. The Init Containers are started in the order they are defined in the manifest.
3. Each Init Container runs its command and must exit with a success code (0). If it fails, Kubernetes will restart it according to the Pod's restartPolicy. The main containers will not start until all init containers have succeeded.
4. Once all Init Containers are complete, the main application containers are started.
- Separation of Concerns: This pattern is a perfect example of this important design principle.
- The ic-msg-devops container had one job: prepare the environment (create the beta file).
- The ic-main-devops container had one job: run the main application logic (read the file).
This keeps the main application container clean and focused, separating setup logic from runtime logic.
Deep Dive: A Line-by-Line Explanation of My Deployment YAML File¶
The key to this task was understanding the initContainers block and how it relates to the containers and volumes blocks.
[Image of a Kubernetes Pod with an Init Container]
apiVersion: apps/v1
kind: Deployment
metadata:
name: ic-deploy-devops
labels:
app: ic-devops
spec:
replicas: 1
selector:
matchLabels:
app: ic-devops
template:
metadata:
labels:
app: ic-devops
spec:
# This 'initContainers' block is the new and most important part.
# It's a list of containers that will run to completion in order before the main containers start.
initContainers:
- name: ic-msg-devops
image: debian:latest
# This command runs once, creates the file, and then the container exits successfully.
command: ["/bin/bash", "-c", "echo Init Done - Welcome to xFusionCorp Industries > /ic/beta"]
# It mounts the shared volume so it can write the file.
volumeMounts:
- name: ic-volume-devops
mountPath: /ic
# This is the standard 'containers' block for the main application.
containers:
- name: ic-main-devops
image: debian:latest
# This command runs a continuous loop to read the file created by the init container.
command: ["/bin/bash", "-c", "while true; do cat /ic/beta; sleep 5; done"]
# It mounts the SAME shared volume so it can read the file.
volumeMounts:
- name: ic-volume-devops
mountPath: /ic
# This 'volumes' block at the Pod level defines the volume that is shared.
volumes:
- name: ic-volume-devops
# 'emptyDir' is a temporary directory that is created with the Pod and
# destroyed when the Pod is deleted. It's perfect for sharing files
# between containers in the same Pod.
emptyDir: {}
Common Pitfalls for Beginners¶
- Init Container Fails to Complete: If the command in my init container had an error and exited with a non-zero code, it would get stuck in a restart loop (Init:CrashLoopBackOff), and my main container would never start.
- Confusing initContainers and containers: The two blocks look very similar, but they have different purposes and lifecycles. It's important to put the setup logic in the initContainers section.
- Volume Name Mismatch: The name in a container's volumeMounts section must exactly match the name of a volume defined in the Pod's volumes section. A typo will cause the Pod to fail to start.
- Forgetting to Verify with logs: The only way to know for sure that the main container is seeing the result of the init container's work is to check its logs. kubectl get pods only shows that it's running, not that it's functioning correctly.
Exploring the Essential kubectl Commands¶
- kubectl apply -f [filename.yaml]: The standard way to create or update resources from a manifest file.
- kubectl get pods -w: Gets a list of Pods and watches for changes. This is incredibly useful for observing the startup sequence of a Pod, including the Init state.
- kubectl describe pod [pod-name]: Describes a Pod in great detail. I could have used this to see the status of the init container and the main container separately.
- kubectl logs [pod-name] -c [container-name]: Shows the logs from a specific container within a multi-container Pod. This was the essential command for my final verification step.