Déploiement des serveurs linux pour Kubernetes

L’étape suivante consiste à préparer les serveurs pour l’installation de Kubernetes.

Je ne vais pas détailler la mise en oeuvre d’une VM sous vSphere, mais uniquement m’attacher à préciser quelques points et éléments de configuration nécessaires au bon fonctionnement de Kubernetes sur une plateforme vmware.

Je précise que je me suis basé sur le tutoriel suivant.

Je ne vais pas le suivre à la lettre et m’en détacher par la suite surtout au niveau de l’étape suivante qui consistera à l’installation de Kubernetes. Mais dans un premier temps, les prérequis proposés sont à mettre en oeuvre.

Point nomenclature et sizing

Il est de bon ton dans l’IT actuelle de ne plus prêter attention à la nomenclature de ses serveurs. À l’heure des microservices ou les ressources sont éphémères et en changement permanent, il n’est plus nécessaire de faire un brainstorming pour choisir le petit nom de ses machines (on passe du temps sur les tags maintenant 😊). Néanmoins s’accorder sur une nomenclature pour ses noeuds Kubernetes reste une bonne idée.

Voici les VMs que je vais déployer. Il s'agit de l'infrastructure de base, elle pourra être complétée par la suite avec des noeuds complémentaires

VMs

Size

Role

IP

prdinfhap501

1vCPU, 1GB Ram,

30 GB Disque

LoadBalancer HA Proxy LAN

192.168.10.45

prdk8smst501

2vCPU, 4GB Ram,

30 GB Disque

Master Kubernetes 01

192.168.10.56

prdk8swok501

4vCPU, 4GB Ram,

30 GB Disque

Worker LAN Kubernetes 01

192.168.10.57

prdk8swok502

4vCPU, 4GB Ram,

30 GB Disque

Worker LAN Kubernetes 01

192.168.10.58

prdinfhap511

1vCPU, 1GB Ram,

30 GB Disque

LoadBalancer HA Proxy DMZ

192.168.5.57

prdk8swok511

4vCPU, 4GB Ram,

60 GB Disque

Worker DMZ Kubernetes 01

192.168.5.57

prdk8swok512

4vCPU, 4GB Ram,

60 GB Disque

Worker DMZ Kubernetes 01

192.168.5.58

Un premier point très important : j'ai utilisé des noms de VMs en minuscule pour les serveurs K8S. Qu'il s'agisse des noms dans l'inventaire vCenter ou des hostnames au niveau OS, il est primordial d'avoir une cohérence dans les noms pour les échanges Kubernetes/vSphere. Il faut avoir une correspondance entre le nom dans le vCenter, le hostname et le nom du node dans Kubernetes. Le plus simple est de tout mettre en minuscule.

Préparation des VMs

Configuration UUID des VMs (noeud kubernetes uniquement)

L’UUID pour « Universal Unique Identifier » est une suite de caractères alphanumériques qui permet d’identifier de façon certaine chaque périphérique de stockage et de partition. Il est calculé automatiquement fonction de la configuration du serveur hôte au moment de la création ou du formatage de la partition. Dans le cas de VMWARE, cette option permet de présenter un UUID cohérent pour éviter tout problème de montage des volumes.

Il faut donc procéder aux opérations suivantes

 Configuration VM

 

Configuration vmware pour disk.EnableUUID 02

 

Configuration vmware pour disk.EnableUUID 03

 

Configuration vmware pour disk.EnableUUID 04


Préparation de l'OS

"PhotonOS" ne présente pas de difficultés particulières pour son installation. Il suffit de récupérer l'ISO, de booter dessus et de suivre l'assistant. Il y'a très peu d'éléments à renseigner. Pour le stockage, on peut rester sur la configuration par défaut. Concernant le réseau, il est conseillé d'appliquer directement l'IP de manière statique, même si on la reconfigurera après. Le profil retenu est "Photon minimal" et le kernel choisi est "VMware Hypervisor optimized".

Pour plus de détail, je vous invite à suivre la documentation d'installation sur le site officiel.

Les éléments suivants sont des éléments de configuration complémentaire, non obligatoire, mais que j'ai souhaité mettre en oeuvre pour plus de praticités.

PhotonOS utilise comme gestionnaire de package une version allégée de l'outil de RedHat 8 nommée ici "tdnf". Pour ceux qui le souhaitent, "yum" peut aussi être déployé et utilisé.

Création d'un utilisateurs supplémentaires

Par défaut, seul l'utilisateur root est disponible et l'accès SSH n'est pas possible. Je vais donc créer un utilisateur dédié à l'accès distant.

useradd mon_user
passwd mon_user

Suppression de l'expiration des mots de passes

Certains vont sans doute crier au scandale pour des questions de sécurité (et ils n'auraient pas tort) mais je vais désactiver l'expiration des mots de passe configurée par défaut pour l'utilisateur root et mon user.

chage -I -1 -m 0 -M 99999 -E -1 mon_user
chage -I -1 -m 0 -M 99999 -E -1 root

Configuration clavier et langue

C'est toujours mieux de travailler avec le bon mappage de clavier et les bons paramètres régionaux.

tdnf install kbd -y
tdnf install glibc-i18n -y

Il faut éditer le fichier "/etc/locale-gen.con" pour ajouter "fr_FR.UTF-8 UTF-8"

echo "fr_FR.UTF-8 UTF-8" >> /etc/locale-gen.conf

locale-gen.conf

locale-gen.sh

localectl set-locale LANG="fr_FR.UTF-8" LC_CTYPE="fr_FR.UTF-8"

Configuration date et temps

La gestion du temps est toujours primordiale sur les serveurs. Je ne saurais trop vous conseiller d'utiliser un serveur NTP comme source de référence. Me concernant, c'est mon contrôleur de domaine, lui-même synchronisé sur internet qui fournit ce service.

Il faut éditer le fichier "/etc/systemd/timesyncd.conf" et ajouter son serveur NTP

timesyncd.conf

systemctl restart systemd-timesyncd

Ajout du certificat CA local

Si vous disposez d'une ou plusieurs PKI internes, il est préférable d'ajouter leurs certificats a la base des certificats de confiance de l'OS.

 tdnf install openssl-c_rehash

Ajoutez vos certificats dans "/etc/ssl/certs"

Puis il faut lancer la commande suivante qui est founie dans le package openssl-c_rehash

 rehash_ca_certificates.sh

Configuration du réseau

Malgré avoir positionné une IP fixe à l'installation de l'OS, il m'est arrivé, notamment après un update du système, de basculer en DHCP. Pour éviter ce problème, je refixe mon IP via un fichier de configuration "10-static-en.network".

cat > /etc/systemd/network/10-static-en.network << "EOF"

[Match]
Name=eth0

[Network]
Address=mon_ip/mask
Gateway=ip_gateway
DNS=ip_dns
EOF


Ne pas oublier d'appliquer les bon droits

chmod 644 /etc/systemd/network/10-static-en.network

Paquets complémentaires

Je vais avoir besoin de mapper des volumes NFS, j'installe donc le paquet nécessaire.

tdnf install nfs-utils -y

Updates du système

Il est conseiller de faire un update complet du système avant de continuer

tdnf distro-sync --refresh

tndf update

Cas de la VM HaProxy

Pour déployer HAproxy, il suffit de taper la commande suivante

 tdnf install haproxy

Nous verrons sa configuration plus tard

Cas des VM Kubernetes

Les VMs qui vont servir aux noeuds Kubernetes, master comme worker doivent avoir une configuration spécifique.

Désactivation de la SWAP

Kubernetes ne supporte pas pour le moment l’usage de la swap. Si vous souhaitez savoir pourquoi n’hésiter pas à lire le topic suivant mais en gros, dans le fonctionnement de Kubernetes les pods ne devraient jamais avoir à utiliser la swap et intégrer son support est compliqué.

La commande suivante est à passer sur chaque serveur K8S

sudo swapoff -v /swapfile

Il faut également commenter la référence à la swap dans le fichier fstab

Désactivation de la swap
Packets supplémentaires

Il faut installer dès à present les packets suivants

tdnf install kubernetes kubernetes-kubeadm

Configuration sysctl

Il est nécessaire d'adapter légèrement son système pour Kubernetes

cat <<EOF | 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


On force la relecture de la config

sysctl --system

Ajout des modules complémentaires pour containerd

Je profite de cette partie pour faire le point sur un petit sujet parfois délicat : le runtime dédié à l'exécution des conteneurs.

Au moment ou Docker est arrivé sur le marché, ce dernier s'occupait de tout, de la création des images à leur exécution (enfin pas tout à fait au tout début) en passant par la manipulation de ces dernières.

Puis, comme l'IT aime la concurrence, d'autres "moteurs" d'exécution de conteneurs sont apparus comme "rkt". Plutôt que de jouer à qui est le meilleur, les différents acteurs gravitant autour du conteneur ont créé l'Open Container Initiative afin de créer un standard concernant la manière de démarrer un conteneur. Docker qui disposait déjà de son module au sein de son écosystème a "donné" ce dernier à la communauté est c'est ainsi que "runc" est devenu la norme.

Mais, "runc" est très bas niveau et se concentre sur le lancement des conteneurs uniquement. Le reste des outils "Docker" sont restés pour la manipulation des images par exemple.....et c'est la que "containerd" fait son apparition, car il "remplace" ce que faisait également Docker avant, notamment pour fournir une API et interagir avec les conteneurs...même si on continu d'utiliser les commandes dockers....

En résumé pour l'instant on n'a

Voici une image d'illustration issue de l'article rédigé par un certain Avijit Sarkar sur Medium qui illustre parfaitement l'explication ci-dessus


Containerd et docker

Ce n’est pas fini, car maintenant on n'a aussi la problématique d'orchestration. Comme déjà évoqué, Kubernetes se focalise sur ce besoin. Il s'assure en permanence d'avoir un parc de conteneurs conforme aux souhaits des utilisateurs, mais le "moteur" en lui-même d'exécution des conteneurs n'est pas son problème. Kubernetes propose donc le Runtime Interface (CRI) qui définit les interactions entre K8S et les runtimes de conteneurs. Dès lors qu'un runtime respecte CRI, il peut être utilisé par Kubernetes.

Par contre, chose importante, depuis Kubernetes 1.20, le runtime d'origine de Docker est déprécié et ne sera plus supporté dans les versions futures. Ce qui a provoqué un certain vent de panique...sauf que si vous avez suivi, ça fait pas mal de temps que le runtime Docker n'est plus docker...mais containerd...et aujourd'hui la plupart du temps quand vous tapez vos commandes docker vous interagissez avec containerd...qui est lui est compatible "CRI" donc utilisable avec Kubernetes !

En faite, les développeurs et les administrateurs ont tellement pris l'habitude de manipuler la CLI Docker, que malgré le faite qu'une CLI containerd soit disponible, on continu d'utiliser les commandes Docker...mais le moteur en dessous à bien changé :)

Pour terminer, RedHat tente également de changer la donne avec Podman qui "permet de lancer des conteneurs rootless et daemonless, tout en gardant une compatibilité avec le CLI docker"...mais je vais m'arrêter la !

Tous ce blabla pour arriver aux élements de configurations à appliquer à containerd face à Kubernetes.

Il est necessaire de s'assurer du chargement de certains modules du kernel via le fichier " /etc/modules-load.d/containerd.conf"

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

modprobe overlay
modprobe br_netfilter


On passe maintenant au fichier "/etc/containerd/config.toml". Il faut qu'il dispose du conteneur suivant:

disabled_plugins = [""]

#root = "/var/lib/containerd"
#state = "/run/containerd"
#subreaper = true
#oom_score = 0

#[grpc]
# address = "/run/containerd/containerd.sock"
# uid = 0
# gid = 0

#[debug]
# address = "/run/containerd/debug.sock"
# uid = 0
# gid = 0
# level = "info"

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

/etc/containerd/config.toml


On redemarre le service containerd et on s'assure que docker soit lancé au démarrage (et donc containerd...)

systemctl restart containerd

On n'oublie pas la partie docker via le fichier "/etc/docker/daemon.json"

cat <<EOF | tee /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

systemctl enable docker
systemctl start docker

Parametre des "kubelet" (facultatif)

Chaque serveur membre d'un cluster Kubernetes dispose d'un "agent" aussi appelé kubelet. Il vient avec l'installation des paquets kubernetes.

Une grosse partie des composants K8S s'exécute sous forme de conteneurs, mais "kubelet" reste sous forme de service classique, car en plus d'assurer la communication entre le master et les workers, ils sont chargés de lancer les "statics pods" soit les pods de bases chargés eux-mêmes d'exécuter le reste des composants Kubernetes....et oui le "scheduler" en charge de la planification de l'exécution des "pods" est lui même un pod...donc il faut bien qu'il soit lancé une première fois...

Pour cela les "Kubelet" surveillent le contenu d'un répertoire spécifique (habituellement /etc/kubernetes/manifest).Si celui-ci contient des yaml décrivant des pods, ils seront automatiquement exécutés.

J'ai été confronté à une erreur d'exécution des kubelets sur PhotonOS, impossible de savoir si c'est normal, mais comme le bon fonctionnement des "kubelet" est un prérequis obligatoire au déploiement du cluster, j'ai du ajouter la ligne suivante au fichier de configuration du service "/etc/sysconfig/kubelet"

echo "KUBELET_EXTRA_ARGS=--cgroup-driver=systemd" > /etc/sysconfig/kubelet

Il n'est forcément nécessaire d'avoir à faire cela pour vous. J'ai fait d'autres installations ou je n'ai pas eu de problème, sachez juste que si vos kubelet refusent de démarrer, c'est peut être une piste.