Como hacer Linux tolerante a apagones, fallos en el sistema de archivos, …

Cuando estamos diseñando un sistema domótico, y llega la hora de probarlo, ¿a quien no le ha pasado que a los pocos días de usarlo se va la luz, y se va todo a tomar viento? ¿Qué se puede hacer para proteger el sistema ante corrupciones del sistema de archivos o apagones repentinos? La respuesta es hacer el sistema de sólo lectura, como un liveCD pero con la posibilidad de hacer modificaciones. Ha varias soluciones, aunque la más fácil es dividir nuestra partición / en varias, unas no modificables (archivos de sistema y configuraciones) y otras modificables (para datos).

Haciendo esto conseguimos que nuestro sistema siempre arranque, aunque no se garantiza la integridad de los datos que manejan los programas: bases de datos, logs, capturas de imágenes… Para proteger los datos deberemos establecer posteriormente una política de copias de seguridad (que se podrían guardar en el propio equipo en otra partición de sólo lectura la mayor parte del tiempo, excepto cuando vayamos a hacer la copia de seguridad).

Es más fácil de lo que parece. Vamos a ello.

En los sistemas Linux los archivos extremadamente críticos están en /boot, que suele estar en una partición separada y que se modifican muy rara vez (cuando se actualiza el kernel). Después tenemos los programas, que se suelen guardar en /bin, /sbin, /usr… Las configuraciones y scripts de inicio están en /etc, y los datos de los programas y logs van en /var.

Una vez que tenemos esto claro podemos intuir que si dividimos el sistema en tres particiones ya tenemos un punto de partida para hacer pruebas:

  1. /boot: sólo lectura
  2. /var y /tmp: lectura y escritura
  3. El resto, /bin, /usr, /etc, /sbin, /lib… sólo lectura

Cuando tenemos el sistema instalado, podemos arrancar con un liveCD o insertar la tarjeta SD o MMC en otro equipo y redimensionar las particiones con gparted o similar. Por ejemplo, con el sistema instalado en una tarjeta SD de 16Gb podemos dejar una partición /boot de 256Mb, otra de sólo lectura de 6Gb, y el resto para la partición de lectura/escritura que llamaremos /mnt/Data con formato ext4. En /mnt/Data copiaremos los directorios que vayan a contener archivos modificables y en su lugar dejaremos un enlace simbólico a la copia en /mnt/Data.

En el caso de las distribuciones que tengan la partición de /boot, debemos asegurarnos de que en el fichero /etc/fstab, esta indicado que se debe montar como sólo lectura, de forma parecida a:

UUID=AA9E-A87B /boot vfat defaults,ro,owner,flush,umask=000 0 0

Si, por el contrario, nuestra distribución incluye el directorio /boot en la partición del sistema archivos raíz, junto con el resto, no pasa nada ya que haremos la partición / de sólo lectura.

Creamos el directorio /mnt/Data y modificamos el ficheros /etc/fstab para que nos monte la nueva partición (la de lectura/escritura en ella), para ello necesitamos su UUID:

root@servidor:~# blkid
/dev/mmcblk0p1: LABEL="Data" UUID="2a556c5b-4a52-4d2a-be81-74cd9c4ddf7f" TYPE="ext4" 
/dev/mmcblk1p1: SEC_TYPE="msdos" LABEL="boot" UUID="AA9E-A87B" TYPE="vfat" 
/dev/mmcblk1p2: LABEL="rootfs" UUID="e139ce78-9841-40fe-8823-96a304a09859" TYPE="ext4" 

 Añadimos la partición al fichero /etc/fstab, que quedaría más o menos así:

UUID=AA9E-A87B                             /boot     vfat     defaults,ro,owner,flush,umask=000  0     0
tmpfs                                      /tmp      tmpfs    nodev,nosuid,mode=1777             0     0
UUID=e139ce78-9841-40fe-8823-96a304a09859  /         ext4     ro,errors=remount-ro,noatime       0     1

UUID=2a556c5b-4a52-4d2a-be81-74cd9c4ddf7f  /mnt/Data ext4     errors=remount-ro                  0     1 

Obsérvese que de las opciones de montaje de /boot y de / se ha cambiado rw por ro, para hacerlas de sólo lectura. En cambio /mnt/Data usa las opciones típicas.

Antes de poder mover los archivos de /var, tenemos que asegurarnos de que no hay ninguno en uso. Recordemos que estos archivos son usados por bases de datos, registros, y demás. Para saber que hay en uso tenemos el comando lsof:

root@servidor:/# lsof | grep /var
init 1 root 11w REG 179,2 4362 117 /var/log/upstart/systemd-logind.log
init 1 root 13w REG 179,2 3181 46 /var/log/upstart/modemmanager.log
init 1 root 16w REG 179,2 5094 1186 /var/log/upstart/network-manager.log
dbus-daem 482 messagebus 4u unix 0xee2f0d80 0t0 4313 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 8u unix 0xee562400 0t0 3932 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 9u unix 0xee2f1200 0t0 5306 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 10u unix 0xee2f18c0 0t0 4402 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 11u unix 0xef436d00 0t0 4418 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 12u unix 0xee2f1b00 0t0 2957 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 13u unix 0xef4373c0 0t0 4466 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 14u unix 0xee561b00 0t0 3992 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 15u unix 0xef434fc0 0t0 3994 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 16u unix 0xef434b40 0t0 5498 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 19u unix 0xee560d80 0t0 4018 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 20u unix 0xee560480 0t0 6579 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 21u unix 0xee5618c0 0t0 4783 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 23u unix 0xed7e5200 0t0 5764 /var/run/dbus/system_bus_socket
dbus-daem 482 messagebus 25u unix 0xed7e5680 0t0 5770 /var/run/dbus/system_bus_socket
rsyslogd 498 syslog 1w REG 179,2 1629499 3419 /var/log/syslog
rsyslogd 498 syslog 2w REG 179,2 1086747 3420 /var/log/kern.log
rsyslogd 498 syslog 4w REG 179,2 46535 3421 /var/log/auth.log
in:imuxso 498 513 syslog 1w REG 179,2 1629499 3419 /var/log/syslog
in:imuxso 498 513 syslog 2w REG 179,2 1086747 3420 /var/log/kern.log
in:imuxso 498 513 syslog 4w REG 179,2 46535 3421 /var/log/auth.log
in:imklog 498 514 syslog 1w REG 179,2 1629499 3419 /var/log/syslog
in:imklog 498 514 syslog 2w REG 179,2 1086747 3420 /var/log/kern.log
in:imklog 498 514 syslog 4w REG 179,2 46535 3421 /var/log/auth.log
rs:main 498 515 syslog 1w REG 179,2 1629499 3419 /var/log/syslog
rs:main 498 515 syslog 2w REG 179,2 1086747 3420 /var/log/kern.log
rs:main 498 515 syslog 4w REG 179,2 46535 3421 /var/log/auth.log
bluetooth 533 root 8u unix 0xee562ac0 0t0 2905 /var/run/sdp
cupsd 556 root 4u REG 179,2 3113 1828 /var/log/cups/access_log
cupsd 556 root 5u REG 179,2 1695 3404 /var/log/cups/error_log
cupsd 556 root 6u REG 179,2 0 185977 /var/log/cups/page_log
cupsd 556 root 12u unix 0xee563600 0t0 2962 /var/run/cups/cups.sock
avahi-dae 570 avahi 4u unix 0xee5633c0 0t0 5367 /var/run/avahi-daemon/socket
avahi-dae 570 avahi 10u unix 0xef436880 0t0 5348 /var/run/avahi-daemon/socket
cron 939 root cwd DIR 179,2 4096 824 /var/spool/cron
dhclient 976 root 4w REG 179,2 1500 1646 /var/lib/NetworkManager/dhclient-a8f6e602-4f46-42b3-9ccf-93e2d2e7d849-eth0.lease
console-k 1248 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1249 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1250 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1251 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1252 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1253 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1254 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1255 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1256 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1257 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1258 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
...
console-k 1248 1307 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1308 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1309 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
console-k 1248 1310 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
gdbus 1248 1311 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history
gmain 1248 1314 root 7w REG 179,2 4550 1869 /var/log/ConsoleKit/history

Detenemos todos los servicios que podamos de los que aparecen en la primera columna para que los archivos dejen de estar en uso. Esto es importante hacerlo con programas que manejen archivos críticos que no se puedan truncar, con los que tengan en uso archivos de log no es tan importante ya que un log truncado no va a provocarnos ningún problema más adelante:

/etc/init.d/dbus stop
killall console-kit-daemon
/etc/init.d/avahi-daemon stop
/etc/init.d/cups stop
...

Ahora vamos a mover los datos de /var a /mnt/Data/var y algunos ficheros de /etc a /mnt/Data/etc.

mkdir /mnt/Data
mount /mnt/Data
cp -r -a /var /mnt/Data
mv /var /var2
ln -s /mnt/Data/var /var
mkdir /mnt/Data/etc

Hay ciertos archivos en el directorio /etc que varían a medida que pasa el tiempo en el sistema, por ejemplo el /etc/mtab, donde se guardan las particiones que hay montadas en un momento dado. Hay que cambiar de ubicación estos archivos:

rm /etc/mtab
ln -s /proc/self/mounts /etc/mtab
echo "BLKID_FILE=/mnt/Data/etc/blkid.tab" > /etc/environment

Y probamos suerte con:

reboot

En la pantalla de arranque del sistema, veremos los errores que van teniendo los programas para arrancar y deberemos mover los archivos afectados o cambiando las rutas que usan los programas durante su ejecución. Para comprobar que el sistema está usando las particiones que le hemos dicho, en modo solo lectura usamos el comando mount:

root@servidor:~# mount
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
udev on /dev type devtmpfs (rw,relatime,size=901524k,nr_inodes=121290,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620)
tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=207136k,mode=755)
/dev/disk/by-uuid/e139ce78-9841-40fe-8823-96a304a09859 on / type ext4 (ro,relatime)
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,nosuid,nodev,noexec,relatime)
none on /sys/fs/cgroup type tmpfs (rw,relatime,size=4k,mode=755)
none on /sys/kernel/debug type debugfs (rw,relatime)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,relatime)
none on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)
none on /run/shm type tmpfs (rw,nosuid,nodev,relatime)
none on /run/user type tmpfs (rw,nosuid,nodev,noexec,relatime,size=102400k,mode=755)
/dev/mmcblk1p1 on /boot type vfat (ro,nosuid,nodev,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,flush,errors=remount-ro)
/dev/mmcblk0p1 on /mnt/Data type ext4 (rw,relatime,errors=remount-ro,data=ordered)
systemd on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,name=systemd)

Podéis ver como /boot y / están montadas con la opción ro, ¡y el sistema arranca!

Como nota hay que decir que cuantos más servicios tengas corriendo en el equipo a la hora de querer hacerlo de sólo lectura, más programas habrá que cambiar, y más archivos habrá colocados en lugares que no nos convengan, pero siempre podremos crear una carpeta para ellos en /mnt/Data, copiarlos allí y crear un enlace simbólico a ellos.

Siguiendo estas instrucciones podéis crear una máquina MAME o un XBMC/Kodi en vuestras Raspberry Pi u Odroid y cuando os canséis la apagáis directamente de botonazo o quitándole la corriente (como hago yo cuando apago el XBMC que tengo alimentado por la TV) si preocuparos, ya que volverá a arrancar sin mayor problema… 🙂

 En el caso de que la distribución este basada en Debian (apt), podemos crear un archivo /etc/apt/apt.conf.d/99ro con el siguiente contenido:

DPkg {
    // Auto re-mounting of a readonly /
    Pre-Invoke { "mount -o remount,rw /"; };
    Post-Invoke { "test ${NO_APT_REMOUNT:-no} = yes || mount -o remount,ro / || true"; };
};

Con los anterior conseguiremos que cuando vayamos a instalar actualizaciones o programas nuevos, el sistema automágicamente monte la partición / como lectura/escritura y al acabar lo deje en sólo lectura. Es posible que al instalar algo nuevo, o si nos hemos dejado algo, al intentar montar el sistema / en modo solo lectura, nos aparezcan mensajes de mount: / esta ocupado o similares, que se pueden solucionar fácilmente reiniciando o currándoselo más y buscando quien es el culpable de tener un fichero abierto en modo escritura en la partición /.

6 comentarios

Añadir un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.