Day 27/40 — Setup a Multi Node Kubernetes Cluster Using Kubeadm
Overview
Day 27 of the 40-day CKA course walks through production-grade Kubernetes cluster installation using kubeadm — the official cluster bootstrapping tool. The video sets up a real multi-node cluster on cloud VMs (AWS EC2), covering control plane initialization, worker node joining, CNI installation, firewall rules, and common troubleshooting.
Installation Options Landscape
| Environment | Tools | Use Case |
|---|---|---|
| Local / POC | Kind, Minikube, K3s | Learning, CI/CD, quick testing |
| Managed Cloud | EKS, AKS, GKE | Production — cloud manages control plane |
| Self-Managed VMs | kubeadm + cloud VMs | Full control over every component |
| Self-Managed Bare Metal | kubeadm + physical servers | On-premise data centers |
For CKA, you must know kubeadm because the exam tests cluster installation, upgrades, and certificate management.
Prerequisites & Ports
VMs Required
- 1 control plane node (minimum 2 GB RAM, 2 vCPUs)
- 2 worker nodes (can be smaller, e.g., 1 GB RAM, 1 vCPU)
Firewall / Security Group Rules
Control Plane Node:
| Port | Component | Scope |
|---|---|---|
| 6443 | kube-apiserver | Entire VPC / admin IP |
| 10248–10260 | Control plane components (scheduler, controller-manager, etc.) | Internal VPC only |
| 2379–2380 | etcd client & peer | Internal VPC only |
| 22 | SSH | Admin IP only |
Worker Nodes:
| Port | Component | Scope |
|---|---|---|
| 10250 | kubelet API | Internal VPC |
| 10256 | kube-proxy | Internal VPC |
| 30000–32767 | NodePort service range | External (for app exposure) |
| 22 | SSH | Admin IP only |
etcd ports (2379–2380) should never be exposed to the public internet — they are internal to the control plane.
Common Steps on ALL Nodes
Run these on the control plane AND every worker node.
1. Disable Swap
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab2. Update Kernel Parameters
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
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
sudo sysctl --system3. Install Container Runtime (containerd)
# Download and extract containerd
wget https://github.com/containerd/containerd/releases/download/v1.7.4/containerd-1.7.4-linux-amd64.tar.gz
sudo tar Cxzvf /usr/local containerd-1.7.4-linux-amd64.tar.gz
# Install containerd as systemd service
sudo wget -O /etc/systemd/system/containerd.service https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl daemon-reload
sudo systemctl enable --now containerd
sudo systemctl status containerd # verify active4. Install runc
wget https://github.com/opencontainers/runc/releases/download/v1.1.9/runc.amd64
sudo install -m 755 runc.amd64 /usr/local/sbin/runc5. Install CNI Plugins
wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz
sudo mkdir -p /opt/cni/bin
sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.3.0.tgz6. Install kubeadm, kubelet, kubectl
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl # prevent accidental upgrades7. Configure crictl
sudo crictl config runtime-endpoint unix:///var/run/containerd/containerd.sock
sudo chmod -R 775 /var/run/containerdControl Plane Specific Steps
Initialize the Cluster
sudo kubeadm init \
--pod-network-cidr=192.168.0.0/16 \
--apiserver-advertise-address=<CONTROL_PLANE_PRIVATE_IP> \
--node-name=masterCritical: The
--pod-network-cidrmust match your CNI’s default IP pool. Calico uses192.168.0.0/16by default. If you use a different CIDR (e.g., Flannel’s10.244.0.0/16), Calico will fail to reconcile.
Set Up kubeconfig for kubectl
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/configInstall Calico CNI
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/custom-resources.yamlWait for Calico pods:
kubectl get pods -n calico-system -wVerify Control Plane
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# master Ready control-plane 2m v1.30.2Worker Node Specific Steps
After completing the Common Steps on a worker node, join it to the cluster:
sudo kubeadm join <CONTROL_PLANE_PRIVATE_IP>:6443 \
--token <TOKEN> \
--discovery-token-ca-cert-hash sha256:<HASH>The kubeadm join command is printed at the end of kubeadm init on the control plane. If you lose it, regenerate:
# On control plane
kubeadm token create --print-join-commandVerify from Control Plane
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.2Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Nodes stuck NotReady after kubeadm init | CNI not installed or Pod CIDR mismatch | Install matching CNI; ensure --pod-network-cidr aligns with CNI config |
CoreDNS pods stuck ContainerCreating | Calico IP pool mismatch | kubeadm reset, then re-run kubeadm init with correct --pod-network-cidr |
kubeadm join times out | Security group / firewall blocking 6443 | Verify worker → control plane connectivity on port 6443 |
crictl ps fails with permission error | Missing socket permissions | sudo chmod -R 775 /var/run/containerd |
kubectl fails on worker node | No kubeconfig | Copy /etc/kubernetes/admin.conf from control plane to ~/.kube/config on worker |
kubeadm Reset (Nuclear Option)
If you need to start over on a node:
sudo kubeadm reset
# Removes all control plane containers, static pod manifests, and kubeconfigCKA Exam Relevance
- Cluster installation with kubeadm is a core CKA domain (~25% of the exam)
- Know the exact ports for control plane (6443, 2379–2380, 10248–10260) and worker nodes (10250, 10256, 30000–32767)
- Know that swap must be disabled before kubeadm will proceed
- Know the difference between
kubeadm init(control plane) andkubeadm join(workers) - Know how to regenerate a join token with
kubeadm token create --print-join-command - Know that containerd is the default runtime post-Kubernetes 1.24 (Docker shim removed)
See Also
Wiki Concepts
- Kubeadm Cluster Setup — Deep-dive into installation options, port matrix, common vs node-specific steps, CNI alignment, and production best practices
- Kubernetes Architecture — How kubeadm generates Static Pod manifests for control plane bootstrapping
- Kubernetes Static Pods — The mechanism that starts control plane components before the API server exists
- Kind Cluster Setup — Local cluster for learning; kubeadm is the production counterpart
- Kubernetes Kubeconfig — How
admin.confbecomes the default kubectl context - Kubernetes Network Policies — Calico installation and CNI selection
Related Sources
- CKA Day 5 — Kubernetes Architecture Explained — Control plane and worker node components
- CKA Day 6 — Kind Multi-Node Cluster Setup — Local dev alternative to kubeadm
- CKA Day 13 — Static Pods, Manual Scheduling, Labels, and Selectors — How kubeadm uses Static Pods for bootstrapping
- CKA Day 21 — Manage TLS Certificates In a Kubernetes Cluster — kubeadm PKI and certificate management
- CKA Day 26 — Kubernetes Network Policies Explained — Calico CNI setup
Creator / Entity
- Tech Tutorials with Piyush — CKA 40-day course creator
Ingested: 2026-07-06