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.
Sommaire
Section intitulée « Sommaire »- Prérequis
- Choisir les composants déployés
- Déploiement du cluster
- Version des images
- Accès
- Cycle de vie du cluster
- Réduire l’empreinte mémoire
- Utiliser une image buildée en local
- Telepresence
- Avertissements
Prérequis
Section intitulée « Prérequis »| 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.
Choisir les composants déployés
Section intitulée « Choisir les composants déployés »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,entityfishingPour ne rien déployer d’une liste, lui donner la chaîne vide — Une clé sans valeur échoue :
components: "" # ✅ aucun composant optionnelvectorizers: "" # ✅ aucun vectorizer
components: # ❌ échoue au renduComposants 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
multiminilml12v2etsentencecamembertbase: 1,4 et 3 Gi de modèle, contre 265 Mi pourallminilml6v2.
⚠️ Chart construite à partir d’un
docker-compose.ymlfigé à 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.
Déploiement du cluster
Section intitulée « Déploiement du cluster »Une seule commande, depuis la racine du dépôt :
deployment-modes/laptop/sherpa-laptop.sh upElle 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 :
kubectl -n sherpa get pods -wL’installation est prête quand les 18 pods sont Running.
Version des images
Section intitulée « Version des images »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 :
VERSION_FILE=sherpa/helm/versions/values-orca.yaml deployment-modes/laptop/sherpa-laptop.sh upChaque 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.
Connexion
Section intitulée « Connexion »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_PASSWORDdoit restersecret(le chart contient le hash correspondant, figé), etSHERPA_ADMIN_PASSWORDdoit faire au moins 8 caractères, sans quoi le compte administrateur n’est pas créé.
Cycle de vie du cluster
Section intitulée « Cycle de vie du cluster »| 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 |
Réduire l’empreinte mémoire
Section intitulée « Réduire l’empreinte mémoire »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).
kubectl -n sherpa top pods --sort-by=memoryLimiter 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 (comparaisonstr/Enum), d’où le dimensionnement sur les cœurs de l’hôte. Voir SHERPA-3033.
Éteindre les suggesters d’entraînement
Section intitulée « Éteindre les suggesters d’entraînement »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.
kubectl -n sherpa scale deploy \ kairntech-sherpa-sklearn-train-suggester \ kairntech-sherpa-phrasematcher-train-suggester \ --replicas=0Remonter à --replicas=1 avant de lancer un entraînement depuis
l’interface : sans pod derrière le Service, l’appel échoue.
Réduire le heap d’Elasticsearch
Section intitulée « Réduire le heap d’Elasticsearch »Elasticsearch réserve 1536 Mi de heap au démarrage. Sur un jeu de données de développement, 1 Gi suffit :
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).
Retirer des composants
Section intitulée « Retirer des composants »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.
sklearnest 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 desuggesters: phrasematcher.- De même,
qualityetchatsont retirables decomponentsquand on ne travaille que sur l’API.
⚠️ Les deux leviers en
kubectlne survivent pas àsherpa-laptop.sh up. Le script rejouehelm upgrade, qui réappliquereplicas: 1et l’ES_JAVA_OPTSdu chart — tous deux codés en dur dans les templates, non pilotables depuis les values. Les rejouer après chaqueup. Les deux leviers qui passent par les values (workerProcesses, listes de composants) sont eux permanents.
Utiliser une image buildée en local
Section intitulée « Utiliser une image buildée en local »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 :
docker build -t kairntech/sherpa:master .k3d image import kairntech/sherpa:master -c sherpakubectl -n sherpa rollout restart deploy/kairntech-sherpa-corePour 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
Section intitulée « Telepresence »À quoi ça sert
Section intitulée « À quoi ça sert »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.
Comment l’installer
Section intitulée « Comment l’installer »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) :
sudo apt install sshfs # ou : sudo dnf install sshfs
# Autorisation exigée par Telepresence pour monter les volumesgrep -qxF user_allow_other /etc/fuse.conf || echo user_allow_other | sudo tee -a /etc/fuse.confPuis la partie serveur, une fois par cluster :
telepresence helm install --set routeController.enabled=trueLe 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.
replaceretire le conteneur du Pod : surmongodb,elasticsearch,rabbitmqoulitellm-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 verslocalhost:8080.
Ouvrir la connexion au cluster, une fois par session, puis afficher les composants remplaçables et leur nom exact :
telepresence connect --namespace sherpa # ou namespace sur lequel sont déployés les composantstelepresence listMéthode A — l’appli est run depuis mon IDE
Section intitulée « Méthode A — l’appli est run depuis mon IDE »Écrire les variables d’environnement du conteneur dans un fichier, monter ses volumes, et rediriger le trafic vers la machine locale :
telepresence replace kairntech-sherpa-pymultirole \ --env-file /tmp/pymultirole.env \ --mount /tmp/pymultirole-volumesCharger /tmp/pymultirole.env dans la configuration de lancement de l’IDE, puis
démarrer l’application.
Les volumes montés par
--mountne 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 desherpa-chatcomprises. En méthode B le conteneur ne tourne pas sous ce compte, d’où l’option--user 0ci-dessous.
Une fois terminé, rendre la main :
telepresence detach kairntech-sherpa-pymultiroleMé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 :
telepresence replace kairntech-sherpa-chat --docker-run -- \ --rm --user 0 --dns 10.43.0.10 \ kairntech/sherpa-chat:masterLes deux options passées à docker run sont nécessaires quel que soit le
composant :
--user 0lance 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 unPermission denied—sherpa-chat, par exemple, qui lit ses clés JWT.--dns 10.43.0.10donne 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 unName or service not known—sherpa-chat, par exemple, sur sa connexion à MongoDB. L’adresse est celle du Servicekube-dns,10.43.0.10sur 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.
Commandes utiles
Section intitulée « Commandes utiles »| 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 |
Avertissements
Section intitulée « Avertissements »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.