Backups automáticos con EC2 API Tools y SimpleDB

Hace unos días escribía sobre cómo instalar worpress en ec2(en catalán). En el post advertía de las dificultades de administración que tiene gestionar tu própio host y en especial con Amazon Web Services, ya que es un sistema Cloud Computing y puede resultar muy abstracto y dificil de entender. Y esto lo puede vivir en el primer proyecto profesional que tuve que realizar en Amazon.

Me pidierion montar un CRM open source (SugarCRM) en una instancia en AWS. SugarCRM es casi más fácil de instalar que wordpress ya que tienen perparado un bundle que unifica la instalación de todos los servicios necesarios junto con el própio software. El reto estaba en que un CRM trabaja con datos muy importantes para una empresa (contactos, comapañías, actividades comerciales, campañas de marketing, etc.) por tanto, esos datos no se pueden perder. Antes de ponerme a ello ya sudaba.

Monté SugarCRM en una instancia con sistema de almacenamiento EBS (Elastic Block Storage). Eso siginifica que la instancia no es efimera y que si se para, los datos están almacenados en una especie de disco virtual permanente. Pero, en lo poco que llevo de sysadmin ya me ha quedado claro una cosa: Las cosas no es que puedan fallar, las cosas fallan.

Dado que el CRM no iba a tener muchos usuarios la carga y la alta disponibilidad no eran prioridades, pero los datos no se podían perder de ninguna manera. Por tanto necesitaba implementar un sistema para hacer copias de seguridad del EBS por si el desastre sucedía (ya pasó en la región de Irlanda en agosto).

EBS tiene una prestación interesante, que es la de poder hacer Snapshots. Los snapshots son como fotografias del estado del disco en un determinado momento, y mediante un snapshot podemos crear un EBS nuevo que sería una copia exacta del primero, en el momento en que se hizo la snapshot. Estas snapshots se guardan en S3, por lo que podemos estar tranquilos de que no las vamos a perder.

En el libro Programming Amazon EC2 O’Reilly (que me recomendó Ricardo Gallir) encontré unos ejemplos muy interesantes de cómo aprovechar las snapshots y la api de EC2 para hacer las copias de seguridad automáticas.

Para montarlo necesitaba:

  • Instalar las api tools de EC2
  • Instalar el cliente PERL de SimpleDB
  • Crear los scripts
  • Configurar el cron para el automatismo

EC2 Api Tools

Si la instancia es ubuntu, instalar la API es muy sencillo ya que están incluidas en los repositorios de aptitude.

Pero yo elegí Amazon Linux AMI ya que una instancia micro nos bastaba. Por tanto tuve que seguir los siguientes pasos:

  1. Bajar la api

    Se puede bajar la api directamente a la instancia con wget o curl. Pero yo ya la tenía bajada en mi pc así que utilizé scp:

    $ scp -i keypair.pem ec2-api-tools.zip ec2-user@[ip_instancia]:/home/ec2-user

    Una vez subida, accedí a la instancia y descomprimí el archivo .zip.

  2. Subir a la instancia las credenciadles X.509

    Para ejecutar la API y trabajar con nuestra cuenta necesitaremos subir a la instancia los archivos de credenciadeles necesarios.
    Antes, tuve que crear unas credenciales X.509 para la cuenta AWS. Esto me generó dos archivos .pem: pk-XXXXXXXXXXXXXXXXXXXXX.pem y cert-XXXXXXXXXXXX.pem. Ambos los subí mediante scp a la instancia.

  3. Script de inicialización

    Al no ser un programa que se instale propiamente dicho, sinó que són un conjunto de scripts y comandos que utilizaremos por Terminal, necesitaremos definir variables de entorno para poder agilizar las ejecuciones.
    Aunque no es del todo necesario, es bueno crear un script que inicialice las variables de entorno de golpe, que ejecutaremos cada vez que vayamos a ejecutar un comando. Este script nos ayudará para hacer pruebas, que para mi fueron cruciales para entender el funcionamiento de la api.
    El script que yo preparé es el siguiente:

    #!/bin/bash
    
    export JAVA_HOME=/usr/lib/jvm/jre
    export EC2_API_HOME=/home/ec2-user/AWS
    export EC2_KEY_DIR=/home/ec2-user/AWS/X.509
    export EC2_PRIVATE_KEY=${EC2_KEY_DIR}/pk-xxxxxxxxxxxxxxxx.pem
    export EC2_CERT=${EC2_KEY_DIR}/cert-xxxxxxxxxxxxxxxx.pem
    export EC2_USER_ID='ACC_NUMBER'
    export EC2_HOME=${EC2_API_HOME}/ec2-api-tools-1.4.4.2
    export AWS_ACCESS_KEY_ID='XXXXXXXXXXXXXXXXXXXXXX'
    export AWS_SECRET_ACCESS_KEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    export PATH="$EC2_HOME/bin:$PATH"

    A este script lo llamé initaws y le di permisos de ejecución.
    Al ser declaraciones de variables de entorno que sólo serán válidas durante la sesión de terminal vigente, deberemos ejecutar el archivo de la siguiente manera:

    $ . ./initaws

    Sin el “.” las variables de entorno no se crearán.

Para comprobar que había instalado bien la api abrí un terminal y ejecuté lo siguiente:

$ . ./initaws
$ ec2-describe-regions

Resultado:

REGION	eu-west-1	ec2.eu-west-1.amazonaws.com
REGION	us-east-1	ec2.us-east-1.amazonaws.com
REGION	ap-northeast-1	ec2.ap-northeast-1.amazonaws.com
REGION	us-west-1	ec2.us-west-1.amazonaws.com
REGION	ap-southeast-1	ec2.ap-southeast-1.amazonaws.com

Al ejecutar ec2-describe-regions obtube una respuesta con el listado de las regiones actuales de EC2.

Cliente SimpleDB

SimpleDB es un servicio de base de datos no relacional, también conocidos como NoSQL. Nos permite guardar información sin tener que preocuparnos de mantener un servidor de base de datos. Además, al no ser relacional, tiene mejor elasticidad y durabilidad ya que se puede escalar y replicar mejor que las bases de datos relacionales.

SimpleDB lo utilizo para llevar un registro de las copias de seguridad y poder asociar a cada copia una fecha de caducidad para que un proceso automático las pueda ir elminando.

Mientras que en ubuntu instalar el cliente simpleDB es immediato con apt-get, la insalación del cliente SimpleDB PEARL en la instancia Amazon Linux AMI me ocasionó muchos dolores de cabeza. Al final lo conseguí realizando lo sisguientes pasos:

  1. Instalar CPAN

    CPAN es un gestor de paquetes que tuve que instalar para poder instalar las dependencias.

    $ sudo yum install perl-CPAN make gcc
  2. Instalar liberaria OpenSSL

    Uno de los problemas que más me costó solucionar es que uno de los paquetes de dependencias necesitaba las librerias open ssl. El problema era que la documentación del paquete no especificaba exactamente qué necesitaba y tuve que ir probando.

    $ sudo yum openssl-devel
  3. Instalar dependencias

    Instalé las siguientes dependencias de paquetes PERL:

    $ sudo cpan -i Digest::SHA
    $ sudo cpan -i XML::Simple
    $ sudo cpan -i Bundle::LWP
    $ sudo cpan -i Crypt::SSLeay
    $ sudo cpan -i Getopt::Long
    $ sudo cpan -i Pod::Usage
    $ sudo cpan -i Digest::SHA1
    $ sudo cpan -i Digest::HMAC
    $ sudo cpan -i Time::HiRes
  4. Bajar e instalar la librearia perl de SimpleDB

    Bajé la librería de la siguiente URL: http://aws.amazon.com/code/1136
    Una vez bajada ejectué los siguientes comandos:

    $ unzip AmazonSimpleDB-2009-04-15-perl-library.zip
    $ sitelib=$(perl -MConfig -le 'print $Config{sitelib}')
    $ sudo scp -r Amazon-SimpleDB-*-perl-library/src/Amazon $sitelib
    
    $ sudo curl -Lo /usr/local/bin/simpledb http://simpledb-cli.notlong.com
    $ sudo chmod +x /usr/local/bin/simpledb
  5. Modificar initaws

    Para poder debugar y hacer pruebas añadí las variables de entorno correspondientes al archivo initaws.

    #!/bin/bash
    export JAVA_HOME=/usr/lib/jvm/jre
    export EC2_API_HOME=/home/ec2-user/AWS
    export EC2_KEY_DIR=/home/ec2-user/AWS/X.509
    export EC2_PRIVATE_KEY=${EC2_KEY_DIR}/pk-xxxxxxxxxxxxxxxx.pem
    export EC2_CERT=${EC2_KEY_DIR}/cert-xxxxxxxxxxxxxxxx.pem
    export EC2_USER_ID='ACC_NUMBER'
    export EC2_HOME=${EC2_API_HOME}/ec2-api-tools-1.4.4.2
    export AWS_ACCESS_KEY_ID='XXXXXXXXXXXXXXXXXXXXXX'
    export AWS_SECRET_ACCESS_KEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    export SIMPLEDB_CLIENT_HOME=/usr/local/
    export PATH="$EC2_HOME/bin:$SIMPLEDB_CLIENT_HOME/bin:$PATH"

Scripts

El sistema de backup se compone de dos scripts. Uno que realiza copia del volumen de la instancia y da de alta la copia en SimpleDB con la fecha en que debe expirar. El otro script lee SimpleDB y borra todas las snapshots que deban expirar según el registro.
Por tanto necesitaba crear un dominio en SimpleDB:

$ . ./initaws
$ simpledb create-domain snapshots

IMPORTANTE:No creeis dominios con caracteres especiales. La primera vez creé mi dominio con un “-“. SimplDB no se quejó, e incluso me dejaba insertar elementos y recuperarlos; pero a la hora de intentar hacer un select me salía siempre un error de sintaxis. Quité el guión y funcionó.

Script backup

#!/bin/bash

#specify location of X.509 certificates for the EC2 command lines
export EC2_KEY_DIR=/home/ec2-user/AWS/X.509
export EC2_PRIVATE_KEY=${EC2_KEY_DIR}/pk-xxxxxxxxxxxxxxxxxxxxxx.pem
export EC2_CERT=${EC2_KEY_DIR}/cert-xxxxxxxxxxxxxxxxxxxxxxxxxx.pem
export EC2_ACCESS_KEY='xxxxxxxxxxxxxxxx'
export AWS_ACCESS_KEY_ID='xxxxxxxxxxxxxxxxxxxxxxxx'
export EC2_SECRET_KEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
export AWS_SECRET_ACCESS_KEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
export EC2_USER_ID='xxxxxxxxxxxxxxx'
export EC2_HOME=/home/ec2-user/AWS/ec2-api-tools-1.4.4.2
export SIMPLEDB_CLIENT_HOME=/usr/local/
export JAVA_HOME=/usr/lib/jvm/jre
export PATH="$EC2_HOME/bin:$SIMPLEDB_CLIENT_HOME/bin:$PATH"

#Region where store snapshots
region="eu-west-1"

#if called with a parameter that is accepted by "date --date"
#it creates a date based on that value. if its is empty we take
#a default expiration of 24 hours

offset=$1

if [ "${offset}" == "" ]
then
    	offset="24 hours"
#	echo $offset
#	exit 0
fi

expiration=$( date -u --date="${offset}" +"%Y-%m-%d %H:%M:%S" )
echo "Snapshot will expire in "$expiration

if [ "$expiration" == "" ]
then
    	echo "Impossible to determine expiration date"
        exit 0
fi

vols=( "vol-xxxxxxxxxxxx" )

mountpoints=( "/dev/sda1" )

for ((i = 0; i < ${#vols[@]}; i++))
do
  	snapshot=($(ec2-create-snapshot ${vols[i]} --region $region --description "CRM1 BACKUP Expiration: ${expiration}"))
        echo "Snapshot of volume ${vols[i]} requested"
        #now add an item to the simpleDB domain
        #containing the snapshot id and its expiration
        simpledb put snapshots ${snapshot[1]} expires="${expiration}"
        echo "${snapshot[1]} registerd at snapshots domain"
done

El script se utiliza como en el siguiente ejemplo:

$ /home/ec2-users/autobackup/backup "24 hours"

En el ejemplo se crea una snapshot que debe expirar en 24 horas a partir del momento de su ejecución.

Script Expire

#!/bin/bash

#specify location of X.509 certificates for the EC2 command lines
export EC2_KEY_DIR=/home/ec2-user/AWS/X.509
export EC2_PRIVATE_KEY=${EC2_KEY_DIR}/pk-xxxxxxxxxxxxxxxxxxxxxxxxxxx.pem
export EC2_CERT=${EC2_KEY_DIR}/cert-xxxxxxxxxxxxxxxxxxxxxx.pem
export EC2_ACCESS_KEY='xxxxxxxxxxxxxxxxxx'
export AWS_ACCESS_KEY_ID='xxxxxxxxxxxxxxxxxxx'
export EC2_SECRET_KEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
export AWS_SECRET_ACCESS_KEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
export EC2_USER_ID='xxxxxxxxxxxxxx'
export EC2_HOME=/home/ec2-user/AWS/ec2-api-tools-1.4.4.2
export SIMPLEDB_CLIENT_HOME=/usr/local/
export JAVA_HOME=/usr/lib/jvm/jre
export PATH="$EC2_HOME/bin:$SIMPLEDB_CLIENT_HOME/bin:$PATH"

region="eu-west-1"

now=$(date +"%Y-%m-%d %H:%M:%S")

snapshots=$(simpledb select "select * from crmbackups where expires < '${now}'")
for snapshot in $snapshots
do
  	snap=`expr match "$snapshot" '.*(snap-........).*'`
        if [ -n "$snap" ]; then
                echo "Removing snap: "$snap
                #remove the item from SimpleDB
                simpledb delete crmbackups $snap
                #delete the snapshot itself
                ec2-delete-snapshot $snap --region $region
        fi
done

Al ejecutar este script se consultan todos los registros de copias de seguridad que tengan fecha de expiración anterior al momento de ejecución del script. Todos los snapshots resultantes serán borrados y eliminados del registro.

Añadir scripts al crontab

Para que los scripts se ejecuten automáticamente los añadí a la tabla del proceso cron. Para mi caso, sabiendo que los datos no cambian muy a menudo, una copia al día que expire a los dos días y una copìa semanal que expire a las dos semanas es suficente.
Un sitio web, sin embargo, dónde los usuarios insertan datos y suben archivos continuamente, se puede programar el backup cada tres horas que expire a las 24 horas, una copia semanal que expire al cabo de un mes, y una copia mensual que expire al cabo de un año. De esta manera, aunque detectáramos un error días después, tendríamos copias suficientes para restaurar los datos.
En mi caso mi tabla cron quedaba así:

@daily                                  /home/ec2-user/AWS/scripts/bakcups/expire > /dev/null 2>&1
@daily                                  /home/ec2-user/AWS/scripts/backups/backup "2 days" > /dev/null 2>&1
@weekly                                 /home/ec2-user/AWS/scripts/backups/backup "2 weeks" > /dev/null 2>&1

Si por defecto no tenéis que el servicio cron se arranque al iniciarse la máquina, os recomiendo que lo añadáis; por si la máquina se debe reinciar por algún motivo y así no hay que acordarse de poner en marcha el servicio.
Para que se arranque al inicio, en caso de Amazon Linux AMI, se debe ejecutar la siguiente instrucción:

$ sudo chkconfig --add crond
$ sudo chkconfig --level 2345 crond on
$ sudo service crond start

Referencias

– Jurg van Vliet & Flavia Paganelli (2011) Programming Amazon EC2. USA: O’Reilly

Anuncis

Deixa un comentari

Fill in your details below or click an icon to log in:

WordPress.com Logo

Esteu comentant fent servir el compte WordPress.com. Log Out / Canvia )

Twitter picture

Esteu comentant fent servir el compte Twitter. Log Out / Canvia )

Facebook photo

Esteu comentant fent servir el compte Facebook. Log Out / Canvia )

Google+ photo

Esteu comentant fent servir el compte Google+. Log Out / Canvia )

Connecting to %s