Installing a production-grade multi-node Kubernetes cluster from scratch using kubeadm — the official cluster bootstrapping tool. This is the standard method for self-managed clusters on VMs, bare metal, or private cloud, and a core topic in the CKA exam (~25% weight).Source: CKA Day 27
Installation Options Landscape
Kubernetes can be installed in many ways depending on your environment and control requirements:
Category
Tools
Best For
Local / Learning
Kind, Minikube, K3s
POCs, CI/CD, CKA practice without cloud cost
Managed Cloud
EKS (AWS), AKS (Azure), GKE (GCP)
Production where the cloud provider manages the control plane
Self-Managed VMs
kubeadm + cloud VMs (EC2, Azure VM, GCP CE)
Full control, on-premise, cost optimization, CKA preparation
Self-Managed Bare Metal
kubeadm + physical servers
Data centers, edge deployments, air-gapped environments
When to choose kubeadm: You need full control over control plane components, upgrades, etcd, and certificates. The CKA exam assumes this path.
CKA Exam Trap: The official Kubernetes docs list control plane and worker ports separately. Remember that etcd (2379–2380) is control-plane-only and NodePort range (30000–32767) is worker-only.
Common Steps on ALL Nodes
These steps run identically on the control plane and every worker node.
1. Disable Swap
Kubernetes requires swap to be off. kubeadm will fail preflight checks otherwise.
sudo swapoff -asudo sed -i '/ swap / s/^/#/' /etc/fstab
Version alignment: Keep kubeadm, kubelet, and kubectl at the same minor version as the cluster. Mixing 1.30 control plane with 1.28 kubelet can cause compatibility issues.
kubectl get pods -n calico-system -wkubectl get nodes
Pod CIDR Alignment Trap: Calico’s default IP pool is 192.168.0.0/16. If you pass --pod-network-cidr=10.244.0.0/16 to kubeadm init, Calico will fail to reconcile and CoreDNS will hang in ContainerCreating. The fix is kubeadm reset and re-initializing with the correct CIDR.
Join Worker Nodes
On each worker, after completing the Common Steps, run the join command printed by kubeadm init:
# On control planekubeadm token create --print-join-command
Verify Cluster
kubectl get nodes# NAME STATUS ROLES AGE VERSION# master Ready control-plane 10m v1.30.2# worker-1 Ready <none> 3m v1.30.2# worker-2 Ready <none> 1m v1.30.2
kubeadm Commands Reference
Command
Purpose
kubeadm init
Bootstraps a control plane node
kubeadm join
Adds a worker node to an existing cluster
kubeadm reset
Tears down kubeadm state on a node (containers, manifests, certificates)
kubeadm token create --print-join-command
Generates a fresh join token and command
kubeadm certs check-expiration
Lists certificate expiry dates
kubeadm certs renew all
Renews all cluster certificates
kubeadm upgrade plan
Shows available upgrade paths
kubeadm upgrade apply v1.31.0
Performs a cluster upgrade
Troubleshooting Matrix
Symptom
Root Cause
Fix
kubeadm init fails preflight
Swap is enabled
sudo swapoff -a and comment out /etc/fstab swap line
Node stays NotReady
CNI not installed or misconfigured
Install matching CNI; check kubectl get pods -n kube-system
CoreDNS ContainerCreating indefinitely
Calico IP pool mismatch with --pod-network-cidr
kubeadm reset → re-run kubeadm init with correct CIDR
kubeadm join hangs / times out
Security group blocks 6443 or worker → master routing broken
Verify TCP 6443 reachable from worker; check VPC routing tables
crictl ps permission denied
containerd socket lacks permissions
sudo chmod -R 775 /var/run/containerd
kubectl fails on worker node
No kubeconfig present
Copy admin.conf from control plane to ~/.kube/config
Control plane pods crashloop
Certificate expired
kubeadm certs renew all and restart static pods
Production Hardening Checklist
Restrict etcd ports to internal VPC only (never public)
Restrict SSH (22) to bastion/jump host IP ranges
Use specific versions for kubeadm/kubelet/kubectl; avoid “latest”
Pin packages with apt-mark hold to prevent accidental upgrades
Back up certificates in /etc/kubernetes/pki before any change
Enable audit logging on the API server
Set up HA control plane (3+ control plane nodes + stacked or external etcd) for production
Configure ETCD backup (hourly snapshots with etcdctl, off-site to encrypted object storage)
Test ETCD restore quarterly on a non-production cluster to validate disaster recovery
Regular certificate monitoring — kubeadm certs expire after 1 year by default
CKA Exam Patterns
Bootstrap a cluster: Given 3 VMs, install containerd, kubeadm, kubelet, run kubeadm init, install CNI, join workers
Port knowledge: Know which ports are control plane vs worker vs internal-only
Troubleshoot join failures: Check firewall, token validity, CA hash correctness
Certificate management: Renew expired certs with kubeadm certs renew all
Upgrade a cluster:kubeadm upgrade plan → drain node → upgrade packages → kubeadm upgrade apply. See Kubernetes Cluster Upgrade for the full rolling-update workflow and version-skew rules.
Know the default runtime: containerd (not Docker) since Kubernetes 1.24
Practical Practice
Exam-style hands-on tasks for this topic. Complete each task before reviewing the solution. Time yourself — CKA tasks average 5–7 minutes.
Task 1: Initialize a Control Plane
Initialize a control plane with kubeadm init using pod network CIDR 10.244.0.0/16.
Requirements: Disable swap first. Use the correct CIDR for your CNI.
Verification:kubectl get nodesSolution:
Task 2: Join a Worker Node
Join a worker node to the cluster using the bootstrap token.
Requirements: Run common steps (containerd, kubelet) on the worker first.
Verification:kubectl get nodesSolution:
# On control plane, generate join commandkubeadm token create --print-join-command# On worker:sudo kubeadm join <CP_IP>:6443 --token <TOKEN> --discovery-token-ca-cert-hash sha256:<HASH># On control plane:kubectl get nodes
Task 3: Troubleshoot Join Failure
A worker node fails to join — verify the token is valid and the API server is reachable on port 6443.
Requirements: Use kubeadm token list and curl from the worker.
Verification:kubectl get nodesSolution:
# On control planekubeadm token list# On workercurl -vk https://<CP_IP>:6443/healthz# If token expired, create new onekubeadm token create --print-join-command# Re-run join on worker
See Also
Kubernetes Architecture — How kubeadm uses Static Pod manifests to bootstrap the control plane
Kubernetes Static Pods — The node-local mechanism that starts control plane containers before the API server exists
Kind Cluster Setup — Local dev alternative; kubeadm is the production equivalent
Kubernetes Kubeconfig — Context switching, certificate auth, and the admin.conf file