Create a Debian 11 kubernetes cluster with kubeadm

In this blog, I’ll try to keep as simple as possible to get up and running a Kubernetes cluster.
The cluster will be composed of three machines, one control plane and two workers.
I used KVM (Kernel-based Virtual Machine) running Debian 11 and installed a minimal system with SSH.
Note: this tutorial is made for learning, doesn’t cover any security or best practices.

References
https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/

Hostnames
c1-control-plane
c1-worker-1
c1-worker-2

The following setup has to be done on all three machines to be more efficient you can use a terminal multiplexer like tmux.

Edit hosts file with your favorite editor and add the following lines with your right IP addresses.

sudo vim /etc/hosts
192.168.254.10 c1-control-plane
192.168.254.11 c1-worker-1
192.168.254.12 c1-worker-2

Load required modules and set kernel settings.
overlay it’s needed for overlayfs, https://www.kernel.org/doc/html/latest/filesystems/overlayfs.html
br_netfilter for iptables to correctly see bridged traffic, http://ebtables.netfilter.org/documentation/bridge-nf.html

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

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

Turn off swap, as kubelet requires that

sudo sed -i '/swap/d' /etc/fstab

Apply settings, you can also skip this by rebooting the machines.

sudo modprobe overlay
sudo modprobe br_netfilter
sudo sysctl --system
sudo swapoff -a

Install an NTP server otherwise etcd will be mad.

sudo apt install -y chrony

Install containerd

# requirements

sudo apt install -y curl gpg lsb-release apparmor apparmor-utils
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# install

sudo apt update
sudo apt-get install -y containerd.io
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml

Edit containerd configuration and restart service

sudo vim /etc/containerd/config.toml
below:
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
add:
            SystemdCgroup = true

result:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true
sudo systemctl restart containerd

Install kubernetes tools, in my case version 1.21.0.

# requirements

sudo apt-get install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "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.list

# install

sudo apt-get update
sudo apt install iptables libiptc0/stable libxtables12/stable
sudo apt-get install -y kubelet=1.21.0-00 kubeadm=1.21.0-00 kubectl=1.21.0-00
sudo apt-mark hold kubelet kubeadm kubectl

Now it's time to create our Kubernetes cluster the following commands needs to be run from the control plane only.

sudo kubeadm init --pod-network-cidr 172.20.0.0/16 --kubernetes-version 1.21.0
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

From the control plane node you can now check you kubernetes cluster, c1-control-plane is in not NotReady mode because we didn't set up the networking yet.

kubectl get nodes
NAME               STATUS     ROLES                  AGE     VERSION
c1-control-plane   NotReady   control-plane,master   3m49s   v1.21.0

Setup networking with calico

kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml

Join the other nodes to our cluster, the command must be run on the worker nodes only.
At the end of the "kubeadmin init ..." command you were prompted for a join command, it should look like:

sudo kubeadm join 192.168.254.10:6443 --token oxaul0.24g50wlwsp4ktiqs --discovery-token-ca-cert-hash sha256:74746c748be5fef131d9c91a591c053591b6ce1e274396bcb7c48b6e6664bded

If you missed it, you can still generate a token and generate the command with:

kubeadm token create --print-join-command

We are ready, the setup can be validate with kubectl, all nodes are in ready state and kube-system pods are running.

kubectl get nodes
NAME               STATUS   ROLES                  AGE     VERSION
c1-control-plane   Ready    control-plane,master   5m52s   v1.21.0
c1-worker-1        Ready                     2m10s   v1.21.0
c1-worker-2        Ready                     66s     v1.21.0
kubectl get pods -A
kube-system   calico-kube-controllers-78d6f96c7b-9q4lq   1/1     Running   0          9h
kube-system   calico-node-4mq7p                          1/1     Running   0          9h
kube-system   calico-node-8km7w                          1/1     Running   0          9h
kube-system   calico-node-sjzs4                          1/1     Running   0          9h
kube-system   coredns-558bd4d5db-7pbjx                   1/1     Running   0          9h
kube-system   coredns-558bd4d5db-ptn59                   1/1     Running   0          9h
kube-system   etcd-c1-control-plane                      1/1     Running   1          9h
kube-system   kube-apiserver-c1-control-plane            1/1     Running   0          9h
kube-system   kube-controller-manager-c1-control-plane   1/1     Running   0          9h
kube-system   kube-proxy-ls768                           1/1     Running   0          9h
kube-system   kube-proxy-mk98k                           1/1     Running   0          9h
kube-system   kube-proxy-qbxwb                           1/1     Running   0          9h
kube-system   kube-scheduler-c1-control-plane            1/1     Running   0          9h

Installing Arch Linux on a Macbook or other UEFI systems

I’m a happy owner of a MacBook Air 13-inch “dinosaur”, but at the same time unhappy as the latest macOS (Catalina) doesn’t suit my expectations.
At lease for me this macOS version was super slow and decided not to spend any more time to try to tweak it or wait for an update but I decided to move on.

Besides the memory (I own a 4GB version), the hardware specs looks pretty good.

CPU (Intel website)

# of Cores: 2
# of Threads: 4
Processor Base Frequency: 1.30 GHz
Max Turbo Frequency: 2.60 GHz
Cache: 3 MB Intel® Smart Cache

Disk: APPLE SSD SD0128, 120GB
Wireless: BCM4360, 802.11ac

Bottom line, the hardware is not bad at all even in 2020.

I tested Debian 10 (pretty good) and Fedora 32 (not very exited), but finally decided to remain with Arch due to his lightweightness.
I used the Arch installation guide for more information please check https://wiki.archlinux.org/index.php/installation_guide

I’ll share the procedure that I used, can be used for any UEFI system!
Let’s leave the Apple’s Jail 🙂 and get some FREEDOM!

1. Download Arch Linux from https://www.archlinux.org/download/
BitTorrent method is recommended.

2. Write Arch Linux image to an USB drive, replace archlinux-image.iso and sdX with your USB drive.
The procedure from below are for Linux/Unix users if you are using Windows please try with Rufus (https://rufus.ie/).
Make sure you are selecting the correct “sdX” drive as the operation will destroy your data.

dd if=archlinux-image.iso of=/dev/sdX status=progress

3. Boot from the USB drive

4. Check if the system is UEFI

ls /sys/firmware/efi/efivars

5. Check internet connectivity, please make sure at this point you have a working internet connection.

ping google.com -c 3
PING google.com (216.58.214.238) 56(84) bytes of data.
64 bytes from bud02s24-in-f14.1e100.net (216.58.214.238): icmp_seq=1 ttl=55 time=9.79 ms
64 bytes from bud02s24-in-f14.1e100.net (216.58.214.238): icmp_seq=2 ttl=55 time=8.97 ms
64 bytes from bud02s24-in-f14.1e100.net (216.58.214.238): icmp_seq=3 ttl=55 time=9.54 ms

--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 8.972/9.432/9.785/0.340 ms

5. Update date and time

timedatectl set-ntp true

6. Partition the disk, we need three partitions, EFI, swap and the ext4 root partition.
Note: In my example the drive will be /dev/sdX, please make sure you select the drive correctly and you backup your data, all the data will be lost!

fdisk /dev/sdX
Command (m for help): g
Created a new GPT disklabel (GUID: 1A87692C-313C-FC4E-98A7-F383497A37CA).

-------------------------------------------------------------------------------------------------------

Command (m for help): n
Partition number (1-128, default 1): 
First sector (2048-236978142, default 2048): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-236978142, default 236978142): +512M

Created a new partition 1 of type 'Linux filesystem' and of size 512 MiB.

Command (m for help): type
Selected partition 1
Partition type (type L to list all types): 1
Changed type of partition 'Linux filesystem' to 'EFI System'.

-------------------------------------------------------------------------------------------------------

Command (m for help): n
Partition number (2-128, default 2): 
First sector (1050624-236978142, default 1050624): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-236978142, default 236978142): +108G

Created a new partition 2 of type 'Linux filesystem' and of size 108 GiB.

-------------------------------------------------------------------------------------------------------

Command (m for help): n
Partition number (3-128, default 3): 
First sector (227543040-236978142, default 227543040): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (227543040-236978142, default 236978142): 

Created a new partition 3 of type 'Linux filesystem' and of size 4.5 GiB.

Command (m for help): type
Selected partition 3
Partition type (type L to list all types): 19
Changed type of partition 'Linux filesystem' to 'Linux swap'.

Command (m for help): w
The partition table has been altered.
Syncing disks.

Your layout should be:

fdisk -l /dev/sdX
Disk /dev/sda: 113 GiB, 121332826112 bytes, 236978176 sectors
Disk model: APPLE SSD SD0128
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: C8A90784-F69D-DF42-A1F2-F8D798227B81

Device         Start       End   Sectors  Size Type
/dev/sda1       2048   1050623   1048576  512M EFI System
/dev/sda2    1050624 227543039 226492416  108G Linux filesystem
/dev/sda3  227543040 236978142   9435103  4.5G Linux swap

7. Format partitions

mkfs.vfat -F32 /dev/sda1
mkfs.fat 4.1 (2017-01-24)
mkfs.ext4 /dev/sda2
mke2fs 1.45.6 (20-Mar-2020)
Discarding device blocks: done                            
Creating filesystem with 28311552 4k blocks and 7077888 inodes
Filesystem UUID: bbbc9738-cb31-4d09-ad0a-afdb955f74cb
Superblock backups stored on blocks: 
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
	4096000, 7962624, 11239424, 20480000, 23887872

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (131072 blocks): 
done
Writing superblocks and filesystem accounting information: done  
mkswap /dev/sda3
Setting up swapspace version 1, size = 4.5 GiB (4830765056 bytes)
no label, UUID=d7340dbe-b432-41f9-8a1e-c50e598efb48

8. Mount filesystem

mount /dev/sdX2 /mnt
swapon /dev/sda3

9. Install the base

pacstrap /mnt base linux linux-firmware

10. Generate fstab

genfstab -U /mnt >> /mnt/etc/fstab

11. Chroot

arch-chroot /mnt

12. Timezone

ln -sf /usr/share/zoneinfo/Europe/Bucharest /etc/localtime

13. Localization

pacman -S vim
vim /etc/locale.gen # uncomment en_US.UTF
locale-gen

14. Change root password and add user

passwd
useradd -m your_user
passwd your_user

15. Install Grub boot loader

mkdir /boot/efi
mount /dev/sdX1 /boot/efi
pacman -S --noconfirm grub efibootmgr
grub-install --target=x86_64-efi --efi-directory=/boot/efi
grub-mkconfig -o /boot/grub/grub.cfg

16. Locale settings

localectl set-locale LANG="en_US.UTF-8"
localectl set-x11-keymap us

16. Install Gnome

pacman -S xorg gdm gnome network-manager xterm
systemctl enable gdm
systemctl enable NetworkManager