Avant de commencer...

Après avoir rédigé plusieurs articles parlant des certifications Kubernetes et mes astuces pour les obtenir, je me suis rendu compte que la plupart des personnes voulant passer ces certifications n'avaient pas de cluster "bac à sable" ou bien que celui-ci était installé dans un Cloud public.

Pour ce qui est d'installer Kubeadm sur le Cloud, pourquoi pas, mais cela rajoute plusieurs complexités : gestion de l'infrastructure, faire attention aux coûts générés, exposition de l'api du cluster via une IP publique.

Je voulais donc partager avec vous ma propre installation de cluster Kubernetes avec Vagrant pour la création des machines virtuelles via Virtualbox et Ansible pour toute l'installation des paquets et de la configuration de la machine.

Pourquoi Kubeadm ? et non pas K3S ou une autre solution ?

La réponse est assez simple : coller le plus possible aux conditions de l'examen. Le simulateur sur lequel vous passez votre certification est sur le système d'exploitation Ubuntu et les clusters sont installés avec kubeadm.

Cela peut paraitre anodin pour la CKAD car cette certification manipule des objets à l'intérieur du cluster de type Pod ou Deployment mais cela peut s'avérer plus impactant pour la CKA ou CKS qui demande parfois de modifier la configuration du composant kube-apiserver.

Kubeadm est, quant à lui, une solution qui permet de mettre en place un cluster Kubernetes avec le minimum nécessaire de services pour avoir un orchestrateur de conteneur opérationnel. Il permet aussi de gérer l'ensemble de cycle de vie du cluster : mise à jour, ajout et suppression d'un noeud par exemple.

Prérequis et connaissances nécessaires

Vous devez avoir trois outils indispensables pour créer le cluster : Vagrant, VirtualBox et Ansible, si vous avez l'habitude de suivre mes articles, alors vous devriez déjà connaître ces technologies.

Présentation du code

Le dépôt de code

Avant de passer à une présentation approfondie, vous pouvez télécharger le projet kubeadm-automation à l'adresse ci-dessous :

https://github.com/axinorm/kubeadm-automation

Vagrant

Tout d'abord, le fichier Vagrantfile permet de définir la structure de nos machines virtuelles (CPU, RAM, système d'exploitation), le réseau et la méthode d'installation des machines.

Plusieurs variables sont initialisées contenant des informations citées ci-dessus :

# Variables
# Image à utiliser
IMAGE_NAME = "bento/ubuntu-20.04"
# RAM
MEM = 2048
# Nombre de CPU
CPU = 2
# Nombre de noeuds
WORKER_NB = 1
# Prefix pour la plage d'adresses IP à utiliser
NODE_NETWORK_BASE = "10.0.0"

Vous pouvez modifier WORKER_NB afin d'ajouter ou d'enlever des noeuds de type worker.

Attention cependant à ne pas descendre en dessous de 2 pour le nombre de CPU, c'est un prérequis pour l'installation de kubeadm.

La variable NODE_NETWORK_BASE permet de définir le réseau privé qui sera utilisé pour la liaison entre le master et les workers.

Sa configuration est visible au niveau des lignes 26 et 34 :

# Pour le master
master.vm.network "private_network", ip: "#{NODE_NETWORK_BASE}.10"
...
# Pour les workers
worker.vm.network "private_network", ip: "#{NODE_NETWORK_BASE}.#{i + 10}"

En ce qui concerne l'exécution du playbook Ansible, tout est défini dans le bloc que l'on trouve à la fin :

  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "site.yml"
    ansible.groups = {  
      "master" => ["k8s-master"],
      "worker" => ["k8s-worker1", "k8s-worker2", "k8s-worker3"],
      "kube-cluster:children" => ["master", "worker"]
    }
  end

Il faut savoir que Vagrant génère un inventaire dynamique avec les adresses IP, ports et noms d'utilisateur, vous n'avez donc rien à faire. Vous pouvez consulter la documentation pour avoir plus d'informations à ce sujet.

Ansible

La suite concerne Ansible et plus précisément le fichier site.yml qui permet de voir ce que l'on exécute sur les machines.

Information importante : le playbook a été pensé dans un premier temps pour fonctionner sur ubuntu, si vous voulez utiliser un autre système d'exploitation, vous devrez modifier/ajouter certaines parties.

Plusieurs rôles sont définis ici pour ce qui concerne l'ensemble du cluster :

  roles:
    # Installation de ntp pour synchroniser la date et l'heure
    - { role: ntp, tags: ntp }
    # Configuration de la langue des machines
    - { role: locale, tags: locale }
    # Installation de containerd
    - { role: containerd, tags: containerd }
    # Vérification du système d'exploitation
    - { role: commons/os-checker, tags: commons }
    # Prépare l'installation des paquets pour kubeadm
    - { role: commons/pre-install, tags: commons }

De manière plus précise, les rôles kubernetes/master et cni seront joués sur le master et kubernetes/node sur les noeuds.

Comme vous pouvez le constater, Docker n'est plus par défaut sur les dernières versions de Kubernetes, c'est aussi le cas avec kubeadm.

L'ensemble des valeurs à surcharger pour ces rôles se trouve dans le fichier group_vars/all.yml, en voici un extrait :

# Kubernetes
kube_package_version: 1.20.11
kube_version: v{{ kube_package_version }}

...

service_cidr: "10.96.0.0/12"
pod_network_cidr: "10.244.0.0/16"

...

# Timezone and language configuration
locale_lang: fr_FR.UTF-8
ntp_timezone: Europe/Paris

On peut y modifier, par exemple, la version, les plages d'adresses IP pour les services et pods ainsi que tout ce qui est propre à la configuration de la langue, de la date et de l'heure.

La partie qui permet de créer le cluster avec la commande kubeadm se trouve dans le fichier roles/kubernetes/master/tasks/init.yml :

...
- name: Init Kubernetes cluster
  when: reset_cluster is succeeded
  shell: |
    kubeadm init --service-cidr {{ service_cidr }} \
                 --kubernetes-version {{ kube_version }} \
                 --pod-network-cidr {{ pod_network_cidr }} \
                 --token {{ token }} \
                 --apiserver-advertise-address {{ master_ip }} \
                 --cri-socket=/var/run/{{ container_runtime }}/{{ container_runtime }}.sock \
                 {{ kubeadm_opts }} \
                 {{ init_opts }}
  register: init_cluster
...

Pour les noeuds, la commande kubeadm join se trouve quant à elle dans le fichier roles/kubernetes/node/tasks/join.yml :

...
- name: Join to Kubernetes cluster
  when: reset_cluster is succeeded
  shell: |
    kubeadm join --token {{ token }} \
                --discovery-token-unsafe-skip-ca-verification \
                --cri-socket=/var/run/{{ container_runtime }}/{{ container_runtime }}.sock \
                {{ master_ip }}:6443
  register: join_cluster
...

Ce sont les commandes kubeadm classiques que vous avez l'habitude d'exécuter si vous déployez votre cluster manuellement.

Vous êtes prêt ?

Une fois l'ensemble des prérequis installés, il ne reste plus qu'à lancer le tout et déployer notre cluster, une seule commande est nécessaire :

vagrant up

Une fois que cette commande est terminée, vous pouvez vous connecter sur les noeuds :

vagrant ssh k8s-master
# Ou
vagrant ssh k8s-worker1

Et regarder la configuration de votre cluster au niveau des noeuds :

kubectl get nodes -o wide

Ce qui devrait vous retourner ceci :

vagrant@k8s-master:~$ kubectl get nodes
NAME          STATUS   ROLES                  AGE   VERSION
k8s-master    Ready    control-plane,master   22m   v1.20.11
k8s-worker1   Ready    <none>                 19m   v1.20.11

Le mot de la fin

Cette solution est assez facile à mettre en place, je l'ai énormément utilisé lors de mes préparations aux certifications en mode "bac à sable".

Cela permet de monter et casser rapidement votre cluster en cas d'erreur, sans perdre de temps, ni d'argent. :)

Il est possible d'aller plus loin et d'en faire un "vrai" simulateur avec des exercices à réaliser et des scripts de vérification, un peu comme si on voulait recréer killer.sh mais sur son poste.

J'espère que ce projet vous sera utile que ce soit pour vous entrainer ou simuler un environnement Kubernetes pour vos développements.