In the upcoming blog posts, I will cover monitoring with Prometheus in a Kubernetes infrastructure. As I started blogging, I decided it would be useful to delve into the design of the k8s infrastructure.
This will allow you to approach these blogs better if you wish to reproduce them.
Therefore, I will start with the basics and explain step by step how to build a Kubernetes cluster with kubeadm easily.
I suggest we start with a relatively simple, if not basic, installation with a cluster including a Master (Control Plane) and two worker nodes. To do this, I will use a Debian distribution, with a rather minimalist sizing with 2 CPUs and 4GB of RAM each.
Install Packages
1. Login to your master server (Control Plane)
2. Create a configuration file containerd, which will contain the necessary packages for the container runtime :
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
3. Load the overlay and br_netfilter kernel modules:
sudo modprobe overlay
sudo modprobe br_netfilter
4. Set system configurations for Kubernetes networking:
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
5. Load sysctl settings from all available configuration files:
sudo sysctl --system
6. Update the package list and install the “containerd” package:
sudo apt-get update && sudo apt-get install -y containerd
7. Create the /etc/containerd directory if it doesn’t exist (we will use it to store the default configuration file for containerd ):
sudo mkdir -p /etc/containerd
8. Generate a default config file for containerd and save it to the newly created default file:
sudo containerd config default | sudo tee /etc/containerd/config.toml
9. Restart containerd to ensure new configuration file usage:
sudo systemctl restart containerd
10. Verify that containerd is running:
sudo systemctl status containerd
[email protected]:~$ sudo systemctl status containerd
● containerd.service - containerd container runtime
Loaded: loaded (/lib/systemd/system/containerd.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2023-01-09 10:28:12 UTC; 22s ago
Docs: https://containerd.io
Process: 2013 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
Main PID: 2014 (containerd)
Tasks: 8
Memory: 10.8M
CPU: 140ms
CGroup: /system.slice/containerd.service
└─2014 /usr/bin/containerd
Jan 09 10:28:12 dbisbx-master01 containerd[2014]: time="2023-01-09T10:28:12.551429182Z" level=info msg=serving... address=/run/containerd/containerd.sock.ttrpc
Jan 09 10:28:12 dbisbx-master01 containerd[2014]: time="2023-01-09T10:28:12.551607403Z" level=info msg=serving... address=/run/containerd/containerd.sock
Jan 09 10:28:12 dbisbx-master01 containerd[2014]: time="2023-01-09T10:28:12.551954435Z" level=info msg="containerd successfully booted in 0.051594s"
Jan 09 10:28:12 dbisbx-master01 systemd[1]: Started containerd container runtime.
Jan 09 10:28:12 dbisbx-master01 containerd[2014]: time="2023-01-09T10:28:12.557282442Z" level=info msg="Start subscribing containerd event"
Jan 09 10:28:12 dbisbx-master01 containerd[2014]: time="2023-01-09T10:28:12.557379562Z" level=info msg="Start recovering state"
Jan 09 10:28:12 dbisbx-master01 containerd[2014]: time="2023-01-09T10:28:12.557588184Z" level=info msg="Start event monitor"
Jan 09 10:28:12 dbisbx-master01 containerd[2014]: time="2023-01-09T10:28:12.557619734Z" level=info msg="Start snapshots syncer"
Jan 09 10:28:12 dbisbx-master01 containerd[2014]: time="2023-01-09T10:28:12.557634754Z" level=info msg="Start cni network conf syncer"
Jan 09 10:28:12 dbisbx-master01 containerd[2014]: time="2023-01-09T10:28:12.557649394Z" level=info msg="Start streaming server"
[email protected]:~$
11. Disable swap:
sudo swapoff -a
12. Update and install dependency packages:
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
13. Download and add the Google Cloud public signing key:
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
Depending on the version of your distribution, you might have a warning about the deprecated command. Therefore you can type instead of:
sudo curl -fsSLo /etc/apt/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
14. Create a new file called “/etc/apt/sources.list.d/kubernetes.list” and add the Kubernetes apt repository to it:
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
15. Update package listings:
sudo apt-get update
16. Install specific versions of the kubelet, kubeadm, and kubectl packages :
sudo apt-get install -y kubelet=1.24.0-00 kubeadm=1.24.0-00 kubectl=1.24.0-00
17. Mark the kubelet, kubeadm, and kubectl packages as “held” to prevent them from being automatically upgraded:
sudo apt-mark hold kubelet kubeadm kubectl
18. The installation of the packages must be performed on all servers. The entire part 1 must be repeated on the worker nodes.
Initialize the Cluster
1. This part had to be done only on the Master server (Control Plane)
Install specific versions of the kubelet, kubeadm, and kubectl packages. And then initialize the Kubernetes cluster with kubeadm, specifying the pod network CIDR and the Kubernetes version
sudo apt-get install -y kubelet=1.24.0-00 kubeadm=1.24.0-00 kubectl=1.24.0-00
sudo kubeadm init --pod-network-cidr 192.168.0.0/16 --kubernetes-version 1.24.0
[email protected]:~$ sudo kubeadm init --pod-network-cidr 192.168.0.0/16 --kubernetes-version 1.24.0
[init] Using Kubernetes version: v1.24.0
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [dbisbx-master01 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.*.*.* 172.*.*.*]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [dbisbx-master01 localhost] and IPs [172.*.*.* 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [dbisbx-master01 localhost] and IPs [172.*.*.* 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[kubelet-check] Initial timeout of 40s passed.
[apiclient] All control plane components are healthy after 53.186020 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node dbisbx-master01 as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node dbisbx-master01 as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: vnv0kw.xmh*************
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
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 172.*.*.*:6443 --token vnv0kw.xmh************* \
--discovery-token-ca-cert-hash sha256:eaec80d623e107619c02f743a726d8e5f6******************************
[email protected]:~$
2. Once the initialization is done, we have to set the kubectl access.
For this aim, let’s create the .kube folder in our user’s home directory if it doesn’t already exist.
We will then copy the admin.conf file from /etc/kubernetes to the .kube directory. Finally, we will change the owner of the copied file to the current user.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
3. Test the access to the cluster.
kubectl get nodes
[email protected]:~$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
dbisbx-master01 NotReady control-plane 02m v1.24.0
[email protected]:~$
Install the Calico Network Add-On
1. On the control plane node, install Calico Networking by creating the necessary custom resource:
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/custom-resources.yaml
More information on configuration option are available here
[email protected]:~$ kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/custom-resources.yaml
poddisruptionbudget.policy/calico-kube-controllers created
serviceaccount/calico-kube-controllers created
serviceaccount/calico-node created
configmap/calico-config created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/caliconodestatuses.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipreservations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created
clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrole.rbac.authorization.k8s.io/calico-node created
clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrolebinding.rbac.authorization.k8s.io/calico-node created
daemonset.apps/calico-node created
deployment.apps/calico-kube-controllers created
[email protected]:~$
2. Check the status of the control plane node:
kubectl get nodes -owide
[email protected]:~$ kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
dbisbx-master01 Ready control-plane 07m v1.24.0 172.*.*.* <none> Ubuntu 20.04.5 LTS 5.15.0-1027-aws containerd://1.5.9
Join the worker nodes to k8s cluster
In the chapter “Cluster Initialization”, we retrieved the command allowing us to add our workers to the cluster.
You can retrieve the command and run it on each worker.
In case you didn’t copy the command, you still have the possibility to display the join command by recreating the token that will allow you to join your nodes.
1. From the control plane server, execute the following command.
kubeadm token create --print-join-command
[email protected]:~$ kubeadm token create --print-join-command
kubeadm join 172.31.99.146:6443 --token u871a6.k*************** --discovery-token-ca-cert-hash sha256:eaec80d623e107619c02f743a726*********************************
2. On the first worker node, execute the previous printed command "kubeadm join <IP:PORT> ..." being root, or running as root user.
sudo kubeadm join 172.*.*.*:6443 --token u871a6.kbzwx2j6jztxyvg3 --discovery-token-ca-cert-hash sha256:eaec80d623e107619c02f743a726*********************************
[email protected]:~$ sudo kubeadm join 172.*.*.*:6443 --token u871a6.kbzwx2j6jztxyvg3 --discovery-token-ca-cert-hash sha256:eaec80d623e107619c02f743a726*********************************
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
[email protected]:~$
All that remains is to check that the node has been added to the cluster; it may take a few minutes for the node to go to the ready status
kubectl get nodes -owide
[email protected]:~$ kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
dbisbx-master01 Ready control-plane 19m v1.24.0 172.31.99.146 <none> Ubuntu 20.04.5 LTS 5.15.0-1027-aws containerd://1.5.9
dbisbx-worker01 Ready <none> 05m v1.24.0 172.31.101.246 <none> Ubuntu 20.04.5 LTS 5.15.0-1027-aws containerd://1.5.9
dbisbx-worker02 Ready <none> 03m v1.24.0 172.31.96.172 <none> Ubuntu 20.04.5 LTS 5.15.0-1027-aws containerd://1.5.9
Here we are; we could quickly build a small Kubernetes cluster in less than 20 minutes.