Effortless Kubernetes Cluster Deployment with Kubeadm and Containerd

Suraj Solanki
8 min readMay 10, 2023
K8S Cluster

As Kubernetes continues to gain popularity, more and more people are looking to build their own clusters for testing and development purposes. If you’re one of them, then you’ve come to the right place! In this article, we will show you how to create a Kubernetes cluster from scratch using Kubeadm.



  • What is kubeadm
  • Benefits of using Kubeadm
  • Features of kubeadm
  • How Kubeadm follows the Kubernetes release cycle
  • Who maintains Kubeadm?


  • Creating the Ubuntu instances
  • Specifying minimum specifications for instances
  • Enabling networking between instances
  • Allowing required ports between instances
  • Disabling swap on instances

Installing kubeadm

  • Updating the system
  • Installing Containerd as the Container Runtime
  • Installing kubeadm
  • Configuring Kubernetes dependencies

Initializing the control plane

  • Initializing the master node
  • Configuring Kubectl on the control plane
  • Installing Network Addon
  • Verifying the control plane

Joining the worker nodes

  • Creating a token
  • Joining the worker nodes to the control plane
  • Verifying the worker nodes
  • Testing workload


What is kubeadm

kubeadm is a command-line tool used to bootstrap a minimal, production-ready Kubernetes cluster. It automates the creation of a Kubernetes cluster by bootstrapping the control plane, joining the nodes, and managing the necessary Kubernetes objects. It is one of the official Kubernetes tools and supports various deployment scenarios.

Benefits of using Kubeadm

Using kubeadm has many benefits, including:

  • Simplifies the process of setting up a Kubernetes cluster
  • Supports various deployment scenarios, including single-node and multi-node clusters
  • Ensures that Kubernetes components are configured correctly and compatible with each other
  • Enables easy upgrades and management of Kubernetes clusters
  • Supports integration with other Kubernetes tools like kubectl and kubeadm-dind-cluster.

Features of kubeadm

Some of the key features of kubeadm are:

  • Automatic TLS bootstrapping
  • Ability to configure various networking plugins
  • Support for different Kubernetes versions and deployment scenarios
  • Automatic detection and handling of different container runtimes like Docker , CRI-O and Containerd.
  • Automatic handling of different OS distributions and package managers.

How Kubeadm follows the Kubernetes release cycle

kubeadm follows the Kubernetes release cycle by releasing new versions that are compatible with the latest Kubernetes releases. This ensures that Kubeadm is always up-to-date with the latest features and bug fixes. Additionally, kubeadm supports different Kubernetes versions, allowing users to deploy clusters with different versions of Kubernetes depending on their requirements.

Who maintains kubeadm

kubeadm is an open-source tool maintained by the Kubernetes community. It is one of the official Kubernetes tools and is actively developed and maintained by a team of contributors from various organizations. The codebase is hosted on GitHub and is available for anyone to contribute to and use.


Creating the Ubuntu instances: To create a Kubernetes cluster using Kubeadm, you will need to create three Ubuntu instances. You can use a cloud provider such as AWS or Google Cloud Platform to create these instances.

Specifying minimum specifications for instances: Each instance should have a minimum specification of 2 CPU and 2 GB RAM to ensure that the Kubernetes cluster runs smoothly.

Enabling networking between instances: Networking must be enabled between the instances so that they can communicate with each other.

Allowing required ports between instances: Certain ports must be allowed between the instances to ensure that the Kubernetes components can communicate with each other. For example, port 6443 is used by the Kubernetes API server.

Disabling swap on instances: Swap must be disabled on the instances to prevent performance issues. Kubernetes requires a lot of memory, and swap usage can significantly slow down the cluster.

Action Steps:

Step 1: Set Up Unique Hostnames on All Instances

To enable communication between the cluster nodes via hostnames, we’ll set up unique hostnames on each instance. Log in to each instance and run the following commands:

$ sudo hostnamectl set-hostname controller-plane
$ sudo hostnamectl set-hostname worker-node-1
$ sudo hostnamectl set-hostname worker-node-2

After running these commands, log out and log back into each instance to reflect the hostname changes.

Step 2: Update the Hosts File on All Instances

Next, we need to update the hosts file on each instance to enable communication via hostnames. Open the hosts file on each instance using the following command:

$ sudo vi /etc/hosts

Then add the following lines to the file, replacing the IP addresses with the private IP addresses of your instances:

controller-plane-ip controller-plane
worker-node-1-ip worker-node-1
worker-node-2-ip worker-node-2

Save and close the file.

Step 3: Disable Swap on All Instances

Kubernetes requires that swap be disabled on all cluster nodes. To disable swap on each instance, run the following commands:

$ sudo swapoff -a
$ sudo vi /etc/fstab

In the fstab file, comment out any lines that reference swap.

Installing kubeadm

Updating the system

Before installing any software on the system, it’s essential to ensure that the system is up to date with the latest patches and updates. This can be done by running the following command in the terminal:

sudo apt update && sudo apt upgrade -y

Installing Containerd as the Container Runtime

In order to set up containerd as the container runtime on all instances, we first need to load some Kernel modules and modify system settings.

Loading Kernel modules and modifying system settings

  • The overlay module provides the ability to overlay filesystems, which is necessary for Docker and containerd.
  • The br_netfilter module enables bridge netfilter, which is used by Kubernetes to implement network policies.

To load the required Kernel modules, run the following commands:

$ cat << EOF | sudo tee /etc/modules-load.d/containerd.conf

$ sudo modprobe overlay
$ sudo modprobe br_netfilter

Next, the settings in /etc/sysctl.d/99-kubernetes-cri.conf enable IPv4 forwarding and IPv6 iptables filtering for container networking.

To modify the system settings by running the following commands:

$ cat << EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
$ sudo sysctl --system

Once the Kernel modules are loaded and system settings are modified, we can proceed to install containerd on all instances [controller-plane, worker-node-1, worker-node-2] by running:

$ sudo apt update
$ sudo apt install -y containerd

After Containerd is installed, generate a default configuration file for contained on all instances [controller-plane, worker-node-1, worker-node-2] and restart the service.

$ sudo mkdir -p /etc/containerd

$ sudo containerd config default | sudo tee /etc/containerd/config.toml

$ sudo systemctl restart containerd

Make sure to execute these commands on all the instances to ensure containerd is set up correctly.

Installing kubeadm

Kubeadm is a package that can be installed on the Ubuntu system and holds its version on all instances

$ sudo apt update
$ sudo apt install -y kubeadm=1.27.0-00$ sudo apt-mark hold kubeadm

Note: If you encounter any confusion during the installation process, refer to the official Kubernetes documentation.

Configuring Kubernetes dependencies

Kubeadm requires additional dependencies such as kubelet and kubectl to be installed on the system. These can be installed using the following commands:

Step 1: Install Kubelet and Kubectl

$ sudo apt install -y kubelet=1.27.0-00 kubectl=1.27.0-00

$ sudo apt-mark hold kubelet kubectl

Step 2: Install Prerequisite Packages

We need to install some prerequisite packages on each instance to configure the Kubernetes apt repository. Run the following commands on each instance:

$ sudo apt update
$ sudo apt install -y apt-transport-https ca-certificates curl

Step 3.: Configure the Kubernetes Apt Repository

Next, we’ll download the Google Cloud public signing key and configure the Kubernetes apt repository on each instance:

$ sudo mkdir /etc/apt/keyrings
$ sudo curl -fsSLo /etc/apt/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
$ echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

Additionally, it’s important to make sure that the firewall is configured to allow traffic on the required ports for Kubernetes to function properly. This can be done using the following command:

sudo ufw allow 6443/tcp

Initializing the control plane

Initializing the master node

After installing Kubeadm, the next step is to initialize the control plane. To do this, run the following command on the control plane node:

$ sudo kubeadm init --pod-network-cidr=<cidr> --kubernetes-version 1.27.0

This command will download the necessary images and create the initial configuration for the Kubernetes control plane. Make sure to replace <cidr> it with the CIDR block for your pod network.

Configuring Kubectl on the control plane

To manage the Kubernetes cluster, you’ll need to add a configuration file. To do this, run the following command on the control plane node:

$ mkdir -p $HOME/.kube

$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

This command will create a directory for the kubectl configuration and copy the Kubernetes configuration file to it. Finally, it sets the owner of the configuration file to the current user. Now you can use kubectl to manage your Kubernetes cluster.

Installing Network Addon

Install a CNI plugin suitable for your use case. For this article, we are using Calico as an example.

$ kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/calico.yaml

Verifying the control plane

Once the control plane initialization is completed, you can verify its status by running the following command:

kubectl get nodes

The controller-plane should now be in the Ready state, indicating that the network addon is successfully installed and configured.

Joining the worker nodes

Creating a token

Before you can join worker nodes to the control plane, you need to create a token. Run the following command on the control plane to generate a token:

$ sudo kubeadm token create --print-join-command

This will output the token and the command to join a worker node to the cluster.

Joining the worker nodes to the control plane

On each worker node, run the command output by the previous command. This command will look something like this:

$ sudo kubeadm join <control-plane-host>:<control-plane-port> --token <token> --discovery-token-ca-cert-hash sha256:<hash>

Replace <control-plane-host> and <control-plane-port> with the IP address and port of the control plane node, <token> with the token generated in the previous step, and <hash> with the hash of the CA certificate.

The CA key hash has the format sha256:<hex_encoded_hash>. By default, the hash value is returned in the kubeadm join command printed at the end of kubeadm init or in the output of kubeadm token create --print-join-command.

Verifying the worker nodes

To verify that the worker nodes have joined the cluster, run the following command on the control plane:

$ kubectl get nodes

This should list all the nodes in the cluster, including the worker nodes that were just added.

Testing workload

Deploy an Nginx pod, expose it as ClusterIP from the [controller-plane] instance, and verify its status

$ kubectl run nginx --image=nginx --port=80 --expose

$ kubectl get pod nginx -o wide
$ kubectl get svc nginx


In this blog post, we have walked through the process of installing Kubernetes with Kubeadm using Containerd as the container runtime. We started by loading the necessary kernel modules and modifying the system settings to prepare our cluster for the installation of Containerd. Then we installed Kubeadm, Kubelet, and Kubectl tools and held their versions on all instances. We initialized the cluster on the control-plane instance and set up our access to the cluster. After that, we installed the Calico network add-on to enable networking, and finally, we joined our workload nodes to the cluster.

By following the steps outlined in this blog post, you should be able to set up a Kubernetes cluster. This setup provides a robust platform for deploying and managing containerized applications at scale.

Thank you for reading this far! I hope you found this blog post informative and useful for your Kubernetes cluster deployment. I welcome any comments or feedback.

About The Author
Suraj Solanki
DevOps Engineer — II
LinkedIn: linkedin.com/in/suraj-solanki
Book your time with me at: topmate.io/suraj_solanki



Suraj Solanki

Senior DevOps Engineer and Fascinated to learn as well as talk about cloud and automation. You can always connect https://www.linkedin.com/in/suraj-solanki