
turnstile : un seul modèle sur la carte, et un service qui ne ment jamais sur ce qui tourne
Dernière révision :
Sur une machine qui fait tourner plusieurs modèles locaux, tout le monde veut la mémoire de la carte : le modèle de discussion, l'assistant de code, la génération d'images, un jeu le soir. On décrit « qui a droit à quoi » dans des fichiers, et ces fichiers dérivent — ils annoncent un 27B chargé alors qu'un 3B répond sur le port. turnstile est notre réponse : un petit tourniquet qui décide qui a la carte, et qui le dit honnêtement.
Ce que vous y gagnez. Un seul modèle à la fois par carte, les demandes mises en file au lieu de se marcher dessus, et un turnstile status qui affiche ce qui tourne vraiment — conteneur vivant, port qui répond, mémoire cohérente, identité du modèle lue dans le journal du serveur. Zéro dépendance : Python 3, Docker, systemd utilisateur, pas de root.
1. Ce que turnstile n'est pas
Ce n'est pas un serveur de modèles. llama.cpp (ou un autre moteur) sert les modèles dans des conteneurs Docker — un conteneur par slot, nommé comme le slot. turnstile se contente de trois gestes : charger le slot demandé (et arrêter celui qui occupait la carte), faire patienter les demandes pendant un chargement, vérifier en boucle que la réalité correspond à ce qu'il annonce. Cette modestie est voulue : le jour où tout tombe, il n'y a qu'un seul endroit à comprendre.
2. Installation
Prérequis : Linux avec systemd et Docker, Python 3.10 ou plus, une carte NVIDIA avec nvidia-smi (sans carte, turnstile démarre quand même et ne gère que des slots CPU).
git clone https://github.com/bengua/turnstile
cd turnstile
./install.sh
Tout est installé pour l'utilisateur courant : les commandes dans ~/.local/bin, les services dans ~/.config/systemd/user, une configuration d'exemple dans ~/.config/turnstile/. Rien ne démarre tout seul — c'est vous qui décidez du moment.
3. Décrire vos slots
Une ligne par slot, dans ~/.config/turnstile/containers.map :
nom|port|carte|poids|arguments
carte vaut un index (0, 1…), all pour un modèle réparti sur les cartes, ou cpu. Partez de examples/containers.map, renommez vos conteneurs comme vos slots, et c'est tout ce que turnstile saura jamais de votre parc — le reste, il le mesure.
4. Premier lancement
systemctl --user enable --now turnstiled
turnstile status # ce qui tourne VRAIMENT, faits vérifiés par slot
turnstile slots # ce qui pourrait tourner
turnstile coder-c1 # charger un slot : already_serving / served / queued
Le premier status surprend souvent : c'est le moment où l'on découvre que ce qui tournait n'était pas ce qu'on croyait.
5. Le geste qui change la vie : la carte se demande
Un jeu ou ComfyUI veut la carte ? Il la demande au lieu de la prendre :
turnstile free-c0 # rendre la carte 0, proprement
turnstile restore # ensuite : remet exactement ce qui tournait avant
Fini les sessions tuées à la main et les modèles qui ne reviennent jamais. Chez nous, les rendus d'images et les sessions de jeu passent par là, et le 27B reprend sa place tout seul ensuite.
6. Pour l'automatisation
La même chose existe en HTTP sur le port 8130 : GET /state, GET /slots, GET /counters, POST /slot. C'est ce que nos agents utilisent pour demander un modèle au lieu de deviner. Et si vous ajoutez des fournisseurs gratuits à quota derrière la passerelle, le garde-fou qui grise un modèle épuisé est raconté dans quota-warden.
Pièges
Un conteneur = un slot = un nom. Si votre conteneur s'appelle llama-prod et votre slot coder-c1, turnstile ne devinera rien : renommez le conteneur, pas la règle.
Par défaut l'API écoute sur 0.0.0.0 — pratique pour que les autres machines du labo demandent des slots, mais restreignez avec TURNSTILE_BIND=127.0.0.1 si la machine est exposée.
NVIDIA est testé au quotidien ; AMD et Intel sont écrits d'après la documentation, pas testés. Les garde-fous (30 bascules/heure max, 20 s d'activité avant toute bascule automatique) se règlent par variables d'environnement via systemctl --user edit turnstiled.
Utile ? Dites-nous ce qu'il faut mesurer ensuite.