What You’ll Learn in This Tutorial
- Build a local Kubernetes cluster with minikube
- Basic concepts of Pod, Deployment, and Service
- How to write YAML manifests
- Application deployment and exposure
- Scaling and rolling updates
- Using ConfigMap, Secret, and Namespace
- Best practices for production environments
Prerequisites: Docker Desktop must be installed. kubectl and minikube installation required.
History and Background of Kubernetes
Why Kubernetes Was Created
Kubernetes (also written as K8s) is a container orchestration platform that Google open-sourced in 2014. Its origins lie in the large-scale container management systems called “Borg” and “Omega” that Google had been operating internally for over 10 years.
“Kubernetes is based on a decade and a half of experience at Google running production workloads at scale, combined with best-of-breed ideas and practices from the community.”
Google is said to launch billions of containers every week, and the knowledge gained from that operational experience is consolidated in Kubernetes.
Origin of the Kubernetes Name
“Kubernetes” means “helmsman” or “pilot” in Greek. It carries the meaning of steering the “ship” of containers, and the logo is designed around a ship’s helm. The abbreviation “K8s” comes from having 8 letters (ubernete) between K and s.
The Need for Container Orchestration
Container technology (Docker) has dramatically simplified application packaging and distribution. However, new challenges arise in production environments:
| Challenge | Description |
|---|---|
| Scaling | Dynamically adjust container count based on traffic |
| Failure Recovery | Automatic restart when containers crash |
| Load Balancing | Traffic distribution across multiple containers |
| Service Discovery | Managing dynamically changing container IP addresses |
| Rolling Updates | Application updates without downtime |
| Secret Management | Secure distribution of passwords and API keys |
Kubernetes solves these challenges with declarative configuration (defining the desired state).
CNCF and Cloud Native Ecosystem
In 2015, Google donated Kubernetes to the CNCF (Cloud Native Computing Foundation). CNCF is an organization under the Linux Foundation that promotes the adoption and standardization of cloud native technologies.
Currently, CNCF includes important projects such as:
- Kubernetes - Container orchestration
- Prometheus - Monitoring and alerting
- Envoy - Service proxy
- Helm - Package manager
- Argo - GitOps workflow
Reference: CNCF Cloud Native Interactive Landscape
Kubernetes Architecture Overview
A Kubernetes cluster consists of the Control Plane (master) and Worker Nodes.
Control Plane Components
flowchart TB
subgraph ControlPlane["Control Plane"]
API["API Server"]
Scheduler["Scheduler"]
Controller["Controller Manager"]
etcd["etcd<br/>(Distributed Key-Value Store)"]
end
| Component | Role |
|---|---|
| kube-apiserver | Entry point for all API operations to the cluster |
| etcd | Distributed database that stores cluster state |
| kube-scheduler | Decides which node to place Pods on |
| kube-controller-manager | Runs various controllers (ReplicaSet, Node, etc.) |
Worker Node Components
flowchart TB
subgraph WorkerNode["Worker Node"]
kubelet["kubelet"]
proxy["kube-proxy"]
runtime["Container Runtime"]
subgraph Pods["Pods"]
Pod1["Pod"]
Pod2["Pod"]
Pod3["Pod"]
Pod4["Pod"]
end
end
| Component | Role |
|---|---|
| kubelet | Agent that manages Pods on the node |
| kube-proxy | Manages network rules to implement Service functionality |
| Container Runtime | Runs containers (containerd, CRI-O, etc.) |
Step 1: Starting minikube
minikube is a tool that makes it easy to build a Kubernetes cluster in your local environment. It’s ideal for learning and development.
# Start minikube cluster
# You can specify the driver with --driver option (docker, virtualbox, hyperkit, etc.)
minikube start
# Start with specified memory and CPU (to simulate production-like environment)
minikube start --memory=4096 --cpus=2
# Check cluster status
minikube status
# Verify kubectl connection
kubectl cluster-info
# Check node information
kubectl get nodes -o wide
Other local environment tools: kind (Kubernetes in Docker) and k3s (lightweight Kubernetes) are also popular. Choose based on your needs.
Step 2: Kubernetes Basic Concepts
Let’s understand the main resources in Kubernetes. Detailed explanations are available in the official concepts documentation.
Relationship Between Main Resources
flowchart BT
External["External Traffic"]
Service["Service
(LB)"]
External --> Service
subgraph Deployment["Deployment
(Manages replica count, update strategy, rollback)"]
subgraph ReplicaSet["ReplicaSet
(Maintains specified number of Pod replicas)"]
Pod1["Pod
(Container)"]
Pod2["Pod
(Container)"]
Pod3["Pod
(Container)"]
Pod4["Pod
(Container)"]
end
end
Service --> Pod1
Service --> Pod2
Service --> Pod3
Service --> Pod4
Details of Each Resource
| Resource | Description | Use Case |
|---|---|---|
| Pod | Smallest deployment unit. Contains one or more containers | Single application instance |
| ReplicaSet | Maintains specified number of Pod replicas | Usually managed automatically by Deployment |
| Deployment | Pod replica management and rolling updates | Stateless applications |
| StatefulSet | Ordered Pod management and persistent storage | Databases, distributed systems |
| DaemonSet | Places one Pod on each node | Log collection, monitoring agents |
| Service | Abstracts network access to Pods | Load balancing, service discovery |
| Ingress | HTTP/HTTPS routing and TLS termination | External exposure, path-based routing |
| ConfigMap | Stores configuration data | Environment variables, config files |
| Secret | Stores sensitive data | Passwords, API keys, certificates |
| Namespace | Logical separation of resources | Environment isolation (dev/staging/prod) |
Step 3: Deploy Your First Pod
Create Directly from Command Line (for development/debugging)
# Create nginx Pod
kubectl run my-nginx --image=nginx:latest
# Check Pod status
# Wait until STATUS becomes Running
kubectl get pods
# Monitor status in real-time
kubectl get pods -w
# Check Pod details (events, resource usage, etc.)
kubectl describe pod my-nginx
# Check Pod logs
kubectl logs my-nginx
# Follow logs in real-time
kubectl logs -f my-nginx
# Delete Pod
kubectl delete pod my-nginx
Tip: Pods created with
kubectl runare not automatically recreated when deleted. Always use Deployment in production environments.
Debugging Inside Pods
# Start shell inside Pod
kubectl exec -it my-nginx -- /bin/bash
# Execute specific command
kubectl exec my-nginx -- cat /etc/nginx/nginx.conf
# Check network from inside Pod
kubectl exec my-nginx -- curl -s localhost
Step 4: Deploy with YAML Manifests
In production environments, follow the Infrastructure as Code principle and define resources in YAML files. This ensures version control, review, and reproducibility.
“Store the declarative configuration in source control. This allows you to roll back and roll forward configurations.”
Deployment Manifest
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
# Use labels to organize resources
environment: development
version: v1.0.0
spec:
replicas: 3 # Maintain 3 Pod replicas
selector:
matchLabels:
app: my-app
# Update strategy configuration
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Number of additional Pods that can be created during update
maxUnavailable: 0 # Number of Pods that can be unavailable during update
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: nginx:1.24
ports:
- containerPort: 80
# Resource limits (essential best practice)
resources:
requests: # Resources reserved during scheduling
memory: "64Mi"
cpu: "250m" # 250 millicores = 0.25 CPU
limits: # Upper limit (OOMKilled or throttled if exceeded)
memory: "128Mi"
cpu: "500m"
# Liveness Probe: Check if Pod is alive
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 5
# Readiness Probe: Check if ready to accept traffic
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 3
Apply Deployment
# Apply manifest (--record to record rollout history)
kubectl apply -f deployment.yaml
# Check Deployment status
kubectl get deployments
# Also check ReplicaSet (automatically created by Deployment)
kubectl get replicasets
# Check Pod status (filter with label selector)
kubectl get pods -l app=my-app
# Output detailed info in YAML format
kubectl get deployment my-app -o yaml
Importance of Health Checks (Probes)
Kubernetes Probes are mechanisms to verify application health.
| Probe | Purpose | Action on Failure |
|---|---|---|
| livenessProbe | Is the Pod alive? | Restart the container |
| readinessProbe | Can it accept requests? | Remove from Service endpoints |
| startupProbe | Has startup completed? | Disable liveness/readiness until startup completes |
Warning: If you check DB connection in livenessProbe, a DB failure could cause infinite Pod restarts, leading to cascade failures.
Step 5: Expose Apps with Service
Service provides stable network access to Pods. While Pods are dynamically created/deleted with changing IP addresses, Service provides a fixed DNS name and IP.
Service Types
| Type | Use | Access Scope |
|---|---|---|
| ClusterIP | Default. Internal cluster only | Internal microservice communication |
| NodePort | External exposure via node port | Development/test environments |
| LoadBalancer | Auto-provision cloud LB | Production external exposure |
| ExternalName | Alias to external service | External DB connection |
Service Manifest
service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app-service
labels:
app: my-app
spec:
type: NodePort # For development. Use LoadBalancer in production
selector:
app: my-app # Forward traffic to Pods with this label
ports:
- name: http
protocol: TCP
port: 80 # Service port
targetPort: 80 # Pod port
nodePort: 30080 # Node port (30000-32767)
Apply and Verify Service
# Create Service
kubectl apply -f service.yaml
# Check Service
kubectl get services
# Service details (check Pod IPs in Endpoints)
kubectl describe service my-app-service
# Check Endpoints directly
kubectl get endpoints my-app-service
# Get access URL with minikube
minikube service my-app-service --url
# Open in browser
minikube service my-app-service
ClusterIP Service Example (for internal communication)
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP # Default
selector:
app: backend
ports:
- port: 8080
targetPort: 8080
Accessible from within the cluster via backend-service.default.svc.cluster.local or simply backend-service.
Step 6: Scaling
One of Kubernetes’ powerful features is flexible scaling.
Manual Scaling
# Change replica count
kubectl scale deployment my-app --replicas=5
# Verify Pods increased
kubectl get pods -l app=my-app
# Check Deployment status
kubectl get deployment my-app
Horizontal Pod Autoscaler (HPA)
HPA automatically adjusts Pod count based on CPU usage or custom metrics.
# Set up CPU-based autoscaling
# Scale out when CPU usage exceeds 50%
kubectl autoscale deployment my-app --min=2 --max=10 --cpu-percent=50
# Check HPA status
kubectl get hpa
# Check details
kubectl describe hpa my-app
HPA YAML Manifest
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
# Fine-grained scaling behavior control
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # Scale down after 5 minutes of stability
Note: To use HPA, Metrics Server must be installed in the cluster. Enable with
minikube addons enable metrics-serverin minikube.
Step 7: Rolling Updates
Kubernetes rolling updates allow updating applications without downtime.
Executing Updates
# Update image
kubectl set image deployment/my-app my-app=nginx:1.25
# Or edit manifest and apply
kubectl apply -f deployment.yaml
# Monitor rollout status in real-time
kubectl rollout status deployment/my-app
# Check rollout history
kubectl rollout history deployment/my-app
# Check details of specific revision
kubectl rollout history deployment/my-app --revision=2
Rollback
# Rollback to previous version
kubectl rollout undo deployment/my-app
# Rollback to specific revision
kubectl rollout undo deployment/my-app --to-revision=1
# Pause rollout (for canary releases, etc.)
kubectl rollout pause deployment/my-app
# Resume rollout
kubectl rollout resume deployment/my-app
Update Strategy
Specify update strategy with the strategy field in Deployment:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # Percentage of additional Pods during update
maxUnavailable: 25% # Percentage of Pods that can be stopped during update
| Setting | Effect |
|---|---|
| maxSurge: 1, maxUnavailable: 0 | Safety first (stop old Pod after new Pod starts) |
| maxSurge: 0, maxUnavailable: 1 | Resource saving (stop old Pod first) |
Step 8: ConfigMap and Secret
By externalizing application settings and sensitive information, you can change configuration without rebuilding images. This follows The Twelve-Factor App principle of “Store config in environment variables”.
ConfigMap
Stores configuration data in key-value format.
configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
# Key-value format
APP_ENV: "production"
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
# Embedded as file
nginx.conf: |
server {
listen 80;
location / {
root /usr/share/nginx/html;
}
}
Secret
Stores sensitive data. Values are base64 encoded.
secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
# Base64 encoded values
# echo -n "password123" | base64 → cGFzc3dvcmQxMjM=
DB_PASSWORD: cGFzc3dvcmQxMjM=
API_KEY: c2VjcmV0LWFwaS1rZXk=
Security Warning: Kubernetes Secrets are only base64 encoded by default, not encrypted. Consider the following for production:
Using ConfigMap/Secret in Pods
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
image: nginx:1.24
# Inject as environment variables
env:
- name: APP_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: DB_PASSWORD
# Inject all keys as environment variables
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secret
# Mount as file
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: nginx.conf
path: default.conf
Step 9: Resource Isolation with Namespace
Use Namespace to logically isolate resources.
Creating and Using Namespaces
# Create Namespaces
kubectl create namespace development
kubectl create namespace staging
kubectl create namespace production
# Check existing Namespaces
kubectl get namespaces
# Create resources in specific Namespace
kubectl apply -f deployment.yaml -n development
# Check resources specifying Namespace
kubectl get pods -n development
# Check resources in all Namespaces
kubectl get pods --all-namespaces
kubectl get pods -A # Short form
Switching Default Namespace
# Check current context
kubectl config current-context
# Change default Namespace
kubectl config set-context --current --namespace=development
# Verify change
kubectl config view --minify | grep namespace
ResourceQuota per Namespace
Limit resource usage per team or environment.
apiVersion: v1
kind: ResourceQuota
metadata:
name: development-quota
namespace: development
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
pods: "20"
services: "10"
Useful kubectl Commands
Checking Resources
# Show all resources
kubectl get all
# Wide output format
kubectl get pods -o wide
# Output in YAML/JSON format
kubectl get deployment my-app -o yaml
kubectl get deployment my-app -o json
# Custom columns output
kubectl get pods -o custom-columns=NAME:.metadata.name,STATUS:.status.phase
# Watch resources (real-time updates)
kubectl get pods -w
Debugging and Troubleshooting
# Execute command inside Pod
kubectl exec -it pod-name -- /bin/sh
# For multi-container Pods, specify container
kubectl exec -it pod-name -c container-name -- /bin/sh
# Port forward (access Pod directly from local)
kubectl port-forward pod/my-app-xxx 8080:80
# Port forward to Service
kubectl port-forward service/my-app-service 8080:80
# Check resource events
kubectl get events --sort-by='.lastTimestamp'
# Events for specific Pod
kubectl describe pod my-app-xxx | grep -A 10 Events
Deleting Resources
# Delete from manifest
kubectl delete -f deployment.yaml
# Bulk delete by label
kubectl delete pods -l app=my-app
# Delete entire Namespace (caution: deletes all resources inside)
kubectl delete namespace development
# Force delete (for stuck Pods, etc.)
kubectl delete pod my-app-xxx --force --grace-period=0
Production Environment Best Practices
1. Set Resource Limits
Set requests and limits for all containers.
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Why is this important: Without resource limits, one Pod could consume all node resources, affecting other Pods.
2. Implement Health Checks (Probes)
Always set livenessProbe and readinessProbe.
3. Pod Disruption Budget (PDB)
Maintain minimum Pod count even during maintenance.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
minAvailable: 2 # or maxUnavailable: 1
selector:
matchLabels:
app: my-app
4. Anti-Affinity Settings
Spread Pods across different nodes to improve availability.
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: my-app
topologyKey: kubernetes.io/hostname
5. NetworkPolicy
Restrict Pod network traffic.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
6. Security Context
Minimize container privileges.
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: my-app
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
Common Mistakes and Anti-patterns
What to Avoid
| Anti-pattern | Problem | Solution |
|---|---|---|
Using latest tag | No reproducibility, unexpected updates | Use explicit version tags |
| No resource limits | Resource exhaustion, node down | Always set requests/limits |
| No Probes | Delayed failure detection | Set liveness/readinessProbe |
| Hardcoded Secrets | Security risk | Use ConfigMap/Secret |
| Running as root | Security risk | Set runAsNonRoot: true |
| Single replica | Single point of failure | Maintain at least 2 replicas |
Recommended Practices
# Good example
spec:
replicas: 3
template:
spec:
containers:
- name: my-app
image: my-app:1.2.3 # Explicit version
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
Cleanup
# Delete resources
kubectl delete -f service.yaml
kubectl delete -f deployment.yaml
kubectl delete -f configmap.yaml
kubectl delete -f secret.yaml
# Or bulk delete
kubectl delete -f .
# Delete Namespace (resources inside are also deleted)
kubectl delete namespace development
# Stop minikube
minikube stop
# Completely delete minikube
minikube delete
# Delete all profiles
minikube delete --all
Next Steps
Based on the fundamentals learned in this tutorial, we recommend proceeding to these topics:
Advanced Topics
- Ingress Controller: HTTP/HTTPS routing and TLS termination
- Persistent Volume: Data persistence
- Helm: Kubernetes package manager
- Kustomize: Manifest customization
- Prometheus/Grafana: Monitoring and dashboards
- Argo CD: GitOps-based deployment automation
Reference Links
Official Documentation
- Kubernetes Official Documentation
- kubectl Cheat Sheet
- Kubernetes API Reference
- minikube Documentation
Learning Resources
- Kubernetes The Hard Way - By Kelsey Hightower, build a cluster manually to understand the mechanisms
- CNCF Cloud Native Trail Map - Learning roadmap for cloud native technologies
- Kubernetes Patterns - Kubernetes design patterns
Best Practices
- Kubernetes Best Practices
- Google Cloud: Kubernetes Best Practices
- Production Best Practices Checklist
Tools
- Lens - Kubernetes IDE
- k9s - Terminal-based Kubernetes UI
- kubectx/kubens - Context/Namespace switching tool
- stern - Multi-Pod log viewer
Certifications
- CKA (Certified Kubernetes Administrator)
- CKAD (Certified Kubernetes Application Developer)
- CKS (Certified Kubernetes Security Specialist)