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

EnvironmentToolsUse Case
Local / POCKind, Minikube, K3sLearning, CI/CD, quick testing
Managed CloudEKS, AKS, GKEProduction — cloud manages control plane
Self-Managed VMskubeadm + cloud VMsFull control over every component
Self-Managed Bare Metalkubeadm + physical serversOn-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:

PortComponentScope
6443kube-apiserverEntire VPC / admin IP
10248–10260Control plane components (scheduler, controller-manager, etc.)Internal VPC only
2379–2380etcd client & peerInternal VPC only
22SSHAdmin IP only

Worker Nodes:

PortComponentScope
10250kubelet APIInternal VPC
10256kube-proxyInternal VPC
30000–32767NodePort service rangeExternal (for app exposure)
22SSHAdmin 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/fstab

2. 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 --system

3. 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 active

4. Install runc

wget https://github.com/opencontainers/runc/releases/download/v1.1.9/runc.amd64
sudo install -m 755 runc.amd64 /usr/local/sbin/runc

5. 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.tgz

6. 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 upgrades

7. Configure crictl

sudo crictl config runtime-endpoint unix:///var/run/containerd/containerd.sock
sudo chmod -R 775 /var/run/containerd

Control 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=master

Critical: The --pod-network-cidr must match your CNI’s default IP pool. Calico uses 192.168.0.0/16 by default. If you use a different CIDR (e.g., Flannel’s 10.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/config

Install 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.yaml

Wait for Calico pods:

kubectl get pods -n calico-system -w

Verify Control Plane

kubectl get nodes
# NAME     STATUS   ROLES           AGE   VERSION
# master   Ready    control-plane   2m    v1.30.2

Worker 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-command

Verify 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.2

Troubleshooting

SymptomCauseFix
Nodes stuck NotReady after kubeadm initCNI not installed or Pod CIDR mismatchInstall matching CNI; ensure --pod-network-cidr aligns with CNI config
CoreDNS pods stuck ContainerCreatingCalico IP pool mismatchkubeadm reset, then re-run kubeadm init with correct --pod-network-cidr
kubeadm join times outSecurity group / firewall blocking 6443Verify worker → control plane connectivity on port 6443
crictl ps fails with permission errorMissing socket permissionssudo chmod -R 775 /var/run/containerd
kubectl fails on worker nodeNo kubeconfigCopy /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 kubeconfig

CKA 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) and kubeadm 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

Creator / Entity


Ingested: 2026-07-06