Resumen
Como tal vez hayáis visto, hemos estado desde el lunes 6 de diciembre, a las 16:00 aproximadamente hasta el viernes 10 a las 02:00 (UTC+1) sin poder hacer directos en Fediverse.tv.
Esto es debido a que el lunes, que en España era festivo, teniamos tiempo de actualizar la versión de Peertube de la 3.4.0 a la 4.0.0-rc.1. Efectivamente era una Release Candidate (Candidata de Lanzamiento, más o menos), lo que quiere decir que no es una versión estable. No estábamos excesivamente preocupades por que peertube tiene un muy buen sistema de cambio de versiones, como se puede comprobar aquí.
A grandes rasgos, peertube provee de un script que permite instalar y se encarga de hacer backup de base de datos, actualizar el esquema y hacer las conversiones necesarias. Y como el servidor web busca siempre en un directorio tipo /var/www/peertube-latest, que es un enlace símbolico a la versión que se esté usando, solo tiene que cambiar el enlace a la versión nueva y ya está todo hecho, solo queda reiniciar el servicio de peertube. En el caso de la versión 4, probablemente al ser una RC, también habia que ejecutar una migración manual para cambiar los nombres de los ficheros H (HTTP Live Streaming), que también ejecutamos.
Una vez hicimos todo lo anterior, reiniciamos y todo parecía ir bien, hasta que vimos que no funcionaban los directos. Hicimos algunas pruebas, nos aseguramos de que habiamos ejecutado bien todos los pasos de la actualización y no vimos nada.
Así que al final decidimos hacer rollback para no dejar el servicio sin servicio de directos. Cúal fue nuestra sorpresa al ver que al hacer rollback, seguiamos teniendo el mismo problema. El problema concretamente era que ffmpeg no tenia los parámetros correctos, se puede ver la traza concreta al inicio del issue de git.
Abrimos un hilo al repositorio de git de Peertube para ver si nos podia orientar, y la verdad es que nos ayudó bastante. Aún así, de forma paralela fuimos haciendo otras cosas.
Lo primero que hicimos, una vez visto que no habia forma de hacer funcionar el servidor principal, fue intentar reproducir el error en otro servidor. Hicimos una instalación de cero de peertube 4 y funcionó. Hicimos una instalación de peertube 3.4.0 y actualizamos a peertube 4.0 y funcionó. Comparamos los ficheros que componen peertube y sus dependencias, del servidor que funcionaba al que no, sacando los hashes (Es una forma de sacar un identificador único de un fichero o conjunto de ficheros. Si nos hubiése salido un resultado distinto en alguno de los dos servidores, sabriamos que habia alguna diferencia en el código.) de los directorios y eran los mismos. Descargamos un binario más moderno de ffmpeg y lo sustituimos así a lo bestia, pero siguió pasando lo mismo. También hicimos cambios de versión de node entre la 14 y la 12, varias veces.
Una vez visto que las versiones de todos los componentes eran igual (peertube, dependencias, SO, ffmpeg, node, ), empezamos a hacer lo que nos proponia Chocobozzz, el desarrollador principal de Peertube. Nos dió un fichero con el código modificado para que nos dise logs más concretos de qué órdenes de ffmpeg se ejecutaba cuando hacíamos un live. Con ello, pudimos ver la orden exacta de ffmpeg, y vimos que era una órden correcta. La ejecutamos en bash y funcionó sin más. Ahí ya nos quedamos un poco pérdidos, por que la libreria fluent-ffmpeg es MUY vieja, no ha tenido cambios desde hace años y se usa para muchas más cosas que para los directos. Aún así, durante un tiempo nos concentramos en ese fichero, sin resultado. Chocobozzz sugirió que podia ser la versión de node, ya que los parámetros que llegaban a la libreria eran correctos. Y el fallo en sí se daba en la función spawn, que viene a ser la forma de ejecutar órdenes del sistema operativo en Javascript. La línea concreta es esta. Pero como ya comentamos arriba, probamos distintas versiones, por lo que descartamos que el problema fuse ese.
Después de esto, se nos ocurrió lanzar nuestro propio stream sin que dependiese de peertube. Es decir, usar ffmpeg con el protocolo rtmp (Real T Streaming ), que es lo que usa por debajo peertube, y intentar lanzar un stream a mano. Las órdenes ejecutadas fueron, en el servidor:
# Escucha en el puerto 2034 en vez del 1935, que estaba siendo usado por peertube y lo que recibe lo transcodea a un fichero llamado output.mp4 ffmpeg -f flv -listen 1 -i rtmp://localhost:2034/live/app/12345 -c copy output.mp4
Primero desde un cliente y luego desde el servidor, lanzamos la siguiente orden para enviar el directo:
ffmpeg -re -i video-ejemplo.ogg -codec copy -f flv rtmp://fediverse.tv:2034/live/12345
Con esto descartamos que el problema fuése ffmpeg ni rtmp, ya que podiamos tanto recibir los ficheros externos como encodearlos a un formato distinto.
Y aquí ya nos quedamos sin ideas. Seguimos investigando, intentamos hacer lo que nos proponia Chocobozzz pero algunas cosas se nos escapaban, ya que no somos expertes en Javascript, y menos en un proyecto ajeno.
Mientras pasaba todo esto nos planteamos cambiar de servidor sin más, pero entre que en España ha habido puente y algunas personas del colectivo se iban de vacaciones, la mayoría de servicios funcionaban y montar otra máquina y migrar los datos nos iba a costar dinero, decidimos dejar reposar la cosa y esperar. Pero de mientras, lo más parecido que habia a no usar el servidor dentro del propio servidor era usar contenedores. Y aquí entra docker.
La documentación de docker de peertube no es la mejor, ya que usa docker-compose, una herramienta que está bien para hacer pruebas, pero nadie deberia usarla en entornos productivos (en opinión de quién escribe esto). Por ello, lo primero que tuvimos que hacer fue sacar la parte relevante del fichero docker-compose. La parte que nos importaba era:
peertube: image: chocobozzz/peertube:production-buster networks: default: ipv4_address: 172.18.0.42 env_file: - .env ports: - "1935:1935" # If you don't want to use the live feature, you can comment this line # - "9000:9000" # If you provide your own webserver and reverse-proxy, otherwise not suitable for production volumes: - assets:/app/client/dist - ./docker-volume/data:/data - ./docker-volume/config:/config depends_on: - postgres - redis - postfix restart: "always"
Con esto sacamos en claro los directorios que necesitabamos mapear en el contenedor y la imagen a usar. El principal de todos era el de configuración, por que con ese podiamos controlar a dónde iba el resto, ya fuesen assets (estáticos como css, js, etc) o datos. Viendo el Dockerfile vemos que variable usa para decir dónde está el directorio de configuración. Y con esto ya tenemos todo lo necesario para lanzar el contenedor. La orden que sacamos al final fué:
docker run --rm --name peertube --network host -v /var/www/vhosts/fediverse.tv/www/:/www \ --entrypoint "" -e NODE_CONFIG_DIR=/www/versions/peertube-v3.4.1/config/ \ chocobozzz/peertube:production-buster node dist/server
En /var/www/vhosts/fediverse.tv/www/ es dónde colocamos la raíz de nuestro peertube, por lo que es la parte más importante a mapear. Con el parámetro -e le decimos dónde está la configuración. Usamos la red del host por que los otros servicios (postgresql, redis, nginx), a diferencia de con docker compose, no son contenedores. Por lo que tienen que llegar y ser accesibles los unos con los otros. Y por último, la parte tal vez más rara es la de –entrypoint «». Esto es debido a que usa un entrypoint que hace muchas cosas con el directorio de datos, que es dónde se guardan los vídeos. Mientras intentabamos hacer funcionar el contenedor, no queriamos interferir con el correcto funcionamiento del servicio y sobretodo no arriesgarnos a causar estropicios en un directorio en el que hay unos 800GB de datos en vídeos. Como vimos que podiamos funcionar sin el entrypoint, lo dejamos así.
Una vez arrancado el contenedor, paramos el servicio principal de peertube y dejamos que apuntáse al contenedor. En los logs veíamos como federaba, recibia peticiones de otras instancias, clientes móviles se conectaban, pero la página no se abria en el navegador. Esto nos dejó un poco perdidos unas horas, hasta que revisamos el fichero de configuración de nginx, el servidor web, y fuimos cambiando cosas. Al final el problema vino por esta sección en concreto:
# Bypass PeerTube for performance reasons. Optional. location ~ ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$ { add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year alias /var/www/vhosts/fediverse.tv/www/peertube-latest/client/dist/$1; }
Es una sección de la configuración oficial de nginx. A grandes rasgos lo que hace es no enviar el tráfico a peertube cuando sirve ficheros css, js, … que estén en la ruta /client/. Esto tiene sentido por temas de eficiencia, nginx gestiona mejor servir estáticos y quitas carga a peertube. Pero en nuestro caso, al usar un contenedor impedia que los estáticos se sirviesen correctamente. Es posible que modificando la configuración pudiésemos hacerlo funcionar, pero decidimos comentarlo sin más y con esto reiniciamos nginx y ya pudimos ver la página principal y lo que es mejor, se restauraron los streamings.
El último paso fue adaptar el servicio de systemd para que arranque el contendor en vez del servicio de peertube en si, por si necesitamos cambiarlo en el futuro y con esto dimos por cerrada la incidencia.
Ahora, el problema de todo esto es que no hemos conseguido dar con el problema raíz. No puede ser un problema muy grande si un contenedor funciona, es decir, no puede ser a nivel de hardware. Así que sólo nos queda el sistema de ficheros, el código de peertube y los binarios que intervenian, cosa que como ya hemos dicho, revisamos por todos lados. La conclusión es que solo sabemios que no sabemos nada.
De momento se quedará la solución con docker para dar un servicio estable, pero tenemos que ver las implicaciones a largo plazo sobre el mantenimiento, por lo que no hemos tomado ninguna decisión final al respecto.