Kubernetes from Scratch
Introduction
Kubernetes (K8s) is the leading container orchestration platform. This comprehensive guide covers pods, services, deployments, configuration, scaling, networking, storage, and production-ready practices for managing containerized applications at scale.
1. Kubernetes Architecture
Kubernetes Cluster Components:
Control Plane (Master):
├── API Server - Frontend for K8s control plane
├── etcd - Key-value store for cluster data
├── Scheduler - Assigns pods to nodes
├── Controller Manager - Runs controller processes
└── Cloud Controller Manager - Cloud-specific logic
Worker Nodes:
├── Kubelet - Ensures containers are running
├── Kube-proxy - Network proxy
└── Container Runtime - Docker/containerd/CRI-O
# Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
# Local development cluster
# Minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube start
# Kind (Kubernetes in Docker)
kind create cluster
# Verify
kubectl cluster-info
kubectl get nodes
2. Pods - Smallest Deployable Unit
# Simple pod definition
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
environment: production
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
# Apply configuration
kubectl apply -f pod.yaml
# Pod with multiple containers
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:1.0
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
value: "postgres://db:5432/mydb"
- name: sidecar-logger
image: fluent/fluent-bit
volumeMounts:
- name: logs
mountPath: /var/log/app
volumes:
- name: logs
emptyDir: {}
# Pod commands
kubectl get pods
kubectl get pods -o wide
kubectl describe pod nginx-pod
kubectl logs nginx-pod
kubectl logs -f nginx-pod -c container-name # Follow logs
kubectl exec -it nginx-pod -- bash
kubectl delete pod nginx-pod
kubectl port-forward nginx-pod 8080:80
3. Deployments - Declarative Updates
# Deployment definition
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
labels:
app: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myapi:1.0.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1000m"
# Deployment commands
kubectl apply -f deployment.yaml
kubectl get deployments
kubectl describe deployment api-deployment
kubectl scale deployment api-deployment --replicas=5
kubectl rollout status deployment api-deployment
kubectl rollout history deployment api-deployment
kubectl rollout undo deployment api-deployment
# Update image
kubectl set image deployment/api-deployment api=myapi:1.1.0
# Rolling update strategy
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Max pods above desired during update
maxUnavailable: 0 # Max pods unavailable during update
4. Services - Network Abstraction
# ClusterIP (internal only)
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
type: ClusterIP
selector:
app: api
ports:
- port: 80
targetPort: 3000
protocol: TCP
# NodePort (external access via node IP)
apiVersion: v1
kind: Service
metadata:
name: api-nodeport
spec:
type: NodePort
selector:
app: api
ports:
- port: 80
targetPort: 3000
nodePort: 30080 # Optional: 30000-32767
# LoadBalancer (cloud provider LB)
apiVersion: v1
kind: Service
metadata:
name: api-loadbalancer
spec:
type: LoadBalancer
selector:
app: api
ports:
- port: 80
targetPort: 3000
# Headless service (direct pod IPs)
apiVersion: v1
kind: Service
metadata:
name: database-headless
spec:
clusterIP: None
selector:
app: database
ports:
- port: 5432
# Service commands
kubectl get services
kubectl describe service api-service
kubectl get endpoints api-service
# DNS resolution
# service-name.namespace.svc.cluster.local
# Example: api-service.default.svc.cluster.local
5. ConfigMaps & Secrets
# ConfigMap - non-sensitive configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
app.properties: |
server.port=3000
log.level=info
database.host: "postgres.default.svc.cluster.local"
redis.host: "redis.default.svc.cluster.local"
# Create from file
kubectl create configmap app-config --from-file=config.json
# Use in pod
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:1.0
# As environment variables
envFrom:
- configMapRef:
name: app-config
# As volume
volumeMounts:
- name: config
mountPath: /etc/config
volumes:
- name: config
configMap:
name: app-config
# Secret - sensitive data (base64 encoded)
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
username: cG9zdGdyZXM= # base64 encoded
password: c2VjcmV0MTIz
stringData:
url: "postgres://user:pass@host:5432/db" # Auto-encoded
# Create secret
kubectl create secret generic db-secret \
--from-literal=username=postgres \
--from-literal=password=secret123
# Use secret
spec:
containers:
- name: app
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
6. Persistent Volumes
# PersistentVolume (admin creates)
apiVersion: v1
kind: PersistentVolume
metadata:
name: postgres-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: standard
hostPath:
path: /mnt/data
# PersistentVolumeClaim (user requests)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
# Use in pod
apiVersion: v1
kind: Pod
metadata:
name: postgres
spec:
containers:
- name: postgres
image: postgres:15
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgres-storage
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
# StatefulSet for stateful applications
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
7. Ingress - HTTP Routing
# Install Ingress Controller (nginx)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/cloud/deploy.yaml
# Ingress resource
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
# Multiple hosts
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-host-ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- host: admin.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-service
port:
number: 80
kubectl get ingress
kubectl describe ingress app-ingress
8. Namespaces & Resource Quotas
# Create namespace
apiVersion: v1
kind: Namespace
metadata:
name: production
kubectl create namespace production
kubectl get namespaces
# Use namespace
kubectl apply -f deployment.yaml -n production
kubectl get pods -n production
kubectl get all -n production
# Set default namespace
kubectl config set-context --current --namespace=production
# ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
name: production-quota
namespace: production
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
persistentvolumeclaims: "10"
pods: "20"
# LimitRange (default limits per pod)
apiVersion: v1
kind: LimitRange
metadata:
name: production-limits
namespace: production
spec:
limits:
- max:
cpu: "2"
memory: "2Gi"
min:
cpu: "100m"
memory: "64Mi"
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "200m"
memory: "256Mi"
type: Container
9. Horizontal Pod Autoscaling
# HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-deployment
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 30
- type: Pods
value: 2
periodSeconds: 30
# Install metrics server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# Check HPA
kubectl get hpa
kubectl describe hpa api-hpa
kubectl top pods
kubectl top nodes
10. Production Best Practices
# Complete production deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-production
namespace: production
labels:
app: api
version: v1.0.0
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels:
app: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: api
version: v1.0.0
spec:
# Security
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
# Init container
initContainers:
- name: wait-for-db
image: busybox
command: ['sh', '-c', 'until nc -z postgres 5432; do sleep 1; done']
containers:
- name: api
image: myapi:1.0.0
imagePullPolicy: Always
ports:
- containerPort: 3000
name: http
protocol: TCP
# Environment from ConfigMap & Secret
envFrom:
- configMapRef:
name: api-config
- secretRef:
name: api-secret
# Health checks
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
# Resources
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1000m"
# Security
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Volumes
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
# Pod anti-affinity (spread across nodes)
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: api
topologyKey: kubernetes.io/hostname
# Network Policy (restrict traffic)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-network-policy
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 3000
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
11. Monitoring & Debugging
# Useful commands
kubectl get all -A # All resources, all namespaces
kubectl get events --sort-by=.metadata.creationTimestamp
kubectl describe pod pod-name
kubectl logs pod-name -c container-name --previous
kubectl exec -it pod-name -- sh
kubectl cp pod-name:/path/to/file ./local-file
kubectl port-forward pod-name 8080:3000
kubectl attach pod-name -i
# Debug pod
kubectl run debug --image=alpine --rm -it -- sh
# View resource usage
kubectl top nodes
kubectl top pods --all-namespaces --sort-by=memory
# Check cluster health
kubectl get componentstatuses
kubectl cluster-info dump
# YAML output
kubectl get deployment api-deployment -o yaml
kubectl get pod pod-name -o json | jq '.spec.containers[0].image'
12. Best Practices Checklist
✓ Kubernetes Best Practices:
- ✓ Always set resource requests and limits
- ✓ Implement liveness and readiness probes
- ✓ Use namespaces to organize resources
- ✓ Run as non-root user
- ✓ Use specific image tags (not :latest)
- ✓ Implement horizontal pod autoscaling
- ✓ Use ConfigMaps and Secrets for configuration
- ✓ Implement network policies
- ✓ Use rolling updates with health checks
- ✓ Set up monitoring and logging
- ✓ Use persistent volumes for stateful apps
- ✓ Implement pod disruption budgets
- ✓ Use labels and selectors consistently
- ✓ Configure pod anti-affinity for HA
- ✓ Regular backup of etcd
Conclusion
Kubernetes provides powerful container orchestration capabilities. Master pods, services, deployments, and scaling to build resilient applications. Always follow security best practices, implement proper monitoring, and test disaster recovery procedures.
💡 Pro Tip: Use Helm charts for packaging and deploying complex applications. Helm is the package manager for Kubernetes that simplifies deployment with templating and versioning. Start with existing charts from Artifact Hub and customize for your needs.