Boot NFS
Pour diminuer au maximum les sources de bruit de Mimosa, je n'utilise plus de disque dur, ni clés USB, ni flash. Il boote sur le réseau en PXE en récupérant une image d'une Debian customisée pour démarrer directement l'interface de MythTV directement.
Le processus de démarrage d'un ordinateur sur le réseau est le suivant :
- au cours du boot du bios, celui ci envoie une requête DHCP;
- le serveur DHCP lui fournit une adresse IP, masque + passerelle ainsi que les options BOOTP contenant le nom du fichier d'amorce ainsi que l'adresse d'un serveur sur lequel le récupérer;
- le bios le télécharge alors via le protocole TFTP (Trivial FTP) puis exécute ce programme d'amorçage qui lit un fichier de description de menu pour choisir l'image Linux à démarrer;
- Le noyau linux choisi est démarré avec des options permettant de monter un dossier spécifique partagé par le serveur NFS en tant que
/
; - Le noyau lance un init comme habituellement sur une installation standard qui lance à son tour les processus/démons traditionnels.
La mise en oeuvre n'est pas simple et requiert beaucoup de configuration/modifications que je vais tenter de détailler :
- Configuration du serveur DHCP + TFTP
- Paramétrage du serveur NFS
- Installation d'une Debian capable de démarrer sur du NFS
Configuration DHCP + TFTP
Dnsmasq est un serveur DHCP que j'utilise depuis longtemps et qui est capable de répondre aux requêtes PXE et intègre un serveur TFTP.
Dans mon installation, le dossier contenant tout l'arborescence nécessaire au TFTP + NFS se trouve dans /mnt/nfsroot
.
Pour activer les options BOOTP dans les réponses DHCP et activer le serveur TFTP intégré à Dnsmasq, ajouter les lignes suivantes :
- /etc/dnsmasq.conf
enable-tftp dhcp-boot=pxelinux.0 tftp-root=/mnt/nfsroot/tftp
enable-tftp
: active le serveur TFTP intégré;dhcp-boot
: nom du fichier que le client doit demander pour démarrer en PXE;tftp-root
: dossier dans le lequel se trouve les fichiers à servir.
Le dossier exposé via TFTP doit contenir au minimum des fichiers pxelinux.0
et pxelinux.cfg/default
, le contenu est le suivant :
├── initrd.img ├── ldlinux.c32 -> /usr/lib/syslinux/modules/bios/ldlinux.c32 ├── pxelinux.0 -> /usr/lib/PXELINUX/pxelinux.0 ├── pxelinux.cfg │ └── default └── vmlinuz
Description des fichiers :
pxelinux.0
+pxelinux.cfg/default
: programme d'amorçage + fichier de listing des images disponibles que le bios va lancer, équivalent à/boot/grub/grub.cfg
;ldlinux.c32
: loader de noyau linux, équivalent àlilo
ougrub
mais dédié au boot réseau;vmlinuz
+initrd.img
: image du noyau linux accompagné de son “archive” pour avoir le minimum vitale pour démarrer : module noyau, quelques commandes… voir ci dessous pour le contenu.
Pour récupérer les fichiers d'amorçage, il faut installer les paquets pxelinux
et syslinux-common
sudo apt-get install -y pxelinux syslinux-common
Copier le programme pxelinux.0
dans l'arborescence cible. C'est un équivalent de grub
ou lilo
:
sudo ln -s /usr/lib/PXELINUX/pxelinux.0 /mnt/nfsroot/tftp/
Idem que le chargeur de menu ldlinux.c32
:
sudo ln -s /usr/lib/syslinux/modules/bios/ldlinux.c32 /mnt/nfsroot/tftp/
Le programme pxelinux est en mesure d'afficher un menu permettant de choisir l'image à démarrer ainsi que les options à passer au noyau.
Voici mon fichier de menu pxelinux.cfg/default
:
- pxelinux.cfg/default
# image par défaut DEFAULT mythtv # déclaration d'une image LABEL mythtv # noyau à utiliser KERNEL vmlinuz # options du noyau APPEND initrd=initrd nfsroot=/mnt/nfsroot/mythtv.0.26,v3 root=/dev/nfs panic=10 ro quiet ipv6.disable=1 # pas d'attente PROMPT 0 TIMEOUT 60
Les options importantes transmises au noyau sont :
nfsroot
: précise où se trouve la racine de l'image à démarrer. Comme il n'est pas précisé de serveur NFS particulier, le noyau le cherchera sur la même IP que celui qui a servit les fichiers PXE. Ce dossier sera monté en que/
sur la machine cliente;root
: nom du block device référençant le périphérique contenant/
.
Serveur NFS
La configuration du serveur NFS est traditionnelle. La ligne suivante précise l'arborescence à exporter, l'option no_root_squash
est importante pour conserver l'appartenance de l'utilisateur root
aux fichiers :
- /etc/exports
/mnt/nfsroot/mythtv.0.26 *(rw,no_subtree_check,no_root_squash,fsid=8)
Installation d'une Debian capable de démarrer sur du NFS
La documentation officielle décrit précisément comment initialiser une arborescence à partir d'une distribution netboot de Debian.
Il faut juste noter que lorsque le noyau de la machine cliente démarre, le dossier /mnt/nfsroot/mythtv.0.26
partagé par le serveur NFS
doit être monté en tant que /
. Pour se faire, en plus d'ajouter l'option nfsroot
au noyau comme indiqué ci dessus, il faut modifier le fichier /etc/fstab
pour qu'il référence alors le device spécifique /dev/nfs
:
- /etc/fstab
# <file system> <mount point> <type> <options> <d><p> /dev/nfs / nfs ro,hard 1 1
Le lecteur attentif aura noté que /
est monté en *lecture seule* (option ro
) : mon objectif est de pouvoir démarrer 2 machines clientes sur la même arborescence de fichiers côté serveur. Ce partage fonctionnera alors comme les live-cd Knoppix ou Ubuntu : il n'est pas possible de modifier les fichiers sur une machine cliente. Une procédure spéciale permet de réaliser cette opération depuis l'hôte.
Spécialisation par host
Pour démarrer 2 machines (Mimosa + Wimpy) sur ce boot PXE en même temps, il faut être en mesure de partager l'arborescence /
tout en évitant les effets de bord (écriture des fichiers temporaires dans /var/…
ou dans /tmp/…
par exemple).
Si des précautions ne sont pas prises, le démarrage d'une machine influera sur la 2e.
Je veux pouvoir en plus spécialiser un minimum les séquences de démarrage des 2 machines notamment pour la conf de X
/ alsa
/ lirc
:
- Wimpy est équipé une carte graphique Intel en affichant l'image sur la sortie VGA et le son en analogique;
- Mimosa avec une carte Nvidia en passant l'image et le son sur la sortie HDMI.
Enfin, chaque machine possède un /home
différent lié à leurs utilisations :
- Wimpy étant dans la cuisine, il sert à la fois pour les recettes (navigation internet) et Mythtv;
Systèmes de fichiers
Pour mettre en place l'ensemble, j'utilise plusieurs types de filesystem :
tmpfs
: filesystem entièrement en mémoire limité en taille. En le montant dans un dossier, il est alors possible d'écrire dedans comme un dossier “normal” sauf qu'à l'arrêt de la machine, toutes les écritures seront perdues.aufs
: permettant de superposer différents répertoires les un sur les autres tout en spécifiant ceux en lecture seule ou lecture / écriture. Voici un exemple de superposition d'un/dossierA
sur un/dossierB
monté dans/dossierC
:
mount -t aufs nom /dossierC -o br:/dossierA=ro:/dossierB=rw
├── dossierA │ ├── fichier1 │ └── fichier2 └── dossierB ├── fichier2 └── fichier3
/dossierC
après le montage
└── dossierC ├── fichier1 ├── fichier2 (celui du dossierA) └── fichier3
Lors des écritures dans /dossierC
, celles ci s'effectueront dans /dossierB
(spécifié via le flag rw
).
ramfs
qui est très similaire au tmpfs
: les fichiers sont stockés en RAM donc volatiles. Il a la particularité d'occuper la mémoire proportionnellement aux fichiers écrit dedans. Par contre, aufs
refuse de manipuler ce filesystem .
Racine /
En combinant ces 2 systèmes de fichiers, il est possible alors de partir d'une image de base en lecture seule et de lui superposer un filesystem volatile autorisant les écritures.
J'ai créé un dossier /target
contenant 2 dossiers normalisés host-<nom de machine>
dans lesquels se trouvent les fichiers de configurations spécifiques à chaque machine. Voici le contenu de ces dossiers :
├── host-mimosa │ └── etc │ ├── asound.conf │ ├── asound.state │ ├── hostname │ ├── lirc │ │ ├── hardware.conf │ │ └── lircd.conf │ ├── motd │ ├── rc.local │ ├── rc.target │ ├── udev │ │ └── rules.d │ │ └── 70-persistent-net.rules │ └── X11 │ ├── edid.bin │ └── xorg.conf └── host-wimpy └── etc ├── hostname ├── lirc │ ├── hardware.conf │ ├── lircd.conf │ └── lircmd.conf ├── motd └── X11 └── xorg.conf
J'ai donc 1 dossier pour Mimosa et Wimpy et avec principalement le paramétrage de la carte son, de la télécommande, un script de démarrage spécifique, un fichier de règles udev et la configuration de X.
Ainsi, les opérations réalisées lors du boot de la machine sont les suivantes :
- le noyau démarre à partir du serveur TFTP et charge le
/
monté en lecture seule à partir du NFS via le/etc/fstab
présenté ci dessus - dans la séquence de boot, un script maison est lancé le plus tôt possible
/etc/rcS.d/S04aufs_ramfs
montant plusieurs filesystem :- un
tmpfs
sur/tmp
- un
aufs
qui superpose un dossier de/tmp
sur/var
avec/var
est en lecture seule,/tmp
est en lecture / écriture. Les programmes démarrant un peu plus tard pourront donc écrire sur/var
. - un 2nd
aufs
en vérifiant au préalable si un dossier/target/host-<nom de machine>/etc
existe.- Si c'est le cas, il superpose
/etc
en lecture seule,/target/host-<nom de machine>/etc
en lecture seule et un dossier de/tmp
en lecture écriture. - Sinon, il superpose
/etc
en lecture seule avec un dossier de/tmp
en lecture écriture.
Script de montage
J'ai développé un script SysVInit
pour être lancé au démarrage de la machine et réalisant les opérations décrites précédemment pour monter sur /etc
et /var
le ramfs
avec aufs
.
- /etc/rcS.d/S04aufs_ramfs
### BEGIN INIT INFO # Provides: aufs_ramfs checkroot-bootclean checkfs # Required-Start: hostname mountkernfs # Required-Stop: # Should-Start: # Should-Stop: # Default-Start: S # Default-Stop: # Short-Description: Aufs Ramfs and specific hostname stuff ### END INIT INFO PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="Aufs Ramfs and specific hostname stuff" NAME=AufsRamfs # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions HOSTNAME=$(hostname) # # Function that starts the daemon/service # do_start() { /bin/mount -t tmpfs ramfsrwtmp /tmp -o noatime,nodev,nosuid,noexec,size=20M,mode=1777 touch /tmp/.clean mkdir /tmp/varfs /tmp/etcfs /bin/mount -t aufs var_aufs /var -o br:/tmp/varfs=rw:/var=ro if [ -d /target/host-$HOSTNAME ]; then /bin/mount -t aufs etc_aufs /etc -o br:/tmp/etcfs=rw:/target/host-$HOSTNAME/etc=ro:/etc=ro else /bin/mount -t aufs etc_aufs /etc -o br:/tmp/etcfs=rw:/etc=ro fi } case "$1" in 'start') log_daemon_msg "Starting $DESC" "$NAME" do_start log_end_msg 0 ;; *) echo "Usage: $SCRIPTNAME {start}" >&2 exit 3 ;; esac
/home
J'utilise le même principe pour le montage du $HOME
du user mythtv
. J'ai créé plusieurs dossiers :
/mnt/nfsroot/mythtv.0.26/home/mythtv
: contient la configuration commune à toutes les machines, sera monté en lecture seule;- autant de
/mnt/nfsroot/mythtv.0.26/home/mythtv-<nom de machine>
que de machines à démarrer : contient la configuration spécifique de l'ordinateur (mapping de touche de la télécommande, cache Mythtv…, sera monté en lecture écriture;
Lors de la séquence de boot avant le démarrage de X
, un autre script maison est lancé pour superposer un dossier spécifique à l'hôte :
- le dossier
/mnt/nfsroot/mythtv.0.26/home
est monté à partir du NFS dans/home
- si un dossier
/home/mythtv-<nom de machine>
existe alors celui si sera monté au dessus du/home/mythtv
déjà existant.
Ici, pas de système de fichier en mémoire, les programmes écrivant dans leur /home/mythtv
écriront directement dans /home/mythtv-<nom de machine>
qui est sur le NFS.
Script de montage
Ce script monte le /home/mythtv-<nom de machine>
sur /home/mythtv
comme décrit précédemment :
- /etc/rcS.d/S17mount_home
#!/bin/sh ### BEGIN INIT INFO # Provides: aufs_home # Required-Start: x11-common nfs-common # Required-Stop: $time # Default-Start: S # Default-Stop: # Short-Description: Aufs specific home ### END INIT INFO PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="Aufs specific home" NAME=AufsHome # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions HOSTNAME=$(hostname) # # Function that starts the daemon/service # do_start() { # automaticaly find / mount source ROOT_DIR=$(mount | grep "on / type nfs" | cut -d' ' -f 1 | cut -d"/" -f1-3 ) /bin/mount -t nfs -o noatime,rsize=65536,wsize=65536,hard,nolock,async,intr,sec=sys ${ROOT_DIR}/home /home if [ -d /home/mythtv-$HOSTNAME ]; then /bin/mount -t aufs home_aufs /home/mythtv -o br:/home/mythtv-$HOSTNAME=rw:/home/mythtv=ro fi } do_stop() { umount /home/mythtv umount /home } case "$1" in 'start') log_daemon_msg "Starting $DESC" "$NAME" do_start log_end_msg 0 ;; 'stop') log_daemon_msg "Stopping $DESC" "$NAME" do_stop log_end_msg 0 ;; *) echo "Usage: $SCRIPTNAME {start|stop}" >&2 exit 3 ;; esac
Lancement de X
Sur une distribution linux standard taillée pour le bureau, le serveur X
est lancé avec un gestionnaire de login (xdm
, gdm
, lightdm
…). C'est bien mais pas top car dans mon cas, cela augmente le temps de démarrage et l'occupation mémoire même si ce gestionnaire est le plus léger possible.
Pour répondre à mes besoins, j'ai écrit un script SysVInit
de lancement de X avec le user mythtv
via la commande startx
:
- /etc/rc2.d/S02startx
#!/bin/sh ### BEGIN INIT INFO # Provides: startx # Required-Start: $remote_fs $time nvidia-kernel rc.target # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 ### END INIT INFO PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/sbin/startx NAME=startx DESC="X server" . /lib/init/vars.sh . /lib/lsb/init-functions case "$1" in start) log_begin_msg "Starting $DESC: " rm -f /home/mythtv/.Xauthority* /bin/su - mythtv -c /usr/bin/startx 2>/dev/null 1>&2 & log_end_msg 0 ;; stop) echo -n "Stopping $DESC: " chvt 1 echo "$NAME." ;; restart) $0 stop sleep 1 $0 start ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart}" >&2 exit 1 ;; esac exit 0
startx
démarre un serveur X
et invoque le script $HOME/.xinitrc
. Voici la configuration pour Mimosa :
- /home/mythtv/.xinitrc
# permet de faire disparaitre le curseur unclutter -idle 0.1 -root -regex -notclass 'Iceweasel|XTerm|Gcalctool|Gthumb' & # charge la configuration utilisateur du driver de carte graphique nvidia-settings -l # désactive l'extinction automatique de l'écran pour l'économie d'énergie xset -dpms # écran de veille xscreensaver & # fond d'écran xloadimage -onroot -center ~/bg.png # mapping des touches de la télécommande (sleep 30 ; irexec -d ) & # IHM de mythtv mythfrontend --logpath /var/log/mythtv & # gestionnaire de fenêtre ultra minimaliste ratpoison -f ~/.ratpoisonrc
Au final, entre l'allumage de l'ordinateur et la fin de démarrage de Mythtv, il s'écoule 1 minute environ.
Maintenance
Pour modifier la configuration, installer des paquets il n'est pas possible de le faire sur une machine cliente bootée car /
est montée en lecture seule.
Ces opérations doivent être réalisées directement sur le serveur sur l'arborescence partagée par le NFS en lançant un chroot
dans /mnt/nfsroot/mythtv.0.26
.
user@popeye $ cd /mnt/nfsroot/mythtv.0.26 user@popeye $ sudo chroot . user@chroot #
Il faut également monter /dev
, /dev/pts
et /proc
dans ce chroot
pour que tous les programmes s'exécutent correctement.
Pour me faciliter le travail, j'ai écrit un script de lancement de montage de ces dossiers et de lancement du chroot
:
- chroot_setup.sh
#!/bin/sh -e # récupération du chemin absolu du dossier dossier courant CHROOT_DIR=$( cd -P -- "$(dirname -- "$0")" && pwd -P) echo "Montage en cours" mount /dev/ $CHROOT_DIR/dev -o bind mount -t devpts devpts $CHROOT_DIR/dev/pts mount -t proc proc $CHROOT_DIR/proc -o noexec,nosuid,nodev echo "Montage OK" chroot .
Lancement :
user@popeye $ cd /mnt/nfsroot/mythtv.0.26 user@popeye $ sudo ./chroot_setup.sh Montage en cours Montage OK user@chroot # apt-get install ...
Ainsi un apt-get install zsh
s'installera bien dans cet environnement plutôt que sur la machine hôte.
Pour démonter les filesystems, il faut le faire dans le sens inverse de montage. Un autre script fait ce travail :
- chroot_de-setup.sh
#!/bin/sh CHROOT_DIR=$( cd -P -- "$(dirname -- "$0")" && pwd -P) echo "demontage en cours" umount $CHROOT_DIR/dev/pts umount $CHROOT_DIR/dev umount $CHROOT_DIR/proc
Lancement :
user@popeye $ cd /mnt/nfsroot/mythtv.0.26 user@popeye $ sudo ./chroot_de-setup.sh
Les filesystemes sont démontés et laissent la machine hôte toute propre .