Introduction
This tutorial guides you through running commands in Kubernetes pods on startup. You will learn how to configure startup commands using the command and args fields, execute multiple commands, and handle startup failures. We will use Minikube to create a local Kubernetes environment where you can practice these concepts hands-on.
By the end of this lab, you will understand how to customize your Kubernetes pods' behavior during startup and ensure smooth application deployment. This knowledge is essential for configuring containerized applications properly in a Kubernetes environment.
Setting Up Your Kubernetes Environment
In this step, we'll prepare our environment by starting Minikube and understanding the basic Kubernetes concepts needed for this lab.
What is Kubernetes?
Kubernetes is an open-source platform designed to automate deploying, scaling, and operating application containers. At the core of Kubernetes are Pods - the smallest deployable units that can be created and managed.
Starting Minikube
Minikube is a tool that lets you run Kubernetes locally. Let's start it:
minikube start --driver=docker
This command will create a local Kubernetes cluster using Docker as the driver. The startup process may take a few minutes as it downloads necessary components.
You should see output similar to:
😄 minikube v1.30.1 on Ubuntu 22.04
✨ Using the docker driver based on user configuration
📌 Using Docker driver with root privileges
👍 Starting control plane node minikube in cluster minikube
🚜 Pulling base image ...
🔥 Creating docker container (CPUs=2, Memory=2200MB) ...
🐳 Preparing Kubernetes v1.27.4 on Docker 24.0.4 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔎 Verifying Kubernetes components...
🌟 Enabled addons: default-storageclass, storage-provisioner
💡 kubectl not found. If you need it, try: 'minikube kubectl -- get pods -A'
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
Verifying the Installation
Let's verify that everything is working correctly by checking the status of our Minikube cluster:
minikube status
You should see output indicating that Minikube is running:
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
Now let's check that kubectl (the Kubernetes command-line tool) is properly configured:
kubectl get nodes
You should see one node (your Minikube instance) listed as Ready:
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 2m15s v1.27.4
Understanding Kubernetes Pods
A Pod is the basic execution unit of a Kubernetes application. Each Pod represents a running process on your cluster and encapsulates one or more containers. Pods are ephemeral by design, meaning they can be created, destroyed, and recreated as needed.
In Kubernetes, you typically define Pods in YAML files. For this lab, we've already prepared some example YAML files in your environment.
Let's go to the directory where these files are located:
cd ~/project/kubernetes-examples
ls -la
You should see the following files:
total 20
drwxr-xr-x 2 labex labex 4096 Sep 21 10:00 .
drwxr-xr-x 3 labex labex 4096 Sep 21 10:00 ..
-rw-r--r-- 1 labex labex 193 Sep 21 10:00 basic-pod.yaml
-rw-r--r-- 1 labex labex 254 Sep 21 10:00 liveness-probe-pod.yaml
-rw-r--r-- 1 labex labex 312 Sep 21 10:00 multi-command-pod.yaml
-rw-r--r-- 1 labex labex 263 Sep 21 10:00 startup-command-pod.yaml
Now that our environment is set up, we're ready to start working with Kubernetes Pods and learning how to run commands on startup.
Creating Your First Pod with Basic Startup Commands
In this step, we'll create our first Kubernetes Pod and understand how it executes commands on startup. We'll examine the command field which defines what the container should run when it starts.
Exploring the Basic Pod Configuration
Let's first examine the configuration of a basic Pod. Open the basic-pod.yaml file with the nano editor:
nano basic-pod.yaml
You'll see a YAML file with the following content:
apiVersion: v1
kind: Pod
metadata:
name: basic-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash", "-c", "echo 'Pod is running' && sleep 3600"]
This configuration defines:
- A Pod named
basic-pod - A single container named
ubuntuusing theubuntu:20.04image - A startup command that echoes a message and then sleeps for 3600 seconds (1 hour)
The command field specifies the executable that will run when the container starts. In this case, we're running the /bin/bash shell with the -c flag, which allows us to pass a string of commands to execute.
Press Ctrl+X to exit the nano editor.
Creating the Pod
Let's create this Pod in our Kubernetes cluster:
kubectl apply -f basic-pod.yaml
You should see output like:
pod/basic-pod created
Checking Pod Status
Now let's check if our Pod is running properly:
kubectl get pods
You should see output similar to:
NAME READY STATUS RESTARTS AGE
basic-pod 1/1 Running 0 30s
This indicates that our Pod is running successfully. The 1/1 under READY means that one container is running out of one expected container.
Viewing Pod Logs
To see the output of our startup command, we can check the logs of the Pod:
kubectl logs basic-pod
You should see:
Pod is running
This confirms that our startup command successfully executed.
Exploring the Running Container
Let's examine what's happening inside our container by executing an interactive shell:
kubectl exec -it basic-pod -- /bin/bash
Now you're inside the container's shell. Let's verify that our process is running:
ps aux
You should see the sleep command running, which is part of our startup command:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4112 3372 ? Ss 10:05 0:00 /bin/bash -c echo 'Pod is running' && sleep 3600
root 7 0.0 0.0 4112 3536 pts/0 Ss 10:06 0:00 /bin/bash
root 14 0.0 0.0 5900 2928 pts/0 R+ 10:06 0:00 ps aux
root 15 0.0 0.0 2512 580 ? S 10:06 0:00 sleep 3600
The process with PID 1 is our startup command, and the sleep 3600 command is running as a separate process.
Exit the container shell by typing:
exit
Understanding the Pod Lifecycle
When you create a Pod, it goes through several phases:
- Pending: The Pod has been accepted by Kubernetes but is not yet running
- Running: The Pod is running with all containers
- Succeeded: All containers in the Pod have terminated successfully
- Failed: All containers have terminated, and at least one container has failed
- Unknown: The state of the Pod cannot be determined
Our Pod is in the Running state because our main process (sleep 3600) is still running. When this process completes or is terminated, the Pod will transition to either the Succeeded or Failed state, depending on the exit code.
Now you understand how to create a basic Pod and run commands on startup using the command field.
Using Command and Args for Startup Configuration
In this step, we'll learn how to use the command and args fields together to configure more complex startup behaviors in Kubernetes Pods.
Understanding Command and Args
Kubernetes provides two main ways to specify the command that runs when a container starts:
command: Specifies the executable to run (similar to Docker's ENTRYPOINT)args: Specifies the arguments to pass to the command (similar to Docker's CMD)
Using these fields separately or together gives you flexibility in how your containers start up.
Exploring the Startup Command Pod
Let's examine the startup-command-pod.yaml file:
nano startup-command-pod.yaml
You'll see the following configuration:
apiVersion: v1
kind: Pod
metadata:
name: startup-command-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash"]
args:
["-c", "echo 'Custom startup message' > /tmp/startup.txt && sleep 3600"]
In this configuration:
- The
commandfield specifies that we want to run/bin/bash - The
argsfield provides the arguments to pass to bash:-cand the string containing our commands
This separation of command and arguments makes the configuration more readable and maintainable.
Press Ctrl+X to exit the nano editor.
Creating the Startup Command Pod
Let's create this Pod:
kubectl apply -f startup-command-pod.yaml
You should see:
pod/startup-command-pod created
Checking Pod Status
Verify that the Pod is running:
kubectl get pods startup-command-pod
You should see:
NAME READY STATUS RESTARTS AGE
startup-command-pod 1/1 Running 0 30s
Verifying the Startup Command
Now let's verify that our startup command executed correctly by checking if it created the expected file:
kubectl exec startup-command-pod -- cat /tmp/startup.txt
You should see:
Custom startup message
This confirms that our command executed successfully and created the file with the specified content.
Understanding the Difference between Command and Args
The relationship between command and args is important to understand:
- If you specify only
command, it overrides the default ENTRYPOINT of the container image - If you specify only
args, it overrides the default CMD of the container image - If you specify both
commandandargs, they override both the ENTRYPOINT and CMD
Let's see what happens if we modify our Pod configuration. Create a new file called modified-command-pod.yaml:
nano modified-command-pod.yaml
Add the following content:
apiVersion: v1
kind: Pod
metadata:
name: modified-command-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/echo"]
args: ["This message is printed by echo"]
Save and exit with Ctrl+X, followed by Y and Enter.
Now let's create this Pod:
kubectl apply -f modified-command-pod.yaml
You should see:
pod/modified-command-pod created
Check the logs to see what happened:
kubectl logs modified-command-pod
You should see:
This message is printed by echo
Notice how the Pod executed the /bin/echo command with our specified argument, then terminated since echo exits after printing its output.
Check the Pod status:
kubectl get pods modified-command-pod
You should see something like:
NAME READY STATUS RESTARTS AGE
modified-command-pod 0/1 Completed 0 45s
The Completed status indicates that the Pod ran to completion and exited successfully.
Now you understand how to use command and args together to configure startup behavior in Kubernetes Pods.
Executing Multiple Commands and Handling Startup Failures
In this step, we'll learn how to execute multiple commands on Pod startup and how to handle startup failures using health checks.
Executing Multiple Commands
Often, you need to run multiple commands when a container starts. There are several ways to do this in Kubernetes:
- Chain commands using shell operators (
&&,;, etc.) - Use a script file
- Use an init container
Let's explore the first method by examining the multi-command-pod.yaml file:
nano multi-command-pod.yaml
You'll see:
apiVersion: v1
kind: Pod
metadata:
name: multi-command-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args:
[
"echo 'First command' > /tmp/first.txt && echo 'Second command' > /tmp/second.txt && sleep 3600"
]
This configuration chains multiple commands together using the && operator, which executes each command only if the previous one succeeds.
Press Ctrl+X to exit the editor.
Let's create this Pod:
kubectl apply -f multi-command-pod.yaml
You should see:
pod/multi-command-pod created
Now let's verify that both commands executed by checking the created files:
kubectl exec multi-command-pod -- cat /tmp/first.txt
You should see:
First command
And for the second file:
kubectl exec multi-command-pod -- cat /tmp/second.txt
You should see:
Second command
Handling Startup Failures with Health Checks
Kubernetes provides mechanisms to detect and handle startup failures:
- Liveness Probes: Check if the container is running; if not, Kubernetes will restart it
- Readiness Probes: Check if the container is ready to receive traffic
- Startup Probes: Check if the application has started; useful for slow-starting containers
Let's examine a Pod that uses a liveness probe:
nano liveness-probe-pod.yaml
You'll see:
apiVersion: v1
kind: Pod
metadata:
name: liveness-probe-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args: ["touch /tmp/healthy && sleep 3600"]
livenessProbe:
exec:
command: ["cat", "/tmp/healthy"]
initialDelaySeconds: 5
periodSeconds: 5
In this configuration:
- The container creates a file
/tmp/healthyon startup - The liveness probe checks for this file every 5 seconds
- If the file is missing, Kubernetes considers the container unhealthy and restarts it
Press Ctrl+X to exit the editor.
Let's create this Pod:
kubectl apply -f liveness-probe-pod.yaml
You should see:
pod/liveness-probe-pod created
Check that the Pod is running:
kubectl get pods liveness-probe-pod
You should see:
NAME READY STATUS RESTARTS AGE
liveness-probe-pod 1/1 Running 0 30s
Now let's see what happens if we remove the health check file:
kubectl exec liveness-probe-pod -- rm /tmp/healthy
Wait about 10 seconds, then check the Pod status again:
kubectl get pods liveness-probe-pod
You should see that the container has been restarted:
NAME READY STATUS RESTARTS AGE
liveness-probe-pod 1/1 Running 1 60s
The RESTARTS count has increased to 1, indicating that Kubernetes detected the unhealthy state and restarted the container.
Let's verify that the health check file exists again (it should have been recreated by the startup command when the container restarted):
kubectl exec liveness-probe-pod -- ls -la /tmp/healthy
You should see that the file exists again:
-rw-r--r-- 1 root root 0 Sep 21 10:30 /tmp/healthy
This demonstrates how Kubernetes can automatically recover from startup failures and maintain the desired state of your applications.
Creating a Custom Startup Script
For more complex initialization, you might want to use a custom startup script. Let's create a Pod that uses a shell script for startup:
nano script-pod.yaml
Add the following content:
apiVersion: v1
kind: Pod
metadata:
name: script-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args:
- |
cat > /tmp/startup.sh << 'EOF'
#!/bin/bash
echo "Script started at $(date)" > /tmp/script-log.txt
echo "Creating configuration files..." >> /tmp/script-log.txt
mkdir -p /tmp/config
echo "app_name=MyApp" > /tmp/config/app.conf
echo "version=1.0" >> /tmp/config/app.conf
echo "Script completed successfully" >> /tmp/script-log.txt
EOF
chmod +x /tmp/startup.sh
/tmp/startup.sh
sleep 3600
This configuration:
- Creates a startup script in the container
- Makes it executable
- Runs the script
- Keeps the container running with sleep
Save and exit with Ctrl+X, followed by Y and Enter.
Let's create this Pod:
kubectl apply -f script-pod.yaml
You should see:
pod/script-pod created
Wait a moment for the Pod to start, then check its status:
kubectl get pods script-pod
You should see:
NAME READY STATUS RESTARTS AGE
script-pod 1/1 Running 0 30s
Now let's check the output of our script:
kubectl exec script-pod -- cat /tmp/script-log.txt
You should see something like:
Script started at Tue Sep 21 10:35:42 UTC 2023
Creating configuration files...
Script completed successfully
And let's verify that the configuration file was created:
kubectl exec script-pod -- cat /tmp/config/app.conf
You should see:
app_name=MyApp
version=1.0
This demonstrates how to use complex startup scripts to initialize your containers in Kubernetes.
Best Practices and Real-World Applications
In this final step, we'll explore best practices for running commands in Kubernetes Pods on startup and create a real-world application example that implements these practices.
Best Practices for Startup Commands
When configuring startup commands for Kubernetes Pods, consider these best practices:
- Keep startup commands idempotent: Commands should be safe to run multiple times without causing issues
- Use health checks: Implement liveness and readiness probes to verify successful startup
- Handle failures gracefully: Include error handling in your startup scripts
- Separate concerns: Use init containers for initialization tasks separate from your main application
- Limit startup time: Keep initialization quick to reduce deployment time
- Use environment variables: Make your startup commands configurable via environment variables
- Log startup progress: Output clear logs for troubleshooting
Using Init Containers for Startup Tasks
Init containers run before app containers start and are ideal for setup tasks. Let's create a Pod with an init container:
nano init-container-pod.yaml
Add the following content:
apiVersion: v1
kind: Pod
metadata:
name: init-container-pod
spec:
initContainers:
- name: init-config
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args:
- |
echo "Initializing configuration..."
mkdir -p /work-dir/config
echo "database_url=mysql://user:password@db:3306/mydb" > /work-dir/config/db.conf
echo "api_key=1234567890" > /work-dir/config/api.conf
echo "Initialization complete"
volumeMounts:
- name: shared-volume
mountPath: /work-dir
containers:
- name: app
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args:
- |
echo "Application starting..."
echo "Reading configuration:"
cat /work-dir/config/db.conf
cat /work-dir/config/api.conf
echo "Application running..."
sleep 3600
volumeMounts:
- name: shared-volume
mountPath: /work-dir
volumes:
- name: shared-volume
emptyDir: {}
In this configuration:
- The init container
init-configruns first and creates configuration files - Both containers share a volume called
shared-volume - The main app container reads the configuration created by the init container
Save and exit with Ctrl+X, followed by Y and Enter.
Let's create this Pod:
kubectl apply -f init-container-pod.yaml
You should see:
pod/init-container-pod created
Check the Pod status:
kubectl get pods init-container-pod
You should see that the Pod is running:
NAME READY STATUS RESTARTS AGE
init-container-pod 1/1 Running 0 30s
Now let's check the logs of the main container:
kubectl logs init-container-pod -c app
You should see something like:
Application starting...
Reading configuration:
database_url=mysql://user:password@db:3306/mydb
api_key=1234567890
Application running...
This confirms that the init container successfully created the configuration files, and the main container was able to read them.
Real-World Example: Web Application with Database Check
Let's create a more realistic example - a web application that checks for database availability before starting:
nano webapp-pod.yaml
Add the following content:
apiVersion: v1
kind: Pod
metadata:
name: webapp-pod
spec:
initContainers:
- name: wait-for-db
image: busybox:1.28
command: ["/bin/sh", "-c"]
args:
- |
echo "Checking for database availability..."
## In a real scenario, this would check an actual database
## For this example, we'll simulate success after a short delay
sleep 5
echo "Database is available"
touch /tmp/db-ready
volumeMounts:
- name: shared-volume
mountPath: /tmp
containers:
- name: webapp
image: nginx:1.19
ports:
- containerPort: 80
command: ["/bin/sh", "-c"]
args:
- |
if [ -f /tmp/db-ready ]; then
echo "Database connection verified, starting web application..."
## Customize nginx configuration
echo "<h1>Web Application Started Successfully</h1>" > /usr/share/nginx/html/index.html
echo "<p>Connected to database</p>" >> /usr/share/nginx/html/index.html
## Start nginx
nginx -g 'daemon off;'
else
echo "Error: Database not available"
exit 1
fi
volumeMounts:
- name: shared-volume
mountPath: /tmp
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: shared-volume
emptyDir: {}
This configuration:
- Uses an init container to check for database availability (simulated)
- The main container checks for a file created by the init container before starting
- Includes a readiness probe to verify the application is serving traffic
- Uses a shared volume for communication between containers
Save and exit with Ctrl+X, followed by Y and Enter.
Let's create this Pod:
kubectl apply -f webapp-pod.yaml
You should see:
pod/webapp-pod created
Wait a moment for the Pod to start fully, then check its status:
kubectl get pods webapp-pod
You should see:
NAME READY STATUS RESTARTS AGE
webapp-pod 1/1 Running 0 30s
The 1/1 in the READY column indicates that the readiness probe has succeeded.
Let's port-forward to access the web application:
kubectl port-forward webapp-pod 8080:80 &
This command runs in the background (due to the &). Now we can access the web application using curl:
curl http://localhost:8080
You should see:
<h1>Web Application Started Successfully</h1>
<p>Connected to database</p>
This confirms that our application initialized correctly, verified database availability, and is now serving traffic.
Stop the port-forwarding process:
pkill -f "kubectl port-forward"
Cleanup
Before concluding the lab, let's clean up the resources we created:
kubectl delete pod basic-pod startup-command-pod modified-command-pod multi-command-pod liveness-probe-pod script-pod init-container-pod webapp-pod
You should see:
pod "basic-pod" deleted
pod "startup-command-pod" deleted
pod "modified-command-pod" deleted
pod "multi-command-pod" deleted
pod "liveness-probe-pod" deleted
pod "script-pod" deleted
pod "init-container-pod" deleted
pod "webapp-pod" deleted
You've now learned how to run commands in Kubernetes Pods on startup, execute multiple commands, handle failures, and apply best practices in real-world scenarios.
Summary
In this lab, you have learned how to run commands in Kubernetes Pods on startup. You've gained practical experience with:
- Setting up a Kubernetes environment using Minikube
- Creating and managing Pods with specific startup commands
- Using the
commandandargsfields to configure container behavior - Executing multiple commands using shell operators and scripts
- Handling startup failures with health checks
- Implementing best practices with init containers
- Building real-world applications that require proper initialization
These skills are essential for deploying containerized applications in Kubernetes environments. By properly configuring startup commands, you can ensure your applications initialize correctly, verify dependencies, and handle errors gracefully.
As you continue your Kubernetes journey, remember that proper application initialization is a critical part of building reliable, scalable, and maintainable systems.


