DEV Community

Xavier Alexander
Xavier Alexander

Posted on

Setting Up A 3-Node Kubernetes Cluster

A tiny computer

Six months ago, I started a new job as a DevSecOps engineer. During the interview process the team gave me a heads up about what tools they used. I was excited to learn Kubernetes was one of them. I thought to myself, "Cool, I'll be ready. I've done a few tutorials on this." But after the first few days,I quickly realized I was wrong—I was not ready 😂. I wasn’t just dealing with single-container pods like in the tutorials. I was now faced with terms like Ingress, CRDs, and PVCs… none of it made sense. The days of simple walkthroughs were long gone. I needed a homelab to start building for real. I ordered the following beelink, installed proxmox, and hit the ground running.

In this post I'll be walking through how I set up a 3 node kubernetes cluster

Requirements

  • 3 VMs
  • 2 GB or more of RAM per machine
  • 2 CPUs or more for control plane machines.
  • OS - I'm using Rocky Linux

On Every Node

The following should be completed on every node.

Disable Swap

Explanation:

The default behavior of a kubelet is to fail to start if swap memory is detected on a node. This means that swap should either be disabled or tolerated by kubelet.

In /etc/fstab comment out the line that declares swap.

#/dev/mapper/rl-swap     none                    swap    defaults        0 0
Enter fullscreen mode Exit fullscreen mode

Confirm by running the free command. You should see 0s for the Swap row.

[xavier@lab-cp ~]$ free -m
               total        used        free      shared  buff/cache   available
Mem:            3562        1725         769          17        1316        1836
Swap:              0           0           0
Enter fullscreen mode Exit fullscreen mode

Install Containerd

Explanation:

To run containers in pods, we need a container runtime. I went with containerd.

# This command adds the docker repoistory 
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# This command installs containerd
sudo dnf install containerd
Enter fullscreen mode Exit fullscreen mode

Ensure containerd is healthy:

[xavier@lab-cp ~]$ systemctl status containerd
● containerd.service - containerd container runtime
     Loaded: loaded (/usr/lib/systemd/system/containerd.service; enabled; preset: disabled)
     Active: active (running) since Tue 2025-01-21 18:32:57 EST; 2 weeks 1 day ago
       Docs: https://containerd.io
Enter fullscreen mode Exit fullscreen mode

Install kubelet, kubeadm, and kubectl

Explanation

kubelet: The primary node agent that runs on each node, ensuring containers are running.
kubeadm: A tool that helps bootstrap and manage Kubernetes clusters.
kubectl: The command-line tool used to interact with the Kubernetes API and manage cluster resources.

# This overwrites any existing configuration in /etc/yum.repos.d/kubernetes.repo
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.32/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.32/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF

#--disableexcludes=kubernetes ensures that no repository exclusions prevent the installation of these packages.
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
Enter fullscreen mode Exit fullscreen mode

Configure cgroup Driver

Explanation:

The kubelet and the underlying container runtime need to interface with cgroups(coontrol groups) to enforce resource management for pods and containers which includes cpu/memory requests and limits for containerized workloads. The cgroup (control group) driver is responsible for managing and allocating system resources such as CPU and memory to containers.

# This command moves (renames) the existing config.toml file to config.toml.bak as a backup.
$ sudo mv /etc/containerd/config.toml /etc/containerd/config.toml.bak

# This command generates the default configuration for containerd, a container runtime, and writes it to a new config.toml file.
$ containerd config default > config.toml
Enter fullscreen mode Exit fullscreen mode

In the config.toml file, set SystemdCgroup to true

Network Configuration

Explanation:

When setting up a Kubernetes cluster, we need to adjust certain kernel parameters to ensure that networking functions correctly. These settings control how the Linux kernel handles network traffic, particularly when dealing with bridged network interfaces and packet forwarding.

Edit the /etc/sysctl.d/k8s.conf file.

net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
Enter fullscreen mode Exit fullscreen mode

On The Control Plane Only

The following should be completed on the control plane, only.

Open The Required Ports

Protocol Direction Port Range Purpose Used By
TCP Inbound 6443 Kubernetes API server All
TCP Inbound 2379-2380 etcd server client API kube-apiserver, etcd
TCP Inbound 10250 Kubelet API Self, Control plane
TCP Inbound 10259 kube-scheduler Self
TCP Inbound 10257 kube-controller-manager Self

Initialize The Cluster

Explanation:

kubeadm init sets up the control plane, configuring essential components like the API server, scheduler, and controller manager.

sudo kubeadm init --apiserver-advertise-address {HOST IP} --pod-network-cidr 10.244.0.0/16
Enter fullscreen mode Exit fullscreen mode

If you're successful, you'll get something like :

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join {HOST IP}:6443 --token {TOKEN} \
    --discovery-token-ca-cert-hash sha256:{HASH}
Enter fullscreen mode Exit fullscreen mode

Save this output somewhere. You'll need it shortly.

Set Up KUBECONFIG

Explanation:

A kubeconfig file is a configuration file used by Kubernetes to store information about clusters, users, namespaces, and authentication mechanisms, allowing the kubectl command-line tool to connect and interact with a Kubernetes cluster by providing necessary credentials and access details to the cluster's API server; essentially, it acts as a central hub to manage access to multiple Kubernetes clusters from a single location.

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Enter fullscreen mode Exit fullscreen mode

Deploy A CNI

Explanation:

In order for your pods to communicate with each other, you need to install a Container Network Interface(CNI)

There are a ton of network plugins to choose from. I ended up going with Cilium. Installation steps can be found here.


On The Worker Nodes Only

The following should be completed on the worker nodes only.

Open The Required Ports

Protocol Direction Port Range Purpose Used By
TCP Inbound 10250 Kubelet API Self, Control plane
TCP Inbound 10256 kube-proxy Self, Load balancers
TCP Inbound 30000-32767 NodePort Services† All

Now you can join worker nodes to the control plane. From the output you saved earlier, as sudo, run the kubeadm join command.

kubeadm join {HOST IP}:6443 --token {TOKEN} --discovery-token-ca-cert-hash sha256:{HASH}
Enter fullscreen mode Exit fullscreen mode

This will take a few minutes, but if it was successful, you'll get a message with the following. This node has joined the cluster

Conclusion

If all went well, you should now have a 3 node cluster! To verify, create a pod

[xavier@lab-cp ~]$ kubectl run mypod --image=nginx:latest
pod/mypod created
[xavier@lab-cp ~]$ k get pods
NAME            READY   STATUS    RESTARTS   AGE
mypod           1/1     Running   0          36s
Enter fullscreen mode Exit fullscreen mode

There are much easier ways to set up a cluster, but using kubeadm gives you a deeper understanding of how Kubernetes works under the hood.

This method also provides more flexibility, allowing you to customize your setup based on your needs. Whether you’re setting up a test environment or preparing for a production deployment, mastering the fundamentals will make troubleshooting and scaling your cluster much easier.

Top comments (0)