Introduction
Suite à cet article, There’s endless choice, but you’re not listening’: fans quitting Spotify to save their love of music , j’ai re-découvert Navidrome que j’avais découvert via le très bien informé Korben comme souvent.
Je me suis dit que c’était l’occasion de tester en auto-hébergé, car mes musiques sont évidement chez moi et surtout c’était l’occasion de mettre en oeuvre ma nouvelle stack favorite Nomad
.
Pour rappel j’avais fais une doc sur l’auto-hébergement hybride
en 2017 avec Docker swarm, VPNcloud, syncthing, traefik, etc. Il faut avouer que c’était une usine à gaz de l’enfer ☠️.
Heureusement si vous avez suivi mon article Une infra avec Nomad, Consul et Tailscale
vous devriez pouvoir mettre en place cette V2 rapidement.
Pourquoi hybride ?
Je souhaite bénéficier des fonctionnalités anti-DDOS des hébergeurs cloud et ainsi ne pas exposer l’IP fournie par mon FAI qui de toute façon n’est pas garantie fixe. Aussi je souhaite pouvoir héberger sur le cloud quelques services (typiquement Uptime Kuma
).
Cette présentation nécessite donc deux VMs chez un hébergeur, une pour Nomad en mode serveur et client avec une IP publique, et une VM pour un serveur Consul. Leurs configurations sont indiquées dans mon précédent article.
Vous pouvez toutefois être en mode full auto-hébergé, il suffira de faire pointer le DNS de votre domaine vers votre IP fixe et faire en sorte qu’un serveur avec caddy-docker-proxy
puisse répondre aux requêtes HTTPS.
Le serveur à la maison
Depuis 2017 j’ai investi dans un mini PC NUC
(core I3/16Go RAM, 1To SSD) qui fait l’affaire, mais un RaspberryPi avec un disque externe le fera aussi sous condition.
En effet un RaspberryPi a une architecture ARM et non pas x86, cela imposera donc des images Docker sous ce format. Navidrome a le bon goût de proposer des images Docker ARM
(merci Go), il faudra choisir une des images ARM selon la version et l’OS de votre RaspberryPi (à priori linux/arm64 à partir du 4 si l’OS est en 64 bits).
Manjaro
Mon NUC il est installé avec Manjaro car il est connecté à la TV et sert aussi à voir des vidéos. Aussi j’ai du appliquer la configuration suivante pour éviter qu’il mette le NUC en veille au bout d’un certain temps. Bien sûr si vous avez installé une distribution serveur cela ne s’applique pas.
avec un terminal exécutez les commandes suivantes (source ):
sudo su - gdm -s /bin/bash
dbus-launch gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing'
exit
sudo shutdown -r now
Tailscale, Nomad, Consul, CNI-plugins, Docker
J’utilise les paquets de Manjaro (pour d’autres OS voir l’article précédent) :
sudo pacman -S tailscale nomad consul docker cni-plugins
tailscale login
renseignez les informations de votre compte tailscale puis vérifier avec tailscale status
récupérez votre IP tailscale avec tailscale ip
Consul
/etc/consul.d/consul.hcl
data_dir = "/var/lib/consul"
# sur Manjaro, le paquet consul créé le répertoire /var/lib/consul
# sur Ubuntu serveur c'est en général dans /opt/consul
bind_addr = "IP_TAILSCALE_NUC"
retry_join = ["IP_TAILSCALE_NODE3"]
Pour rappel selon ma précédente doc le serveur consul est installé sur le node3.
sudo systemctl enable consul
sudo systemctl start consul
Nomad
/etc/nomad.d/defaults.hcl
## https://www.nomadproject.io/docs/agent/configuration/index.html
# state directory
data_dir = "/var/lib/nomad"
# sur Manjaro, le paquet nomad créé le répertoire /var/lib/nomad
# sur Ubuntu serveur c'est en général dans /opt/nomad
bind_addr = "IP_TAILSCALE_NUC"
# binaries shouldn't go in /var/lib
plugin_dir = "/usr/lib/nomad/plugins"
server {
# license_path is required for Nomad Enterprise as of Nomad v1.1.1+
#license_path = "/etc/nomad.d/license.hclic"
enabled = false
bootstrap_expect = 1
}
client {
enabled = true
servers = ["IP_TAILSCALE_NODE1"]
host_network "tailscale" {
cidr = "IP_TAILSCALE_NUC/32"
}
}
plugin "docker" {
config {
volumes {
enabled = true
}
extra_labels = ["job_name", "job_id", "task_group_name", "task_name", "namespace", "node_name", "node_id"]
}
}
consul {
address = "127.0.0.1:8500"
}
on lance Nomad
sudo systemctl enable nomad
sudo systemctl start nomad
Si tout est ok, vous devriez voir sur les interfaces web de consul et nomad votre serveur :
- Pour consul :
http://IP_TAILSCALE_NODE3:8500/ui/dc1/nodes
- pour nomad :
http://IP_TAILSCALE_NODE1:4646/ui/clients
La configuration de votre cluster Nomad est terminée, votre serveur est prêt à recevoir des conteneurs.
Navidrome
Leur configuration Docker est ici . On va transformer le docker-compose au format HCL :
docker-compose:
version: "3"
services:
navidrome:
image: deluan/navidrome:latest
user: 1000:1000 # should be owner of volumes
ports:
- "4533:4533"
restart: unless-stopped
environment:
# Optional: put your config options customization here. Examples:
ND_SCANSCHEDULE: 1h
ND_LOGLEVEL: info
ND_SESSIONTIMEOUT: 24h
ND_BASEURL: ""
volumes:
- "/path/to/data:/data"
- "/path/to/your/music/folder:/music:ro"
navidrome.hcl
job "navidrome" {
datacenters = ["dc1"]
type = "service"
group "home" {
count = 1
network {
port "http" {
to = 4533 # container port the app runs on
static = 4533 # host port to expose
host_network = "tailscale"
}
}
task "navidrome" {
driver = "docker"
constraint {
attribute = "${attr.unique.hostname}"
value = "nuc"
}
env {
ND_SCANSCHEDULE= "1h"
ND_LOGLEVEL = "info"
ND_SESSIONTIMEOUT = "24h"
ND_BASEURL = ""
}
config {
image = "deluan/navidrome:latest"
volumes = [
"/data/volumes/navidrome/:/data",
"/data/musiques:/music"
]
ports = [
"http"
]
}
resources {
cpu = 500
memory = 500
}
service {
name = "navidrome"
tags = ["global", "app"]
provider = "consul"
port = "http"
}
}
}
}
Explications
datacenters = ["dc1"]
indique le nom de mon datacenter, par simplicité c’est le même entre mes VMs sur le cloud et chez moi. Si vous avez un usage professionnel, vous pouvez indiquer un autre datacenter, mais en pratique cela nécessitera un autre serveur consul et nomad dans ce DC.
host_network = "tailscale"
cette directive force Consul a enregistrer votre service sur L’IP de Tailscale. Sans celle-ci le serveur Consul résoudrait le service navidrome.service.consul vers l’IP locale du NUC, c’est à dire 192.168.X.X ce qui ne fonctionnerait évidemment pas.
value = "nuc"
correspond au nom de votre serveur, tapez la commande hostname
pour connaitre le votre. Cela force ainsi Nomad a déployer le conteneur Navidrome sur ce serveur.
volumes = [
"/data/volumes/navidrome/:/data",
"/data/musiques:/music"
]
indique à Navidrome où sont les données. J’ai un répertoire /data attaché à un disque dédié. Attention comme précisé dans leur doc le répertoire /data/volumes/navidrome doit appartenir à l’utilisateur id 1000, donc bien exécuter cette commande avant de lancer le conteneur: sudo chown 1000:1000 /data/volumes/navidrome/
resources {
cpu = 500
memory = 500
}
indique à Nomad les ressources allouées au conteneur. A priori 500Mhz de CPU et 500Mo de RAM devraient suffir, à adapter selon votre config matériel et votre usage.
Lancement
Vous pouvez maintenant lancer le job depuis votre PC perso si vous avez installé tailscale, Nomad et configuré la variable d’environnement NOMAD_ADDR=http://IP_TAILSCALE_NODE1:4646
. Ajouter la variable NOMAD_TOKEN
si vous avez activé les ACL sur Nomad.
nomad job run navidrome.hcl
En cas de soucis il faut aller sur l’interface web du serveur Nomad pour consulter le job :
http://IP_TAILSCALE_NODE1:4646/ui/jobs
Si tout va bien, voici l’URL pour créer votre compte Navidrome admin :
http://IP_TAILSCALE_NUC:4533/
Vous devriez avoir une joli interface web avec vos musiques prêtes à jouer.
Les clients de bureau
Une interface web c’est bien gentil mais rien de vaut un bon client natif. Navidrome propose une liste d’Apps
, j’ai testé Supersonic
et Sublime Music
disponibles tous les 2 sur Manjaro :
yay -S supersonic-desktop-bin
et yay -S sublime-music
Une petite préférence pour Supersonic en Go plutôt que Sublime Music en python mais ce dernier est mieux intégré grâce à GTK.
Pour la connexion au service utilisez l’URL http://IP_TAILSCALE_NUC:4533/
ou celle publique (que l’on va voir maintenant) si vous vous déplacez à l’extérieur.
Clients Android
Il y aurait peu d’intérêt à utiliser cette stack si le service n’était pas accessible depuis l’extérieur via un smartphone par exemple ou même depuis votre laptop. On va donc créer un conteneur qui va ajouter des labels pour votre caddy-docker-proxy
job "navidrome-caddy" {
datacenters = ["dc1"]
type = "service"
group "app" {
count = 1
task "navidrome-caddy" {
driver = "docker"
constraint {
attribute = "${attr.unique.hostname}"
value = "node1"
}
config {
image = "kamlando/ubuntu-sleep"
labels = {
"caddy" = "navi.fredix.xyz"
"caddy.reverse_proxy" = "http://navidrome.service.consul:4533"
# remove the following line when you have verified your setup
# Otherwise you risk being rate limited by let's encrypt
"caddy.tls.ca" = "https://acme-v02.api.letsencrypt.org/directory"
}
}
resources {
cpu = 10
memory = 10
}
service {
name = "navidrome-caddy"
tags = ["global", "app"]
provider = "consul"
}
}
}
}
Modifiez value = "node1"
avec le hostname de votre serveur Nomad et modifiez le label caddy
vers votre domaine puis lancez le job :
nomad job run navidrome-caddy.hcl
Et voilà, votre service Navidrome est exposé sur Internet.
Pour Android j’ai testé le client Ultrasonic
logiciel libre en lui donnant comme URL de connexion https://navi.fredix.xyz
Conclusion
Vous savez maintenant auto-héberger un grand nombre de services chez vous sans devoir montez en gamme côté cloud, car les conteneurs ajoutant des labels à caddy-docker-proxy sont positionnés au minimum accepté par Nomad : 10Mhz et 10Mo 😝
PS : Vous retrouvez mes fichiers HCL ici : https://codeberg.org/fredix/nomad
(Ce texte a été écrit avec Ghostwriter )