Building a Kubernetes Cluster using kubeadm
Tags: kubernetes, cloudnative, opensource
Published: 2023-05-03 12:09:45

> In this article, we will look how we can create a three node Kubernetes cluster using kubeadm

## Introduction

* kubeadm is a tool used to create Kubernetes clusters
    
* It automates the creation of Kubernetes clusters by bootstrapping the control plane, joining the nodes etc
    
* Follows Kubernetes release cycle
    
* Open-source tool maintained by the Kubernetes community
    

## Prerequisites

* Create three Ubuntu 20.04 LTS instances **\[control-plane, node-1, node-2\]**
    
* Each instance has a minimum specification of 2 CPU and 2 GB RAM
    
* Networking must be enabled between instances
    
* Required ports must be allowed between instances
    
* Swap must be disabled on instances
    

## Steps

* Set up unique hostnames on all instances **\[control-plane, node-1, node-2\]**  
    Once the hostnames are set, logout from the current session and log back in to reflect the changes
    

```bash
$ sudo hostnamectl set-hostname control-plane
```

```bash
$ sudo hostnamectl set-hostname node-1
```

```bash
$ sudo hostnamectl set-hostname node-2
```

* Update the hosts file on all instances **\[control-plane, node-1, node-2\]** to enable communication via hostnames
    

```bash
$ sudo vi /etc/hosts

172.31.12.122 control-plane
172.31.7.52  node-1
172.31.10.184 node-2
```

* Disable swap on all instances **\[control-plane, node-1, node-2\]** and if a swap entry is present in the fstab file then comment out the line
    

```bash
$ sudo swapoff -a

$ sudo vi /etc/fstab
  # comment out swap entry
```

* Setup containerd as our container runtime on all instances **\[control-plane, node-1, node-2\]** and to do that, first we need to load some Kernel modules and modify system settings
    

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

```bash
$ sudo modprobe overlay

$ sudo modprobe br_netfilter
```

```bash
$ 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
EOF
```

```bash
$ sudo sysctl --system
```

* Once the Kernel modules are loaded and modified the system settings, then can we can install containerd on all instances **\[control-plane, node-1, node-2\]**
    

```bash
$ sudo apt update

$ sudo apt install -y containerd
```

* Once installed, generate a default configuration file for contained on all instances **\[control-plane, node-1, node-2\]** and restart the service
    

```bash
$ sudo mkdir -p /etc/containerd

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

```bash
$ sudo systemctl restart containerd
```

* Install some prerequisite packages on all instances **\[control-plane, node-1, node-2\]** for configuring Kubernetes apt repository
    

```bash
$ sudo apt update

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

* Download the Google Cloud public signing key and configure Kubernetes apt repository on all instances **\[control-plane, node-1, node-2\]**
    

```bash
$ 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
```

* Install kubeadm, kubelet and kubectl tools and hold their version on all instances **\[control-plane, node-1, node-2\]**
    

```bash
$ sudo apt update

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

$ sudo apt-mark hold kubeadm kubelet kubectl
```

* Initialize the cluster by executing the below command on **\[control-plane\]** instance
    

```bash
$ sudo kubeadm init --pod-network-cidr 192.168.0.0/16 --kubernetes-version 1.27.0
```

* Once the installation is completed, set up our access to the cluster on **\[control-plane\]** instance
    

```bash
$ mkdir -p $HOME/.kube

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

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

* Verify our cluster by listing the nodes on **\[control-plane\]** instance  
    But our nodes are in NotReady state because we haven’t set up networking
    

```bash
$ kubectl get nodes
NAME            STATUS     ROLES           AGE   VERSION
control-plane   NotReady   control-plane   36s   v1.27.0
```

* Install the Calico network addon on the **\[control-plane\]** instance and verify the status
    

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

```bash
$ kubectl get nodes
NAME            STATUS   ROLES           AGE     VERSION
control-plane   Ready    control-plane   2m51s   v1.27.0
```

* Once the networking is enabled, join our workload nodes to the cluster  
    Get the join command from the **\[control-plane\]** instance
    

```bash
$ kubeadm token create --print-join-command
kubeadm join 172.31.12.122:6443 --token 6t3r2c.5zzsgtekwwltofwt --discovery-token-ca-cert-hash sha256:2f1c4115125ec62af8ae6ab2648277ace3a625ebc0281e85ca8145e0e9077ee4
```

* Once the join command is copied from the **\[control-plane\]** instance, execute them in the **\[node-1, node-2\]** instances
    

```bash
$ sudo kubeadm join 172.31.12.122:6443 --token 6t3r2c.5zzsgtekwwltofwt --discovery-token-ca-cert-hash sha256:2f1c4115125ec62af8ae6ab2648277ace3a625ebc0281e85ca8145e0e9077ee4
```

* Verify our cluster from the **\[control-plane\]** instance, it should be in a READY state
    

```bash
$ kubectl get nodes
NAME            STATUS   ROLES           AGE    VERSION
control-plane   Ready    control-plane   7m5s   v1.27.0
node-1          Ready    <none>          91s    v1.27.0
node-2          Ready    <none>          17s    v1.27.0
```

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

```bash
$ kubectl run nginx --image=nginx --port=80 --expose
service/nginx created
pod/nginx created
```

```bash
$ kubectl get pod nginx -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP               NODE     NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          30s   192.168.84.129   node-1   <none>           <none>

$ kubectl get svc nginx
NAME    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   10.111.228.20   <none>        80/TCP    39s
```

## Reference

[https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/)

[https://acloudguru.com/course/introduction-to-kubernetes](https://acloudguru.com/course/introduction-to-kubernetes)