Ga naar inhoud

Installatie

Castmux wordt geleverd als container-image, en dat is de ondersteunde manier om het te draaien. De image bevat de Go-binary en beide streaming-engines, plus een kleine set commandoregeltools die het proces aanroept.

Terminal window
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:latest

Of als compose-bestand:

/opt/castmux/docker-compose.yml
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-better

Een Docker-bridgenetwerk is een ander layer-2-segment dan het segment waarop de televisies zitten. Multicast die erin wordt gestuurd, wordt binnen de bridge afgeleverd en nergens anders. De manier waarop dit misgaat is ongewoon onbehulpzaam: de verzendaanroep slaagt, de engine meldt dat hij streamt, de pakkettellers lopen op, en geen enkele televisie ontvangt ooit iets. Er is geen foutmelding te vinden, omdat er geen fout is opgetreden.

Hostnetwerk is dus geen gemak, maar een functionele vereiste. Het betekent ook dat de webinterface rechtstreeks aan poort 8080 van de host bindt, zodat een poortmapping niet nodig en ook niet mogelijk is.

Castmux vangt pakketten op met libpcap in promiscuous mode. Dat doet het voor twee dingen die op geen andere manier kunnen:

  • Kijkerdetectie. IGMP-membership-reports van andere hosts zijn gericht aan groepen waarbij deze machine zich niet heeft aangemeld, en de kernel gooit multicast voor niet-aangemelde groepen weg voordat een gewone socket die kan zien. Promiscuous capture is de enige manier om vast te stellen welke televisies zich bij welke groep hebben aangemeld.
  • Bitrate op de kabel. Tellen wat de interface werkelijk heeft verlaten, in plaats van geloven wat de engine zegt te hebben verzonden. Dit is het enige levenssignaal dat voor beide engines op dezelfde manier werkt, en het enige dat een verwarde engine niet kan vervalsen.

Zonder NET_RAW heeft on-demand streaming niets om op te reageren en blijven de tegels voor UDP-bitrate leeg. Of zenders dan always-on draaien of helemaal niet, hangt af van de fail-open-instelling die wordt beschreven in On-demand streaming.

Beheer en inspectie van de multicastroute. Castmux controleert de routeringstabel van de host van buiten het streamingpad, omdat — zoals de volgende sectie uitlegt — een onrouteerbare bestemming volledig onzichtbaar is vanuit een proces dat ernaartoe zendt.

Alle status staat in één bestand: /config/config.yaml in de container, wat overeenkomt met de plek waar u het op de host via een bind-mount hebt gekoppeld.

Koppel een map van de host via een bind-mount in plaats van een named volume te gebruiken. Dit is een bewuste aanbeveling, geen vergissing. Het moment waarop u de configuratie het hardst nodig hebt, is het moment waarop de webinterface niet reageert, en op dat moment is een named volume een map diep in de opslagmap van de container-runtime met een naam die niemand onthoudt. Met een gewoon pad kunt u de configuratie met een editor lezen, vergelijken met die van vorige week, met cp back-uppen en in versiebeheer zetten als u een wijzigingsgeschiedenis wilt.

Bij de eerste start wordt het bestand met standaardwaarden aangemaakt. Daarna geldt:

  • Ontbrekende sleutels worden automatisch aangevuld, zodat een configuratie die door een oudere versie is geschreven na een upgrade blijft werken en de nieuwe sleutels met hun standaardwaarden krijgt.
  • Vóór elke migratie die de structuur van het bestand wijzigt, wordt er een .pre-migration-kopie naast gezet. Interpreteert een upgrade iets anders dan u bedoelde, dan is het origineel er nog.

Het bestand met de hand bewerken wordt ondersteund. Het proces leest het bij het opstarten, dus herstart de container na een bewerking buiten de interface om, en houd er rekening mee dat wijzigingen die tijdens uw bewerking in de webinterface worden gedaan, worden overschreven door de schrijfactie die als laatste plaatsvindt.

Dit is het onderdeel dat bij installaties misgaat, en het verdient meer aandacht dan de rest van deze pagina bij elkaar.

De kernel bepaalt via welke interface een multicastpakket vertrekt door de routeringstabel te raadplegen. Het bronadres van de UDP-socket binden kiest de uitgaande interface niet — een veelgemaakte en begrijpelijke aanname, maar een onjuiste. Waar het om gaat, is waar 224.0.0.0/4 naartoe wijst.

Op veel apparaten wijst die naar de interface die de installateur toevallig als eerste heeft geconfigureerd, en dat is vaak een interface die niet is aangesloten. Streams worden dan correct aangemaakt en gaan geruisloos nergens heen.

Routeer het volledige multicastbereik naar de interface richting de televisies:

Terminal window
# Point the entire multicast range at the TV-facing NIC
sudo ip route replace 224.0.0.0/4 dev eth1
# Verify — this must name the TV-facing interface
ip route get 239.10.0.1

Een ip route-commando is niet blijvend. Installeer in plaats daarvan de meegeleverde systemd-unit en de bijbehorende timer. De unit:

  • kiest de eerste aangesloten kandidaat-interface, met voorkeur voor de NIC richting de televisies en met terugval op de beheer-NIC als de voorkeursinterface geen carrier heeft;
  • wordt elke minuut opnieuw geëvalueerd door de timer, zodat de route de kabel volgt als iemand die verplaatst, in plaats van dat pas een herstart het opmerkt.

Een dode route is van binnenuit onzichtbaar

Section titled “Een dode route is van binnenuit onzichtbaar”

sendto() naar een onrouteerbare multicastbestemming meldt succes. Geen enkele streaming-engine kan deze toestand detecteren, want vanuit de engine gezien is er niets misgegaan. Daaruit volgt:

  • de storing moet van buiten het streamingpad tegen de routeringstabel worden gecontroleerd — dat doet Castmux, en daarom heeft het NET_ADMIN nodig;
  • herstarten lost het nooit op. Zowel de applicatie als de watchdog weigeren daarom een zender te herstarten vanwege een routeringsfout, en geven in plaats daarvan een melding. Een herstartlus tegen een kapotte route is onrust die de werkelijke oorzaak verbergt.

De image declareert een container-healthcheck. Wees u bewust van wat dat wel en niet oplevert: gewone Docker registreert de gezondheidsstatus en onderneemt er geen actie op. Een container kan onbeperkt als unhealthy gemarkeerd staan terwijl het herstartbeleid, dat alleen reageert op het beëindigen van het proces, niets doet. Orchestrators handelen op basis van health; docker run --restart doet dat niet.

Een externe watchdog-unit neemt het handelen daarom over. Die gebruikt twee onafhankelijke signalen:

SignaalWat het betekent en wat er gebeurt
Gezondheid van de containerHet proces als geheel reageert niet. De reactie is de container herstarten.
UDP-stilte per zenderEén zender zet geen pakketten meer op de kabel terwijl de rest in orde is. De reactie is alleen die zender herstarten.

Twee signalen in plaats van één, omdat ze storingen met een heel verschillende impact van elkaar onderscheiden. De hele container herstarten om één vastgelopen zender te herstellen, haalt alle andere zenders van de lucht zolang de herstart en de daaropvolgende gespreide start duren — bij twintig zenders ongeveer een minuut zwart beeld om een probleem op te lossen dat er één treft.

De watchdog weigert iets te herstarten vanwege een routeringsfout, om de reden hierboven: een herstart kan een route niet repareren, dus herstarten levert een lus op in plaats van een herstel.

Twee situaties leveren een tweede instantie op zonder dat iemand dat bedoelt:

  • Een achtergebleven systemd-unit van een eerdere installatie zonder container. Een unit uitschakelen voorkomt dat hij bij het opstarten start; het voorkomt niet dat iets anders hem start. Gebruik systemctl mask, zodat hij helemaal niet kan worden gestart.
  • Een nachtelijke herstart in de crontab van root. Controleer hierop wanneer u een apparaat van een pakketinstallatie naar de container migreert. Een cron-regel die systemctl restart op de oude unit uitvoert, start om drie uur ‘s nachts zonder morren een uitgeschakelde unit, en geeft u een split brain die ‘s nachts ontstaat en verdwenen is tegen de tijd dat iemand het onderzoekt. Nogmaals: masken, niet alleen uitschakelen.

Haal de nieuwe image op en maak de container opnieuw aan. De configuratie migreert automatisch mee, ontbrekende sleutels worden aangevuld, en vóór elke structurele wijziging wordt een .pre-migration-back-up geschreven.

Terminal window
sudo docker pull castmux/castmux:latest
sudo docker stop castmux && sudo docker rm castmux
# then re-run the same docker run command, or, in /opt/castmux:
sudo docker compose up -d

Zenders liggen plat zolang de herstart duurt plus de daaropvolgende gespreide start — ongeveer drie seconden per zender. Plan het onderhoudsvenster bij een grote installatie daarop.

Migratie werkt alleen vooruit. Moet u terug naar een oudere image, zet dan de .pre-migration-kopie van de configuratie ernaast terug; een oudere binary die een nieuwer bestand leest, begrijpt geen sleutels die nog niet bestonden toen hij werd gebouwd.

Kopieer de configuratiemap. Dat is de hele procedure, en die is met opzet kort:

Terminal window
sudo cp -a /opt/castmux/config /backup/castmux-$(date +%F)

De map bevat alles wat het bewaren waard is: de configuratie, de licentie-identiteit van het apparaat en de opgeslagen programmagids. Kopieer de map, niet alleen config.yaml, anders komt een teruggezet apparaat terug als een nieuw, niet-goedgekeurd apparaat. Terugzetten is dezelfde handeling in omgekeerde richting, met de container gestopt — zie Back-up en herstel. Zenderlogs zijn niet inbegrepen, omdat die in een circulaire buffer in het geheugen staan en een herstart sowieso niet overleven — zie De webinterface → Logs.