Mediacenter DIY (saison 2) avec LibreElec et k3s

J’ai changé d’appareil et de distribution Linux pour mon mediacenter. Et ai réussi à l’intégrer à mon cluster k3s (kubernetes).

Nouvel appareil

Pendant de nombreuses années, j’ai utilisé un vieux PC portable comme mediacenter. Après quasiment 10 ans de bons et loyaux services, cet appareil a commencé à donner des signes de faiblesse: sautes d’image, et gros ralentissements avec les versions récentes de Kodi.

J’ai testé de le remplacer par un serveur Olinuxino A64, suite à l’annonce d’Olimex du support de LibreElec. Hélas, cela souffrait des mêmes problèmes que sous Debian: instabilités, et surtout aucune maintenance faite par Olimex après l’annonce. Et les appareils d’Olimex ne font pas partie de ceux officiellement supportés par LibreElec (contrairement à d’autres basés sur le même CPU A64, probablement à cause du fait qu’Olimex n’ait pas remonté ses patchs kernel upstream).

Bref, j’ai finalement décidé de reconduire l’utilisation d’un PC portable désossé. J’ai trouvé sur LeBonCoin un vieux PC portable avec écran cassé: un Toshiba Portégé Z830. Comme pour le précédent, j’ai démonté l’écran (et enlevé la batterie).

Comme télécommande, j’utilise une Minix A2 Lite, qui fait de l’émulation clavier.

Nouvelle distribution Linux : LibreELEC

Sur l’ancien appareil, j’avais fait des choses un peu compliquées pour avoir un Kodi aux petits oignons sur xubuntu.

Cette fois-ci, j’ai utilisé une distribution linux faite pour ça: LibreElec.

J’y ai perdu le chiffrement du filesystem, mais y ai gagné un système simple à installer, et prêt à l’emploi.

Je n’ai pas eu de mal à y remettre ma librairie Kodi (export/import), ni à y recopier les keymaps de ma télécommande.

Intégration dans mon cluster k3s

Pourquoi cette idée saugrenue?

D’abord, le démarrage de mon mediacenter est un peu lent: pas le démarrage de LibreElec lui-même, mais l’étape BIOS. Je suppose que l’appareil est perturbé de ne pas trouver son écran…

D’autre part, c’est un appareil plutôt puissant par rapport à mes petits serveurs auto-hébergés (Core i5-2467M, 4Go de RAM, disque NVMe), qui consomme peu (7W quand il est idle, 20W en lecture full HD), et en architecture AMD64 (et non ARM comme tous mes serveurs, ce qui limite beaucoup les images Docker compatibles)

Donc je me suis dit qu’il pourrait être pertinent de laisser mon mediacenter tourner en permanence (donc toujours prêt à l’usage), et d’exploiter ses ressources pour mon auto-hébergement, en l’intégrant dans mon cluster k3s. Au moins pour le fun.

Particularités de LibreElec

LibreElec est présenté comme « Just Enough OS for Kodi ». Il n’est pas basé sur Debian ou autre distribution Linux classique: ils n’embarquent que le strict minimum, avec un binaire compilé depuis les sources à chaque fois.

D’autre part, l’essentiel du filesystem est en read-only (ce qui facilite le processus de mise à jour, et est forcément bien niveau sécurité).

Pour autant, j’ai été encouragé par le fait de voir qu’il existe un addon officiel qui permet de faire tourner des conteneurs. Je me suis dit que, puisque Docker tournait dessus, il était certainement possible d’y faire tourner k3s.

Oui, mais ça n’a pas été si simple.

Implémentation « dirty hack »

J’ai investigué le sujet, avec un peu d’aide de quelqu’un de k3s : https://github.com/k3s-io/k3s/issues/4859

Assez vite, j’ai vu qu’il fallait des options de compilation du kernel supplémentaires. J’ai pu les ajouter en recompilant LibreElec moi-même. Je les ai proposées avec une PR chez LibreElec, mais ils l’ont refusée (ils considèrent que cela sort du cadre d’un « Just enough OS for Kodi », ce qui est probablement vrai).

Avec ces options de kernel, k3s a ce qu’il faut pour fonctionner, mais il faut encore contourner le fait que / soit en readonly, et que /var soit du tmpfs.

J’ai fini par réussir. Il s’agit d’un « Dirty hack » dont je ne suis pas spécialement fier, mais qui fonctionne.

D’abord, à l’installation de k3s, il faut lui préciser de ne pas utiliser les répertoires par défaut, mais (autant que possible) tout mettre dans /storage (qui est en readwrite, et persistant)

curl -sfL https://get.k3s.io | INSTALL_K3S_SYSTEMD_DIR=/storage/k3s-systemd INSTALL_K3S_BIN_DIR=/storage/k3s K3S_URL=https://alias-dns-control-plane:6443 K3S_TOKEN=K101bfdda519dc15b3c9400bf91d8ea70961152d44f686bc3feee481481f76157ad::server:01ad7a756859e4a9133a7ebd2c5d54cc sh -s -

Hélas, il n’est pas possible de lui dire de mettre sa configuration ailleurs que dans /etc/rancher, ses données ailleurs que dans /var/lib/rancher, et il a besoin aussi d’écrire dans /usr/libexec.

A moins que ces options ne soient un jour ajoutées à l’installeur (j’en ai fait la demande), il a fallu trouver un contournement.

Et c’est là que ça devient pas très propre: comme je ne peux pas créer de symlinks sur un filesystem readonly, je copie au démarrage les répertoires /etc et /usr dans /storage, puis les monte à la place de /etc et /usr. Et je mets un symlink pour /var/lib/rancher dans /storage aussi. J’ai créé un fichier /storage/k3s-systemd/mount-fake-readwrite-root-fs.sh avec ce contenu:

# Remove existing symlinks and copies
rm /etc/rancher
rm /usr/libexec
rm /var/lib/rancher
rm -rf /storage/libreELEC-fake-readwrite-root-fs

# Copy data and create mounts and symlinks
mkdir -p /storage/libreELEC-fake-readwrite-root-fs/etc /storage/libreELEC-fake-readwrite-root-fs/usr
mkdir /storage/k3s /storage/k3s-systemd /storage/k3s-etc-rancher /storage/k3s-usr-libexec /storage/k3s-var-lib-rancher

cp -ar /etc /storage/libreELEC-fake-readwrite-root-fs/
mount /storage/libreELEC-fake-readwrite-root-fs/etc /etc
ln -s /storage/k3s-etc-rancher /etc/rancher

cp -ar /usr /storage/libreELEC-fake-readwrite-root-fs/
mount /storage/libreELEC-fake-readwrite-root-fs/usr /usr
ln -s /storage/k3s-usr-libexec /usr/libexec

ln -s /storage/k3s-var-lib-rancher /var/lib/rancher

Et je référence ce fichier dans le service Systemd de k3s (pour qu’il soit lancé avant le démarrage de k3s): dans /storage/k3s-systemd/k3s-agent.service, j’ajoute la ligne suivante dans la section [Service]:

ExecStartPre=/bin/sh /storage/k3s-systemd/mount-fake-readwrite-root-fs.sh

J’y ai aussi ajouté la ligne suivante pour ne pas trop ralentir l’arrêt de la machine (mais c’est probablement discutable. Source : https://github.com/k3s-io/k3s/issues/2400)

KillMode=Mixed (Mise à jour du 10/09/2022: ce n’est finalement plus nécessaire, en tous cas avec k3s 1.24.4)

Et ça marche!

J’ai bien conscience que ce n’est pas très propre, et que je ne gère pas bien un éventuel redémarrage du service k3s. Mais, vu mon besoin, je n’ai pas creusé plus.

NB: je m’appuyais initialement sur l’addon Docker pour faire tourner les images, mais n’en avais finalement pas besoin: k3s fournit le binaire de containerd qui fait très bien le travail.

Configuration NFS

J’ai quand même eu un problème avec NFS: en utilisant le nfs-subdir-external-provisioner pour la gestion des PVs, k3s s’appuie sur le client NFS de l’hôte (ça tombe bien, LibreElec en fournit un).

Sauf que mes accès NFS se coupaient au bout de quelques minutes sur cet appareil.

J’ai finalement trouvé un contournement, en configurant ce provisioner pour qu’il utilise une version plus récente du protocole NFS:

    nfs:
      mountOptions:
      - nfsvers=4.1

Conclusion

Trop la classe que mon mediacenter soit dans le cluster k3s! Ca me permet d’utiliser des images Docker amd64 (quand ils ne fournissent pas, ou pas encore, de version arm), et d’assurer la continuité de service quand mes serveurs Olinuxino sont indisponibles.

Et je tire mieux avantage de ce matériel, plutôt qu’il soit inutilisé la plupart du temps.

Oui, c’est un peu un délire de geek, et ça m’oblige à recompiler LibreElec de temps en temps pour avoir les mises à jour. Mais j’assume! Et ça m’a permis au passage d’apprendre/comprendre pas mal de choses sur k3s.

Une réflexion sur « Mediacenter DIY (saison 2) avec LibreElec et k3s »

  1. Replicating this but trying rke2 on libreelec that’s booting from pxe/nfs.
    Hoping that I can get some CSI working so disks in the nodes can be used for k8s storage.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *