11 KiB
Mise en production
Déploiement de Stream Control : l'interface en Docker Compose, les agents en une commande, les artefacts construits et publiés par Gitea Actions.
┌──────────────────────────┐ ┌───────────────────────────────┐
│ Gitea │ │ Machine du plan de contrôle │
│ ├─ registre d'images ───┼───────►│ docker compose up -d │
│ └─ paquets génériques │ │ → http://<IP_DU_CONTROLE>:8080 │
│ │ agent.cjs │ └───────────────┬───────────────┘
└─────────┼────────────────┘ │ ws:// (réseau privé)
│ install-agent.sh/.ps1 ┌──────────┴──────────┐
└─────────────────────────────►│ VM Ubuntu/Windows │
│ agent + OBS │
└─────────────────────┘
L'interface est conteneurisée ; les agents ne le sont pas. Ils doivent voir l'OBS local et la fenêtre du navigateur, ce qu'un conteneur ne permet pas.
Prérequis
| Où | Quoi |
|---|---|
| Gitea | Paquets activés, un runner ubuntu-latest avec accès au socket Docker, un runner windows-latest |
| Plan de contrôle | Docker + Docker Compose, sur le réseau privé |
| VM d'enregistrement | OBS 28+ avec obs-websocket activé. Node.js est installé par le script si absent |
Étape 1 — Préparer Gitea
Secret
Dans Paramètres du dépôt → Actions → Secrets, ajoute :
| Nom | Valeur |
|---|---|
REGISTERYKEY |
Jeton d'accès du compte jeanbon, portées repository et package |
Variable optionnelle
Le workflow déduit l'hôte du registre de l'URL de ton instance. Si elle est
publiée sous un autre nom que celui vu par les runners, ajoute la variable
REGISTRY_HOST (par ex. git.jeanbonapp.com, sans schéma).
Vérifier les runners
Dans Site Administration → Runners, les labels doivent inclure
ubuntu-latest et windows-latest. S'ils diffèrent, adapte les deux champs
runs-on: de .gitea/workflows/release.yml.
Étape 2 — Premier build
Pousse sur main, ou lance le workflow à la main. Il produit :
| Artefact | Emplacement |
|---|---|
| Image du plan de contrôle | <gitea>/jeanbon/stream-control-server:latest |
| Bundle d'agent | paquet générique stream-control-agent, fichier agent.cjs |
| Scripts d'installation | même paquet, install-agent.sh et install-agent.ps1 |
Chaque build publie une version immuable (0.1.0-<sha>, ou 1.2.3 sur une
étiquette v1.2.3). L'alias latest ne suit que main et les étiquettes.
Le pipeline refuse de publier un bundle qui ne démarre pas : il exécute
agent.cjs --check sur Linux et sur Windows, et valide la syntaxe de
install-agent.ps1 sur le runner Windows.
Étape 3 — Déployer l'interface
Sur la machine du plan de contrôle :
mkdir -p /opt/stream-control && cd /opt/stream-control
# Récupérer compose et modèle d'environnement depuis le dépôt
curl -fsSLO https://git.jeanbonapp.com/jeanbon/stream-control/raw/branch/main/docker-compose.yml
curl -fsSL https://git.jeanbonapp.com/jeanbon/stream-control/raw/branch/main/.env.production.example -o .env
chmod 600 .env
Génère les secrets et complète .env :
echo "SESSION_SECRET=$(openssl rand -hex 32)"
echo "ENROLLMENT_TOKEN=$(openssl rand -hex 24)"
À renseigner impérativement :
| Variable | Rôle |
|---|---|
IMAGE |
git.jeanbonapp.com/jeanbon/stream-control-server:latest |
ADMIN_PASSWORD |
Mot de passe du dashboard |
SESSION_SECRET |
Clé de signature des sessions |
ENROLLMENT_TOKEN |
Jeton présenté par les agents à leur première connexion |
BIND_ADDR |
Interface d'écoute — mets l'IP LAN/VPN si la machine a une patte publique |
Puis :
docker login git.jeanbonapp.com -u jeanbon # jeton avec la portée package
docker compose up -d
docker compose logs -f
Contrôle : curl http://localhost:8080/healthz doit répondre
{"ok":true,"agents":0}.
La base SQLite vit dans ./data, monté depuis l'hôte — c'est le seul état à
sauvegarder.
La base tourne en mode WAL : les écritures récentes vivent dans
stream-control.sqlite-wal et pas encore dans le fichier principal. Copier le
seul .sqlite pendant que le serveur tourne donne une sauvegarde tronquée. Le
plus sûr est un arrêt de deux secondes :
docker compose stop server
tar czf ~/stream-control-$(date +%F).tar.gz data/
docker compose start server
À chaud, copie impérativement les trois fichiers ensemble (.sqlite,
-wal, -shm) — jamais le premier seul.
Étape 4 — Déployer un agent
Une seule commande par VM. Le <JETON_ENROLEMENT> est l'ENROLLMENT_TOKEN du
serveur : l'agent s'enregistre tout seul, reçoit un jeton permanent et le
persiste.
Ubuntu
curl -fsSL https://git.jeanbonapp.com/api/packages/jeanbon/generic/stream-control-agent/latest/install-agent.sh \
| sudo bash -s -- \
--registry https://git.jeanbonapp.com \
--server ws://<IP_DU_CONTROLE>:8080/ws/agent \
--token <JETON_ENROLEMENT> \
--name vm-rec-01 \
--obs-password <MDP_OBS>
Le script installe Node.js et xdotool si besoin, dépose le bundle dans
/opt/stream-control-agent, écrit un service systemd et lance un diagnostic.
L'agent tourne sous le compte qui a invoqué sudo — celui qui ouvre la session
graphique d'OBS. Force-le avec --user si ce n'est pas le bon.
Windows
Dans un PowerShell administrateur, sur le compte qui lance OBS :
& ([scriptblock]::Create((irm 'https://git.jeanbonapp.com/api/packages/jeanbon/generic/stream-control-agent/latest/install-agent.ps1'))) `
-Registry 'https://git.jeanbonapp.com' `
-Server 'ws://<IP_DU_CONTROLE>:8080/ws/agent' `
-Token '<JETON_ENROLEMENT>' `
-Name 'vm-rec-02' `
-ObsPassword '<MDP_OBS>'
L'agent est enregistré comme tâche planifiée à l'ouverture de session, pas comme service : un service Windows tourne en session 0 et ne verrait ni OBS ni la fenêtre du navigateur.
Si le paquet est privé
Ajoute --package-token <JETON> (Linux) ou -PackageToken '<JETON>' (Windows),
avec un jeton en lecture sur les paquets.
Vérifier
sudo -u <user> node /opt/stream-control-agent/agent.cjs --check # Linux
node C:\stream-control-agent\agent.cjs --check # Windows
Le diagnostic contrôle la configuration, la joignabilité du serveur et d'OBS, et
les prérequis du rappel plein écran (xdotool, DISPLAY, PowerShell).
Mises à jour
Interface — le tag latest bouge à chaque build sur main :
cd /opt/stream-control && docker compose pull && docker compose up -d
Épingle une version précise dans IMAGE si tu préfères maîtriser le moment.
Agents — relance exactement la même commande d'installation. Le script
détecte l'installation existante, remplace le binaire et conserve
agent.config.json : l'identité de l'agent est préservée. Sans cela, l'agent
se ré-enrôlerait et apparaîtrait en double dans le dashboard.
Pour installer une version figée plutôt que latest, remplace latest par la
version voulue dans l'URL et passe --version <VERSION>.
Sécurité
Ce déploiement est en HTTP sur réseau privé, conformément au choix retenu. Ce que cela implique concrètement :
- Les jetons d'agent, le mot de passe du dashboard et les jetons de session circulent en clair. Toute machine capable de sniffer le segment réseau peut les récupérer et prendre la main sur les agents.
- Le port ne doit jamais être joignable depuis Internet. Restreins
BIND_ADDRà l'IP LAN ou VPN, et ferme le port au pare-feu :ufw allow from 10.0.0.0/8 to any port 8080 proto tcp - Le mot de passe obs-websocket est stocké en base et poussé aux agents ; il n'est
jamais renvoyé au navigateur (masqué en
********). agent.config.jsonest en600et porte le jeton permanent de l'agent.
Passer en HTTPS plus tard ne demande qu'un reverse proxy devant le conteneur et
le remplacement de ws:// par wss:// dans les commandes d'installation —
deploy/nginx.conf.example donne le bloc de conf.
La rotation des jetons se fait alors depuis le dashboard, agent par agent.
Dépannage
| Symptôme | Piste |
|---|---|
docker compose up : manifest unknown |
docker login non fait, ou build jamais lancé |
| Le conteneur redémarre en boucle | docker compose logs server — le plus souvent ADMIN_PASSWORD ou SESSION_SECRET absent de .env |
unable to open database file (ERR_SQLITE_ERROR, errcode 14) |
Le répertoire ./data de l'hôte n'appartient pas à l'uid du conteneur. L'entrypoint le corrige automatiquement ; si tu as forcé un user: dans le compose, il ne peut plus le faire — aligne alors la propriété à la main : sudo chown -R 1000:1000 data |
| Agent absent du dashboard | --server erroné, ou ENROLLMENT_TOKEN différent de celui du serveur. journalctl -u stream-control-agent -f |
Agent en ligne, OBS déconnecté — ECONNREFUSED |
Rien n'écoute : OBS n'est pas lancé, ou Outils → Paramètres du serveur WebSocket n'est pas activé. Le mot de passe n'intervient pas à ce stade |
| Agent en ligne, OBS déconnecté — erreur d'authentification | Le mot de passe du dashboard et celui d'OBS diffèrent. Laisse-le vide uniquement si l'authentification est décochée dans OBS |
| Rappel plein écran sans effet (Linux) | Session Wayland au lieu de X11, ou DISPLAY inaccessible au service. Le diagnostic signale les deux |
| Rappel plein écran sans effet (Windows) | La tâche ne tourne pas dans la session interactive : vérifie que le compte de -RunAsUser est bien celui ouvert sur la VM |
| Agent en double après réinstallation | agent.config.json avait été supprimé : l'agent s'est ré-enrôlé. Supprime le doublon dans le dashboard |
| Workflow : unauthorized au push | REGISTERYKEY sans la portée package, ou expiré |
Journaux
docker compose logs -f server # plan de contrôle
journalctl -u stream-control-agent -f # agent Linux
Get-ScheduledTaskInfo -TaskName StreamControlAgent # agent Windows
Le dashboard affiche aussi le journal consolidé de tous les agents, persisté en base — souvent le point de départ le plus rapide.