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.

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 )