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.
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",
]
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
Son installation est très simple :
uv run ansible-galaxy install -r requirements.yml
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:
La création du cluster tient en une commande :
uv run ansible-playbook -i inventory.yml k3s.orchestration.site
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.
À la fin de l'installation, j'ai vérifié le status du service systemd k3s, du noeud et des 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
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
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.
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.