Deploy Your First Application on Kubernetes

Learn Kubernetes fundamentals by deploying a web app with a Deployment, Service, ConfigMap, and HorizontalPodAutoscaler on a local cluster using minikube.

Domains & Technologies

Domains
InfrastructureDevOps
Technologies
KUBERNETESDOCKER

Blueprint Walkthrough

Architecture Overview

This project teaches you the core building blocks of Kubernetes by deploying a real web application. Kubernetes is the platform that runs containers at scale — every major tech company uses it. This project gives you hands-on experience with the resources you will use every single day as a DevOps engineer.

You will deploy a web application using four essential Kubernetes resources: a Deployment that manages your pods, a Service that exposes the app, a ConfigMap that injects configuration, and a HorizontalPodAutoscaler that scales automatically under load.

Bash
kubectl apply -f *.yaml
|
v
Kubernetes Cluster (minikube)
|
+----+----+----+
| | | |
v v v v
Pod Pod Pod Pod (Deployment manages these)
| | | |
+----+----+----+
|
v
Service
(load balances between pods)
|
v
http://localhost
(your app is accessible!)
Problem Solved

Running a single Docker container is fine for one developer. But in production, you need multiple replicas for reliability, automatic restarts when containers crash, rolling updates with zero downtime, and automatic scaling when traffic increases. Docker alone cannot do any of this.

Kubernetes solves all of it. You declare your desired state — three replicas of my web app — and Kubernetes continuously works to make reality match that declaration. If a pod crashes, Kubernetes starts a new one. If a node fails, Kubernetes reschedules pods onto healthy nodes. If traffic spikes, the HorizontalPodAutoscaler adds more pods automatically.

Step-by-Step Implementation Guide

Step 1: Install minikube and kubectl

minikube runs a single-node Kubernetes cluster on your laptop — perfect for learning without cloud costs.

Bash
## Install minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube version
## Expected: minikube version: v1.33.0
## Install kubectl (the CLI for Kubernetes)
curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client
## Expected: Client Version: v1.29.x
## Start the minikube cluster
## This creates a local Kubernetes cluster (takes 2-3 minutes)
minikube start --driver=docker --memory=4096 --cpus=2
## Verify the cluster is running
kubectl cluster-info
## Expected: Kubernetes control plane is running at https://127.0.0.1:PORT
kubectl get nodes
## Expected: minikube Ready control-plane 2m v1.29.x
Remember

minikube is for learning only. In production, you use managed Kubernetes like Amazon EKS, Google GKE, or Azure AKS. The concepts you learn with minikube apply identically to all of these.

Step 2: Understand the Key Kubernetes Concepts

Before deploying, understand what each resource does:

  • Pod — the smallest unit in Kubernetes. One or more containers running together. Pods are temporary — when they crash, Kubernetes creates new ones.
  • Deployment — manages a group of identical pods. You tell it how many replicas you want. It creates pods, replaces crashed ones, and handles rolling updates.
  • Service — a stable network endpoint for your pods. Pods have changing IP addresses, but the Service IP never changes. Load balances traffic across all pods.
  • ConfigMap — stores configuration data (environment variables, config files) separately from your container image. Change config without rebuilding the image.
  • HorizontalPodAutoscaler — watches CPU/memory usage and adds or removes pods automatically.

Step 3: Create the Application Manifests

Create a directory for your Kubernetes files:

Bash
mkdir k8s-first-deploy && cd k8s-first-deploy

Create configmap.yaml — configuration separate from the container:

YAML
apiVersion: v1
kind: ConfigMap
metadata:
name: webapp-config
labels:
app: webapp
data:
# These key-value pairs become environment variables in the pods
APP_NAME: "DevOps Network App"
APP_VERSION: "1.0.0"
ENVIRONMENT: "learning"
LOG_LEVEL: "info"
# Multi-line values are also supported
WELCOME_MESSAGE: |
Welcome to Kubernetes!
Running on DevOps Network Platform.

Create deployment.yaml — the core resource that runs your app:

YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
labels:
app: webapp
spec:
# How many identical pod replicas to run
replicas: 3
# Which pods this Deployment manages
# Must match the labels in template.metadata.labels
selector:
matchLabels:
app: webapp
# Rolling update strategy — how to update pods without downtime
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Create 1 extra pod during update
maxUnavailable: 0 # Never reduce below desired count
# Pod template — this is what each pod looks like
template:
metadata:
labels:
app: webapp # Must match selector.matchLabels
version: v1
spec:
containers:
- name: webapp
image: nginx:1.25-alpine # Using nginx as a simple web server
ports:
- containerPort: 80
name: http
# Inject ConfigMap values as environment variables
envFrom:
- configMapRef:
name: webapp-config
# Resource requests and limits are IMPORTANT
# requests = minimum guaranteed resources
# limits = maximum the container can use
resources:
requests:
memory: "64Mi" # 64 megabytes RAM minimum
cpu: "50m" # 50 millicores CPU minimum
limits:
memory: "128Mi" # Never exceed 128MB RAM
cpu: "200m" # Never exceed 0.2 CPU cores
# Health checks tell Kubernetes if the pod is working
# readinessProbe: is the pod ready to receive traffic?
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
# livenessProbe: should Kubernetes restart this pod?
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 20
failureThreshold: 3 # Restart after 3 consecutive failures

Create service.yaml — exposes the deployment:

YAML
apiVersion: v1
kind: Service
metadata:
name: webapp
labels:
app: webapp
spec:
# NodePort exposes the service on a port on every node
# This is how minikube can reach your service from your laptop
type: NodePort
# Routes traffic to pods with this label
selector:
app: webapp
ports:
- name: http
port: 80 # Port the Service listens on
targetPort: 80 # Port on the pod to forward to
nodePort: 30080 # Port on the node (30000-32767 range)

Create hpa.yaml — automatic scaling based on CPU:

YAML
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: webapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: webapp
minReplicas: 2 # Never scale below 2 pods
maxReplicas: 10 # Never scale above 10 pods
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Scale up when avg CPU > 70%

Step 4: Deploy Everything

Bash
## Enable the metrics server (needed for HPA to work)
minikube addons enable metrics-server
## Apply all manifests
## The order matters: ConfigMap first, then Deployment, then Service
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f hpa.yaml
## Or apply everything at once (Kubernetes handles order)
kubectl apply -f .
## Watch pods starting up
kubectl get pods --watch
## Expected: 3 pods starting, then showing Running 1/1
## Check the deployment status
kubectl get deployments
## Expected:
## NAME READY UP-TO-DATE AVAILABLE AGE
## webapp 3/3 3 3 60s
## Check the service
kubectl get services
## Expected:
## NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
## webapp NodePort 10.x.x.x <none> 80:30080/TCP 60s
## Access the application
minikube service webapp --url
## Expected: http://127.0.0.1:PORT
## Open this URL in your browser

Step 5: Explore and Troubleshoot

These are the most important commands for daily Kubernetes work:

Bash
## Describe a pod — see events, resource usage, volumes
kubectl describe pod webapp-POD-NAME
## Look at the Events section at the bottom — shows what happened
## View logs from a pod
kubectl logs webapp-POD-NAME
## Follow logs in real time
kubectl logs -f webapp-POD-NAME
## Get logs from all pods with the label app=webapp
kubectl logs -l app=webapp
## Execute a command inside a running pod
kubectl exec -it webapp-POD-NAME -- sh
## You are now inside the container!
pwd # /
env | grep APP # See environment variables from ConfigMap
whoami # nginx
exit # Leave the container
## See resource usage (needs metrics-server)
kubectl top pods
## Expected: CPU and memory usage per pod
## See the HPA status
kubectl get hpa
## Expected: TARGETS shows current CPU%, MINPODS, MAXPODS, REPLICAS

Step 6: Perform a Rolling Update

A rolling update changes the container image without downtime — new pods start before old ones stop.

Bash
## Update the image to a newer nginx version
kubectl set image deployment/webapp \
webapp=nginx:1.26-alpine
## Watch the rolling update happen
kubectl rollout status deployment/webapp
## Expected: Waiting for rollout... then Successfully rolled out
## See the update history
kubectl rollout history deployment/webapp
## Expected: Two revisions listed
## Rollback to the previous version
kubectl rollout undo deployment/webapp
kubectl rollout status deployment/webapp
## Expected: Successfully rolled out (back to old version)
## Verify the rollback
kubectl get pods -o wide
## Expected: 3 new pods running the old image
Tip

The kubectl rollout undo command is your emergency button in production. If you deploy a broken version, run this immediately and your traffic goes back to the last working version in under 30 seconds.

Step 7: Test Pod Self-Healing

Bash
## Delete a pod manually — simulate a crash
POD=$(kubectl get pods -l app=webapp \
-o jsonpath='{.items[0].metadata.name}')
kubectl delete pod $POD
## Watch Kubernetes immediately create a replacement
kubectl get pods --watch
## Expected: Old pod shows Terminating
## New pod appears immediately and reaches Running
## The Deployment always maintains 3 replicas
kubectl get deployment webapp
## Expected: READY 3/3 within 30 seconds
Validation & Testing
Bash
## 1. Verify all 3 pods are running
kubectl get pods -l app=webapp
## Expected: 3 pods, all STATUS=Running, READY=1/1
## 2. Verify ConfigMap values are injected
POD=$(kubectl get pods -l app=webapp \
-o jsonpath='{.items[0].metadata.name}')
kubectl exec $POD -- env | grep APP_NAME
## Expected: APP_NAME=DevOps Network App
## 3. Verify the app responds
APP_URL=$(minikube service webapp --url)
curl $APP_URL
## Expected: Nginx HTML page
## 4. Verify load balancing works
## Run multiple requests and see different pod names in logs
for i in {1..10}; do curl -s $APP_URL > /dev/null; done
kubectl logs -l app=webapp --tail=5
## Expected: Requests distributed across all 3 pods
## 5. Verify HPA is working
kubectl get hpa webapp-hpa
## Expected: TARGETS shows current CPU% (likely <10%)
## Min and Max pods shown correctly
## 6. Verify rolling update leaves no downtime
## Run this in one terminal:
while true; do
curl -s -o /dev/null -w "%{http_code}\n" $APP_URL
sleep 0.5
done
## In another terminal, trigger a rolling update:
kubectl set image deployment/webapp webapp=nginx:1.26-alpine
## Expected: All responses remain 200 during the update
## 7. Clean up
kubectl delete -f .
minikube stop
echo "Kubernetes project complete!"