Installation
Déploiement en conteneur
Section titled “Déploiement en conteneur”Castmux est distribué sous forme d’image de conteneur, et c’est le mode d’exécution pris en charge. L’image contient le binaire Go et les deux moteurs de diffusion, ainsi qu’un petit ensemble d’outils en ligne de commande que le processus appelle.
sudo mkdir -p /opt/castmux/config
sudo docker run -d --name castmux --restart unless-stopped \ --network host \ --cap-add NET_RAW \ --cap-add NET_ADMIN \ -v /opt/castmux/config:/config \ -e CASTMUX_WEB_USERNAME=admin \ -e CASTMUX_WEB_PASSWORD=choose-something-better \ castmux/castmux:latestOu sous forme de fichier compose :
services: castmux: image: castmux/castmux:latest container_name: castmux restart: unless-stopped # Multicast must be emitted onto the real LAN, not a bridge. network_mode: host cap_add: - NET_RAW # libpcap capture: viewer detection, wire bitrate - NET_ADMIN # multicast route inspection and management volumes: # A host directory, not a named volume — see below. - /opt/castmux/config:/config environment: CASTMUX_WEB_USERNAME: admin CASTMUX_WEB_PASSWORD: choose-something-betterPourquoi le réseau hôte
Section titled “Pourquoi le réseau hôte”Un réseau Docker en mode bridge est un segment de couche 2 distinct de celui où se trouvent les téléviseurs. Le multicast qui y est envoyé est distribué à l’intérieur du bridge et nulle part ailleurs. Le mode de défaillance est particulièrement trompeur : l’appel d’envoi réussit, le moteur indique qu’il diffuse, les compteurs de paquets augmentent, et aucun téléviseur ne reçoit jamais rien. Il n’y a aucune erreur à trouver, parce qu’aucune erreur ne s’est produite.
Le réseau hôte n’est donc pas une commodité, c’est une exigence fonctionnelle. Cela signifie aussi que l’interface web écoute directement sur le port 8080 de l’hôte : aucune redirection de port n’est nécessaire, ni même possible.
Pourquoi NET_RAW
Section titled “Pourquoi NET_RAW”Castmux capture les paquets avec libpcap en mode promiscuous. Il le fait pour deux choses impossibles à obtenir autrement :
- Détection des spectateurs. Les rapports d’appartenance IGMP des autres hôtes sont adressés à des groupes que cette machine n’a pas rejoints, et le noyau écarte le multicast non rejoint avant qu’une socket ordinaire puisse le voir. La capture en mode promiscuous est le seul moyen d’observer quels téléviseurs ont rejoint quel groupe.
- Débit sur le fil. Compter ce qui est réellement sorti de l’interface, plutôt que de croire ce que le moteur dit avoir envoyé. C’est le seul signal de vie qui fonctionne de manière identique pour les deux moteurs, et le seul qu’un moteur défaillant ne peut pas falsifier.
Sans NET_RAW, la diffusion à la demande n’a rien sur quoi agir et les tuiles de débit UDP restent vides. Que les chaînes tournent alors en permanence ou pas du tout dépend du réglage de repli décrit dans Diffusion à la demande.
Pourquoi NET_ADMIN
Section titled “Pourquoi NET_ADMIN”Gestion et inspection de la route multicast. Castmux vérifie la table de routage de l’hôte depuis l’extérieur du chemin de diffusion, car — comme l’explique la section suivante — une destination non routable est totalement invisible depuis l’intérieur d’un processus qui y envoie des données.
Répertoire de configuration
Section titled “Répertoire de configuration”Tout l’état tient dans un seul fichier : /config/config.yaml dans le conteneur, c’est-à-dire l’emplacement où vous l’avez monté sur l’hôte.
Montez un répertoire de l’hôte (bind mount) plutôt qu’un volume nommé. C’est une recommandation délibérée, pas un oubli. Le moment où vous avez le plus besoin de lire la configuration est celui où l’interface web ne répond plus, et à ce moment-là un volume nommé est un répertoire enfoui sous la racine de stockage du runtime de conteneurs, avec un nom dont personne ne se souvient. Un chemin simple permet de lire la configuration avec un éditeur, de la comparer à celle de la semaine dernière, de la sauvegarder avec cp et de la placer sous gestion de versions si vous souhaitez un historique des modifications.
Au premier lancement, le fichier est créé avec les valeurs par défaut. Ensuite :
- Les clés manquantes sont complétées automatiquement : une configuration écrite par une version plus ancienne continue de fonctionner après une mise à jour et reçoit les nouvelles clés avec leurs valeurs par défaut.
- Avant toute migration qui modifie la structure du fichier, une copie
.pre-migrationest écrite à côté. Si une mise à jour interprète quelque chose autrement que vous ne l’entendiez, l’original est toujours là.
La modification manuelle du fichier est prise en charge. Le processus le lit au démarrage : redémarrez donc le conteneur après une modification hors interface, et sachez que les changements faits dans l’interface web pendant que vous éditez seront écrasés par la dernière écriture, quelle qu’elle soit.
Routage multicast
Section titled “Routage multicast”C’est la partie que les installations ratent, et elle mérite plus d’attention que tout le reste de cette page réuni.
Le noyau décide par quelle interface sort un paquet multicast en consultant la table de routage. Lier l’adresse source de la socket UDP ne sélectionne pas l’interface de sortie — une supposition courante et raisonnable, mais fausse. Ce qui compte, c’est vers où pointe 224.0.0.0/4.
Sur de nombreux appareils, elle pointe vers l’interface que l’installateur a configurée en premier, qui n’est souvent pas branchée. Les flux sont alors correctement produits et ne vont silencieusement nulle part.
Corrigez la plage, pas le groupe
Section titled “Corrigez la plage, pas le groupe”Routez toute la plage multicast vers l’interface tournée vers les téléviseurs :
# Point the entire multicast range at the TV-facing NICsudo ip route replace 224.0.0.0/4 dev eth1
# Verify — this must name the TV-facing interfaceip route get 239.10.0.1Les routes ne survivent pas à un redémarrage
Section titled “Les routes ne survivent pas à un redémarrage”Une commande ip route n’est pas persistante. Installez plutôt l’unité systemd fournie et le timer qui l’accompagne. L’unité :
- choisit la première interface candidate connectée, en privilégiant la carte réseau tournée vers les téléviseurs et en se repliant sur la carte d’administration lorsque la carte privilégiée n’a pas de porteuse ;
- est réévaluée par le timer chaque minute : la route suit ainsi le câble si quelqu’un le déplace, sans attendre un redémarrage pour s’en apercevoir.
Une route morte est invisible de l’intérieur
Section titled “Une route morte est invisible de l’intérieur”sendto() vers une destination multicast non routable renvoie un succès. Aucun moteur de diffusion ne peut détecter cette situation, car du point de vue du moteur rien ne s’est mal passé. Il en découle que :
- le défaut doit être vérifié par rapport à la table de routage depuis l’extérieur du chemin de diffusion — c’est ce que fait Castmux, et c’est pourquoi il a besoin de NET_ADMIN ;
- un redémarrage ne le corrige jamais. L’application comme le watchdog refusent donc de redémarrer une chaîne pour un défaut de routage, et lèvent une alerte à la place. Une boucle de redémarrages face à une route cassée n’est qu’une agitation qui masque la véritable cause.
Contrôle de santé et watchdog
Section titled “Contrôle de santé et watchdog”L’image déclare un healthcheck de conteneur. Sachez ce que cela vous apporte et ce que cela ne vous apporte pas : Docker seul enregistre l’état de santé et n’agit pas en conséquence. Un conteneur peut rester indéfiniment marqué unhealthy pendant que la politique de redémarrage, qui ne réagit qu’à la sortie du processus, ne fait rien. Les orchestrateurs agissent sur l’état de santé ; docker run --restart, non.
C’est donc une unité watchdog externe qui agit. Elle s’appuie sur deux signaux indépendants :
| Signal | Signification et réaction |
|---|---|
| Santé du conteneur | Le processus dans son ensemble ne répond plus. La réaction consiste à redémarrer le conteneur. |
| Silence UDP par chaîne | Une chaîne a cessé d'émettre des paquets sur le fil alors que les autres fonctionnent. La réaction consiste à redémarrer cette chaîne uniquement. |
Deux signaux plutôt qu’un, parce qu’ils distinguent des défauts dont la portée est très différente. Redémarrer tout le conteneur pour réparer une seule chaîne bloquée coupe toutes les autres chaînes pendant la durée du redémarrage et du démarrage échelonné qui suit — soit, sur vingt chaînes, environ une minute d’écrans noirs pour corriger un problème qui n’en touche qu’une.
Le watchdog refuse de redémarrer quoi que ce soit pour un défaut de routage, pour la raison exposée plus haut : un redémarrage ne peut pas réparer une route, et redémarrer produit donc une boucle au lieu d’une réparation.
N’exécutez jamais deux instances
Section titled “N’exécutez jamais deux instances”Deux situations produisent une seconde instance sans que personne ne l’ait voulu :
- Une unité systemd restée d’une ancienne installation hors conteneur. Désactiver une unité l’empêche de démarrer au boot ; cela n’empêche rien d’autre de la démarrer. Utilisez
systemctl maskpour qu’elle ne puisse plus être démarrée du tout. - Un redémarrage nocturne dans la crontab de root. Vérifiez sa présence lors de la migration d’un appareil d’une installation par paquet vers le conteneur. Une entrée cron qui exécute
systemctl restartsur l’ancienne unité démarrera sans broncher une unité désactivée à trois heures du matin, vous laissant un split brain qui apparaît la nuit et a disparu quand quelqu’un se penche dessus. Encore une fois : masquez, ne vous contentez pas de désactiver.
Mise à jour
Section titled “Mise à jour”Récupérez la nouvelle image et recréez le conteneur. La configuration migre automatiquement vers la nouvelle version, les clés manquantes sont complétées, et une sauvegarde .pre-migration est écrite avant toute modification structurelle.
sudo docker pull castmux/castmux:latestsudo docker stop castmux && sudo docker rm castmux# then re-run the same docker run command, or, in /opt/castmux:sudo docker compose up -dLes chaînes sont coupées pendant la durée du redémarrage plus le démarrage échelonné qui suit — environ trois secondes par chaîne. Sur une grosse installation, prévoyez la fenêtre de maintenance en conséquence.
La migration ne fonctionne que vers l’avant. Si vous devez revenir à une image plus ancienne, restaurez avec elle la copie .pre-migration de la configuration ; un binaire plus ancien qui lit un fichier plus récent ne comprendra pas les clés qui n’existaient pas lors de sa compilation.
Sauvegarde
Section titled “Sauvegarde”Copiez le répertoire de configuration. C’est toute la procédure, et elle est courte à dessein :
sudo cp -a /opt/castmux/config /backup/castmux-$(date +%F)Le répertoire contient tout ce qui mérite d’être conservé : la configuration, l’identité de licence de l’appareil et le guide des programmes enregistré. Copiez le répertoire, pas seulement config.yaml, faute de quoi un appareil restauré revient comme un nouvel appareil non approuvé. La restauration est la même opération en sens inverse, conteneur arrêté — voir Sauvegarde et restauration. Les journaux des chaînes ne sont pas inclus : ce sont un tampon circulaire résidant en mémoire, qui ne persiste de toute façon pas d’un redémarrage à l’autre — voir L’interface web → Journaux.