Modern software development has to run and scale complex applications efficiently. Kubernetes (often abbreviated K8s) has become a capable answer to that demand. As an open-source platform it automates management, deployment and scaling of containerised applications.
With automatic load balancing, self-healing and flexible resource management, Kubernetes supplies the tools needed to operate modern cloud-native applications. The platform was originally developed at Google and handed to the open-source community in 2014, where it has evolved continuously since.
❗ Important note: The material builds on the fundamentals in DevOps fundamentals: modern software development. Basic DevOps knowledge is assumed:
- A basic understanding of container technologies
- Experience with Linux systems and the command line
- A basic understanding of DevOps practices
Why Kubernetes?
In modern microservice architectures, development teams face complex challenges:
- Managing hundreds or thousands of containers
- Automatic scaling under load
- Self-healing after failures
- Rolling updates without downtime
- Load balancing between services
- Service discovery in dynamic environments
Kubernetes solves these challenges through a carefully designed system of abstractions and automation.
Architecture and components
Control Plane (Master Node)
The control plane is the “brain” of the Kubernetes cluster and consists of several critical components:
API Server (kube-apiserver):
┌─────────────────────────────────────────────────────────────┐
│ API server request processing │
├─────────────────────────────────────────────────────────────┤
│ │
│ Inbound kubectl or client request │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 1. Authentication ──► Check identity (TLS/token) │ │
│ │ 2. Authorization ──► Check rights (RBAC) │ │
│ │ 3. Admission Control──► Mutating and validating webh. │ │
│ │ 4. API Server Core ──► Schema validation │ │
│ │ 5. Data storage ──► Persistence in etcd │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
The API server is the central communication hub and does the following:
- REST interface for every cluster operation
- Authentication and authorisation of requests
- Validation of object configurations
- Persistence in etcd
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=192.168.1.10
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
image: k8s.gcr.io/kube-apiserver:v1.24.0
name: kube-apiserver
etcd:
The distributed key-value store holds the entire cluster state:
- Highly available data storage
- Consistent data holding through Raft consensus
- Versioned storage of every Kubernetes object
apiVersion: v1
kind: Pod
metadata:
name: etcd
namespace: kube-system
spec:
containers:
- command:
- etcd
- --advertise-client-urls=https://192.168.1.10:2379
- --data-dir=/var/lib/etcd
- --initial-cluster-state=new
- --initial-cluster-token=etcd-cluster
image: k8s.gcr.io/etcd:3.5.3-0
name: etcd
Scheduler (kube-scheduler):
The scheduler is responsible for intelligent placement of pods:
- Resource analysis of worker nodes
- Consideration of affinity/anti-affinity
- Prioritisation based on defined policies
┌─────────────────────────────────────────────────────────────┐
│ Scheduling algorithm (kube-scheduler) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Pod Creation ──► Filtering ──► Scoring ──► Binding │ │
│ │ ▲ │ │ │
│ │ └───────────────── (Retry on Failure) ──┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ * Filtering: Excludes nodes without enough resources │
│ * Scoring: Rates remaining nodes against criteria │
│ * Binding: Assigns the pod to the best worker node │
│ │
└─────────────────────────────────────────────────────────────┘
Controller Manager (kube-controller-manager):
The controller manager implements the core logic of the Kubernetes control loop:
- Node controller: monitors node state
- Replication controller: ensures the desired number of pods is running
- Endpoints controller: links services to pods
- Service account and token controller: manages access tokens
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes reconciliation control loop │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Desired state (from manifest / etcd) │ │
│ └─────────────────────────┬─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Capture current state (via kubelet) │◄──┐ │
│ └─────────────────────────┬─────────────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌───────────────────────────────────────────────────┐ │ │
│ │ Compare and run actions (reconciliation) │───┘ │
│ └───────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
apiVersion: v1
kind: Pod
metadata:
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --allocate-node-cidrs=true
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=127.0.0.1
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --cluster-cidr=10.244.0.0/16
- --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
image: k8s.gcr.io/kube-controller-manager:v1.24.0
name: kube-controller-manager
Worker Node components
Kubelet:
The kubelet is the primary node agent:
- Pod lifecycle management
- Container health checks
- Volume mounting
- Node status reporting
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: "0.0.0.0"
port: 10250
serializeImagePulls: true
evictionHard:
memory.available: "100Mi"
nodefs.available: "10%"
nodefs.inodesFree: "5%"
Container runtime:
The container runtime (for example containerd) is responsible for:
- Container execution
- Image management
- Container isolation
version = 2
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "k8s.gcr.io/pause:3.6"
[plugins."io.containerd.grpc.v1.cri".containerd]
default_runtime_name = "runc"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
Kube-proxy:
Kube-proxy implements the Kubernetes Service concept:
- Service load balancing
- iptables rules
- Session affinity
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
bindAddress: 0.0.0.0
clientConnection:
acceptContentTypes: ""
burst: 10
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
qps: 5
clusterCIDR: 10.244.0.0/16
mode: "ipvs"
Installation and setup
💡 Information: The examples use Ubuntu/Debian as the sample distribution. The core concepts are the same on every Linux system, but package installation and some configuration paths can differ by distribution.
Prerequisites
Before the actual installation, it is important to understand what a Kubernetes cluster requires from hardware and software.
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes cluster architecture │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Control Plane (Master Node) │ │
│ │ ├── kube-apiserver ├── kube-scheduler │ │
│ │ ├── etcd (key-value store) └── kube-controller-mgr │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ TLS / gRPC connection │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Worker nodes (runtime for workloads) │ │
│ │ ├── Kubelet (node agent) ├── Container runtime │ │
│ │ └── kube-proxy (network) └── Pods / containers │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Hardware requirements:
Control Plane Node (Master):
- CPU: Minimum 2 cores, recommended 4 cores, production 8+ cores
- RAM: Minimum 2 GB, recommended 4 GB, production 16+ GB
- Disk: Minimum 50 GB, recommended 100 GB, production 250+ GB (SSD recommended)
Worker Nodes:
- CPU: Minimum 1 core, recommended 2 cores, production 4+ cores
- RAM: Minimum 1 GB, recommended 2 GB, production 8+ GB
- Disk: Minimum 20 GB, recommended 50 GB, production 100+ GB
💡 Important notes for planning:
- Plan cluster size according to your workloads
- Account for high-availability requirements
- Observe network-policy requirements
- Plan storage resources carefully
Software requirements:
Operating system:
# Check the Ubuntu version
lsb_release -a
# Minimum requirements:
# - Ubuntu 20.04 LTS or newer
# - Linux kernel 3.10 or higher
uname -r
System configuration:
# Check the required kernel modules
lsmod | grep -e br_netfilter -e overlay
# If they are not loaded, enable the modules:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
overlay
EOF
modprobe br_netfilter
modprobe overlay
Network configuration:
# Kernel parameters for Kubernetes
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# Activate the parameters
sysctl --system
# Verification
sysctl net.bridge.bridge-nf-call-iptables
sysctl net.bridge.bridge-nf-call-ip6tables
sysctl net.ipv4.ip_forward
Installation of the base components
System preparation:
# Update the system
sudo apt update
sudo apt upgrade -y
# Install required base packages
sudo apt install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release \
software-properties-common
⚠️ Important: After the system update you should check whether a reboot is required and make sure swap is disabled:
# Disable swap
sudo swapoff -a
# Comment out the swap entry in /etc/fstab
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# Check whether a reboot is required
if [ -f /var/run/reboot-required ]; then
echo 'Reboot required!'
fi
Container runtime installation:
Kubernetes needs a container runtime. We use containerd, because it is the current standard:
# Add the Docker repository
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install containerd
sudo apt update
sudo apt install -y containerd.io
Configuration of containerd:
# Create the default configuration
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# Enable SystemdCgroup
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# Restart containerd
sudo systemctl restart containerd
sudo systemctl enable containerd
💡 Best practices for containerd:
- Configure SystemdCgroup
- Set appropriate resource limits
- Enable log rotation
Kubernetes component installation
# Add the Kubernetes repository
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
# Install Kubernetes components
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
# Pin versions to prevent unwanted updates
sudo apt-mark hold kubelet kubeadm kubectl
⚠️ Important: Check the installed versions:
kubectl version --client
kubeadm version
kubelet --version
💡 Typical pitfalls:
- Forgetting to enable kernel modules
- Incorrect network configuration
- Insufficient resources
Cluster initialisation
Control Plane setup:
┌─────────────────────────────────────────────────────────────┐
│ Cluster initialisation and node join │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────┬─────────────────────────┐ │
│ │ Control Plane (Master) │ Worker nodes │ │
│ ├───────────────────────────┼─────────────────────────┤ │
│ │ • kubeadm init │ • kubeadm join │ │
│ │ • CNI plugin installation │ • Token authentication │ │
│ │ • Admin kubeconfig created│ • Register kubelet │ │
│ └───────────────────────────┴─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Cluster CNI network mesh (Calico / Flannel / Cilium) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
⚠️ Check before initialisation:
- Every prerequisite is met
- Enough resources are available
- Network ports are reachable
- Hostname and DNS are configured correctly
# Initialise the cluster
sudo kubeadm init \
--pod-network-cidr=192.168.0.0/16 \
--kubernetes-version=$(kubeadm version -o short) \
--control-plane-endpoint="$(hostname -I | awk '{print $1}')" \
--upload-certs
# Set up kubeconfig for the current user
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
💡 Important notes on cluster initialisation:
- Write down the
kubeadm joincommand printed at the end of initialisation- Back up certificates and token information
- The control plane node is not intended for workloads by default
Network plugin installation
We use Calico as the CNI plugin because of its flexibility and advanced network features:
# Install the Calico operator
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/tigera-operator.yaml
# Create Calico custom resources
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/custom-resources.yaml
Network policy configuration:
# Example of a basic network policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
⚠️ Typical pitfalls in network configuration:
- Overlapping pod CIDR ranges
- Missing network policies
- Incorrect MTU settings
- DNS problems
Check the installation:
# Check cluster status
kubectl get nodes
kubectl get pods --all-namespaces
kubectl cluster-info
# Check CNI status
kubectl get pods -n calico-system
Worker node integration
┌─────────────────────────────────────────────────────────────┐
│ Worker node integration workflow │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Preparation ──► Kernel modules and swap off │
│ │ │
│ ▼ │
│ 2. Container runtime ──► Install containerd │
│ │ │
│ ▼ │
│ 3. Join and taints ──► kubeadm join with token and CA │
│ │ │
│ ▼ │
│ 4. Validation ──► kubectl get nodes (Ready) │
│ │
└─────────────────────────────────────────────────────────────┘
Worker node preparation:
# System update and base package installation
sudo apt update && sudo apt upgrade -y
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
# Container runtime (containerd) installation
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y containerd.io
💡 Important preparation steps:
- Set a unique hostname
- Adjust firewall rules
- Check resources
- Configure SELinux/AppArmor
Worker node configuration:
# Configure containerd
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl enable containerd
# Install Kubernetes components
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
Worker node join:
# Generate a token on the control plane node
kubeadm token create --print-join-command
# Run on the worker node
sudo kubeadm join <control-plane-ip>:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
💡 Important notes:
- The join command must be run with root rights
- Tokens are valid for 24 hours by default
- If a token has expired, create a new one:
kubeadm token create --print-join-command
Node labels and taints:
# Node labelling for specific workloads
kubectl label node worker-1 workload=production
kubectl label node worker-2 workload=staging
# Taint for dedicated workloads
kubectl taint nodes worker-1 dedicated=production:NoSchedule
💡 Best practices for node management:
- Implement systematic naming conventions
- Use labels for workload segregation
- Plan node capacity carefully
- Implement node auto-scaling
Validation and troubleshooting:
# Check node status
kubectl get nodes -o wide
# Show node details
kubectl describe node worker-1
# System component status
kubectl get pods -n kube-system
Advanced network configuration
Network policies:
# Example of a basic network policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
Service network configuration:
# Check the service CIDR configuration
kubectl cluster-info dump | grep -m 1 service-cluster-ip-range
# Check CoreDNS status
kubectl get pods -n kube-system -l k8s-app=kube-dns
Load balancing:
# Example of a LoadBalancer service
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: example
Checking the installation
# Check node status
kubectl get nodes -o wide
# Check system pods
kubectl get pods --all-namespaces
# Cluster component status
kubectl get componentstatuses
# Test network functionality
kubectl run test-pod --image=busybox -- sleep 3600
kubectl exec test-pod -- ping -c 3 8.8.8.8
⚠️ Important: Document every configuration step you take and store important information such as tokens and certificates safely.
Practical implementation
This section covers the practical use of Kubernetes.
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes resource categories │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Workloads: Pods, Deployments, StatefulSets, DaemonS. │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ Discovery and routing: Services, Ingress, EndpointSl. │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ Storage: Volumes, PersistentVolumes, StorageClasses │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ Configuration: ConfigMaps, Secrets, ResourceQuotas │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Deployment strategies
Kubernetes offers several strategies for deploying applications.
💡 Best practices for deployments:
- Always set resource limits
- Define readiness/liveness probes
- Use labels for better organisation
- Plan update strategies carefully
Rolling updates:
Rolling updates are the default strategy in Kubernetes and allow updates without downtime:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: example
template:
metadata:
labels:
app: example
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
💡 Important notes on rolling updates:
- The parameters
maxSurgeandmaxUnavailabledetermine update speed- Readiness probes are critical for successful rolling updates
- Set resource limits and requests for predictable behaviour
Blue-green deployments:
Blue-green deployments enable low-risk updates through parallel environments:
# Blue Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: myapp
image: myapp:1.0
---
# Service for the blue-green switch
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
version: blue # Change to 'green' for the switch
ports:
- port: 80
targetPort: 8080
Canary deployments:
Canary deployments allow stepwise testing of new versions with a portion of the traffic:
# Stable Deployment (90% traffic)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-stable
spec:
replicas: 9
selector:
matchLabels:
app: myapp
version: stable
template:
metadata:
labels:
app: myapp
version: stable
spec:
containers:
- name: myapp
image: myapp:1.0
---
# Canary Deployment (10% traffic)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-canary
spec:
replicas: 1
selector:
matchLabels:
app: myapp
version: canary
template:
metadata:
labels:
app: myapp
version: canary
spec:
containers:
- name: myapp
image: myapp:2.0
⚠️ Important for canary deployments:
- Implement thorough monitoring
- Plan rollback strategies
- Use a service mesh for fine-grained traffic control
Service and Ingress configuration
┌─────────────────────────────────────────────────────────────┐
│ External traffic flow in the cluster │
├─────────────────────────────────────────────────────────────┤
│ │
│ External user request (HTTPS) │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Ingress controller (Nginx / Traefik / Envoy) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ Host / path routing │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Kubernetes Service (ClusterIP / NodePort) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ Round-robin load balancing │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Pods (application containers with a local IP) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Service types:
💡 Important: Choose the service type based on your requirements:
- ClusterIP: Internal
- NodePort: Directly via node ports
- LoadBalancer: Cloud-provider integration
# ClusterIP (default)
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP
selector:
app: backend
ports:
- port: 80
targetPort: 8080
---
# NodePort
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: NodePort
selector:
app: frontend
ports:
- port: 80
targetPort: 80
nodePort: 30080
---
# LoadBalancer
apiVersion: v1
kind: Service
metadata:
name: public-service
spec:
type: LoadBalancer
selector:
app: public
ports:
- port: 80
targetPort: 8080
Ingress configuration:
Ingress enables layer-7 routing and SSL termination:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
💡 Best practices:
- Configure SSL/TLS
- Set rate limiting
- Implement path-based routing
- Use meaningful annotations
Service mesh integration:
# Virtual Service (Istio)
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews-route
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1
Storage management
Persistent Volumes:
┌─────────────────────────────────────────────────────────────┐
│ Persistent storage architecture │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ StorageClass (dynamic provisioning / CSI driver) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ Provisioning │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ PersistentVolume (PV — real storage resource) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ Bind (claim) │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ PersistentVolumeClaim (PVC) ──► Pod volume mount │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: standard
hostPath:
path: /mnt/data
---
# Persistent Volume Claim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: example-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard
Storage Classes:
# Definition of a Storage Class for SSD storage
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
iopsPerGB: "10"
encrypted: "true"
reclaimPolicy: Retain
allowVolumeExpansion: true
💡 Important notes on Storage Classes:
- The provisioner depends on your cloud platform or storage solution
- Parameters vary by provisioner
- The reclaimPolicy determines what happens to the volume after the PVC is deleted
Volume snapshots:
# Volume Snapshot Class
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-hostpath-snapclass
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
---
# Volume Snapshot
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: data-snapshot
spec:
volumeSnapshotClassName: csi-hostpath-snapclass
source:
persistentVolumeClaimName: example-pvc
Encrypted storage:
# Secret for the encryption key
apiVersion: v1
kind: Secret
metadata:
name: luks-key
type: Opaque
data:
key: <base64-encoded-key>
---
# Encrypted PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
name: encrypted-pv
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
encryption:
secretRef:
name: luks-key
hostPath:
path: /mnt/encrypted-data
⚠️ Important security notes:
- Encryption keys must be managed securely
- Implement regular backup strategies
- Configure access rights carefully
Best practices for storage management:
- Capacity planning: Implement proactive monitoring, plan growth ahead, set sensible quotas.
- Backup strategies: Regular snapshots, off-site backups, disaster recovery tests.
- Performance optimisation: Choose matching storage classes, optimise chunk sizes, implement caching where it makes sense.
Monitoring and maintenance
Monitoring and maintenance of a Kubernetes cluster are decisive for stable operation.
┌─────────────────────────────────────────────────────────────┐
│ Monitoring and maintenance overview │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────┬─────────────────────────┐ │
│ │ Monitoring and observability│ Cluster maintenance │ │
│ ├───────────────────────────┼─────────────────────────┤ │
│ │ • Node and pod metrics │ • kubeadm upgrade │ │
│ │ • Prometheus alerts │ • Node drain and cordon │ │
│ │ • Log aggregation (Loki) │ • etcd snapshot backups │ │
│ │ • Tracing (Jaeger) │ • Certificate update │ │
│ └───────────────────────────┴─────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Monitoring architecture
┌─────────────────────────────────────────────────────────────┐
│ Prometheus and Grafana monitoring stack │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Grafana dashboards (visualisation and alert panels) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ PromQL queries │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Prometheus server (metrics scraping and TSDB) │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ ├── Node-Exporter ├── cAdvisor (container) │ │
│ │ └── kube-state-metrics └── Alertmanager (notif.) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Prometheus installation
# Create the namespace
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
---
# Prometheus Operator installation
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-operator
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: prometheus-operator
template:
metadata:
labels:
app: prometheus-operator
spec:
containers:
- name: prometheus-operator
image: quay.io/prometheus-operator/prometheus-operator:v0.59.1
args:
- --kubelet-service=kube-system/kubelet
- --config-reloader-image=jimmidyson/configmap-reload:v0.5.0
💡 Important notes on Prometheus installation:
- Make sure enough resources are available
- Configure retention policies according to your requirements
- Plan the scaling of storage requirements
Monitoring configuration
Service monitoring:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: app-monitor
namespace: monitoring
spec:
selector:
matchLabels:
app: myapp
endpoints:
- port: metrics
interval: 15s
path: /metrics
Alert rules:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: node-alerts
namespace: monitoring
spec:
groups:
- name: node.rules
rules:
- alert: HighCPUUsage
expr: node_cpu_usage_percentage > 80
for: 5m
labels:
severity: warning
annotations:
description: "CPU usage is above 80% for 5 minutes"
Alerting and notifications
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: kubernetes-alerts
namespace: monitoring
spec:
groups:
- name: kubernetes.rules
rules:
- alert: KubernetesPodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[15m]) * 60 * 5 > 0
for: 15m
labels:
severity: warning
annotations:
description: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping"
summary: "Pod is crash looping"
💡 Important notes on alerting:
- Define sensible thresholds
- Avoid alert fatigue from too many notifications
- Implement escalation paths
Resource quotas monitoring:
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
Log aggregation:
┌─────────────────────────────────────────────────────────────┐
│ Log aggregation architecture │
├─────────────────────────────────────────────────────────────┤
│ │
│ Log sources: pods (stdout/stderr) and node systemd logs │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Log collector DaemonSet (Promtail / Fluentd / Vector) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ Batch push (JSON/Loki) │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Central log store (Grafana Loki / OpenSearch) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ LogQL / dashboards │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Analysis, alerts and incident response in Grafana │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Maintenance strategies
Cluster updates:
┌─────────────────────────────────────────────────────────────┐
│ Rolling update workflow for cluster nodes │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Preparation ──► Create etcd snapshot and plan │
│ │ │
│ ▼ │
│ 2. Node cordon ──► kubectl cordon (stop scheduling) │
│ │ │
│ ▼ │
│ 3. Node drain ──► kubectl drain (evacuate) │
│ │ │
│ ▼ │
│ 4. Node upgrade ──► kubeadm upgrade and kubelet restart │
│ │ │
│ ▼ │
│ 5. Node uncordon──► kubectl uncordon (release) │
│ │
└─────────────────────────────────────────────────────────────┘
Update procedure for the control plane:
# 1. Back up important components
ETCD_BACKUP_DIR="/backup/etcd-$(date +%Y%m%d)"
mkdir -p $ETCD_BACKUP_DIR
# Create an etcd snapshot
kubectl exec -n kube-system etcd-master-1 -- etcdctl snapshot save \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
$ETCD_BACKUP_DIR/etcd-snapshot.db
⚠️ Important notes on the update process:
- Always run updates in a test environment first
- Plan sufficient maintenance windows
- Keep rollback procedures ready
- Document every step carefully
Backup and recovery
┌─────────────────────────────────────────────────────────────┐
│ Backup strategy for Kubernetes │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 1. etcd data backup (etcdctl snapshot save) │ │
│ │ * Saves cluster state, secrets, CRDs and status │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ 2. Declarative manifests (GitOps / ArgoCD / Helm) │ │
│ │ * Infrastructure as code in the Git repository │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ 3. Persistent data (Velero / CSI volume snapshots) │ │
│ │ * Backs up databases and filesystems on PVs │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
#!/bin/bash
# Backup script for critical cluster components
BACKUP_DIR="/backup/k8s-$(date +%Y%m%d)"
RETENTION_DAYS=30
mkdir -p "${BACKUP_DIR}"/{etcd,manifests,secrets}
# etcd backup
etcdctl snapshot save "${BACKUP_DIR}/etcd/snapshot.db"
# Back up Kubernetes manifests
kubectl get all --all-namespaces -o yaml > \
"${BACKUP_DIR}/manifests/all-resources.yaml"
# Back up secrets (encrypted)
kubectl get secrets --all-namespaces -o yaml | \
gpg --encrypt > "${BACKUP_DIR}/secrets/all-secrets.yaml.gpg"
# Clean old backups
find /backup -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} +
Security and best practices
The security of a Kubernetes cluster is of decisive importance.
RBAC (Role-Based Access Control)
┌─────────────────────────────────────────────────────────────┐
│ RBAC role and rights structure │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Subjects (users, groups and ServiceAccounts) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ Binding via RoleBinding │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Role (namespace-specific: read/write pods) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ Binding via ClusterRoleBinding │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ ClusterRole (cluster-wide: nodes, namespaces, PVs) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
# Namespace-specific role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: development
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
# Cluster-wide role
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-reader-global
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
💡 Important RBAC principles:
- Principle of least privilege
- Regular audit reviews
- Documentation of every role and permission
Pod Security Policies
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
spec:
privileged: false
seLinux:
rule: RunAsAny
runAsUser:
rule: MustRunAsNonRoot
fsGroup:
rule: RunAsAny
volumes:
- 'configMap'
- 'emptyDir'
- 'persistentVolumeClaim'
Network Policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Secrets management
# Secret definition
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
db-password: BASE64_ENCODED_PASSWORD
api-key: BASE64_ENCODED_API_KEY
💡 Best practices for secrets:
- Encryption at rest
- Regular rotation
- Access restriction
- Audit logging
Container security
┌─────────────────────────────────────────────────────────────┐
│ Container security layered model │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Build phase: minimal base images and Trivy scanning │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ Registry phase: Cosign image signing and admission │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ Runtime phase: non-root user and read-only rootfs │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ Network phase: strict default-deny NetworkPolicies │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Cluster hardening
# Pod Security Policy
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
spec:
privileged: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
fsGroup:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
Audit logging
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["pods", "services"]
- level: Metadata
resources:
- group: ""
resources: ["configmaps"]
- level: None
users: ["system:kube-proxy"]
resources:
- group: ""
resources: ["endpoints"]
Automated security checks
# kube-bench job for automated CIS checks
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
spec:
template:
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench", "--benchmark", "cis-1.6"]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
- name: etc-systemd
mountPath: /etc/systemd
- name: etc-kubernetes
mountPath: /etc/kubernetes
restartPolicy: Never
volumes:
- name: var-lib-kubelet
hostPath:
path: "/var/lib/kubelet"
- name: etc-systemd
hostPath:
path: "/etc/systemd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"
Advanced concepts
Custom Resources
Custom Resources (CRs) extend the Kubernetes API with user-defined resource types.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: webapps.mycompany.com
spec:
group: mycompany.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
image:
type: string
replicas:
type: integer
minimum: 1
scope: Namespaced
names:
plural: webapps
singular: webapp
kind: WebApp
shortNames:
- wa
Operators
Operators automate the management of complex applications in Kubernetes.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-operator
spec:
replicas: 1
selector:
matchLabels:
name: mysql-operator
template:
metadata:
labels:
name: mysql-operator
spec:
containers:
- name: operator
image: mysql-operator:v1.0
ports:
- containerPort: 8080
env:
- name: WATCH_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
Service Mesh
┌─────────────────────────────────────────────────────────────┐
│ Service mesh architecture (Istio / Linkerd) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Control plane (Istiod: configuration and certificates)│ │
│ └───────────────────────────────────────────────────────┘ │
│ │ mTLS and routing rules │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Data plane: sidecar proxies (Envoy beside every pod) │ │
│ │ ├── Service A (Pod) ◄──► Envoy sidecar proxy │ │
│ │ │ │ Encrypted (mTLS) │ │
│ │ └── Service B (Pod) ◄──► Envoy sidecar proxy │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Exercise
This exercise deploys a web application with all important Kubernetes concepts.
Exercise goals
- Create a deployment with several replicas
- Configure a service and ingress
- Implement monitoring
- Apply security policies
Step 1: Create the deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 3
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Step 2: Configure the service
apiVersion: v1
kind: Service
metadata:
name: webapp-service
spec:
selector:
app: webapp
ports:
- port: 80
targetPort: 80
type: ClusterIP
Step 3: Set up Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-ingress
spec:
rules:
- host: webapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp-service
port:
number: 80
Step 4: Enable monitoring
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: webapp-monitor
spec:
selector:
matchLabels:
app: webapp
endpoints:
- port: metrics
Step 5: Apply a security policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: webapp-policy
spec:
podSelector:
matchLabels:
app: webapp
ingress:
- from:
- podSelector:
matchLabels:
access: allowed
Checking the exercise
# Check deployment status
kubectl get deployments
kubectl get pods
# Check service and ingress
kubectl get services
kubectl get ingress
# Test access
curl -H "Host: webapp.example.com" http://CLUSTER-IP
Command Reference (Cheatsheet)
For quick access in day-to-day operations, the following reference table summarises the most important Kubernetes commands for cluster management, fault diagnosis and resource scaling:
| Command / syntax | Description and practical use |
|---|---|
kubectl get nodes -o wide |
Lists every cluster node including status, version, kernel and internal IP. |
kubectl get pods -A |
Shows every pod across all namespaces plus status and restart counters. |
kubectl describe pod <pod-name> |
Detailed fault diagnosis with events, container status and exit codes. |
kubectl logs -f <pod-name> [-c <container>] |
Streams live logs of a pod or a specific sidecar container. |
kubectl exec -it <pod-name> -- /bin/sh |
Opens an interactive shell in the container for live inspection. |
kubectl apply -f <manifest.yaml> |
Declarative rollout or update of Kubernetes resources. |
kubectl rollout status deployment/<name> |
Monitors the progress of a rolling update in real time. |
kubectl rollout undo deployment/<name> |
Performs an immediate rollback to the previous revision. |
kubectl get services,ingress |
Shows active services, ClusterIPs, NodePorts and Ingress routes. |
kubectl get pv,pvc |
Overview of PersistentVolumes and their bind status to claims. |
kubectl top nodes && kubectl top pods |
Shows current CPU and memory use of nodes and pods. |
kubectl cordon <node> && kubectl drain <node> |
Blocks a node for new pods and evacuates existing workloads safely. |
kubectl uncordon <node> |
Releases a maintained worker node for scheduling again. |
kubeadm init --pod-network-cidr=192.168.0.0/16 |
Initialises the Kubernetes control plane with a CNI network range. |
kubeadm join <master>:6443 --token <t> --discovery-token-ca-cert-hash <h> |
Joins a worker node to the existing cluster. |
kubeadm upgrade plan |
Checks available versions and shows upgrade steps for the control plane. |
etcdctl snapshot save snapshot.db |
Creates a consistent point-in-time snapshot of etcd state. |
Further Resources
The following official documentation, tutorials and community projects go deeper into the concepts and best practices covered here:
| Resource | Description |
|---|---|
| Kubernetes documentation | Official and complete documentation of every K8s component and API. |
| Kubernetes tutorials | Practice-oriented walkthroughs for deployments, services and scaling. |
| Kubernetes blog | Current announcements, release notes and in-depth architecture pieces. |
| Interactive basics tutorial | Interactive introduction to cluster creation and workload management. |
| Minikube getting started | Official reference for running single-node clusters locally. |
| Kubernetes training labs | CNCF training materials and interactive exercise environments. |
| Kubernetes GitHub repository | Source code, issue tracker and technical design proposals (KEPs). |
| Kubernetes community | Forums, special interest groups (SIGs) and Slack channels of the community. |
| CNCF Kubernetes project | Governance, ecosystem overview and graduation status within the CNCF. |
| kubectl cheat sheet | Official reference of every kubectl option and JSONPath query. |
| Helm package manager | Package management for Kubernetes for templating and distributing apps. |
| Lens Kubernetes IDE | Powerful graphical user interface for cluster management. |
💡 Note: The Kubernetes landscape evolves constantly. Always consult the latest documentation.
Conclusion
Kubernetes has become the de-facto standard for container orchestration and provides a robust platform for modern cloud-native applications. The concepts and practices covered here show the versatility and capability of Kubernetes. From basic installation through practical implementation to advanced concepts such as Custom Resources and a service mesh, Kubernetes supplies the tools needed to operate complex container environments.
Continuous development of Kubernetes, especially in automation and security, keeps the platform viable. With growing adoption of cloud-native technologies and the rising importance of DevOps practices, Kubernetes will continue to play a central role in modern IT infrastructure. New developments such as GitOps, KubeVirt for VM management and tighter integration of AI-assisted operations will make the platform even more capable.
👉 Overview: All DevOps articles