Crear y restaurar backups de Cacti

Como sabrán los lectores de TDB, en este blog ya existe un artículo dedicado a Cacti, en el cual se exponen los pasos para instalar y configurar la herramienta, instalar complementos, entre otros.

Sin embargo, el mencionado artículo no abarca los pasos a seguir para realizar una copia de seguridad de un entorno de producción, y los necesarios para volver a restaurar la herramienta a partir de dichos backups, pasos que serán tratados a continuación.

Primero veremos cómo realizar las copias de seguridad, esto incluye a la configuración de la herramienta almacenada en la base de datos MySQL o PostgreSQL y a la información de los gráficos generados mediante RRDtool.


Copia de seguridad de Cacti

Para realizar los backups vamos a necesitar de un script bash, cuyo código es muy similar al que he utilizado en otras ocasiones y del cual sin duda tendré que preparar un artículo exclusivo, ya que sus características permiten que sea utilizado en numerosas aplicaciones y casos.
  1. Comenzamos creando un archivo para el script bash que se encargará de realizar la copia automática de seguridad. También le asignamos los permisos de ejecución necesarios:
    testsrv:~ # touch /usr/share/cacti/scripts/backup
    testsrv:~ # chmod 755 /usr/share/cacti/scripts/backup

  2. Luego procedemos a editar el archivo..
    testsrv:~ # vi /usr/share/cacti/scripts/backup
    Para posteriormente agregarle el siguiente script:
    #!/bin/bash
    # Realiza una copia de seguridad completa de la herramienta.

    # DEFINICION DE CONSTANTES.
    # =========================

    DIA=`date +"%Y%m%d"`
    HORA=`date +"%H%M"`

    # VARIABLES DE CONFIGURACION.
    # ===========================
    # -- Configuraciones de la aplicacion.

    APP_NAME=Cacti
    APP_HOME=/usr/share/cacti
    APP_DB_TYPE=mysql #[pg|mysql]
    APP_DB_NAME=cacti
    APP_DB_USER=cactiuser
    APP_DB_PASS=cactiuser
    APP_RRD_HOME=$APP_HOME/rra
    APP_BACKUP_HOME=$APP_HOME/backups
    APP_BACKUP_DB=$APP_BACKUP_HOME/cacti_db.sql
    APP_BACKUP_RRD=$APP_BACKUP_HOME/rrd

    # -- Configuraciones de backup
    BACKUP_PATH=/backups/cacti
    BACKUP_NAME="cacti_"$DIA"_"$HORA".tar.gz"
    BACKUP_HISTO="cacti_histo_"$DIA"_"$HORA".tar.gz"
    BACKUP_LIVE_TIME=30
    BACKUP_HISTO_LIVE_TIME=365

    # -- FTP
    FTP_SERVER=ip_o_dominio_servidor
    FTP_USER=userbkp
    FTP_PASS=userbkp
    FTP_REMOTE_CD=remote_backups
    FTP_LOCAL_CD=$BACKUP_PATH
    FTP_BACKUP_NAME=cacti.tar.gz

    # -- Patron limpieza CACTI
    PATRON_LIMPIEZA=cacti_*.tar.gz
    PATRON_LIMPIEZA_HISTO=cacti_histo_*.tar.gz

    # SECCION DE PROCEDIMIENTOS BACKUP.
    # =================================
    # -- Comprobacion y creacion de directorios de backup.

    echo
    echo "-->Verificacion de directorios de Backup"
    echo "===================================================================="
    if [ ! -d $APP_BACKUP_RRD ]; then
    echo -n "Creando directorio $APP_BACKUP_RRD: "
    mkdir -p $APP_BACKUP_RRD
    echo "OK"
    sleep 1
    else
    echo "Directorio $APP_BACKUP_RRD: OK"
    fi
    if [ ! -d $BACKUP_PATH ]; then
    echo -n "Creando directorio $BACKUP_PATH: "
    mkdir -p $BACKUP_PATH
    echo "OK"
    sleep 1
    else
    echo "Directorio $BACKUP_PATH: OK"
    fi

    # -- Segun el motor de base de datos especificado se procede a exportar la base de datos.
    echo
    echo "-->BACKUP DE LA BASE DE DATOS DE $APP_NAME"
    echo "===================================================================="
    if [ $APP_DB_TYPE == "mysql" ]; then
    echo "Exportando la base de datos de $APP_NAME a partir de MySQL. Aguarde un momento..."
    mysqldump -v -u $APP_DB_USER -p$APP_DB_PASS --database $APP_DB_NAME > $APP_BACKUP_DB
    echo "Fin del proceso de exportacion."
    echo
    elif [ $APP_DB_TYPE == "pg" ]; then
    echo "Exportando la base de datos de $APP_NAME a partir de PostgreSQL. Aguarde un momento..."
    export PGUSER=$APP_DB_USER
    export PGPASSWORD=$APP_DB_PASS
    pg_dump -b -F p --column-inserts $APP_DB_NAME > $APP_BACKUP_DB
    unset PGUSER
    unset PGPASSWORD
    echo "Fin del proceso de exportacion."
    fi

    # -- Backup de la base de datos RRDTOOL.
    echo
    echo "-->BACKUP DE LAS BASES DE DATOS RRDTOOL DE $APP_NAME"
    echo "===================================================================="
    LISTA=`ls -1 $APP_RRD_HOME/*.rrd`
    for i in $LISTA; do
    echo $i | awk '{print "rrdtool dump "$1" > "$1".xml"}' | sh -x
    done
    mv -vf $APP_RRD_HOME/*.xml $APP_BACKUP_RRD

    # Se generan los archivos con todas las copias de seguridad para ser enviadas a una unidad de cinta.
    echo
    echo "-->CREANDO FULL BACKUP DE $APP_NAME"
    echo "===================================================================="
    cd $APP_BACKUP_HOME
    tar -czvf $BACKUP_PATH/$BACKUP_NAME *

    # Almacenando una copia de seguridad alternativa si la fecha corresponde a fin de mes.
    TOMORROW=`date --date=tomorrow +%d`
    if [ $TOMORROW -eq "1" ]; then
    echo
    echo "-->CREANDO BACKUP HISTORICO POR SER FIN DE MES"
    echo "===================================================================="
    cp -v $BACKUP_PATH/$BACKUP_NAME $BACKUP_PATH/$BACKUP_HISTO
    fi

    # Para no llenar el disco duro con backups, se eliminan todos los backups diarios pasados la cantidad
    # de dias especificados en la variable BACKUP_LIVE_TIME, lo backups historicos se eliminan luego de la
    # cantidad de dias especificados en la variable BACKUP_HISTO_LIVE_TIME.

    echo
    echo "-->LIMPIEZA DE BACKUPS ANTIGUOS"
    echo "===================================================================="
    find $BACKUP_PATH/$PATRON_LIMPIEZA -mtime +$BACKUP_LIVE_TIME -exec rm {} \;
    find $BACKUP_PATH/$PATRON_LIMPIEZA_HISTO -mtime +$BACKUP_HISTO_LIVE_TIME -exec rm {} \;

    # Se copia el full backup a un servidor de respaldo.
    echo
    echo "-->COPIANDO EL FULL BACKUP DE $APP_NAME A UN SERVIDOR DE RESPALDO"
    echo "===================================================================="
    /usr/bin/ftp -n <<EOD
    open $FTP_SERVER
    quote USER $FTP_USER
    quote PASS $FTP_PASS
    bin
    cd $FTP_REMOTE_CD
    lcd $FTP_LOCAL_CD
    del $FTP_BACKUP_NAME
    put $BACKUP_NAME
    rename $BACKUP_NAME $FTP_BACKUP_NAME
    bye
    EOD
    OBS: Pueden ver el script en Gist haciendo clic aquí.

    El script anterior es bastante extenso y abarca la verificación de los directorios de backup, la exportación de las bases de datos de la herramienta y de los gráficos, la generación y limpieza de backups diarios e históricos, y la transferencia de la última copia de seguridad generada a otro servidor remoto.

    Los directorios en donde serán ubicadas las copias de seguridad son creados automáticamente por el script si no existen. En el script se encuentra configurada por defecto la ruta /backups/cacti como el directorio para ubicar los archivos generados.
    ADVERTENCIA: El script cuenta con numerosas variables de configuración que deben ser definidas adecuadamente. Al realizar la definición, se debe tener especial cuidado con la especificación de rutas a directorios.
  3. Registramos una tarea programada que se encargue de ejecutar el script cada cierto periodo de tiempo. La mejor forma de hacerlo es agregando una entrada al archivo cacti ubicado en el directorio /etc/cron.d, para ello lo editamos..
    testsrv:~ # vi /etc/cron.d/cacti
    y al final le agregamos las siguientes dos líneas (o similares) que ejecuta el script todas las noches a las 23 horas y 45 minutos:
    # Backup Cacti
    45 23 * * * root /usr/share/cacti/scripts/backup >> /dev/null
    Salimos del archivo guardando los cambios, restando solamente reiniciar el servicio cron para que tome la nueva configuración.
    testsrv:~ # service cron restart


Restaurar copias de seguridad de Cacti

Si necesitamos restaurar las copias de seguridad que hemos generado con el script sugerido más arriba, debemos proceder con los siguientes pasos:
  1. Para comenzar debemos descomprimir el archivo backup en cualquier directorio de nuestra preferencia. A modo de referencia, en este artículo el archivo tar.gz se descomprime dentro del directorio /backups/cacti:
    testsrv:~ # cd /backups/cacti
    testsrv:/backups/cacti # tar -xzf cacti_20130125_2345.tar.gz

  2. En el servidor en el cual vamos a restablecer la copia de seguridad tenemos que tener instalados el motor de base de datos MySQL y la herramienta Cacti. También debemos tener especial cuidado con la hora del equipo que debe estar correctamente configurada, ya que la herramienta Cacti es muy sensible en cuanto a la sincronización horaria ya que es fundamental para desplegar la información gráfica en la hora que corresponde.

    Primeramente comenzamos por las directivas para eliminar cualquier base de datos y usuario Cacti existente, para ello nos conectamos a la línea de comandos de MySQL
    testsrv:~ # mysql -u root -pcontraseña_de_root mysql
    y ejecutamos las siguientes directivas resaltadas en rojo:
    mysql> drop database cacti;
    Query OK, 52 rows affected (0.04 sec)

    mysql> drop user cactiuser@localhost;
    Query OK, 0 rows affected (0.00 sec)

    mysql> exit
    Bye
  3. Volvemos a crear una base de datos vacía para la herramienta Cacti.
    testsrv:~ # mysqladmin -u root -pcontraseña_de_root create cacti
  4. Luego importamos la base de datos para la herramienta Cacti a partir de la copia de seguridad realizada:
    testsrv:~ # mysql -u root -pcontraseña_de_root cacti < /backups/cacti/cacti_db.sql
  5. A continuación nos conectamos a la base de datos con el usuario root,
    testsrv:~ # mysql -u root -pcontraseña_de_root mysql
    Y ejecutamos las siguientes líneas resaltadas en rojo para crear nuevamente el usuario de base de datos para la herramienta ,y otorgarle los permisos necesarios para acceder y modificar la base de datos, siempre respetando el nombre y la contraseña existentes en el archivo de configuración config.php de Cacti.
    mysql> GRANT ALL ON cacti.* TO cactiuser@localhost IDENTIFIED BY 'contraseña_para_cactiuser';
    Query OK, 0 rows affected (0.00 sec)

    mysql> flush privileges;
    Query OK, 0 rows affected (0.00 sec)

    mysql> exit
    Bye
  6. Una vez concluido con el punto anterior nos dirigimos al directorio /backups/cacti/rrd donde generamos las bases de datos RRDtool de cada uno de los gráficos a partir de los archivos xml ubicados en el mismo directorio:
    testsrv:~ # cd /backups/cacti/rrd
    testsrv:/backups/cacti/rrd # ls -1 *.rrd.xml | sed 's/\.xml//' | awk '{print "rrdtool restore "$1".xml "$1}' | sh -x

  7. Procedemos a eliminar todos los archivo .rrd del directorio /srv/www/cacti/rra que se pudieron haber creado durante el procedimiento de instalación y luego movemos al directorio los archivos rrd generados en el paso anterior:
    testsrv:/backups/cacti/rrd # rm -Rf /srv/www/cacti/rra/*.rrd
    testsrv:/backups/cacti/rrd # mv *.rrd /srv/www/cacti/rra

  8. Le otorgamos los permisos de usuario y grupo a las bases de datos de los gráficos previamente generados:
    testsrv:~ # chown -Rf wwwrun:www /srv/www/cacti/rra/*
  9. Y finalmente reiniciamos el servicio Apache con lo cual ya tendremos restablecida la configuración de la herramienta a partir de nuestra copia de seguridad.
    testsrv:~ # service apache2 restart
  10. Pero esto aún no ha terminado, ahora nos toca acceder a la interfaz web de la herramienta con nuestro navegador preferido:
    http://localhost/cacti/
    Al acceder al sitio es probable que nos solicite seguir una serie de pasos para actualizar la herramienta. La primera ventana nos informará acerca del procedimiento de actualización:

  11. En el caso de este ejemplo, la base de datos de la copia de seguridad corresponde a una versión previa de la herramienta (0.8.7g), que deberá ser actualizada para la versión de referencia utilizada en este artículo. Eso es justamente lo que nos informará la siguiente ventana, en la que presionamos el botón Next para proceder con la actualización:

  12. Se nos informará de los cambios que fueron introducidos en la base de datos luego de la actualización. Para continuar presionamos nuevamente el botón Next.

  13. Luego del paso anterior nos aparecerá la ventana de comprobación de rutas hacia las aplicaciones externas que utiliza la herramienta Cacti.

    Si se cumplen con todos los requisitos podemos presionar el botón Finish para finalizar el procedimiento de actualización, que nos llevará a la página de conexión tradicional:

    Con los pasos anteriores, la herramienta Cacti debería funcionar con la información contenida en la copia de seguridad.


Problemas de sincronización por horarios

Luego de la importar los gráficos, los mismos deberían mostrar la información previa restablecida de los backups, a estos datos se les debería ir sumando la nueva información que se va recopilando desde el nuevo servidor, en mi caso sin embargo, esto último no estaba funcionando del todo bien. Revisando el log de Cacti (/var/log/cacti/cacti.log) pude comprobar que el script poller.php no utilizaba la misma hora del equipo durante su ejecución.

Este error parecía ser un problema de UTC. La herramienta web desplegaba la información según la hora del equipo local en base al uso horario de mi región, pero por algún motivo desconocido la ejecución del poller.php mediante cron utilizaba el horario UTC, dando por consiguiente una diferencia de horario entre la información recopilada y los gráficos vía web. Estos últimos tardaban en desplegar la información al no corresponder al horario actual, ya que los mismos recién correspondían ser mostrados varias horas más tarde.

En el artículo Solving Cacti time problems había cierta información relacionada al problema de sincronización. En principio he intentado varias cosas, intenté con desmarcar la configuración Hardware Clock Set to UTC en Yast->Date and Time, configurar la hora manualmente y también mediante servidores NTP, pero nada funcionaba hasta que una chispa dio con el origen del problema.

Resulta ser que en mi nuevo servidor de pruebas en el que restablecí la copia de seguridad, PHP viene con la directiva date.timezone = 'UTC' habilitada por defecto, algo que en mi anterior servidor de Cacti en openSUSE no estaba presente. Así, para solucionar el problema simplemente tuve que seguir los siguientes pasos:
  1. Primero tenemos que editar los archivos de configuración php.ini para Apache y Cli,
    testsrv:~ # vi +1008 /etc/php5/apache2/php.ini
    testsrv:~ # vi +1008 /etc/php5/cli/php.ini

    buscar la directiva date.timezone = 'UTC' y asignarle la zona horaria que nos corresponde, en mi caso America/Asuncion:
    [Date]
    ; Defines the default timezone used by the date functions
    ; http://php.net/date.timezone

    date.timezone = 'America/Asuncion'
    ADVERTENCIA: Modificar la directiva date.timezone en los archivos php.ini puede alterar el correcto funcionamiento de otras aplicaciones PHP del servidor que hagan uso de dicha directiva.
  2. Es posible que necesitemos reiniciar el servicio Apache para que tome la nueva configuración.
    testsrv:~ # service apache2 restart
  3. Por último tenemos que editar la configuración de la tarea programada de Cacti, para ello abrimos el archivo /etc/cron.d/cacti:
    testsrv:~ # vi /etc/cron.d/cacti
    Y le agregamos el argumento --force luego del comando de ejecución para que realice la captura sin tener en cuenta las diferencias entre la hora de la última captura y la hora del proceso en curso.
    */5 * * * * wwwrun php /srv/www/cacti/poller.php --force > /dev/null 2>&1
  4. Reiniciamos el servicio cron:
    testsrv:~ # service cron restart
  5. Y con esto ya debería funcionar. Aunque si los gráficos siguen sin actualizarse esto puede deberse a que aún hay información registrada en la base de datos con hora a futuro con respecto a la que se trata de capturar, esto lo podemos comprobar viendo la salida del siguiente comando:
    testsrv:~ # php /srv/www/cacti/poller.php --force
    ERROR: /srv/www/cacti/rra/router_in_58.rrd: illegal attempt to update using time 1359658164 when last update time is 1359663749 (minimum one second step)
    01/31/2013 03:49:25 PM - SYSTEM STATS: Time:1.6941 Method:cmd.php Processes:20 Threads:N/A Hosts:14 HostsPerProcess:1 DataSources:34 RRDsProcessed:17
    testsrv:~ #
    Claro no? El script poller.php no puede actualizar la base de datos porque existe un registro previo con hora 1359663749, bastante más en el futuro que la hora 1359658164 con la cual se pretendía registrar la nueva información.


Nota final

En principio pensé que un artículo sobre como realizar y restaurar una copia de seguridad de Cacti sería bastante breve y conciso, nada más lejos de la realidad. Al realizar las pruebas de laboratorio he tenido varios problemas, su depuración me ha llevado más tiempo y esfuerzo del que hubiese deseado, retrasando mucho la publicación del artículo.

Posiblemente las diferencias entre las versiones del software y del sistema operativo me han jugado una mala pasada, pero es una realidad que se presenta en los entornos de producción cuando los mismos no pueden ser actualizados constantemente con las últimas versiones. Ahora, quizás viendo de todo esto el lado positivo puedo decir que he aprendido un poco acerca de PHP y otro tanto de la herramienta Cacti, pero no todo lo que hubiera esperado. Solo espero que el tiempo haya sido bien invertido y todo esto le sirva a alguien.


Entorno de instalación y prueba

  • Linux 3.4.11-2.16-default i686
  • openSUSE 12.2 i586
  • Cacti 0.8.8a-2.1.1


Fuentes

Comentarios

  1. Muchas gracias por la publicacion.

    ResponderEliminar
  2. Muy buen articulo, en breve estare cambiendo el srv y tendre que pasar cacti. Sera de mucha utilidad toda esta informacion.

    Saludos

    ResponderEliminar

Publicar un comentario