Actualizado el 19 de Octubre de 2021
Con la llegada de Raspberry surgieron muchas distribuciones multimedia como Kodi, OSMC, OpenELEC,… todas tienen en común que vienen preparadas para ser utilizadas sin ninguna instalación y con su propia interfaz gráfica y proporcionan plugins para ampliar la funcionalidad, por ejemplo emisoras de radio o clientes Torrent.
Estas distribuciones al estar dedicadas en exclusiva a funciones multimedia no permiten realizar otras acciones. Puede ser que no estemos todo el tiempo usándola de este modo y queramos darle más usos, sobre todo con los últimos modelos que son más potentes así que una alternativa es usar contenedores Docker, en este caso se van a crear tres contenedores:
- Transmission: Un cliente torrent personalizable.
- Mopidy: Servidor de audio compatible con Spotify.
- Plex: Un servidor de streaming de video.
Dockerfile
Todos los contenedores se pueden ejecutar por separado usando los siguientes ficheros Dockerfile o usando el fichero compose que se indica al final. El código fuente está aquí. Vamos a suponer que las carpetas locales están bajo el raíz /mnt/media
Docker Mopidy
El fichero Docker para Mopidy está basado en este artículo, tiene consejos y recomendaciones sobre la configuración de sonido, la variable de entorno AUDIO maneja la salida de audio. Hay que tener en cuenta que si los altavoces están compartidos, por ejemplo tanto un ordenador como una Raspberry están conectados solo uno puede estar usándolo a la vez y el otro dispositivo tendrá que cambiar su salida de audio para liberar el canal.
# 0. Official armv32 image
FROM debian:latest
LABEL "guru.raraavis.creator"="blog@raraavis.guru"
LABEL "guru.raraavis.version"="1.0.0"
LABEL "guru.raraavis.release-date"="20/03/2021"
LABEL "guru.raraavis.description"="Raspberry Pi 3 image with Mopidy"
# 1. Environment vars
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=Europe/Madrid
ENV TZ=$TZ
# Use 1 for headphones, 0 for HDMI
ARG AUDIO=1
# 2. Update system and install necessary packages
RUN apt-get update && \
apt-get install -y \
wget \
lsb-release \
gnupg2 \
gnupg \
gnupg1 \
python3-dev \
python3-pip \
libffi-dev \
sudo \
gosu \
dumb-init && \
cron && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /tmp/*
# 3. Add mopidy repository
RUN wget -q -O - https://apt.mopidy.com/mopidy.gpg | apt-key add -
RUN DEBIAN_CODENAME=`lsb_release -sc` && \
wget -vO /etc/apt/sources.list.d/mopidy.list "https://apt.mopidy.com/$DEBIAN_CODENAME.list"
# 4. Install pip packages and Mopidy
RUN apt-get update && \
apt-get install -y \
mopidy \
libspotify-dev && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /tmp/*
RUN python3 -m pip install \
pyspotify \
Mopidy-Local \
Mopidy-Iris \
Mopidy-Mobile \
Mopidy-Spotify \
Mopidy-Mopify
# 5. Configure user
RUN usermod -d /mnt/mopidy mopidy
RUN echo "mopidy ALL=NOPASSWD: /usr/local/lib/python3.7/dist-packages/mopidy_iris/system.sh" >> /etc/sudoers
# 6. Create folders
WORKDIR /mnt/mopidy
RUN mkdir /mnt/mopidy/data
RUN mkdir /mnt/mopidy/cache
COPY scripts/ scripts/
# 7. Configuration local scan
RUN (crontab -l 2>/dev/null || true; echo "0 */1 * * * mopidy --config /mnt/mopidy/config/mopidy.conf local scan") | crontab -
RUN ln -sf /mnt/mopidy/config/mopidy.conf /etc/mopidy/mopidy.conf
# 8. Audio settings
RUN echo \
"pcm.!default { \n\
type asym \n\
playback.pcm { \n\
type plug \n\
slave.pcm "output" \n\
} \n\
capture.pcm { \n\
type plug \n\
slave.pcm "input" \n\
} \n\
} \n\
pcm.output { \n\
type hw \n\
card $AUDIO \n\
} \n\
ctl.!default { \n\
type hw \n\
card $AUDIO \n\
}" >> /etc/asound.conf
# 9. Container
EXPOSE 6680
VOLUME /mnt/mopidy/config
VOLUME /mnt/mopidy/local
ENTRYPOINT ["/usr/bin/dumb-init","--"]
CMD ["/bin/sh", "scripts/entry_point.sh", "mopidy", "--config", "/mnt/mopidy/config/mopidy.conf"]
Instalamos algunos paquetes de sistema necesarios para poder instalar Mopidy, el paquete curl se instala para poder usar el comando HEALTHCHECK en el fichero docker-compose.yml
Instalamos Mopidy usando el repositorio oficial, en este caso usamos Debian porque el repositorio está orientado a esta distribución de tal forma que al usar el comando lsb_release podemos parametrizar el acceso al repositorio.
Una vez instalado Mopidy instalamos algunos plugins (se podrían instalar más). Usamos el usuario creado mopidy para arrancar el servicio y configuramos las carpetas donde guardar la configuración (/config) y la carpeta de archivos locales (/local) para el plugin local.
Como el plugin local tiene que escanear si hay cambios en las carpetas añadimos una tarea cron que se ejecutará cada hora para refrescar carpetas. Este mismo comando se puede ejecutar desde la opción Start local scan para eso añadimos al usuario mopidy al fichero sudoers para que pueda lanzar el comando. Este comando lanzado a través de la web utilizar el fichero de configuración por defecto así que creamos un enlace simbólico al que vamos a usar.
En este caso el usuario mopidy no hace falta que tenga derechos de root por seguridad, aunque es verdad que sería necesario para actualizar algunos plugins en este caso es más práctico actualizar el contenedor.
También se usa el paquete dump-init, el proceso mopidy no está orientado a ser usado como proceso padre así que es difícil gestionar su ciclo de vida si sucede algo y es necesario reiniciarlo, por eso gestionamos el contenedor a través de dumb-init que es el proceso padre del contenedor y el que arranca el proceso de mopidy.
Para obtener un id de cliente y un secret de Spotify podemos ir aquí.
Compilación y ejecución
Para compilar podemos usar:
docker build –tag user/mopidy -f Dockerfile.mopidy .
Para ejecutar podemos usar:
docker container run \
–init \
–publish 6680:6680 \
–device /dev/snd \
–volume /mnt/media/mopidy/config:/mnt/mopidy/config \
–volume /mnt/media/mopidy/local:/mnt/mopidy/local \
–env RA_UUID=1000 \
–env RA_GUID=1000 \
–env RA_SRVC=mopidy \
–env RA_FLDR=/mnt/mopidy \
–detach \
user/mopidy
Un detalle importante es usar –device /dev/snd para que el dispositivo de sonido sea utilizado dentro del contenedor. Una vez que arranquemos el contenedor tendremos que hacer login en Spotify y puede que también refrescar el token y la lista de reproducción.
Migración
Si ya tenemos una instalación de Mopidy podemos coger el fichero de configuración buscándolo con:
sudo find / -name mopidy*
Normalmente estará en el home del usuario en la carpeta .config, por ejemplo /home/pi/.config/mopidy/mopidy.conf
Docker Transmission
El fichero Docker para Transmission está basado en este artículo aunque también existe una variante de linuxserver.
# 0. Official armv32 image
FROM ubuntu:latest
LABEL "guru.raraavis.creator"="blog@raraavis.guru"
LABEL "guru.raraavis.version"="1.0.0"
LABEL "guru.raraavis.release-date"="20/03/2021"
LABEL "guru.raraavis.description"="Raspberry Pi 3 image with Transmission"
# 1. Environment vars
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=Europe/Madrid
ENV TZ=$TZ
# 2. Update system and install necessary packages
RUN apt-get update && \
apt-get install -y \
gosu \
software-properties-common \
gnupg2 \
gnupg \
gnupg1 && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /tmp/*
# 3. Add transmission repository and install Transmission
RUN add-apt-repository ppa:transmissionbt/ppa && \
apt-get update && \
apt-get install -y \
transmission-cli \
transmission-common \
transmission-daemon && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /tmp/*
# 4. Configure user
RUN usermod -d /mnt/transmission debian-transmission
# 5. Create folder
WORKDIR /mnt/transmission
COPY scripts/ scripts/
# 6. Container
EXPOSE 9091
VOLUME /mnt/transmission/config
VOLUME /mnt/transmission/watch
VOLUME /mnt/transmission/download
VOLUME /mnt/transmission/temp
ENTRYPOINT ["/bin/sh", "scripts/entry_point.sh"]
CMD ["/usr/bin/transmission-daemon", "-g", "/mnt/transmission/config", "-f", "-x", "/mnt/transmission/config/trans.PID"]
Para evitar un error parecido a este:
UDP Failed to set receive buffer: requested 4194304, got 425984 (tr-udp.c:84)
Tenemos que editar algunos valores del kernel en el host estos valores no se pueden modificar en el contenedor al ser valores compartidos. Hay varias formas de editar estos valores, una forma sería la siguiente:
Primero editamos el fichero sysctl.conf y añadimos lo siguiente (hay varias formas de editar sysctl).
net.core.rmem_max = 16777216
net.core.wmem_max = 4194304
Y refrescamos los valores con sysctl -p
Tenemos también varios volúmenes configurados:
- /mnt/transmission/config para la configuración
- /mnt/transmission/watch la carpeta watch donde buscar nuevos ficheros torrent para descargar
- /mnt/transmission/download carpeta de descargas
- /mnt/transmission/temp la carpeta temporal
Se usa un script especial para arrancar el contenedor y configurar los permisos, los detalles aquí.
Compilación y ejecución
Para compilar podemos usar:
docker build –tag user/transmission -f Dockerfile.transmission .
Para ejecutar podemos usar:
docker container run –init -p 9091:9091 -p 51413:51413 \
–volume /mnt/media/transmission/config:/mnt/transmission/config \
–volume /mnt/media/transmission/download:/mnt/transmission/download \
–volume /mnt/media/transmission/temp:/mnt/transmission/temp \
–volume /mnt/media/transmission/watch:/mnt/transmission/watch \
–env RA_UUID=1000 \
–env RA_GUID=1000 \
–env RA_SRVC=debian-transmission \
–env RA_FLDR=/mnt/transmission \
–detach user/transmission
Migración
La carpeta de Transmission tiene no solo la configuración en el fichero settings.json también otras carpetas donde se guardan los ficheros torrent descargados y las estadísticas, se puede encontrar con sudo find / -name settings.json y normalmente estará ubicada en /var/lib/transmission-daemon
Hay que copiar todo este contenido dentro de la carpeta /mnt/transmission/config y actualizar el fichero settings.json para que apunte a las carpetas del contenedor (/mnt/transmission/download, ….)
Docker Plex
En este caso se ha usado un contenedor ya existente de Plex suministrado por linuxserver.
Compilación y ejecución
En este caso no hace falta compilar al ser una imagen sacada del registro de contenedores. Para ejecutar podemos ver los detalles aquí.
Migración
Si tenemos cuenta en Plex con hacer login se puede recuperar gran parte de la configuración, la carpeta con el resto de la información se puede sacar de aquí /var/lib/plexmediaserver/Library/Application Support/Plex Media Server tal y como se cuenta aquí.
Docker Compose
Ahora todos los contenedores se arrancan a la vez usando este fichero:
version: "3"
services:
mopidy:
container_name: mopidy
image: raraavis/mopidy
build:
context: ..
dockerfile: media/mopidy/Dockerfile.mopidy
ports:
- 6680:6680
volumes:
- /mnt/media/mopidy/config:/mnt/mopidy/config
- /mnt/media/mopidy/local:/mnt/mopidy/local
devices:
- /dev/snd
environment:
- RA_UUID=1000
- RA_GUID=1000
- RA_SRVC=mopidy
- RA_FLDR=/mnt/mopidy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:6680 || bash -c 'kill -s 15 -1 && (sleep 10; kill -s 9 -1)'"]
interval: 30m
timeout: 30s
retries: 3
start_period: 30s
restart: unless-stopped
transmission:
container_name: transmission
image: raraavis/transmission
build:
context: ..
dockerfile: media/transmission/Dockerfile.transmission
ports:
- 9091:9091
- 51413:51413
volumes:
- /mnt/media/transmission/config:/mnt/transmission/config
- /mnt/media/temp:/mnt/transmission/temp
- /mnt/media/download:/mnt/transmission/download
- /mnt/media/watch:/mnt/transmission/watch
environment:
- RA_UUID=1000
- RA_GUID=1000
- RA_SRVC=debian-transmission
- RA_FLDR=/mnt/transmission
plex:
container_name: plex
image: ghcr.io/linuxserver/plex
container_name: plex
network_mode: host
environment:
- PUID=105
- PGID=106
- VERSION=docker
#- PLEX_CLAIM= see .env file
volumes:
- /mnt/media/plex/config:/config
#- /mnt/media/plex/tv:/tv
- /mnt/media/movies:/movies
restart: unless-stopped
Algunos aspectos a destacar son que el servidor Mopidy usa un health check para verificar si sigue activo, esto es debido a un bug reportado a Mopidy, el comando lo que hace es verificar si el servidor sigue activo y sino lo está mata el contenedor para que se reinicie usando el comando restart, más información aquí.
Para rellenar PLEX_CLAIM puede usarse esta página, esta variable está en un fichero de configuración separado .env otra opción sería usar secrets pero esto condiciona a la creación de un swarm.
Para docker transmission se pueden configurar varias carpetas pero si algunas de ellas está anidadas dentro de otras pueden ocultarse entre ellas de tal forma que no verá el contenedor los ficheros que dejemos, esto no es algo que suceda usando la línea de comandos de docker. La forma de evitarlo es indicar solo la carpeta raíz.
La estructura de carpetas es la misma que aparece en el código fuente.
Una vez que las imágenes han sido creadas y subidas al repositorio se puede comentar la sección de build.
Compose: compilación y ejecución
El fichero está configurado para compilar los ficheros dockerfile indicados usando docker-compose build el nombre de la imagen será el indicado en image.
Para ejecutar simplemente usamos docker-compose up y podremos ir viendo los mensajes o si lo queremos en segundo plano agregamos la opción –detach y quedaría docker-compose up –detach
Puede pasar que algunos valores del contenedor se mantengan entre ejecuciones y veamos mensajes como:
WARNING: Service «transmission» is using volume «/watch» from the previous container. Host mapping «/watch» has no effect. Remove the existing containers (with docker-compose rm transmission) to use the host volume mapping.
Solo hay que fijarse en el mensaje y ejecutar la instrucción que indica docker-compose rm transmission
Posible arquitectura
Se puede plantear lo siguiente:
- Los contenedores Docker se colocan en la misma Rpi usando Docker compose.
- Las carpetas de descarga apuntan a un almacenamiento externo, para saber como hacerlo se puede ver aquí.
- La carpeta watch se puede crear como un recurso compartido vía Samba para poder dejar los ficheros torrent desde cualquier otra máquina y que se vayan descargando sin tener que hacerlo a través del cliente web.
- Haría falta un cliente Plex que puede ser una SmartTv, u otra Raspberry Pi con esta distribución.
- Opcionalmente se puede colocar por delante el proxy Apache como se indica más abajo.
(Opcional) Proxy Apache
Es posible usar un servidor Apache que sirva de proxy para poder acceder a los servicios de forma más cómoda usando una URL con nombre en lugar de a través de la IP y del puerto, en cualquier caso tiene que haber un DNS que pueda resolver los nombres.
Tenemos que habilitar algunos módulos, para permitir que funcionen los websockets y cors.
a2enmod headers
a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel
a2enmod headers
Vamos a suponer que la IP del contenedor es 192.168.1.2, y que el dominio es rpi.local, hay que crear los siguientes ficheros de configuración de apache y colocarlos en la carpeta /etc/apache2/sites-available
001-transmission.conf
<VirtualHost *:80>
ServerName torrent.rpi.local
ServerAlias torrent
ProxyPreserveHost On
ProxyPass / http://192.168.1.2:9091/
ProxyPassReverse / http://192.168.1.2:9091/
CustomLog /var/log/apache2/transmission.log combined
ErrorLog /var/log/apache2/transmission.error.log
</VirtualHost>
Para habilitar el sitio ejecutamos:
sudo a2ensite 001-transmission.conf
002-mopidy.conf
<VirtualHost *:80>
ServerName spotify.rpi.local
ServerAlias spotify
ProxyPreserveHost On
ProxyPass / http://192.168.1.2:6680/
ProxyPassReverse / http://192.168.1.2:6680/
CustomLog /var/log/apache2/mopidy.log combined
ErrorLog /var/log/apache2/mopidy.error.log
</VirtualHost>
Para habilitar el sitio ejecutamos:
sudo a2ensite 002-mopidy.conf
003-plex.conf
<VirtualHost *:80>
ServerName plex.rpi.local
ServerAlias plex
ProxyPreserveHost On
ProxyPass / http://192.168.1.2:32400/
ProxyPassReverse / http://192.168.1.2:32400/
CustomLog /var/log/apache2/plex.log combined
ErrorLog /var/log/apache2/plex.error.log
</VirtualHost>
Para habilitar el sitio ejecutamos este comando para los diferentes virtual host, por ejemplo:
sudo a2ensite 003-plex.conf
