Sincronizar directorios con inotify

From zerutek.com
Jump to: navigation, search

Sincronizar directorios con inotify

Sincronizar directorios entre máquinas con rsync es sencillo, pero puede que requiramos que esto se realice bajo demanda, que cuando se modifique algo en un máquina se vea reflejado inmediatamente en las demás. Con inotify podemos realizar ésto de manera sencilla.

Requerimientos

Explicación rápida

La idea es muy sencilla. Monitorizamos un directorio con inotifywait, y cuando se produce alguna acción en el mismo, realizamos un rsync a la máquina remota. Utilizamos rsync en vez de scp o similar para que si montamos esto en varias máquinas que sincronicen entre ellas no se queden bucladas copiando el mismo fichero de manera infinita.

Un ejemplo sencillo, donde miramos el si creamos, modificamos o borramos ficheros en /srv/sync, y lo sincronizamos con el mismo directorio de la máquina blas:

while OUTPUT=`inotifywait --format '%f' -e create -e modify -e delete /srv/sync`;do
   echo "El fichero ganador es ${OUTPUT}"
   rsync -a --delete /srv/sync blas:/srv/sync
done

Ejemplo práctico

Tenemos dos máquinas (epi y blas) en las que queremos sincronizar el directorio /srv/sync, utilizando para ello el usuario jim.

Generación y copia de las claves ssh

Para realizar la sincronización se requiere la generación de una pareja de claves (pública y privada) para que el usuario jim pueda sincronizar datos entre las dos máquinas. Generaremos las claves de mánera idéntica en las dos máquinas, y no las protegeremos con contraseña:

jim@epi:~$ ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/jim/.ssh/id_rsa): 
Created directory '/home/jim/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/jim/.ssh/id_rsa.
Your public key has been saved in /home/jim/.ssh/id_rsa.pub.
The key fingerprint is:
59:04:2c:a6:95:df:cb:b1:f3:34:e4:b9:76:2f:2e:b2 jim@epi
The key's randomart image is:
+--[ RSA 2048]----+
|       o...      |
|      = ..       |
|     + o ..      |
|    .   .oo .    |
|        S. * .   |
|          = =    |
|           + o   |
|          . = o  |
|          E+ +.o.|
+-----------------+

Con ésto conseguimos dos claves dentro del directorio ~jim/.ssh/:

  • id_rsa: La clave privada que utiliza ssh. No debe moverse de donde está.
  • id_rsa.pub: La clave pública que utiliza ssh. Hay que colocarla en el fichero ~/.ssh/authorized_keys de la máquina y usuario donde queramos conectarnos.

En nuestro caso, copiaremos de epi el fichero ~/.ssh/id_rsa.pub a blas en ~/.ssh/authorized_keys y viceversa :

jim@epi:~$ scp ~/.ssh/id_rsa.pub blas@~/.ssh/authorized_keys
jim@blas:~$ scp ~/.ssh/id_rsa.pub epi@~/.ssh/authorized_keys

También se puede realizar ésta acción utilizando ssh-copy-id

jim@epi:~$ ssh-copy-id  blas
jim@blas:~$ ssh-copy-id  epi

El script que controla la sincronización

Un ejemplo de script para vigilar y sincronizar los cambios. Utiliza logger para generar algo de log vía syslog. La idea es que el script sea el mismo en todos los nodos donde lo necesitemos.

Un buen sitio para ubicarlo es /usr/local/bin/notity-sync:

#!/bin/sh
# Sincroniza un directorio entre dos máquinas utilizando 'inotifywait'.
# 'inotifywait' es parte del paquete "inotify-tools".

VER='1'
USER='jim'
MACHINES='epi blas' # Las máquinas afectadas en el proceso.
ORIGEN=`hostname -s`
DIR_ORIGEN='/srv/sync'
DIR_DESTINO='/srv/sync'
DESTINOS=`echo ${MACHINES}|sed "s/$ORIGEN//"` # Las máquinas a sincronizar
## Log
PRIORITY='local0.notice'
LOGTAG='INOTIFY'

Main() {
while OUTPUT=`inotifywait --format '%f %e' -e create -e modify -e delete ${DIR_ORIGEN}/`;do
   for DESTINO in ${DESTINOS};do
      # Log de inotify
      echo ${DIR_ORIGEN}/${OUTPUT} |logger -p ${PRIORITY} -t ${LOGTAG}
      su - ${USER} -c "rsync -a --delete ${DIR_ORIGEN}/ ${USER}@${DESTINO}:${DIR_DESTINO}/"

      # Log de sincronización
      ERROR=$?
      if [ ${ERROR} -eq 0 ];then
         echo "${USER}@${DESTINO}:${DIR_DESTINO} SYNC" |logger -p ${PRIORITY} -t ${LOGTAG}
      else
         echo "${USER}@${DESTINO}:${DIR_DESTINO} ERROR ${ERROR}" |logger -p ${PRIORITY} -t ${LOGTAG}
      fi
      # / Log de sincronización
   done
done
}

case ${1} in
   --daemon)
      Main > /dev/null 2>&1 &
      ;;
   *)
      Main
      ;;
esac

script de arranque y parada

Un ejemplo del script de arranque y parada realizado para Red Hat. Su sitio es /etc/init.d/notity-sync.

#!/bin/bash
#
# notify-sync        This starts and stops notify-sync.
#
# description: Sincroniza directorios entre máquinas a base de \
#              'rsync' e 'inotify'.

### BEGIN INIT INFO
# Provides:
# Required-Start: $network
# Required-Stop:
# Should-Start:
# Should-Stop:
# Default-Start: 3 4 5
# Default-Stop: 0 1 2 6
# Short-Description: starts and stops notify-sync
# Description: Sincroniza directorios entre máquinas a base de \
#              'rsync' e 'inotify'.
### END INIT INFO

PATH=/sbin:/bin:/usr/bin:/usr/sbin

# Source function library.
. /etc/init.d/functions

prog=/usr/local/bin/notity-sync
lockfile=/var/lock/subsys/`basename $prog`

start() {
        [ "$EUID" != "0" ] && exit 4
        # Start daemons.
        status $prog > /dev/null
        CODE=$?
        if [ $CODE = "0" ];then
                echo "$prog: funcionando"
                echo `status $prog`
                exit 0
        fi
        echo -n $"Starting $prog: "
        daemon $prog --daemon
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && touch $lockfile
        return $RETVAL
}

stop() {
        [ "$EUID" != "0" ] && exit 4
        echo -n $"Shutting down $prog: "
        killproc $prog -9
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && rm -f $lockfile
        return $RETVAL
}

# See how we were called.
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status $prog
        ;;
  *)
        echo $"Usage: $0 {start|stop|status}"
        exit 2
esac

Habilitar y arrancar el proceso

Para que ésto funcione en todos los arranques hemos optado por introducirlo en un runlevel.

ln -s /etc/init.d/notity-sync /etc/rc3.d/S99notity-sync
service notity-sync start

Referencias

https://github.com/rvoicilas/inotify-tools/wiki/

http://forums.gentoo.org/viewtopic-t-684623.html