Aller au contenu

Sherpa sur un laptop (k3d)

Déploiement de Sherpa sur un poste de développement : le chart Helm de ce dépôt, installé sur un cluster Kubernetes local fourni par k3d — un lanceur qui exécute des nœuds k3s (Kubernetes léger) dans des conteneurs Docker. Aucun serveur, aucun cloud.

Outil Version Installation
Docker 20.10+ doit être démarré : les nœuds du cluster sont des conteneurs
k3d 5.x bash <(curl -sfL https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh)
kubectl 1.25+ curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" puis sudo install -m 0755 kubectl /usr/local/bin/
helm 3.x bash <(curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3)

Machine : 16 Go de RAM et 50 Go d’espace disque libre. Mesuré avec la sélection de composants par défaut : ~9,5 Gi de RAM et 34 Go de disque.

Les composants installés sont listés dans sherpa/helm/clients/laptop/values.yaml, sous forme de listes séparées par des virgules : components, suggesters, vectorizers.

Une liste remplace entièrement celle du chart de base : pour ajouter un élément, redéclarer la liste complète.

suggesters: sklearn,phrasematcher,crfsuite,flair
components: quality,chat,litellm,entityfishing

Pour ne rien déployer d’une liste, lui donner la chaîne vide — Une clé sans valeur échoue :

components: "" # ✅ aucun composant optionnel
vectorizers: "" # ✅ aucun vectorizer
components: # ❌ échoue au rendu

Composants déployés par défaut: sherpa-core, mongodb, elasticsearch et rabbitmq.

Certains composants sont écartés par défaut pour rester léger — les ajouter en connaissance de cause :

  • entityfishing (~71 Gi de ressources à télécharger avant que le déploiement ne démarre), flair (~14 Gi), fasttext (~15 Gi) ;
  • bertopic : rien à télécharger, mais deux pods gourmands en RAM ;
  • spacy : son pod d’entraînement tire l’image GPU (CUDA) même sans carte graphique ;
  • vectorizers multiminilml12v2 et sentencecamembertbase : 1,4 et 3 Gi de modèle, contre 265 Mi pour allminilml6v2.

⚠️ Chart construite à partir d’un docker-compose.yml figé à un instant T : il est probable que des composants manquent encore. Pour ajouter un composant non pris en charge par cette chart Helm, contacter Harold.

Une seule commande, depuis la racine du dépôt :

Fenêtre de terminal
deployment-modes/laptop/sherpa-laptop.sh up

Elle crée le cluster, copie sherpa/helm/clients/laptop/example.secrets.yaml en secrets.yaml au premier lancement, installe la release Helm kairntech dans le namespace sherpa, puis affiche les URLs d’accès.

Le premier lancement télécharge ~9,5 Go d’images : compter environ 6 minutes. Les suivants repartent à chaud.

Suivre le démarrage des composants :

Fenêtre de terminal
kubectl -n sherpa get pods -w

L’installation est prête quand les 18 pods sont Running.

Pour déployer une version livrée plutôt que les tags de test, désigner un autre fichier de sherpa/helm/versions/ via la variable VERSION_FILE :

Fenêtre de terminal
VERSION_FILE=sherpa/helm/versions/values-orca.yaml deployment-modes/laptop/sherpa-laptop.sh up

Chaque service est exposé sur localhost, sur le port qu’il utilisait déjà dans le docker-compose.yml historique.

Service Adresse
API Sherpa http://localhost:7070/sherpa/
Chat http://localhost:8000/chatbot/
LiteLLM http://localhost:4000
Elasticsearch http://localhost:9200
MongoDB localhost:27017
PostgreSQL (LiteLLM) localhost:5432
RabbitMQ localhost:5672
Quality localhost:11011
Importer (builtins) localhost:10009
Multirole localhost:12008
Pymultirole localhost:12009
Pymultirole (ner) localhost:12011
Suggester crfsuite localhost:8008
Suggester sklearn (train) localhost:8808
Suggester sklearn (test) localhost:8708
Suggester phrasematcher (train) localhost:9208
Suggester phrasematcher (test) localhost:9808
Vectorizer allminilml6v2 localhost:18082

Ajouter un composant ajoute son port à cette liste — le script la recalcule à chaque up et l’affiche.

Interface Sherpa : identifiant admin, mot de passe adminadmin.

Tous les credentials de sherpa/helm/clients/laptop/secrets.yaml sont des valeurs de développement triviales, pour un cluster local non exposé. À ne jamais réutiliser ailleurs.

Seule valeur à renseigner soi-même : OPENAI_API_KEY, pour exercer les fonctions LLM (sans elle, les appels LLM échouent en 401, le reste fonctionne).

Deux valeurs ne sont pas librement modifiables : RABBITMQ_PASSWORD doit rester secret (le chart contient le hash correspondant, figé), et SHERPA_ADMIN_PASSWORD doit faire au moins 8 caractères, sans quoi le compte administrateur n’est pas créé.

Commande Effet
up crée le cluster (ou le redémarre), puis installe / met à jour la release
down arrête le cluster ; images et données conservées, up repart à chaud
nuke supprime le cluster et toutes ses données, après confirmation

La sélection par défaut tient dans ~9,5 Gi. Quand le laptop est juste, quatre leviers, du plus rentable au plus intrusif. Mesurer d’abord avec la commande top (installée par défaut).

Fenêtre de terminal
kubectl -n sherpa top pods --sort-by=memory

Limiter le nombre de process des composants Python

Section intitulée « Limiter le nombre de process des composants Python »

C’est la solution à privilégier.

Sans cette limite, les composants Python utilisent par défaut tous les cœurs de la machine : sur un laptop 12 cœurs, sklearn démarre 12 process et sature les ressources.

Dans sherpa/helm/clients/laptop/values.yaml :

workerProcesses: "2"

Mesuré sur un cluster k3d, machine 12 cœurs, avant/après :

Composant Avant Après
sklearn-test 2 271 Mi 399 Mi (−82 %)
phrasematcher-test 826 Mi 294 Mi (−64 %)
builtins-importer 324 Mi 251 Mi (−23 %)
Total des pods 7 876 Mi 5 391 Mi (−32 %)

Laisser vide en production : les conteneurs y ont des limits.cpu, et le nombre de process s’ajuste alors à cette limite, non à la machine entière.

Pourquoi ce cran est nécessaire : les images posent déjà HORIZONTAL_SCALING=external, qui devrait justement forcer 1 process sur Kubernetes. Le test est inopérant (comparaison str/Enum), d’où le dimensionnement sur les cœurs de l’hôte. Voir SHERPA-3033.

Chaque suggester se déploie en deux pods : un -train-suggester qui exécute les entraînements et un -test-suggester qui sert les annotations.

Les deux pods utilisent la même image et consomment donc autant de mémoire l’un que l’autre : 2,2 Gi pour sklearn, 825 Mi pour phrasematcher (mesuré côté test).

Or le pod d’entraînement ne sert à rien tant qu’on n’entraîne pas de modèle : il peut rester à zéro réplique le reste du temps.

Fenêtre de terminal
kubectl -n sherpa scale deploy \
kairntech-sherpa-sklearn-train-suggester \
kairntech-sherpa-phrasematcher-train-suggester \
--replicas=0

Remonter à --replicas=1 avant de lancer un entraînement depuis l’interface : sans pod derrière le Service, l’appel échoue.

Elasticsearch réserve 1536 Mi de heap au démarrage. Sur un jeu de données de développement, 1 Gi suffit :

Fenêtre de terminal
kubectl -n sherpa set env deploy/kairntech-sherpa-elasticsearch \
ES_JAVA_OPTS="-Xms1g -Xmx1g"

Le pod redémarre aussitôt (strategy: Recreate : l’ancien est arrêté avant que le nouveau ne démarre, donc quelques secondes sans Elasticsearch).

Le levier le plus efficace, et le seul qui soit permanent : ne pas déployer ce dont on ne se sert pas, via les listes de Choisir les composants déployés.

  • sklearn est le suggester le plus lourd de la sélection par défaut (2,2 Gi par pod) : un travail qui ne touche qu’aux ressources terminologiques peut se contenter de suggesters: phrasematcher.
  • De même, quality et chat sont retirables de components quand on ne travaille que sur l’API.

⚠️ Les deux leviers en kubectl ne survivent pas à sherpa-laptop.sh up. Le script rejoue helm upgrade, qui réapplique replicas: 1 et l’ES_JAVA_OPTS du chart — tous deux codés en dur dans les templates, non pilotables depuis les values. Les rejouer après chaque up. Les deux leviers qui passent par les values (workerProcesses, listes de composants) sont eux permanents.

Builder l’image avec le tag déclaré dans sherpa/helm/versions/values-test.yaml (voir Version des images), puis l’importer dans le cluster :

Fenêtre de terminal
docker build -t kairntech/sherpa:master .
k3d image import kairntech/sherpa:master -c sherpa
kubectl -n sherpa rollout restart deploy/kairntech-sherpa-core

Pour un autre tag (:ma-branche), le reporter d’abord dans values-test.yaml, puis relancer sherpa-laptop.sh up au lieu du rollout restart.

L’image importée est bien celle utilisée grâce à image.pullPolicy: IfNotPresent, posé par la surcharge laptop. Avant de tirer une image, kubelet regarde d’abord si une image portant ce tag existe déjà dans le containerd du nœud, et si oui l’utilise sans jamais contacter le registry, même si celui-ci contient un contenu différent sous le même tag.

C’est ce qui permet à l’image importée localement de rester utilisée après un rollout restart, sans être écrasée par le registry (ce que ferait en revanche pullPolicy: Always).

Telepresence retire le conteneur d’un Pod du cluster et fait suivre vers la machine locale tout le trafic qui lui était destiné. Le composant tourne alors en local — depuis un IDE ou depuis une image Docker — et le reste du cluster continue de l’appeler par son nom de Service habituel. Dans l’autre sens, la machine locale résout et joint les Services du cluster par leur nom.

Le client, sur la machine locale :

Système Installation
Debian/Ubuntu curl -fLO https://github.com/telepresenceio/telepresence/releases/latest/download/telepresence-linux-amd64.deb && sudo apt install ./telepresence-linux-amd64.deb

Le paquet sshfs est nécessaire pour monter les volumes d’un Pod (méthodes A et B) :

Fenêtre de terminal
sudo apt install sshfs # ou : sudo dnf install sshfs
# Autorisation exigée par Telepresence pour monter les volumes
grep -qxF user_allow_other /etc/fuse.conf || echo user_allow_other | sudo tee -a /etc/fuse.conf

Puis la partie serveur, une fois par cluster :

Fenêtre de terminal
telepresence helm install --set routeController.enabled=true

Le flag active un composant qui évite des boucles de routage sur les clusters locaux comme k3d.

Comment remplacer un Pod du cluster par un développement en local

Section intitulée « Comment remplacer un Pod du cluster par un développement en local »

Viser un composant applicatif. replace retire le conteneur du Pod : sur mongodb, elasticsearch, rabbitmq ou litellm-db, plus rien ne sert les données et les composants qui en dépendent tombent.

Tous les composants Sherpa écoutent sur le port 8080 dans leur conteneur. C’est ce port que le développement local doit servir. Pour écouter sur un autre port en local, ajouter --port <port local>:8080 aux commandes ci-dessous.

⚠️ Un nom de service ne porte pas de port. Connexion Telepresence ouverte, un composant s’atteint par http://<service>:8080 — http://<service> seul vise le port 80, sur lequel rien n’écoute, et échoue.

Les ports du tableau Accès restent eux aussi valides depuis localhost : l’appel au Service NodePort traverse le cluster, qui le renvoie vers la machine locale — donc vers localhost:8080.

Ouvrir la connexion au cluster, une fois par session, puis afficher les composants remplaçables et leur nom exact :

Fenêtre de terminal
telepresence connect --namespace sherpa # ou namespace sur lequel sont déployés les composants
telepresence list

Écrire les variables d’environnement du conteneur dans un fichier, monter ses volumes, et rediriger le trafic vers la machine locale :

Fenêtre de terminal
telepresence replace kairntech-sherpa-pymultirole \
--env-file /tmp/pymultirole.env \
--mount /tmp/pymultirole-volumes

Charger /tmp/pymultirole.env dans la configuration de lancement de l’IDE, puis démarrer l’application.

Les volumes montés par --mount ne sont lisibles que par le compte de l’utilisateur qui a lancé Telepresence (et par root) : l’appli lancée depuis l’IDE tourne sous ce compte, elle les lit donc sans réglage particulier — les clés JWT de sherpa-chat comprises. En méthode B le conteneur ne tourne pas sous ce compte, d’où l’option --user 0 ci-dessous.

Une fois terminé, rendre la main :

Fenêtre de terminal
telepresence detach kairntech-sherpa-pymultirole

Méthode B — j’ai déjà mon image Docker buildée (exemple sherpa-chat)

Section intitulée « Méthode B — j’ai déjà mon image Docker buildée (exemple sherpa-chat) »

Telepresence démarre l’image en lui passant les variables d’environnement et les volumes du conteneur remplacé. Tout ce qui suit -- est transmis à docker run :

Fenêtre de terminal
telepresence replace kairntech-sherpa-chat --docker-run -- \
--rm --user 0 --dns 10.43.0.10 \
kairntech/sherpa-chat:master

Les deux options passées à docker run sont nécessaires quel que soit le composant :

  • --user 0 lance le conteneur en root. Les images Sherpa tournent sous un compte interne (kairntech) qui n’est pas autorisé à lire les volumes montés par Telepresence : sans ce flag, un composant qui lit un de ses volumes s’arrête au démarrage sur un Permission denied — sherpa-chat, par exemple, qui lit ses clés JWT.
  • --dns 10.43.0.10 donne au conteneur le DNS du cluster. Sans elle, il hérite des résolveurs de la machine locale, aucun nom de Service ne résout et le composant s’arrête au démarrage sur un Name or service not known — sherpa-chat, par exemple, sur sa connexion à MongoDB. L’adresse est celle du Service kube-dns, 10.43.0.10 sur tout cluster k3s ; en cas de doute : kubectl -n kube-system get svc kube-dns -o jsonpath='{.spec.clusterIP}'.

Cette commande occupe le terminal jusqu’à l’arrêt du composant : Telepresence refuse le -d de docker run.

Telepresence monte lui-même les volumes du composant remplacé et affiche leur point de montage au démarrage : rien à ajouter pour les clés JWT de sherpa-chat. Un composant sans volume n’a pas besoin de --user 0.

Ctrl+C arrête le conteneur remplaçant et rend la main au replace du cluster avec lui : telepresence detach n’est donc plus nécessaire à ce stade.

Commande Effet
telepresence connect --namespace sherpa ouvre la connexion au cluster
telepresence status état de la connexion et des redirections
telepresence list composants du namespace et leur état
telepresence detach <deployment> rend la main au conteneur du cluster
telepresence quit ferme la connexion
telepresence uninstall --all-agents retire les agents laissés dans les Pods
telepresence gather-logs archive les logs de la machine locale pour un diagnostic

Le stockage n’applique aucun quota. Les volumes sont des répertoires sur le disque de la machine locale : les tailles affichées par kubectl get pvc ne sont pas respectées et tous les volumes partagent le même filesystem Docker. Son remplissage dégrade donc le cluster entier (DiskPressure, éviction de pods) au lieu de saturer un seul volume — et Elasticsearch passe ses index en lecture seule dès 95 % de remplissage, quelle qu’en soit la cause. D’où les 50 Go libres en prérequis.

Écarts avec le docker-compose.yml historique. Le compose embarque HAProxy, une pile de supervision (Prometheus, Grafana, Kibana, Filebeat, cAdvisor et plusieurs exporters) et des composants absents du chart (entityruler, trf, grouperf, scai, xcago, sharepoint, quality-eval). À l’inverse, le chart déploie LiteLLM et les vectorizers, absents du compose.