Featured image of post Déployer un cluster Kubernetes avec Ansible

Déployer un cluster Kubernetes avec Ansible

Déploiement d'un cluster K8S sur Proxmox avec Kubespray

Introduction

Dans mon homelab, j’ai souhaité aller plus loin dans l’automatisation en déployant un cluster Kubernetes complet sur Proxmox, en m’appuyant sur des outils d’infrastructure as code et d’automatisation comme Ansible. L’objectif était de pouvoir provisionner rapidement un cluster k8s reproductible, prêt à accueillir des workloads, tout en gardant la maîtrise de chaque étape du déploiement via du code. Ce processus d’évolution m’a naturellement conduit vers Kubespray.


Contexte

Après avoir initialement installé un cluster Kubernetes de façon manuelle avec kubeadm, j’ai constaté que le processus était particulièrement fastidieux. Cette expérience m’a convaincu dans la nécessité d’automatiser et de standardiser le déploiement, afin de gagner en reproductibilité et en efficacité.

C’est là que Kubespray intervient, ce projet open source propose des playbooks Ansible prêts à l’emploi pour installer, configurer et maintenir un cluster Kubernetes avec une approche 100% as code. Contrairement à kubeadm, il offre un contrôle fin sur les composants du cluster (CRI, CNI, DNS, etc.) tout en restant automatisé et reproductible. Dans cet article, je détaille la démarche suivie, les choix techniques réalisés, et les outils utilisés pour atteindre cet objectif.

Logo Kubespray

Composants

  • Proxmox : Fournit l’infrastructure pour exécuter et gérer des machines virtuelles.
  • Ansible : Outil d’automatisation utilisé par Kubespray pour déployer et configurer Kubernetes.
  • Terraform & Packer : Pour automatiser la création et la configuration des VMs.

Architecture cible du cluster

Architecture cible


Création des VMs

Avant de détailler le processus pour le déploiement des instances, il est important de définir en amont le dimensionnement du futur cluster afin de répondre au besoin. Pour mon homelab, j’ai opté pour une architecture simple composée de trois VMs : un control plane et deux worker nodes.

Ce choix s’explique par plusieurs raisons :

  • Haute disponibilité : Avec au moins trois nœuds, le cluster peut tolérer la perte d’un nœud sans interruption de service, ce qui est essentiel pour la résilience, même en environnement de test.
  • Simplicité et maîtrise des ressources : Ce dimensionnement reste raisonnable en termes de ressources consommées (CPU, RAM, stockage), tout en permettant d’expérimenter la plupart des fonctionnalités Kubernetes sans surcharger l’infrastructure.
  • Flexibilité : Deux workers offrent la possibilité de répartir les workloads et de tester des scénarios de montée en charge ou de tolérance aux pannes.

Une fois le dimensionnement réalisé, passons à la création des VMs, pour cela, j’ai utilisé Terraform et Packer, ce qui me permet de déployer rapidement mon infrastructure de manière totalement automatisée. J’explique en détail cette approche, notamment la création de templates Proxmox avec Packer et leur déploiement avec Terraform, dans cet article.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
############################
# VMs for Kubernetes Cluster
############################
resource "proxmox_virtual_environment_vm" "k8s_cluster" {
  for_each = {
    k8s-master = {
      description = "K8S Control Plane"
      vm_id       = 110
      ip_address  = "192.168.1.110/24"
      tags        = ["k8s_cluster", "kube_control_plane", "etcd"]
    }
    k8s-node1 = {
      description = "K8S Worker Node 1"
      vm_id       = 111
      ip_address  = "192.168.1.111/24"
      tags        = ["k8s_cluster", "kube_node"]
    }
    k8s-node2 = {
      description = "K8S Worker Node 2"
      vm_id       = 112
      ip_address  = "192.168.1.112/24"
      tags        = ["k8s_cluster", "kube_node"]
    }
  }

  name                = each.key
  description         = each.value.description
  tags                = concat(["debian12", "terraform"], each.value.tags)
  node_name           = "pve"
  vm_id               = each.value.vm_id
  pool_id             = "PROD"
  reboot_after_update = false

  ...

  clone {
    vm_id        = 1101
    datastore_id = "local-lvm"
    full         = true
    node_name    = "pve"
  }
}

Après l’application du code Terraform, les trois machines virtuelles sont déployées sur Proxmox. Chaque VM est associée à un ou plusieurs tags parmi : (k8s_cluster, kube_control_plane, etcd, kube_node). Ces tags faciliteront la génération de l’inventaire pour Kubespray que je détaille plus bas dans l’article.

VMs dans Proxmox

Configuration réseau et accès SSH

Grâce à l’utilisation de Cloud-Init lors du processus de la création des VMs (via le template Packer et l’argument initialization dans la ressource proxmox_virtual_environment_vm du provider Terraform), la configuration réseau et l’accès SSH sont automatisés :

  • Adresse IP statique : Chaque VM reçoit directement son IP statique lors du déploiement, définie dans la section ip_config de Terraform. Cela évite toute configuration manuelle post-déploiement.
  • Clé SSH : La clé publique SSH est injectée automatiquement dans chaque VM via Cloud-Init, ce qui permet une connexion immédiate sans avoir à copier la clé manuellement.

Ce processus va permettre de gérer que toutes les VMs via Ansible dès leur création, sans intervention manuelle supplémentaire. Dans mon cas, je peux donc directement utiliser les playbooks Kubespray.

Les ressources sont désormais prêtes et configurées, nous pouvons maintenant passer à la mise en place du cluster Kubernetes avec Kubespray.


Mise en place de Kubespray

Cloner et configurer le dépôt Kubespray

J’ai intégré Kubespray dans mon projet en tant qu’un submodule git. Ceci permet d’inclure un autre repository tout en gardant son historique séparé. Cela facilite la gestion des dépendances et permet de suivre une version précise de Kubespray, la mettre à jour facilement, et avoir les évolutions du projet sans mélanger les codes sources.

1
git submodule add https://github.com/kubernetes-sigs/kubespray.git kubespray

Afin de fixer la version du submodule sur un tag ou un commit spécifique, il faut se rendre dans le répertoire du submodule et checkout sur le commit ou tag désiré dans le projet. Ensuite, il faut retourner dans le répertoire principal du projet, commit les changements et pousser les modifications.

1
2
3
4
cd kubespray/
git checkout <tag_or_commit_sha>
cd ../
git commit -m "Pinned submodule to specific tag/commit"

Il est important de fixer la version de Kubespray afin de garantir la reproductibilité des déploiements et d’éviter toute instabilité liée à des changements non maîtrisés dans le projet en amont.

Une fois le projet Kubespray intégré et configuré dans le dépôt Ansible, on peut passer à la génération de l’inventaire pour Kubespray.

Création de l’inventaire Kubespray

Pour la partie inventaire ansible pour Kubespray, j’ai choisi la solution de l’inventaire dynamique avec Proxmox. Ceci permet d’avoir un inventaire qui est constamment à jour, car il est chargé sur chaque lancement de playbook, ce qui évite une maintenance manuelle du ficher d’inventaire.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
plugin: community.general.proxmox
url: https://<proxmox_host>:8006
user: "<user>"
token_id: "<token_id>"
token_secret: "<token_secret>"
validate_certs: false
want_facts: true
keyed_groups:
  - key: proxmox_tags_parsed
    separator: ""
compose:
  ansible_host: proxmox_agent_interfaces[1]["ip-addresses"][0].split('/')[0]

L’inventaire ci-dessus permet de récupérer toutes les VMs dans proxmox et de les regrouper par tags proxmox_tags_parsed et de configurer la variable ansible_host de chaque entrée dans l’inventaire avec l’IP associée à chaque VM.

Dans le cas de Kubespray, il faut respecter la nomenclature des tags comme c’est écrit dans la documentation, car c’est ce qui va servir à lancer les playbooks sur les bons hôtes.

Pour générer l’inventaire, j’utilise la commande ci-dessous, où l’option --graph va permettre de rendre la sortie de la commande un peu plus visuelle afin de bien distinguer les groupes et les hôtes de l’inventaire ansible.

1
ansible-inventory -i inventories/proxmox.yml --graph

Et dans mon cas ça donne le résultat suivant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@all:
  ...
  ...
  |--@debian12:
  |  |--k8s-node1
  |  |--k8s-master
  |  |--k8s-node2
  |--@k8s_cluster:
  |  |--k8s-node1
  |  |--k8s-master
  |  |--k8s-node2
  |--@kube_node:
  |  |--k8s-node1
  |  |--k8s-node2
  |--@terraform:
  |  |--k8s-node1
  |  |--k8s-master
  |  |--k8s-node2
  |--@etcd:
  |  |--k8s-master
  |--@kube_control_plane:
  |  |--k8s-master
  ...
  ...

Une fois l’inventaire en place, je peux tester la connectivité des VMs via la commande ad hoc ci-dessous qui utilise le module ansible ping :

1
ansible -i inventories/proxmox.yml k8s_cluster -m ping -u <username>

Ce qui donne le résultat suivant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
k8s-node1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
k8s-master | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
k8s-node2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Maintenant que je dispose de l’inventaire dynamique pour Proxmox et de machines virtuelles accessibles, je vais pouvoir passer à la partie customisation de mon cluster K8s.

Personnalisation des variables

Afin de pouvoir customiser le cluster Kubernetes, il va falloir passer par les group_vars ansible. Grâce à ces variables, nous allons pouvoir jouer sur différents points de la configuration de notre cluster tel que :

  • Paramètrage Kubernetes (Version de kube, réseau interne, DNS,…)
  • Container Runtime Interface (CRI)
  • Container Network Interface (CNI)
  • Container Storage Interface (CSI)
  • Configuration ETCD
  • Addons et composants optionnels (ArgoCD, Helm, cert-manager, MetalLB, …)
  • Et bien d’autre chose…

Ceci va être rangé dans les différents fichiers de l’inventaire afin d’avoir une meilleure organisation dans les variables pour la configuration :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
inventories/group_vars/
├── etcd
│   ├── all.yml
│   └── etcd.yml
└── k8s_cluster
    ├── addons.yml
    ├── all.yml
    ├── cri-o.yml
    ├── k8s-cluster.yml
    └── k8s-net-flannel.yml

Pour plus d’informations à propos de la configuration possible du cluster Kubernetes et des composants disponibles, il faut se référer à la documentation de Kubespray.

La configuration cible du cluster Kubernetes a été défini la prochaine étape va être le déploiement de tout ça !


Déploiement du cluster Kubernetes

Avant de lancer le déploiement avec ansible, j’ai besoin de configurer mon environnement, pour ce faire, je passe par un venv python afin de fixer la version d’ansible ainsi que de mes dépendances dans le but d’isoler le tout pour éviter les potentiels conflits avec d’autres projets. Le venv va également permettre d’avoir un environnement reproductible ce qui est crucial pour exécuter les déploiements de manière identique.

1
2
3
python3 -m venv venv
source venv/bin/activate
pip install -r kubespray/requirements.txt

Une fois l’environnement prêt, il est enfin temps de passer au déploiement du cluster, en lançant le playbook cluster.yml et en utilisant l’inventaire dynamique ansible pour Proxmox configurer au préalable.

1
ansible-playbook -i inventories/proxmox.yml -b -u <username> kubespray/cluster.yml

Une fois le playbook terminé, le cluster Kubernetes est prêt à l’emploi avec la configuration définie plus haut.

1
2
3
4
PLAY RECAP *************************************************************************
k8s-master: ok=599 changed=39 unreachable=0 failed=0 skipped=965 rescued=0 ignored=1
k8s-node1 : ok=421 changed=25 unreachable=0 failed=0 skipped=618 rescued=0 ignored=1
k8s-node2 : ok=443 changed=28 unreachable=0 failed=0 skipped=681 rescued=0 ignored=1

Vérification du cluster

Une fois le déploiement terminé, il est essentiel de vérifier que le cluster Kubernetes est opérationnel et que tous les composants fonctionnent correctement. Pour cela, plusieurs commandes kubectl permettent de valider l’état des nœuds et des pods.

Pour valider l’état des nœuds du cluster :

1
kubectl get nodes

Résultat obtenu :

1
2
3
4
NAME         STATUS   ROLES           AGE   VERSION
k8s-master   Ready    control-plane   1d    v1.31.4
k8s-node1    Ready    worker          1d    v1.31.4
k8s-node2    Ready    worker          1d    v1.31.4

Tous les nœuds doivent être dans l’état Ready, ce qui indique qu’ils sont prêts à exécuter des workloads.

Pour s’assurer que tous les pods sont en état de fonctionnement, il faut passer par la commande :

1
kubectl get pods -A

Résultat obtenu :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
NAMESPACE              NAME                                         READY   STATUS      RESTARTS      AGE
external-dns           external-dns-754bcb687f-cqncz                1/1     Running     0             24h
ingress-nginx          ingress-nginx-controller-6d7ff9f49f-2b9dq    1/1     Running     0             24h
kube-system            coredns-d665d669-7h6nf                       1/1     Running     0             24h
kube-system            coredns-d665d669-fklwz                       1/1     Running     0             24h
kube-system            dns-autoscaler-5cb4578f5f-zkr67              1/1     Running     0             24h
kube-system            kube-apiserver-k8s-master                    1/1     Running     0             24h
kube-system            kube-controller-manager-k8s-master           1/1     Running     0             24h
kube-system            kube-flannel-9n6zs                           1/1     Running     0             24h
kube-system            kube-flannel-m5xtr                           1/1     Running     0             24h
kube-system            kube-flannel-qchzm                           1/1     Running     0             24h
kube-system            kube-proxy-54k4k                             1/1     Running     0             24h
kube-system            kube-proxy-qpb4m                             1/1     Running     0             24h
kube-system            kube-proxy-zq7ww                             1/1     Running     0             24h
kube-system            kube-scheduler-k8s-master                    1/1     Running     0             24h
kube-system            nginx-proxy-k8s-node1                        1/1     Running     0             24h
kube-system            nginx-proxy-k8s-node2                        1/1     Running     0             24h
kube-system            nodelocaldns-l2nc4                           1/1     Running     0             24h
kube-system            nodelocaldns-pp8ng                           1/1     Running     0             24h
kube-system            nodelocaldns-x522v                           1/1     Running     0             24h
kubernetes-dashboard   kubernetes-dashboard-8984b9757-2pcmz         1/1     Running     0             24h
kubernetes-dashboard   kubernetes-metrics-scraper-6d4c5d99f9-z6g6b  1/1     Running     0             24h
metallb-system         controller-7f649565d4-6dcd8                  1/1     Running     0             24h
metallb-system         speaker-6pj99                                1/1     Running     0             24h
metallb-system         speaker-v44zd                                1/1     Running     0             24h
metallb-system         speaker-w8wzp                                1/1     Running     0             24h

Tous les pods doivent être dans l’état Running. Cela garantit que les composants du cluster sont correctement déployés et fonctionnels.

Ces vérifications permettent de valider que le cluster Kubernetes est prêt à accueillir des workloads et que les services essentiels sont opérationnels.

Résultat

Cluster Kubernetes post déploiement

Conclusion

L’automatisation du déploiement d’un cluster Kubernetes avec ansible via le projet Kubespray permet de gagner en fiabilité, en rapidité et en reproductibilité, ce qui est essentiel pour moi dans mon contexte de homelab.

Grâce à l’inventaire dynamique et aux variables ansible, il est simple d’adapter la configuration du cluster en fonction de mes besoins, tout en gardant la maîtrise sur chaque composant. Cette approche permet également de faciliter la maintenance et l’évolution du cluster k8s, car tout est fait as code et que kubespray prévoit également des playbooks pour ajouter un nœud dans le cluster (scale.yml) ou même remettre le cluster à zéro (reset.yml).

Workflow

Résumé du workflow

Liens utiles

Généré avec Hugo
Thème Stack conçu par Jimmy