Home

Setting up a Kubernetes cluster with kubeadm

A walkthrough for setting up a 2-node Kubernetes cluster using kubeadm. Both nodes here are Ubuntu Server 20.04 LTS. The same flow works for larger clusters — you just join more workers at the end.

Prep both nodes

Disable swap

Kubernetes requires swap to be off. There's a flag to ignore swap, but it's not recommended. Disable swap on every node in the cluster:

bash
swapoff -asudo rm /swap.imgswapon --show

To make it permanent, also remove the swap entry from /etc/fstab.

Network and iptables

Load br_netfilter and tell iptables to see bridged traffic. (See the official kubeadm install docs for the canonical version.) Run on both nodes:

bash
cat <<EOF | sudo tee /etc/modules-load.d/k8s.confbr_netfilterEOFcat <<EOF | sudo tee /etc/sysctl.d/k8s.confnet.bridge.bridge-nf-call-ip6tables = 1net.bridge.bridge-nf-call-iptables = 1EOF

Install containerd

Containerd is the container runtime Kubernetes will talk to.

bash
cat <<EOF | sudo tee /etc/modules-load.d/containerd.confoverlaybr_netfilterEOFmodprobe overlaymodprobe br_netfiltercat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.confnet.bridge.bridge-nf-call-iptables  = 1net.ipv4.ip_forward                 = 1net.bridge.bridge-nf-call-ip6tables = 1EOFsysctl --systemapt-get updateapt-get upgrade -yapt-get install containerd -ymkdir -p /etc/containerdcontainerd config default | tee /etc/containerd/config.tomlsystemctl restart containerd

Install kubeadm, kubelet, kubectl

bash
apt-get updateapt-get install -y apt-transport-https ca-certificates curlcurl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpgecho "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.listapt-get updateapt-get install -y kubelet kubeadm kubectlapt-mark hold kubelet kubeadm kubectl

apt-mark hold pins the versions so a routine apt upgrade doesn't yank Kubernetes out from under you.

The repo URL apt.kubernetes.io was retired in 2023 — newer installs should use pkgs.k8s.io with a per-minor-version repo. Check the current docs for the latest paths.

Init the control-plane node

Run this on the master only — not on the workers.

Pull the images first:

bash
kubeadm config images pull

Now bring up the control plane:

bash
kubeadm init --pod-network-cidr=172.16.0.0/16 \  --apiserver-advertise-address=192.168.10.40 \  --control-plane-endpoint=192.168.10.40

What the flags mean:

Configure kubectl access

bash
mkdir -p $HOME/.kubecp -i /etc/kubernetes/admin.conf $HOME/.kube/configchown $(id -u):$(id -g) $HOME/.kube/config

Verify:

bash
kubectl get nodes

Join the worker

kubeadm init printed a kubeadm join command at the end of its output. Copy that and run it on the worker node — it'll register and join the cluster.

If you've lost the join command, regenerate it on the master with kubeadm token create --print-join-command.

Install a CNI plugin

Out of the box, the nodes are NotReady because there's no pod network. Install Calico:

bash
curl https://docs.projectcalico.org/manifests/calico-typha.yaml -o calico.yamlkubectl apply -f calico.yaml

Once Calico's pods are up, the node statuses flip to Ready and the cluster is usable.

That's it 🙂

References