| Written by Administrator, on 30-11-2007 15:41 |
| Views |
3605  |
|
Ce document décrit des techniques pour mettre en oeuvre un système où le disque root est sur une carte compact flash (CF) dans le but de construire un système embarqué robuste.
Problématiques
Les installations classiques sont sensibles aux coupures électriques et aux arrêt
brutaux qui peuvent conduire à des pertes de donnée voir empêcher un
redémarrage normal. Par ailleurs les disques compact flash supportent un
nombre limité d'écriture qu'il est important de contrôler dans le cas
d'un système sur compact-flash
Pour mettre en œuvre facilement un système robuste où les disques ne sont pas montés en écriture il est possible d'utiliser des systèmes de fichier en ram
'fusionnés' avec des partitions sur disque en utilisant le module unionfs.
Je décris ici plusieurs
approches en sachant que nous voulons à la fois avoir un système read
only mais aussi pouvoir sauver les modifications et garantir
l'intégrité des données...
Qu'est ce qu'unionfs?
Unionfs est un système de fichier permettant de fusionner virtuellement des systèmes de fichier (appelé branches).
Chaque branche se voie attribuée une priorité et peut-être read-only ou
read-write. Unionfs est largement utilisé sur les live CD car il
implémente un mécanisme de copy on write, c'est à dire que
quand un fichier d'une union est modifiée, le contenu du fichier est
recopié dans la branche read-write est ainsi l'union parait read-write
alors qu'une ou plusieurs branches sont read-only.
Mise en œuvre
Il faut utiliser un noyau récent (cad > 2.6.18 pour utiliser unionfs v2) et compiler le module unionfs disponible ici
. Malheureusement unionfs n'est pas présent dans le noyau officiel, mais il est dans la branche d'Andrew Morton (-mm). Je ne parle ici que d'unionfs 2.1 qui apporte des amélioration
intéressantes par rapport à la précédant, en particulier elle permet d'acceder aux branches "raw" d'une union. Notez que la version
dans debian etch est ancienne est à déconseiller!
Pour créer des union on utilise simplement la syntaxe:
mount -t unionfs unionfs /nom_de_lunion -o dirs=/rep1=rw:/rep2=ro:...
Il est possible de fusionner de nombreuses branches, la première apparaissant étant la plus prioritaire.
Sous unix, les répertoires /etc, /var et /tmp doivent être read-write.
Pour /tmp, pas de problème, un tmpfs fait l'affaire, pour /var et /etc
il faut jouer avec les unions...
Première approche
On va créer des systèmes de fichier tmpfs qui seront des branches read-write pour /var et /etc. Appelons les /rametc et /ramvar. Ensuite on crée des unions respectivement avec /etc et /var.
Pour ne pas s'embêter avec la gestion de /etc/mtab, on peut faire un lien vers /proc/mounts
Pour monter les répertoires en unionfs très tôt lors du boot, on peut ajouter les lignes suivantes au début du fichier /etc/init.d/rcS (sur une debian):
/bin/mount -n -t tmpfs tmpfs /rametc -o size=30M
/bin/mount -n -t tmpfs tmpfs /ramvar -o size=50M
/bin/mount -n -t unionfs unionfs /etc -o dirs=/rametc=rw:/etc=ro
/bin/mount -n -t unionfs unionfs /var -o dirs=/ramvar=rw:/var=ro
echo "tmpfs /rametc tmpfs rw,size=30M 0 0" >> /etc/mtab
echo "tmpfs /ramvar tmpfs rw,size=50M 0 0" >> /etc/mtab
echo "unionfs /etc unionfs rw,dirs=/rametc=rw:/etc=ro 0 0" >> /etc/mtab
echo "unionfs /var unionfs rw,dirs=/ramvar=rw:/var=ro 0 0" >> /etc/mtab
Dans /etc/fstab on n'oublie pas tmp:
none /tmp tmpfs defaults,nosuid,nodev,noexec 0 0
et du coup on peut mettre /,/boot et /var en ro
Cette technique est également décrite: ici
Cette approche pose problème pour remonter les partitions en rw et surtout on n'accède plus aux branches /var et /etc initiales, donc la resynchronisation n'est pas possible.
Deuxième approche
|
|
Une autre solution consisterai à créer des unions ou l'on veux et ensuite utiliser la commande pivot_root. Cette commande permet de remplacer le répertoire
racine et le répertoire courant de tous les processus en cours d'exécution.
|
En général on utilise pivot_root à la fin du script d'init présent dans initramfs. Pour se simplifier la vie, on peut le lancer dans rcS (debian)
Dans ce cas on procède ainsi:
- Montage de tmpfs dans /tmpfs_layer
- Montage de la partition pour la syncro dans /rw_layer
- mount unionfs dans /union avec dirs=/tmpfs_layer/=rw:/rw_layer/=ro:/=ro
- pivot_root dans /unionfs
- chroot du shell en cours dans l'union
- Eventuellement mount --move de /dev/
Voici un exemple de script:
#! /bin/sh
# rcS
# Call all S??* scripts in /etc/rcS.d/ in numerical/alphabetical order
if [ -e /etc/nounionfs ] ; then
echo "#######DEMARRAGE SANS UNIONFS#######\n"
exec /etc/init.d/rc S
else
echo "!!!!!DEMARRAGE AVEC UNIONFS!!!!!!!\n"
mount -o ro /dev/hda4 /rw_layer/
/bin/mount -t tmpfs tmpfs /tmpfs_layer/ -o size=200M
/bin/mount -t unionfs unionfs /union -o dirs=/tmpfs_layer/=rw:/rw_layer/=ro:/=ro
mkdir /union/old
cd /union
pivot_root . old
chroot . sh -c 'mount --move /old/dev /dev; mount --move /old/sys /sys ; mount --move /old/proc /proc'
exec chroot . sh -c '/etc/init.d/rc S' <dev/console >dev/console 2>&1
fi
Attention, chroot doit être accessible dans le système pivoté... une copie en dehors de /usr ne fait pas de mal si /usr est sur une autre partition.
Encore une solution
Sur la partition / on crée les répertoires /etc et /var qui resterons read-only.
On crée des partitions sur le disque qui serviront à la synchro des repertoires tmpfs (dans l'exemple suivant on les monte dans /union/rw_etc
et /union/rw_var)
On crée des répertoires /union/ram_var etc /union/ram_etc en tmpfs .
On monte dans /etc et /var respectivement les unions de /ram_etc(rw)+/rw_etc(ro)+/etc(ro) et de /ram_etc+rw_var+/var
Dans ce cas on peut remonter quand on veut /union/rw_etc etc /rw_var en mode rw et faire des rsync
Comme précédemment, pour ne pas s'embêter avec la gestion de /etc/mtab, on peut faire un lien vers /proc/mounts
Code pour /etc/init.d/rcS
/bin/mount -t tmpfs tmpfs /union/ram_etc -o size=50M
/bin/mount -t tmpfs tmpfs /union/ram_var -o size=150M
/bin/mount /dev/hda6 /union/rw_etc -o ro
/bin/mount /dev/hda5 /union/rw_var -o ro
/bin/mount -t unionfs unionfs /etc/ -o dirs=/union/ram_etc=rw:/union/rw_etc=ro:/etc=ro
/bin/mount -t unionfs unionfs /var/ -o dirs=/union/ram_var=rw:/union/rw_var=ro:/var=ro
...
exec /etc/init.d/rc S
Le gros avantage de cette structure à trois branches par union est outre de permettre des copies entre branche pour la synchro, de pouvoir effacer quand on veux les repertoire rw_* et ainsi revenir dans l'état d'instalation (reset to factory).
Il y a plein de façon de faire avec unionfs...
Resyncronisation
Avec unionfs 2.1 (et contrairement à ce que dit la page de manuel) on peut intervenir directement sur les branches. Il est donc possible de remonter les partitions en rw le temps de faire un rsync.
Un problème qui se pose est alors de purger les partitions tmpfs pour quelles ne continuent pas à remplir la mémoire et de bien effacer de la compact flash les fichiers qui ont été détruit. Pour cela unionfs crée des fichiers "whiteout" (.wh.nomdufichier) dans la branche rw quand un fichier ou un répertoire est détruit. Il est facile de pacourir ces fichiers et de détruire les fichiers originaux.
Voici un exemple de script de syncho (à lancer périodiquement et/ou au reboot)
#On remonte /rw_etc en rw pour copier les modifs dessus.
mount -o remount,rw /rw_etc
rsync -a --exclude '.wh*' /rametc /rw_etc
#Pour être sur de bien flusher les caches
mount -o remount,incgen /etc
#Il vaut mieux retirer la branche avant d'effacer les fichiers
mount -t unionfs -o remount,del=/rametc none /etc
find /rametc \( -regex '.*/\.wh\.[^/]*' -type f \) | sed -e 's/\.\///;s/\.wh\.//' |
while read N
do
rm -rf /rw_etc/$N
done
rm -rf /rametc/*
mount -t unionfs -o remount,add=/rametc none /etc
mount -o remount,ro /rw_etc
Évidement, faire des copies de fichiers ouverts, éventuellement en écriture, ce n'est pas forcement fiable. Idéalement il faudrai synchroniser lors d'un shutdown, mais la copie à chaud ne marche pas si mal à condition de prendre certaines précautions.
Pour une base de donnée, on peut locker les tables en lecture uniquement. Ca fonctionne correctement avec MySQL avec des tables MyISAM.
L'astuce pour locker les tables pendant le temps le plus court possible consiste à faire un double rsync.
- Un premier rsync crée une première copie
- On lock les table: mysql -p ... -u ... < "FLUSH TABLES WITH READ LOCK"
- Un deuxième rsync qui va être ultra rapide cette fois
- on delock: "UNLOCK TABLES"
Une autre solution élégante serai de faire un snapshot avec LVM, par contre on ne peut pas utiliser LVM avec tmpfs, donc il faudrai utiliser un ramdisk, pas très pratique à mettre en œuvre... Il y a des discussions intéressante à ce propos ici
Références:
Site officiel de Unionfs
Unionfs: User- and Community-Oriented Development of a
Unification File System: http://www.fsl.cs.sunysb.edu/docs/sipek-ols2006/index.html
Tutorial Gentoo, http://gentoo-wiki.com/HOWTO_Read-only_root_filesystem#Unionfs_Method
Snapshot LVM et MySQL : http://mike.kruckenberg.com/archives/2006/05/mysql_backups_u.html
Aufs, un concurant de unionfs: http://aufs.sourceforge.net/
Users' Comments (0)
Comment language: English (0), French (1) |
|
|