vendredi 24 mars 2017

rhel-readonly

Il y a des avantages et des inconvénients à ce que tous les Raspberry pi du cluster bootent ensemble avec le même root-file-system situé sur un serveur nfs.

- L’énorme avantage est la maintenabilité : une mise  à jour sur le serveur nfs et tous les nœuds sont automatiquement à jour. Finit de dupliquer les sd-cartes, de faire des yum update sur chaque nœud, de répliquer les fichiers de configuration etc. (même si avec des outils comme Ansible on peut automatiser cela)

- L’inconvénient majeur est qu’il y a des fichiers modifiés simultanément par plusieurs  nœuds. Par exemple les fichiers de log, les caches etc. Pour éviter ces confits il va falloir faire 4 choses :

  1. Monter le root file system en read-only.
  2. Monter certains fichiers ou répertoire dans un file system temporaire en mémoire
  3. Attribuer un espace de stockage persistent spécifique à chaque nœuds
  4. Organiser/configurer les applications pour quelles écrivent dans cette zone dédiée.

1) Monter le root-file system en read-only

Dans le répertoire du serveur tftp (/var/lib/tftpboot/) nous avons le fichier cmdline.txt qui contenait jusqu’à maintenant cette unique ligne :
    root=/dev/nfs nfsroot=192.168.1.100:/var/nfs/rpi-fs  rw  ip=dhcp  rootwait
Il faut y remplacer le « rw »  par « ro ». Ce qui donne :
    root=/dev/nfs nfsroot=192.168.1.100:/var/nfs/rpi-fs  ro  ip=dhcp  rootwait

(ne booter par encore)

2) Monter certains fichiers et répertoires en mémoire

3) Attribuer un espace de stockage persistent à chaque Raspberry Pi du cluster

Avec CentOS 7.2+ ces deux points sont pris en charge par le pseudo service ‘rhel-readonly’.


Le service est:       /lib/systemd/rhel-readonly
Il décrit par:        /lib/systemd/system/rhel-readonly.service
Il est configuré par: /etc/sysconfig/readonly-root

Pour voir ses log:    journalctl -u rhel-readonly


Ce n’est pas un véritable service car il ne peut être ni activé ni désactivé avec systemctl car il est toujours exécuté au boot mais ne fait rien. Pour l’activer il faut éditer  /etc/sysconfig/readonly-root

ATTENTION: ne pas éditer les fichiers du serveur nfs, mais ceux qui sont dans la partie exportée par le serveur nfs : /var/nfs/rpi-fs/ . Le fichier  /path/to/file sur le client (le nœud du cluster) est sur le serveur nfs : /var/nfs/rpi-fs/path/to/file. 

Il y a plusieurs subtilités de configuration entre la ligne de commande du kernel et les deux variables READONLY et TEMPORARY_STATE. Pour faire simple et général on va configurer ce fichier ainsi:

READONLY=yes
TEMPORARY_STATE=yes

RW_MOUNT=/var/lib/stateless/writable
RW_LABEL=stateless-rw
RW_OPTIONS=

STATE_LABEL=stateless-state
STATE_MOUNT=/var/lib/stateless/state
STATE_OPTIONS=
CLIENTSTATE=192.168.1.100:/var/nfs

SLAVE_MOUNTS=yes


Adapter l'ip et le path de CLIENTSTATE à votre config.

En fait client va monter via nfs le path $CLIENTSTATE/$HOSTNAME sur $STATE_MOUNT.
Par example le noeud 'node04' va monter 192.168.1.100:/var/nfs/node04 sur /var/lib/stateless/state

Ce sera une zone persistante dédiée à l’activité de ce nœud. (Revoir le post sur la configuration du serveur nfs).


Pour le reste, ce qui va se passer dépend de ce qui est dans le fichier /etc/rwtab et /etc/statetab.


  • /etc/rwtab et /etc/rwtab.d désignent quelles parties du file system seront montées dans un file system temporaire en mémoire. Ceci a deux implications : Ces zones ne peuvent pas servir à stoker de gros fichiers et ces fichiers seront perdus au reboot.
  • /etc/statetab et /etc/statetab.d désignent quelles parties du file system seront montées dans le file system persistant désigné par $STATE_MOUNT (dans notre cas un export nfs). Ici pas de problème de taille de fichiers et tous les fichier sont préservés au reboot.
  • $STATE_MOUNT/files joue le même rôle que /etc/statetab, et permet a chaque nœud de designer les zones du file system a monter dans la zone NFS.


4) Organiser/configurer les applications

Certaine application sont tres configurable d'aute moins. Il faudra bien déterminer les zones disque en écriture dons elles ont besoins et si ce doit être persistent ou volumineux (dont via nfs) ou petit et temporaire (donc en ram). On pourra jouer sur la configuration de l'application et sur la configuration de rhel-readonly via /etc/rwtab.d et /etc/statetab.d.

Note: COMMENT METTRE A JOUR ?!!

Une fois ceci en place aucun nœud ne peut est utilisé pour mettre à jour les packages avec "yum update" puisque pour tous les nœuds le root file system est read-only !

De plus le serveur tftp et nfs est un x86 donc pas la même architecture, impossible de 'partager' ses mises à jour.

On verra plus loin comment faire à l’étape tftp pour que l’un des nœuds n’utilise pas la ligne de commande qui force le root file system en ro.



mercredi 15 février 2017

NFS Boot pour Raspberry Pi

Si vous avez mis en place le serveur NFSv2 comme indiqué dans ce post et si vous avez mis en place le DHCP et le serveur TFTP comme indiqué dans cet autre post alors votre Raspberry boote via le réseau mais s’arrête avec  la terrible erreur :

PANIC Unable to mount root fs. 

Pour améliorer cela il faut faire 3 choses :

1) Dans le répertoire servit par le serveur TFTP (qui est l’équivalent de la partition FAT d’une sd-card) il faut créer le fichier cmdline.txt avec comme contenu cette unique ligne :

root=/dev/nfs nfsroot=192.168.0.100:/var/nfs/rpi-fs rw ip=dhcp rootwait 

(Remplacez 192.168.0.100 par l’ip de votre serveur NFSv2)


2) Quand nous avions configuré le serveur NFS nous avions prévu d’exporter le répertoire /var/nfs/rpi-fs Ce qui se traduit dans le fichier /etc/exports par cette ligne :

/var/nfs/rpi-fs 192.168.0.0/24(rw,sync,no_root_squash,no_all_squash,no_subtree_check)

Il faut maintenant remplir ce répertoire avec ce qu’il y a dans la partition ‘/’ d’une sd-card.
Une fois  le fichier kernel7.img chargé via TFTP, le noyau prendra le contrôle et chargera le reste en utilisant le serveur NFSv2 désigné sur sa ligne de commande par nfsroot=aa.bb.cc.dd:/path

Attention: c’est bien NFS version 2.
Version très ancienne et désactivée sur certaines distributions linux comme CentOS.

3) Dans les fichiers copiés depuis une sdcard dans le repertoire /var/nfs/rpi-fs il faut au minimum éditer le fichier /var/nfs/rpi-fs/etc/mtab et y mettre en commentaire les mount de /boot, de / et de swap. (/boot est maintenant géré par la partie TFTP, / est deja monte via nfs, et il n'y a pas de swap. Si nécessaire on pourrait créer une partition de swap en deuxième partition sur la sdcard).

Truc: Pour éviter que la phase de login n'efface l’écran et fasse disparaître les log du boot il faut éditer le fichier /var/nfs/rpi-fs/usr/lib/systemd/system/getty@.service et dans la section [service] remplacer TTYVTDisallocate=yes par TTYVTDisallocate=no
Ainsi, quand vous serez au niveau du "login: " vous pourrez remonter dans les logs du boot avec SHIFT+PgUP et SHIFT+PgDown.

Une fois ces 3 opérations effectuées vous pouvez mettre votre Raspberry Pi sous tension et observer la procédure de boot à l’écran. Il y aura des erreurs mais vous devriez arriver jusqu'au un login.

Une analyse du trafic réseau avec tcpdump (ou wireshark) montre que les premières opérations réseaux du noyau sont:
- DHCP  pour obtenir (a nouveau) une ip.
- ARP  pour trouver l'adresse mac du serveur nfs.
- Portmap  pour obtenir le port du service 'mount'.
- MOUNT de /var/nfs/rpi-fs
- Portmap pour obtenir le port du service NFS
- NFS pour charger les fichiers

Les premiers répertoires et fichiers chargés via NFS sont
- dev
- sbin
- usr/bin
- init
- ../lib/systemd/systemd
- ld-linux-armhf.so.3
...

Pour analyser le trafic réseau, le plus efficace, est de capturer dans un fichier avec tcpdump sur la machine qui fait serveur NFS et de visualiser avec Wireshark.


mercredi 1 février 2017

Network Boot Raspberry Pi2

Voici un très court résumé de ce que nous allons devoir faire pour faire booter un Raspberry Pi 2 (RPi2) via le réseaux.

  1. sdcard + bootcode.ini
  2. DHCP/BOOTP
  3. TFTP
  4. NFSv2 (voir ce post)
  5. Brancher

Prérequis : Vous devez avoir un serveur DHCP configurable sur votre réseau.
Attention : Le serveur DHCP intégré dans les box des opérateurs internet n’est en général pas suffisamment configurable. 


1) Télécharger bootbin.bin  ici : https://github.com/raspberrypi/firmware/tree/master/boot
Mettre ce fichier sur une  carte micro SD vierge formatée en FAT32.
Mettre cette carte dans le RPi 2. (ne pas mettre en marche)


2) Configurer le serveur DHCP. Ici je donne la configuration du DHCP sur un switch Cisco.
A vous d'adapter à vos adresses ip.

ip dhcp excluded-address 192.168.0.1 192.168.0.100

ip dhcp pool CLUSTER
   network 192.168.0.64 255.255.255.192
   dns-server 8.8.8.8 
   option 43 ascii "Raspberry Pi Boot   "
   default-router 192.168.0.100 
   option 66 ascii 192.168.0.70

IMPORTANT : Il y a 3 espaces après Boot à la fin de "Raspberry Pi Boot   "

- network définit le (sous)réseaux dans lequel  les adresse ip sont allouées par le serveur dhcp.
Le « ip dhcp excluded-address exclue les ip  de 1 à 100. L’allocation aura donc lieu dans l’intervalle 101..126
- dns-server définit l’ip du résolveur dns. Ici celui de Google.
- default-router définit l'ip de la gateway (routeur)
- option 66 définit le nom du serveur TFTP, donné sous forme d’adresse IP.


3) Configuration du serveur TFTP  (ici configuration avec Centos 7)

yum install tftp-server tftp
systemctl start tftp 
systemctl enable tftp

Mettre tous les fichiers nécessaires au boot d’un RPi2 (ou d’un RPi3) dans la racine du serveur TFTP.
Par défaut le serveur TFTP sert  les fichiers qui sont dans /var/lib/tftpboot

Télécharger les dernière versions depuis  https://github.com/raspberrypi/firmware/tree/master/boot
ou prendre des version plus 'mature' sur la partition FAST d'une sdcarte carte. Cette partition est ensuite montée sous le nom de /boot.


bootcode.bin
On l’a déjà mis sur la carte de notre Rpi2, mais un Rpi3 sans sdcard peut le prendre directement sur le serveur TFTP.

Les 4 fichiers start*.elf
Celui utilisé dépendra de la répartition mémoire entre GPU et CPU
(Voir paramètre gpu_mem=... dans config.txt)
    start.elf
    start_cd.elf
    start_x.elf
    start_db.elf 

A chaque fichiers start*.elf correspond un fichier fixup*.dat
    fixup.dat  
    fixup_cd.dat 
    fixup_x.dat
    fixup_db.dat  

Le kernel ARM7 32 bits multi cores commun à RPi2 et RPi3
    kernel7.img

Les Device Tree
    bcm2709-rpi-2-b.dtb
    bcm2710-rpi-3-b.dtb

Créer les deux fichiers de configuration vide (on les remplira plus tard)
    config.txt
    cmdline.txt


4) Enfin, mettre le RPi sous tension.
Dans /var/log/message on doit trouver l’activité du serveur TFTP
   tftpd: Client 192.168.0.126 finished config.txt
   tftpd: Client 192.168.0.126 finished start.elf
   tftpd: Client 192.168.0.126 finished fixup.dat
   tftpd: Client 192.168.0.126 finished config.txt
   tftpd: Client 192.168.0.126 finished config.txt
   tftpd: Client 192.168.0.126 finished cmdline.txt
   tftpd: Client 192.168.0.126 finished kernel7.img
   tftpd: Client 192.168.0.126 finished bcm2709-rpi-2-b.dtb
   tftpd: Client 192.168.0.126 finished config.txt

Note: Le serveur TFTP ne montre que les transferts effectifs. Si on regarde avec tcpdump le trafic réseaux on découvre que le processus de boot teste l’existence de bien d'autres fichiers:

tcpdump -l -n -v -ttttt -i eth0 -s 1500 udp  | grep –line-buffered RRQ | awk ‘{print $6}’


   358bec05/start.elf 
   autoboot.txt 
   config.txt 
   recovery.elf 
   start.elf 
   fixup.dat 
   recovery.elf 
   config.txt 
   dt-blob.bin 
   recovery.elf 
   config.txt 
   bootcfg.txt 
   cmdline.txt 
   recovery7.img 
   recovery.img 
   kernel7.img 
   armstub7.bin 
   armstub.bin 
   kernel7.img 
   bcm2709-rpi-2-b.dtb 
   config.txt

Cette séquence varie en fonction des fichiers effectivement trouvés sur le serveur TFTP et en fonction du contenu de config.txt

Si tout se passe bien vous aurez au bout d'environ 5 ou 6 secondes la mire colorée, puis les info du chargement du kernel qui se terminera pas un message du type:

PANIC Unable to mount root fs. 

C'est normal ! L’étape suivante est de justement fournir ce root file system via NFSv2.

Si rien ne ce passe il faudra debugger le DHCP puis le TFTP avec tcpdump ou wireshark.
Pour voir tout le trafic il faudra utiliser un hub, pas un switch.
Quand le processus de boot échoue le led d’activité clignote 4 fois (parfois 7).
Si le RPi2 émet plusieurs requêtes DHCP alors que le serveur DHCP lui répond c'est que la réponse ne lui convient pas.