Travaillant avec Kubernetes depuis plusieurs années, j’ai l’habitude d’utiliser des clusters en mode managé comme GKE, AKS ou Kapsule. Je me suis souvent posé la question de l’effort à fournir pour créer et surtout gérer au quotidien mon propre cluster Kubernetes.
Appréciant transmettre ma passion autour des conteneurs et de Kubernetes, je vais essayer de partager mon expérience vers cet objectif. Les choix lors de la mise en place sont liés à mon expérience et sont mon point de vue à cet instant. Ma vision peut, et va sûrement, évoluer avec le temps.
Installer son cluster
Rappel : composition d’un cluster
Un cluster Kubernetes est constitué de 2 composants majeurs :
- les
masters
, qui composent le plan de contrôle (control-plane
) - les
nodes
, où sont exécutes les charges de travail ou conteneurs
Dans un cluster managé, les masters
sont entièrement gérés par le fournisseur du service (Google, Azure, AWS, Scaleway, etc.)
Vous pouvez trouver plus de détails sur les composants de Kubernetes dans la documentation officielle
Méthodes d’installation
Il existe plusieurs façons d’installer son cluster, notamment à l’aide d’outils tiers. Avant d’écrire cet article, les méthodes que je connaissais étaient :
Une section de la documentation de Kubernetes en recense un certains nombre selon votre besoin.
Le Landspace de la CNCF repertorie également des installateurs certifiés.
Mon choix
L’outil que je vais utiliser pour gérer mon cluster est kubeadm
.
Voici quelques raisons qui m’ont amenés vers ce choix :
- il est simple d’utilisation
- il permet de gèrer certains opérations du cycle de vie d’un cluster : notamment la mise à jour des composants du cluster
- il permet d’obtenir un cluster Kubernetes passant les tests de conformité 1
- il installe le minimum de fonctionnalitées : cela peut être un inconvénient pour certains, dans mon cas j’ai choisi d’installer et de comprendre les composants nécessaires au bon fonctionnement de Kubernetes sans “extensions” superflues
- les composants des
masters
sont exécutés entant que pods
Installation des masters
kubeadm
offre la possibilité d’installer un cluster en mode HA (Haute Disponibilité).
Dans un premier temps, je vais installer un cluster avec un noeud master
unique, par question de simplicité.
Pré-requis
La documentation de kubeadm
indique les pré-requis nécessaires à l’installation des masters
.
Voici les caractéristiques de mon master
:
- Système d’exploitation :
Ubuntu 20.04 LTS
- CPU :
3
- Mémoire :
4 Go
Container Runtime
kubeadm
installe les composants du master
(etcd
, api-server
, kube-scheduler
, kube-controller-manager
) sous la forme de conteneurs. Il est donc nécessaire d’installer un container runtime pour exécuter ces derniers.
Docker est le plus souvent utilisé, mais je préfère utiliser containerd
.
J’expliquerais plus en détail ce choix, dans un autre article.
Pré-requis
Il est nécessaire de configurer certains paramètres du système afin d’utiliser containerd
entant que CRI2
Voici les instructions à effectuer :
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
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
sudo sysctl --system
Installation de containerd
Nous allons installer la version CRI3 de containerd
. Cette dernière contient d’autres dépendances comme crictl
ou runc
.
cd /tmp
CTD_VER=1.4.1
wget -q https://github.com/containerd/containerd/releases/download/v${CTD_VER}/cri-containerd-cni-${CTD_VER}-linux-amd64.tar.gz
curl -s -L https://github.com/containerd/containerd/releases/download/v${CTD_VER}/cri-containerd-cni-${CTD_VER}-linux-amd64.tar.gz.sha256sum | sha256sum -c
sudo tar --no-overwrite-dir -C / -xzf cri-containerd-cni-${CTD_VER}-linux-amd64.tar.gz
rm /etc/cni/net.d/10-containerd-net.conflist
rm -r /opt/containerd
rm /tmp/cri-containerd-cni-${CTD_VER}-linux-amd64.tar.gz
sudo systemctl start containerd
sudo systemctl enable containerd
Pour vérifier si containerd
est fonctionnel il suffit d’executer la commande crictl version
qui retourne un résultat similaire à :
Version: 0.1.0
RuntimeName: containerd
RuntimeVersion: v1.4.1
RuntimeApiVersion: v1alpha2
Kubeadm, kubelet et kubectl
L’étape suivante est d’installer kubeadm
, kubelet
et kubectl
.
Ces 2 derniers seront respectivement utilisés
- pour gérer les pods des composants formant le
control-plane
- pour communiquer avec votre cluster Kubernetes
Nous allons les installer depuis le dépôt APT de Kubernetes
apt-get update && apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
Pour le moment le service kubelet
ne démarre pas car le fichier de configuration de ce dernier (/var/lib/kubelet/config.yaml
) n’est pas encore présent.
Initialisation du cluster
Pour initialiser le cluster il suffit d’exécuter la commande suivante
kubeadm init
Accès au cluster
Il suffit ensuite de copier le fichier de configuration pour accéder au cluster
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
La commande kubectl get nodes
vous permet de lister les noeuds du cluster :
NAME STATUS ROLES AGE VERSION
k8s-m01 NotReady master 7s v1.19.4
Comme on peut le voir le status du master
est NotReady
.
Les logs de kubelet
ou un kubectl describe
sur le node en question nous permettent d’en connaître la raison :
runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized
En effet, il est nécessaire d’installer un add-on réseau (avec kubeadm
il est nécessaire d’utiliser un réseau basé sur un CNI) afin que les pods puissent obtenir des adresses IP et communiquer entre eux.
Ce point est abordé dans l’article Mise en place du plugin CNI.
Points intéressants
Pour finir cet article, revenons un instant sur quelques points intéressants de l’intialisation du control-plane
par kubeadm
.
En listant les pods du cluster, on remarque plusieurs choses :
- des pods en
Pending
(dans notre exemple il s’agît des instances decoredns
) - des pods ayant comme suffixe le nom d’hôte du
master
(icik8s-m01
) - des pods en
Running
malgré un statusNotReady
sur lemaster
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-f9fd979d6-vjwbq 0/1 Pending 0 2m23s
kube-system coredns-f9fd979d6-z5tlg 0/1 Pending 0 2m23s
kube-system etcd-k8s-m01 1/1 Running 0 2m17s
kube-system kube-apiserver-k8s-m01 1/1 Running 0 2m17s
kube-system kube-controller-manager-k8s-m01 1/1 Running 0 2m17s
kube-system kube-proxy-5qh59 1/1 Running 0 2m23s
kube-system kube-scheduler-k8s-m01 1/1 Running 0 2m17s
Pods en Pending
Un describe
sur ces derniers nous permet d’en connaître la raison : ils sont dans l’attente d’un noeud avec le statut Ready
(et donc du CNI)
FailedScheduling default-scheduler 0/1 nodes are available: 1 node(s) had taint {node.kubernetes.io/not-ready: }, that the pod didn't tolerate.
Static pods
Les pods ayant comme suffixe -k8s-m01
sont des Static Pods4.
En effet, il est possible de définir des pods locaux à un noeud. Ces derniers sont gérés par le kubelet
et non par l’API Kubernetes.
Dans la configuration du kubelet
le paramètre staticPodPath
indique le dossier contenant les manifestes des Static Pods.
$ grep staticPodPath /var/lib/kubelet/config.yaml
staticPodPath: /etc/kubernetes/manifests
Dans ce dossier on retrouve bien 4 fichiers correspondants aux pods ayant le suffixe -k8s-m01
$ ls -l /etc/kubernetes/manifests/
-rw------- 1 root root 2081 Dec 9 11:58 etcd.yaml
-rw------- 1 root root 3808 Dec 9 11:58 kube-apiserver.yaml
-rw------- 1 root root 3350 Dec 9 11:58 kube-controller-manager.yaml
-rw------- 1 root root 1384 Dec 9 11:58 kube-scheduler.yaml
Les Static Pods sont :
- listés par
kubectl
- suffixés par le nom d’hôte où ils sont exécutés
Mais il sera impossible d’effectuer une action sur eux via l’API de Kubernetes
- les Admissions Controllers n’auront donc aucuns effets sur ces derniers, car ils sont initiés avant par le
kubelet
- essayez un
kubectl delete
sur un Static Pod 😉
Pods en Running
Malgré le fait que le seul noeud du cluster (k8s-m01
) soit en NotReady
certains pods sont fonctionnels…
Mais pourquoi ? Quel est donc la différence entres les pods coredns
et les autres ?
Examinons les pods en affichant plus d’informations avec kubectl get pods -A -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-system coredns-f9fd979d6-6gp6h 0/1 Pending 0 81s <none> <none> <none> <none>
kube-system coredns-f9fd979d6-bpfg9 0/1 Pending 0 81s <none> <none> <none> <none>
kube-system etcd-k8s-m01 0/1 Running 0 76s 10.64.246.3 k8s-m01 <none> <none>
kube-system kube-apiserver-k8s-m01 1/1 Running 0 76s 10.64.246.3 k8s-m01 <none> <none>
kube-system kube-controller-manager-k8s-m01 1/1 Running 0 76s 10.64.246.3 k8s-m01 <none> <none>
kube-system kube-proxy-2gjv6 1/1 Running 0 81s 10.64.246.3 k8s-m01 <none> <none>
kube-system kube-scheduler-k8s-m01 1/1 Running 0 75s 10.64.246.3 k8s-m01 <none> <none>
Les pods en Running
ont une adresse IP alors qu’il n’y a pas encore de CNI… 🤔
Examinons alors les noeuds en affichant également plus d’informations avec kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-m01 NotReady master 106s v1.19.4 10.64.246.3 <none> Ubuntu 20.04 LTS 5.4.0-1018-kvm containerd://1.4.1
L’adresse IP des pods correspond à celle de k8s-m01
.
Il existe un cas où un pod peut exister sans CNI : quand l’option hostNetwork
est à true
dans la définition de ce dernier.
Vérifions la valeur de cette dernière pour les Static Pods
$ grep hostNetwork /etc/kubernetes/manifests/*
etcd.yaml: hostNetwork: true
kube-apiserver.yaml: hostNetwork: true
kube-controller-manager.yaml: hostNetwork: true
kube-scheduler.yaml: hostNetwork: true
Pour kube-proxy
$ kubectl get daemonset kube-proxy -n kube-system -oyaml | grep hostNetwork
hostNetwork: true
Et pour coredns
$ kubetcl get deploy coredns -n kube-system -oyaml | grep hostNetwork