Ir al contenido

Instalación

Castmux se distribuye como imagen de contenedor, y esta es la forma admitida de ejecutarlo. La imagen contiene el binario de Go y los dos motores de emisión, junto con un pequeño conjunto de herramientas de línea de comandos que invoca el proceso.

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

O como archivo compose:

/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

Una red bridge de Docker es un segmento de capa 2 distinto de aquel en el que están los televisores. El multicast enviado a ella se entrega dentro del bridge y en ningún otro sitio. El modo de fallo es especialmente poco útil: la llamada de envío tiene éxito, el motor informa de que está emitiendo, los contadores de paquetes aumentan y ningún televisor recibe nunca nada. No hay ningún error que encontrar porque no se ha producido ninguno.

Por tanto, la red de host no es una comodidad, sino un requisito funcional. También significa que la interfaz web se vincula directamente al puerto 8080 del host, de modo que no se necesita ni es posible ningún mapeo de puertos.

Castmux captura paquetes con libpcap en modo promiscuo. Lo hace para dos cosas que no pueden hacerse de ninguna otra manera:

  • Detección de espectadores. Los informes de pertenencia IGMP de otros hosts van dirigidos a grupos a los que esta máquina no se ha unido, y el kernel descarta el multicast de grupos no unidos antes de que ningún socket ordinario pueda verlo. La captura en modo promiscuo es la única forma de observar qué televisores se han unido a qué grupo.
  • Bitrate en el cable. Contar lo que realmente ha salido por la interfaz, en lugar de creer lo que el motor dice que ha enviado. Es la única señal de actividad que funciona igual con ambos motores y la única que un motor confundido no puede falsear.

Sin NET_RAW, la emisión bajo demanda no tiene nada en lo que basarse y los indicadores de bitrate UDP permanecen vacíos. Que los canales pasen entonces a funcionar siempre activos o no funcionen en absoluto depende del ajuste fail-open descrito en Emisión bajo demanda.

Gestión e inspección de la ruta multicast. Castmux comprueba la tabla de rutas del host desde fuera de la ruta de emisión porque —como explica la sección siguiente— un destino sin ruta es completamente invisible desde dentro de un proceso que le está enviando datos.

Todo el estado reside en un único archivo: /config/config.yaml dentro del contenedor, que corresponde al lugar del host donde lo haya montado.

Monte un directorio del host (bind mount) en lugar de usar un volumen con nombre. Es una recomendación deliberada, no un descuido. El momento en que más necesita leer la configuración es aquel en que la interfaz web no responde, y en ese momento un volumen con nombre es un directorio enterrado bajo la raíz de almacenamiento del runtime de contenedores con un nombre que nadie recuerda. Una ruta normal permite leer la configuración con un editor, compararla con la de la semana pasada, hacer una copia de seguridad con cp y ponerla bajo control de versiones si quiere un historial de cambios.

En la primera ejecución, el archivo se crea con los valores predeterminados. A partir de entonces:

  • Las claves que faltan se completan automáticamente, de modo que una configuración escrita por una versión anterior sigue funcionando tras una actualización y recibe las claves nuevas con sus valores predeterminados.
  • Antes de cualquier migración que cambie la estructura del archivo, se escribe junto a él una copia .pre-migration. Si una actualización interpreta algo de forma distinta a como usted pretendía, el original sigue ahí.

Se admite la edición manual del archivo. El proceso lo lee al arrancar, así que reinicie el contenedor después de una edición externa, y tenga en cuenta que los cambios realizados en la interfaz web mientras usted edita se sobrescribirán con la escritura que llegue en último lugar.

Esta es la parte en la que fallan las instalaciones, y merece más atención que todo el resto de esta página junto.

El kernel decide por qué interfaz sale un paquete multicast consultando la tabla de rutas. Vincular la dirección de origen del socket UDP no selecciona la interfaz de salida: es una suposición común y razonable, y es incorrecta. Lo que importa es adónde apunta 224.0.0.0/4.

En muchos equipos apunta a la interfaz que el instalador configuró primero, que con frecuencia es una que no está conectada. Los flujos se generan entonces correctamente y, sin avisar, no van a ninguna parte.

Enrute todo el rango multicast hacia la interfaz que da a los televisores:

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

Un comando ip route no es persistente. En su lugar, instale la unidad systemd suministrada y su temporizador. La unidad:

  • elige la primera interfaz candidata conectada, dando preferencia a la NIC que da a los televisores y recurriendo a la NIC de gestión cuando la preferida no tiene portadora;
  • se reevalúa mediante el temporizador cada minuto, de modo que la ruta sigue al cable si alguien lo cambia de sitio, en lugar de necesitar un reinicio para detectarlo.

sendto() hacia un destino multicast sin ruta devuelve éxito. Ningún motor de emisión puede detectar esta situación, porque desde el punto de vista del motor nada ha fallado. De ello se deduce que:

  • el fallo debe comprobarse contra la tabla de rutas desde fuera de la ruta de emisión, que es lo que hace Castmux y el motivo por el que necesita NET_ADMIN;
  • reiniciar nunca lo soluciona. Por ello, tanto la aplicación como el watchdog se niegan a reiniciar un canal por un fallo de enrutamiento y, en su lugar, emiten una alerta. Un bucle de reinicios frente a una ruta rota es actividad inútil que oculta la causa real.

La imagen declara un healthcheck de contenedor. Tenga claro lo que eso le aporta y lo que no: Docker por sí solo registra el estado de salud y no actúa en consecuencia. Un contenedor puede permanecer marcado como unhealthy indefinidamente mientras la política de reinicio, que solo reacciona cuando el proceso termina, no hace nada. Los orquestadores actúan según el estado de salud; docker run --restart no.

Por eso es una unidad watchdog externa la que actúa. Usa dos señales independientes:

SeñalQué significa y qué ocurre
Salud del contenedorEl proceso en su conjunto no responde. La respuesta es reiniciar el contenedor.
Silencio UDP por canalUn canal ha dejado de poner paquetes en el cable mientras el resto funciona. La respuesta es reiniciar solo ese canal.

Dos señales en lugar de una, porque distinguen fallos con alcances muy distintos. Reiniciar todo el contenedor para arreglar un canal atascado deja fuera del aire a todos los demás canales durante el reinicio y el arranque escalonado posterior, lo que con veinte canales supone aproximadamente un minuto de pantallas en negro para resolver un problema que afecta a uno.

El watchdog se niega a reiniciar nada por un fallo de enrutamiento, por el motivo expuesto antes: un reinicio no puede arreglar una ruta, así que reiniciar produce un bucle en lugar de una reparación.

Hay dos situaciones que generan una segunda instancia sin que nadie lo pretenda:

  • Una unidad systemd sobrante de una instalación anterior sin contenedor. Deshabilitar una unidad impide que arranque al iniciar el sistema; no impide que otra cosa la inicie. Use systemctl mask para que no pueda iniciarse en absoluto.
  • Un reinicio nocturno en el crontab de root. Compruébelo al migrar un equipo de una instalación por paquete al contenedor. Una entrada de cron que ejecute systemctl restart sobre la unidad antigua iniciará sin problema una unidad deshabilitada a las tres de la madrugada, provocando un split brain que aparece durante la noche y ha desaparecido cuando alguien lo investiga. Insistimos: enmascare (mask), no se limite a deshabilitar.

Descargue la nueva imagen y vuelva a crear el contenedor. La configuración se migra automáticamente, las claves que faltan se completan y se escribe una copia de seguridad .pre-migration antes de cualquier cambio estructural.

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

Los canales están caídos durante el reinicio más el arranque escalonado posterior, aproximadamente tres segundos por canal. En una instalación grande, planifique la ventana de mantenimiento en consecuencia.

La migración solo funciona hacia delante. Si necesita volver a una imagen anterior, restaure junto con ella la copia .pre-migration de la configuración; un binario más antiguo que lea un archivo más reciente no entenderá claves que no existían cuando se compiló.

Copie el directorio de configuración. Ese es todo el procedimiento, y es breve a propósito:

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

El directorio contiene todo lo que merece la pena conservar: la configuración, la identidad de licencia del equipo y la guía de programación almacenada. Copie el directorio, no solo config.yaml, o un equipo restaurado volverá como un dispositivo nuevo sin aprobar. La restauración es la misma operación a la inversa, con el contenedor detenido; consulte Copia de seguridad y restauración. Los registros de los canales no se incluyen, porque son un búfer circular residente en memoria y, en cualquier caso, no se conservan tras un reinicio; consulte La interfaz web → Registros.