In this note, I just want to share how to install a Kubernetes cluster using Kubeadm and Calico on Ubuntu.
When I wrote this note, I used Kubeadm version 1.29 and Ubuntu 22.04 for the implementation. Okay, so without further ado, let’s get started! 💪


1. Update and Upgrade Ubuntu ( all nodes )

sudo apt update
sudo apt upgrade

2. Disable Swap ( all nodes )

sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

3. Add Kernel Parameters ( all nodes )

Load the required kernel modules on all nodes:

sudo tee /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter

Configure the critical kernel parameters for Kubernetes using the following:

sudo tee /etc/sysctl.d/kubernetes.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

Then, reload the changes:

sudo sysctl --system

4. Install Containerd Runtime (all nodes)

sudo apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates

Enable the Docker repository:

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

Update the package list and install containerd:

sudo apt update
sudo apt install -y containerd.io

Configure containerd to start using systemd as cgroup:

containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1
sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml

Restart and enable the containerd service:

sudo systemctl restart containerd
sudo systemctl enable containerd

5. Add Apt Repository for Kubernetes (All Nodes)

These instructions are for Kubernetes v1.29.

  1. Update the apt package index and install packages needed to use the Kubernetes apt repository:
   sudo apt update
   # apt-transport-https may be a dummy package; if so, you can skip that package
   sudo apt install -y apt-transport-https ca-certificates curl gpg
  1. Download the public signing key for the Kubernetes package repositories. The same signing key is used for all repositories so you can disregard the version in the URL:
  # If the directory `/etc/apt/keyrings` does not exist, it should be created before the curl command, read the note below.
    # sudo mkdir -p -m 755 /etc/apt/keyrings
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
  1. Add the appropriate Kubernetes apt repository. Please note that this repository have packages only for Kubernetes 1.29; for other Kubernetes minor versions, you need to change the Kubernetes minor version in the URL to match your desired minor version (you should also check that you are reading the documentation for the version of Kubernetes that you plan to install).
    # This overwrites any existing configuration in /etc/apt/sources.list.d/kubernetes.list
    echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

6. Install Kubectl, Kubeadm, and Kubelet ( all nodes)

    sudo apt update
    sudo apt install -y kubelet kubeadm kubectl
    sudo apt-mark hold kubelet kubeadm kubectl

7. (Optional) Enable the kubelet service before running kubeadm:

sudo systemctl enable --now kubelet

8. Initialize Kubernetes Cluster with Kubeadm (master node)

#change the cidr ip range, depends on your network,
# ensure not clash with your Range / subnet IP network
#default calico cidr
#sudo kubeadm init —pod-network-cidr=192.168.0.0/16

# in my case, my ip network range is 192.168.0.0/24
# i change my cidr to 10.10.0.0/16

#notes :
# If you install in cloud instances and have public ip do this instead
# sudo kubeadm init --apiserver-cert-extra-sans=your-public-ip --apiserver-advertise-address=your-public-ip --control-plane-endpoint=your-public-ip:6443 --pod-network-cidr=xx.xx.xx.xx/xx --node-name your-node-name

# Explanation :
# --apiserver-cert-extra-sans => When initializing a Kubernetes cluster with kubeadm, if you plan to access the API server via its public IP address or any additional DNS names, you should use the --apiserver-cert-extra-sans flag. This ensures that the certificate used by the API server includes these addresses, avoiding certificate validation errors.

# --apiserver-advertise-address :
# Definition:
# This flag specifies the IP address that the Kubernetes API server will advertise to nodes and other # # components in the cluster.
# Use Case:
# You would use this flag to set the IP address of the API server explicitly. This is useful in environments where your machine has multiple network interfaces or IP addresses, and you need to specify which one should be used.

# --control-plane-endpoint
# Definition:
# This flag specifies a stable IP address or DNS name for the control plane. It is particularly useful in highly available (HA) setups where multiple control plane nodes are behind a load balancer.
# Use Case:
# In a highly available Kubernetes cluster, you typically have multiple control plane nodes behind a load balancer. You can use this flag to set the load balancer's address or DNS name as the control plane endpoint. This ensures that all the components use a stable address to communicate with the API server.

# In GCP use this command 
# sudo kubeadm init --apiserver-cert-extra-sans=35.226.75.54 --pod-network-cidr=192.168.0.0/16 --control-plane-endpoint=35.226.75.54:6443 --node-name masternode -v=9

Create kube config ( Master Node )

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

Notes : Don’t forget to copy Kubeadm join token

9. Install & Configure Calico for network ( Master Node )

  1. Install the operator on your cluster
    kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.2/manifests/tigera-operator.yaml
  1. Download the custom resources necessary to configure Calico.
curl https://raw.githubusercontent.com/projectcalico/calico/v3.27.2/manifests/custom-resources.yaml -O
  1. Edit the CIDR for pods if its custom
vi custom-resources.yaml

# in my case change the subnet/ CIDR section from 192.168.0.0/16 to 10.10.0.0/16
  1. Create the manifest to install calico
kubectl apply -f custom-resources.yaml```
  1. Verify Calico installation in your cluster.
watch kubectl get pods -n calico-system

You should see a result similar to the below.

NAMESPACE     NAME                READY   STATUS                  RESTARTS         AGE
kube-system   calico-node-txngh   1/1     Running                   0              54s

10. Print Token to join additional nodes into cluster

kubeadm token create --print-join-command

11. Run the output from kubeadm token create in your worker nodes to join.