Anatomie d'un campus auto-hébergé : openEDU sur un cluster k3s à trois nœuds
La porte d'entrée du déploiement est volontairement banale : une seule page de connexion. Mais derrière cet identifiant OIDC sur home.openedu.graphwiz.ai se cache un espace de travail numérique complet et auto-hébergeable pour l'enseignement supérieur — documents, groupware, messagerie instantanée, enseignement vidéo, plateforme d'apprentissage, rédaction collaborative et IA sur site — fonctionnant sur trois nœuds bare-metal avec 72 vCPU et environ 1 To de RAM. Pas d'hyperscaler, pas de facturation SaaS par siège, aucune donnée qui quitte l'établissement.
Voici la vitrine de ce déploiement : ce qui tourne où, comment tout est câblé, et les leçons opérationnelles qui n'apparaissent qu'en exploitant réellement la plateforme.
La pile en un coup d'œil
| Couche | Choix | Pourquoi |
|---|---|---|
| OS | Ubuntu 24.04 LTS | ennuyeux, soutenu, prévisible |
| Kubernetes | k3s v1.36 | API K8s complète, une fraction du poids opérationnel |
| Ingress | HAProxy Ingress + VIP L2 MetalLB | une IP d'entrée stable, aucun load balancer cloud requis |
| TLS | cert-manager + Let's Encrypt (wildcard) | un certificat wildcard, renouvelé automatiquement, partagé par tous les ingress |
| GitOps | Argo CD | six Applications, état du cluster = état du git |
| Identité | Keycloak (OIDC) + OAuth2 Proxy v7.15.4 | une seule connexion pour chaque service |
| Stockage | volumes locaux + blocs RWO, sauvegardes compatibles S3 (SeaweedFS) | simple, autonome |
| IA | Ollama (deux instances) + Open WebUI | l'inférence ne quitte jamais le bâtiment |
La suite derrière le portail
Le portail lui-même est un tableau de bord. Après connexion via OAuth2 Proxy, qui délègue à Keycloak via OpenID Connect, les utilisateurs arrivent sur une page de tuiles de services. Chaque tuile mène à une composante de l'espace de travail openEDU :
- Documents et collaboration — Nextcloud 34 avec Collabora, CryptPad pour l'édition collaborative en temps réel.
- Communication — Matrix (Synapse) avec Element pour la messagerie, SOGo pour la groupware et le courrier.
- Enseignement — Ilias comme LMS, BigBlueButton et Jitsi pour les séminaires vidéo, JupyterHub pour les notebooks de cours, Overleaf pour la rédaction LaTeX collaborative.
- Automatisation et opérations — n8n pour l'automatisation des flux, un service de facturation, des tableaux de bord Grafana pour l'équipe plateforme.
Tout est déployé sous forme de manifests Kubernetes dans des espaces de noms par domaine (openedu, llm, home, monitoring, backup), de sorte qu'un composant défaillant reste confiné :
$ kubectl get ingress -A
NAMESPACE NAME HOSTS
home home-portal home.openedu.graphwiz.ai
argocd argocd argocd.home.openedu.graphwiz.ai
llm open-webui ai.home.openedu.graphwiz.ai
llm ollama-ingress ollama.home.openedu.graphwiz.ai
openedu ilias-ingress lms.home.openedu.graphwiz.ai
openedu invoicing invoicing.home.openedu.graphwiz.ai
monitoring grafana grafana.home.openedu.graphwiz.ai
Un nom d'hôte par service, un certificat wildcard, une connexion. C'est tout le modèle mental dont les utilisateurs ont besoin.
Pourquoi k3s sur du bare metal
Le cluster se compose de trois nœuds Ubuntu 24.04 — un plan de contrôle, deux workers — reliés par k3s. Pour une organisation sans équipe plateforme, k3s atteint le point d'équilibre idéal : l'API Kubernetes complète pour Argo CD et cert-manager, sans l'élevage d'etcd, les factures de plan de contrôle cloud, ni une installation à treize composants.
Le trafic entre par une VIP de couche 2 MetalLB devant un contrôleur d'ingress HAProxy. Comme la VIP est annoncée sur le réseau local, les services obtiennent des adresses internes stables aussi bien que publiques — utile pour des services comme le relais SMTP et la cible de sauvegarde qui ne devraient pas se trouver derrière un ingress HTTP du tout.
TLS est un problème résolu par conception : un seul ClusterIssuer émet un certificat wildcard de Let's Encrypt, chaque ingress référence le même secret, et le renouvellement est invisible :
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
solver:
- dns01: {...}
Les certificats wildcard sont le choix pragmatique pour un déploiement de plus de vingt noms d'hôte. L'alternative — une ressource certificat par ingress — est plus « correcte » et considérablement plus fastidieuse.
GitOps, ou : le cluster est un artefact de build
Argo CD gère six Applications : le portail, la suite openEDU, le serveur de messagerie, l'infrastructure plateforme et les piles produits voisines. Rien n'est installé à la main. Un changement est un commit ; la synchronisation est déclarative ; la dérive est visible dans l'interface Argo CD.
Cela porte ses fruits surtout lors des incidents et des migrations. Quand une migration de domaine a récemment touché chaque composant, la solution n'a pas été un runbook d'incantations manuelles kubectl edit — c'était la mise à jour des manifests dans git, en laissant Argo CD converger. Les deux vrais bugs rencontrés en chemin étaient tous deux dans git, tous deux corrigeables par des commits :
- Des liveness probes qui mentaient. Les probes
httpGetdu kubelet n'honorent pas fiablement un en-têteHost:personnalisé, donc Nextcloud répondait à chaque probe par un 400 et entrait dans une boucle de redémarrage. La correction a consisté à passer à des probesexecexécutant uncurldéterministe dans le conteneur :
exec:
command:
- curl
- -fsS
- -H
- "Host: nextcloud.home.openedu.graphwiz.ai"
- http://localhost/status.php
- Des variables d'environnement valables uniquement à l'installation.
NEXTCLOUD_TRUSTED_DOMAINSne configure qu'une installation fraîche. Les changements de domaine après installation doivent passer parocc config:system:set— la variable d'env du conteneur ne fait silencieusement rien. Cette classe de bug mérite d'être retenue pour chaque image avec état : les variables d'env configurent l'installateur, pas l'application.
Débugger des pods avec état sans magie
Les données de la suite vivent sur des volumes persistants RWO (read-write-once). Quand la base de données de Synapse a dû être opérée après la migration de domaine, kubectl debug ne pouvait pas simplement s'attacher au volume — un volume RWO est attaché à exactement un nœud. Le schéma qui fonctionne : épingler un pod éphémère au nœud précis qui porte l'attachement du volume, monter le PVC et opérer depuis là.
$ kubectl get volumeattachments | grep pvc-synapse-data
$ kubectl debug -it node/<holding-node> --image=alpine \
-- mount /dev/vol /mnt && sqlite3 /mnt/homeserver.db
Rien de tout cela n'est exotique. Tout cela est documenté dans le dépôt, juste à côté des manifests concernés — c'est exactement le propos. Un déploiement maintenu par une petite équipe ne peut pas se permettre un savoir tribal.
L'IA sur site comme citoyen de première classe
Deux instances Ollama se trouvent derrière leurs propres ingress, avec Open WebUI devant pour l'usage interactif. Les modèles sont téléchargés et servis localement ; les prompts des chercheurs ne traversent jamais une API tierce. Pour un secteur lié par le RGPD et la confidentialité institutionnelle, l'inférence locale n'est pas un nice-to-have — c'est la raison pour laquelle le déploiement peut offrir des fonctionnalités d'IA du tout. Combiné avec n8n pour l'automatisation, les services peuvent chaîner des modèles locaux en workflows sans une seule dépendance externe.
Ce que cela coûte à faire tourner
Le registre honnête d'un campus auto-hébergé :
- Matériel : trois nœuds, déjà amortis — capacité disponible à 72 vCPU/1 To.
- Personnel : le vrai coût. Une personne à l'esprit plateforme peut gérer cela ; une équipe d'une personne et demie le rend confortable.
- Licences : zéro. Chaque composant est open source.
- Dépenses cloud : un domaine, un hébergement DNS, rien d'autre.
Les compromis sont tout aussi honnêtes : vous possédez le patching, la planification de capacité et la reprise après sinistre. Les sauvegardes partent selon un calendrier vers une cible compatible S3 (SeaweedFS), les restaurations sont répétées et — l'étape que tout le monde saute — le chemin de restauration est testé avec la même rigueur que les sauvegardes sont prises.
À retenir
Un espace de travail numérique open source crédible pour l'éducation n'a pas besoin d'un hyperscaler, d'une offre Kubernetes managée ni d'un département platform engineering. Il lui faut : un petit cluster k3s, du GitOps dès le premier jour, un fournisseur d'identité, du TLS wildcard et la discipline de garder toute la pile dans git. Commencez par le portail et l'IdP — dès que le SSO fonctionne, chaque service supplémentaire n'est qu'un manifest et un enregistrement DNS.
La pile décrite ici est l'infrastructure qui propulse openEDU, un espace de travail numérique open source pour les établissements d'enseignement supérieur. La vue interactive de son écosystème se trouve sur landscape.openedu.graphwiz.ai.