DEV Community

Cover image for Installer mon premier cluster K3s pour BMI : choix, automatisation et premières difficultés
Anatole HAGBE
Anatole HAGBE

Posted on Edited on

Installer mon premier cluster K3s pour BMI : choix, automatisation et premières difficultés

Dans le premier article, j’ai présenté BMI et le plan de mon mentorat avec Edouard HINVI qui a généreusement mis à ma disposition un VPS qui tourne sous Ubuntu 26.04 LTS, avec environ 8 Go de RAM et 190 Go de disque.

Dans cet article, je fais le point de comment j’ai installé un cluster K3s avec Ansible, et y déployé BMI à l'aide de mon pipeline GitLab.

Pourquoi K3s

K3s est une distribution Kubernetes certifiée, légère, livrée en un seul binaire avec les composants de base déjà inclus. Je l’ai choisi, avec ses réglages par défaut, parce que mon workload est simple et léger, et que je voulais un cluster que mon VPS puisse porter sans difficulté. K3s simplifie l’installation. Plus tard, j’aimerais monter chaque composant (réseau, stockage, control plane, certificats, etc.) pour mieux comprendre le fonctionnement d’un cluster.

Pourquoi Ansible plutôt que Terraform

Le backlog prévoyait de créer le cluster avec Terraform et Cloud-init. Cependant, j'ai retenu Ansible pour l'installation. En effet le VPS existait déjà : il n’y avait rien à provisionner, seulement K3s à installer. De plus le projet K3s maintient une collection, k3s-ansible, qui automatise la création du cluster. Je n’ai qu’à décrire mon serveur et à lancer le playbook. Si j’avais dû créer le serveur, Terraform avec cloud-init aurait été le bon choix. Ce choix m'a poussé à modifier légèrement mon schéma d'architecture.

Nouvelle architecture

Installation

J'ai créé un nouveau projet Ansible en dehors de BMI pour installer le cluster et conserver les templates des manifests de déploiement. Il est géré avec uv, un gestionnaire de paquets et d’environnements Python rapide. Il isole les dépendances, facilite la gestion des versions et évite les conflits avec le système.

[project]
name = "bmi-cluster"
version = "0.1.0"
description = "Create BMI cluster and manage deployments"
requires-python = ">=3.14"
dependencies = [
    "ansible>=14.5.0",
    "ansible-lint>=26.9.0",
    "netaddr>=1.3.0",
]
Enter fullscreen mode Exit fullscreen mode

Ensuite, j'ai créé un fichier requirements.yml qui référence la collection k3s-ansible.

collections:
  - name: https://github.com/k3s-io/k3s-ansible.git
    type: git
    version: 1.2.2
Enter fullscreen mode Exit fullscreen mode

Son installation est très simple :

uv run ansible-galaxy install -r requirements.yml
Enter fullscreen mode Exit fullscreen mode

Le fichier inventory.yml définit les paramètres de connexion SSH au VPS, qui sont lus dans les variables d’environnement.

k3s_cluster:
  vars:
    ansible_host: "{{ lookup('env', 'CLUSTER_HOST') }}"
    ansible_port: "{{ lookup('env', 'CLUSTER_PORT') }}"
    ansible_user: "{{ lookup('env', 'CLUSTER_USER') }}"
    api_endpoint: "{{ lookup('env', 'CLUSTER_HOST') }}"
    k3s_version: v1.37.0+k3s1
  children:
    server:
      hosts:
        staging:
Enter fullscreen mode Exit fullscreen mode

La création du cluster tient en une commande :

uv run ansible-playbook -i inventory.yml k3s.orchestration.site
Enter fullscreen mode Exit fullscreen mode

Les captures suivantes montrent le début et la fin de l'installation. Le warning sur la deuxième capture s'explique par le fait que j'ai un cluster mono-noeud : juste le server, pas d'agent.

Début de l'installation

Fin de l'installation

À la fin de l'installation, j'ai vérifié le status du service systemd k3s, du noeud et des pods.

Service k3s status

Node + Pods

Le playbook k3s-ansible crée aussi sur le control node, un fichier ~/.kube/config.new qui donne un accès admin au cluster.

Déployer BMI depuis le pipeline

Côté BMI, j’ai modifié le pipeline pour déployer sur le cluster. Il récupère le fichier bmi.tpl.yml qui contient des templates de namespace, de deployment, de service et d' ingress. Il y injecte des paramètres pour générer un manifest Kubernetes, l’applique, vérifie le déploiement et sauvegarde le manifest dans les artifacts. Voici un extrait des templates :

# Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: ${APP_NAMESPACE}
---

# Service
apiVersion: v1
kind: Service
metadata:
  name: bmi
  namespace: ${APP_NAMESPACE}
spec:
  selector:
    app: bmi
  ports:
    - port: 80
      targetPort: http
Enter fullscreen mode Exit fullscreen mode

Le manifest complet est dans le dépôt.

Le déploiement se fait automatiquement dans l'environnement staging quand je pousse sur la branche main. Pour la production, il faut un tag et un clic manuel. Voici un extrait du pipeline de déploiement :

deploy-staging:
  extends: .deploy
  environment: staging
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
    - when: never

deploy-production:
  extends: .deploy
  environment: production
  rules:
    - if: '$CI_COMMIT_TAG'
      when: manual
    - when: never
Enter fullscreen mode Exit fullscreen mode

Le kubeconfig est stocké dans une variable GitLab secrète et masquée (KUBECONFIG_B64), encodée en base64. Il n’apparaît donc jamais dans les logs.

Extrait du pipeline

Difficultés

Ma principale difficulté a été de choisir les bonnes valeurs pour les ressources (cpu et memory) du Deployment, ainsi que les paramètres des probes (readinessProbe et livenessProbe). Pour l’instant, mes valeurs sont des estimations. Je vais les ajuster en observant la consommation réelle de BMI avec le monitoring, et en testant les probes sur le temps de démarrage de l’application.

Une autre est liée aux limites des runners du compte gratuit sur GitLab : mon pipeline reste parfois plus de 30 minutes en attente avant de démarrer. Je compte donc déployer mon propre runner dans le cluster Kubernetes et l'utiliser les prochaines fois.

Prochaines étapes

Rien n’a cassé, mais un cluster qui répond ne fait pas un service exploité. La semaine prochaine, je travaillerai sur le monitoring du cluster et de l’application, puis sur la centralisation des logs.

Je suis conscient que le kubeconfig actuellement donne un accès admin au cluster, ce qui est trop large pour un simple déploiement. Je vais créer un ServiceAccount limité au namespace de BMI, un par environnement. Et vous, comment sécurisez-vous l’accès de vos pipelines à votre cluster ?

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.