En este tutorial veremos como automatizar la creación de una copia de seguridad a la hora de instalar nuestras aplicaciones en una máquina remota gracias a Ansible y Dropbox.
Índice de contenidos
- 1. Introducción
- 2. Entorno
- 3. ¿Qué es Dropbox Uploader?
- 4. Automatización de copia de seguridad
- 5. Conclusiones
- 6. Referencias
1. Introducción
Tarde o temprano a la hora de desarrollar una aplicación necesitamos tener una copia de respaldo que sea capaz de guardar el estado previo que tenían las aplicaciones ya sea almacenando, por ejemplo, periódicamete los datos de la base de datos. Este tutorial pretende automatizar todo el proceso de creación de una copia de seguridad que estará almacenada en Dropbox. Para ello utilizaremos Ansible, herramienta de automatización que ya he mencionado en otros tutoriales como éste.
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 15′ (2.2 Ghz Intel Core I7, 8GB DDR3).
- Sistema Operativo: Mac OS El Capitan 10.11
- Ansible 1.9.3
- Vagrant 1.7.2
- Virtual Box
3. ¿Qué es dropbox uploader?
Dropbox Uploader es un script BASH que puede ser utilizado para subir y descargar archivos de Dropbox (entre muchas otras cosas), así como compartir, sincronizar o hacer copias de seguridad de estos archivos.
¿Por qué Dropbox Uploader? porque al ser bash nos va a permitir utilizarlo con Ansible, de forma que al crear la máquina en la que estará alojada nuestra aplicación, se hará la copia de seguridad de los ficheros que queramos de forma completamente automática.
Tras esta breve introducción vamos al lío 🙂
4. Automatización de copia de seguridad
Para automatizar las copias de seguridad en primera instancia necesitamos:
- Instalar Virtualbox, Vagrant y Ansible.
- Crear una aplicación de Dropbox (con la que nos comunicaremos gracias al script de Dropbox Uploader).
- Ejecutar los playbook de Ansible y disfrutar de la «magia». 😀
4.1. Instalación previa
Para este tutorial partimos de un entorno con VirtualBox, Vagrant y Ansible instalados. Si queréis saber como se instalan estas herramientas así como una breve descripción de las mismas, se pueden ver desde este
tutorial.
4.2. Preparación del entorno
Antes de meternos en el meollo de crear máquinas virtuales y trastear con ellas, vamos a crear la aplicación de Dropbox. Para ello necesitamos crearnos una cuenta de Dropbox si no la tenemos todavía. Por defecto las cuentas de Dropbox vienen con 2GB de memoria, más que suficiente para el backup de nuestros datos de prueba.
4.2.1. Creación de la aplicación de Dropbox
Si no tenemos una cuenta de Dropbox creada puedes crearla de forma completamente gratuita desde aquí
.
Una vez registrados con nuestra cuenta de Dropbox accedemos a https://www.dropbox.com/developers/apps dónde comenzaremos con la creación de la aplicación.
En primer lugar, pinchamos en create app.
A continuación configuramos las características de la aplicación. En nuestro caso elegiremos el API de Dropbox, el tipo de acceso solo al directorio de la aplicación y nombraremos a la copia de seguridad con el nombre tutorial backup.
Al volver a pinchar en el botón create app nos llevará a una pantalla de configuración donde veremos la key y el secret de la aplicacion, que son los datos que a nosotros nos interesan para poder acceder a Dropbox desde la terminal con Dropbox Uploader.
4.2.2. Creación y configuración previa de la máquina virtual
Una vez creada la aplicación de Dropbox vamos a crear la máquina virtual en la que realizaremos la copia de seguridad, y vamos a configurarla de forma sencilla para que tenga un PostgreSQL y una base de datos de prueba de la que haremos el backup.
Para ello selecionamos un directorio en el cual pondremos la configuración de Vagrant (en mi caso está dentro de maquinas-virtuales/tutorial-backup) y ejecutamos el comando vagrant init
mkdir maquinas-virtuales/tutorial-backup cd maquinas-virtuales/tutorial-backup vagrant init
Deberían de crearse un fichero llamado Vagrantfile en el que configuraremos nuestra máquina virtual. Para ello lo abrimos con nuestro editor preferido y lo dejamos con las siguientes líneas:
Vagrantfile
Vagrant.configure(2) do |config| config.vm.box = "ubuntu/trusty64" config.vm.network "private_network", ip: "192.168.33.10" end
En estas líneas le estamos indicando a Vagrant con que «box» queremos iniciar el sistema y la IP específica que va tener nuestra máquina virtual.
Una vez configurada la máquina, con el comando vagrant up levantaremos la máquina virtual.
Accedemos a la máquina con el comando vagrant ssh y procederemos a configurar manualmente la configuración de acceso a Dropbox. Espera un momento… ¿Manualmente? ¿No decíamos que todo era automático? Efectivamente así es, pero para poder automatizar todo el proceso y que este sea replicable para la misma aplicación de Dropbox debe configurarse previamente un fichero de configuración de forma manual, que luego exportaremos via Ansible de forma automática.
Para ello cogeremos manualmente el script de dropbox_uploader con el comando:
curl "https://raw.githubusercontent.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh" -o dropbox_uploader.sh
Cambiamos los permisos del fichero para que sea ejecutable y ejecutamos el fichero. Al hacerlo por primera vez nos pedirá datos relativos a la aplicación de Dropbox, y este es el motivo por el cual es necesario hacer la primera vez el proceso manual. Introducimos los datos que nos pide sobre la aplicación (app key, app secret, y los permisos sobre la aplicación o sobre todo el dropbox) y verificamos la url que nos proporciona.
chmod 777 dropbox_uploader.sh ./dropbox_uploader.sh
Tras hacer esta configuración debería de crearse en el HOME del usuario que estamos utilizando un fichero llamado .dropbox_uploader que contiene toda la información relevante a nuestro acceso a Dropbox por el script. De hecho, al ejecutarse el script sin parámentros comprueba si este fichero se encuentra en el HOME del usuario con el que se ejecuta, y si no lo crea (que es el caso que acabamos de comprobar). A continuación copiaremos este fichero al directorio /vagrant, que es un directorio compartido entre la máquina virtual y la real donde nos llevaremos la configuración que acabamos de realizar para automatizar el proceso con Ansible.
cp /home/vagrant/.dropbox_uploader /vagrant/
Ahora es cuando entra en juego ansible 😀
4.2.3. Automatización del backup con Ansible
Por ahora nuestra máquina no tiene ni base de datos ni copia de seguridad. Para crear la base de datos y automatizar las copias de seguridad utilizaremos Ansible.
Dentro de la carpeta tutorial-backup crearemos el directorio ansible, donde se pondrán todos los ficheros relacionados con la automatización.
Comprobamos como no solo está el fichero Vagrantfile que usamos para crear la máquina virtual si no que la primera vez que levantas la máquina virtual se crea un directorio .vagrant. Además también vemos el fichero .dropbox_uploader que hemos movido previamente. ahora dentro de ansible vamos a crear la siguente estructura (los que acaban con / son directorios):
Estructura ansible
ansible |_environment/ | |_development/ | |_inventory | | | |_group_vars/ | |_development | |_roles/ | |_database/ | | |_files/ | | | |_booktown.sql | | | | | |_tasks/ | | |_main.yml | | | |_dropbox_backup/ | | |_files/ | | | |_.dropbox_uploader | | | | | |_tasks/ | | | |_backup_setup.yml | | | |_main.yml | | | |_restore_backup.yml | | | | | |_templates/ | | |_backup.sh | | | |_postgres/ | |_tasks/ | |_common_os_setup | |_main.yml | |_postgres_install.yml | |_playbook.yml
Como vemos en la estructura nuestro fichero .dropbox_uploader lo situaremos dentro de ansible/roles/dropbox_backup/files/
A continuación creamos los ficheros necesarios para el funcionamiento de ansible.
environment/development/inventory
[development] 192.168.33.10
En el inventory se especifican los grupos de máquinas del entorno (en nuestro caso solo tenemos un grupo de máquinas con una máquina asociada) con la IP de las máquinas asociadas a ese grupo. Precisamente por este motivo especificamos la IP de la máquina virtual en el Vagrantfile anteriormente.
environment/development/group_vars/development
--- postgres: version: 9.3 datasource: dbname: booktown dbuser: booktown dbpassword: booktownpass dropbox_backup: days_to_keep: 30 directory: /home/backup uploader_script_folder: /opt/dropbox_uploader enable_restore: false backup_filename: no-file configuration_folder: /opt/dropbox_uploader/config
Este es el fichero de variables que utilizaremos en los playbooks. Ya veremos para que sirve cada una luego. 😀
A continuación antes de meternos en los roles vamos a ver como está estructurado el playbook, que es el orquestador que indica en que orden (y en que condiciones) se ejecuta cada rol.
playbook.yml
--- # file: playbook.yml - hosts: development sudo: yes gather_facts: no roles: - postgres - hosts: development sudo: yes sudo_user: postgres gather_facts: no roles: - database - hosts: development sudo: yes gather_facts: no roles: - dropbox_backup
Como vemos primero se ejecutará el rol encargado de instalar PostgresSQL, luego se creará y se inicializará la base de datos y por último se automatizará la copia de seguridad. Echemos un vistazo rápido a cada uno de estos roles, hasta llegar al de Dropbox que es el que nos interesa.
roles/postgres/tasks/main.yml
--- # file: roles/postgres/tasks/main.yml - include: common_os_setup.yml - include: postgres_install.yml
En este fichero se indica el orden de ejecución de las demás tareas mediante include.
roles/postgres/tasks/common_os_setup.yml
--- # file: roles/postgres/tasks/common_os_setup.yml - name: ensure apt cache is up to date apt: update_cache=yes - name: ensure the language packs are installed apt: name={{item}} with_items: - language-pack-en - language-pack-es - name: reconfigure locales command: sudo update-locale LANG=en_US.UTF-8 LC_ADDRESS=es_ES.UTF-8 LC_COLLATE=es_ES.UTF-8 LC_CTYPE=es_ES.UTF-8 LC_MONETARY=es_ES.UTF-8 LC_MEASUREMENT=es_ES.UTF-8 LC_NUMERIC=es_ES.UTF-8 LC_PAPER=es_ES.UTF-8 LC_TELEPHONE=es_ES.UTF-8 LC_TIME=es_ES.UTF-8 - name: ensure apt packages are upgraded apt: upgrade=yes
Aquí nos encargamos de realizar las tareas básicas de asegurarnos de que el sistema está actualizado correctamente y añadimos los paquetes de español e inglés.
roles/postgres/tasks/postgres_install.yml
--- # file: /roles/postgres/tasks/postgres_install.yml - name: ensure PostgreSQL are installed apt: name={{item}} with_items: - postgresql-{{postgres.version}} - postgresql-contrib-{{postgres.version}} - python-psycopg2 - name: ensure client's encoding is UTF-8 lineinfile: dest: /etc/postgresql/{{postgres.version}}/main/postgresql.conf backup: yes insertafter: "#client_encoding = sql_ascii" line: "client_encoding = utf8" - name: ensure PostgreSQL is running service: name=postgresql state=restarted enabled=yes
En este rol otorgamos acceso a PostgresSQL desde otros host.
roles/database/tasks/main.yml
--- # file: /roles/database/tasks/main.yml - name: ensure database is created postgresql_db: name: "{{datasource.dbname}}" encoding: UTF-8 lc_collate: es_ES.UTF-8 lc_ctype: es_ES.UTF-8 tags: database - name: ensure user has access to database postgresql_user: db: "{{datasource.dbname}}" name: "{{datasource.dbuser}}" password: "{{datasource.dbpassword}}" priv: ALL tags: database - name: Copy database default script copy: src="booktown.sql" dest=/tmp mode=644 tags: database - name: Add sample data to database shell: psql {{datasource.dbname}} < /tmp/booktown.sql when: dropbox_backup.enable_restore == false tags: database - name: Delete database default script file: path=/tmp/booktown.sql state=absent tags: database
En este rol creamos la base de datos y la inicializamos con una base de datos de prueba (de la que luego haremos el backup).
En el directorio ansible/roles/dropbox_backup/files/ se encuentra nuestro fichero .dropbox_uploader que tiene la configuración necesaria para acceder a Dropbox.
roles/dropbox_backup/tasks/main.yml
--- # file: roles/dropbox_backup/tasks/main.yml - include: backup_setup.yml - include: restore_backup.yml when: dropbox_backup.enable_restore
Igual que el main del rol de postgres, es encargado de orquestar el orden de las tareas del rol por ficheros para ordenar su contenido.
Es interesante ver ahora el módulo when, ya que quiere decir que el contenido de esa tarea (en este caso hacer el backup), solo se va a realizar si la expresión es resuelta a true. De modo que como ahora solo queremos hacer una copia de seguridad, nos aseguramos de que la propiedad enable_restore dentro del grupo de propiedades de dropbox_backup está puesta a false dentro de las variables que se utilizan a lo largo del playbook. Si nos fijamos en el fichero development en el que establecíamos las variables anteriormente esta propiedad tiene el valor false.
Es precisamente por que queremos hacer una copia de seguridad que también hemos usado el módulo when a la hora de inicializar la base de datos ya que cuando hagamos el restore de la base de datos después no queremos que se inicialize la base de datos.
roles/dropbox_backup/tasks/backup_setup.yml
--- # file: roles/dropbox_backup/tasks/backup_setup.yml - name: Create backups directories file: path={{item}} state=directory mode=0755 with_items: - "{{dropbox_backup.uploader_script_folder}}" - "{{dropbox_backup.directory}}" - "{{dropbox_backup.configuration_folder}}" tags: - backup - name: Download dropbox_uploader get_url: url=https://raw.githubusercontent.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh dest={{dropbox_backup.uploader_script_folder}}/dropbox_uploader.sh mode=0755 tags: - backup - name: Copy dropbox configuration file copy: src=.dropbox_uploader dest="{{dropbox_backup.configuration_folder}}/.dropbox_uploader" mode=0600 tags: - backup - name: Copy dropbox script template: src=backup.sh dest="{{dropbox_backup.configuration_folder}}/backup.sh" mode=0600 tags: - backup - name: Create cron job to perform five minute backups cron: name="Dropbox backups" minute="*/5" hour="*" job="sh "{{dropbox_backup.configuration_folder}}"/backup.sh >> /var/log/backup.log 2>&1" tags: - backup
Este es el rol que nos interesa, vamos a entrar en profundidad en como se realiza:
- En primer lugar crearemos el directorio de backup, que será el directorio en el que copiaremos el sql que queremos subir a dropbox.
- Después nos descargamos el dropbox_uploader en el destino que elijamos por parámetro. Esto es exactamente la misma acción que hemos realizado antes a mano con el comando curl ya que cuando hagamos un restore de la máquina necesitaremos del script para bajarnos nuestra copia de seguridad de dropbox.
- Copiamos después la configuración que habíamos realizado manualmente donde se lo hayamos especificado en nuestras variables de configuración (en nuestro caso /opt/dropbox_uploader/config).
- Copiamos el script (del que hablaremos en breves) encargado de hacer el dump de la base de datos, mover todos los archivos a la carpeta de backup, subirlo y borrar los backups más antiguos.
- Por ultimo usamos el módulo cron de ansible para programar tareas y programamos nuestro job para que haga una copia de seguridad cada cinco minutos (para que veamos de forma temprana la salida).
roles/database/tasks/backup.sh
#!/bin/bash hostname=`hostname | awk -F. '{print $1}'` date=$(date +"%d-%m-%Y") database="{{datasource.dbname}}" uncompressed_backup_db_file="{{dropbox_backup.directory}}/${database}.sql" backup_db="/tmp/${hostname}-${date}.sql.tar.gz" backup_dirs="{{dropbox_backup.directory}}" backup_filename="/tmp/${hostname}-${date}.tar.gz" dropbox_uploader="{{dropbox_backup.uploader_script_folder}}/dropbox_uploader.sh -f "{{dropbox_backup.configuration_folder}}"/.dropbox_uploader" echo "[$(date +"%d-%m-%Y-%T")] Database dump..." sudo -u postgres pg_dump $database > $uncompressed_backup_db_file echo "\n[$(date +"%d-%m-%Y-%T")] Compress data..." tar -czvf $backup_filename $backup_dirs $uncompressed_backup_db_file # Send to Dropbox echo "\n[$(date +"%d-%m-%Y-%T")] Upload to dropbox..." $dropbox_uploader upload $backup_filename / # Delete local backup sudo rm $uncompressed_backup_db_file sudo rm -rf $backup_filename sudo rm -rf $backup_db # Delete old remote backups echo "\n[$(date +"%d-%m-%Y-%T")] Delete old remote backups..." delete_date=`date --date="-{{dropbox_backup.days_to_keep}} day" +%d-%m-%Y` $dropbox_uploader delete "${hostname}-${delete_date}.tar.gz"
Es un script sencillo que indica el proceso de realización del backup. En primera instancia se puede ver como se hace un dump de la base de datos, luego comprime los datos y los envía a Dropbox. Una vez subido elimina todos los ficheros y como todos los ficheros que se suben tienen una nomenclatura busca el fichero de hace 30 días para borrarlo de dropbox y que así no nos quedemos nunca sin espacio
El último fichero que nos queda es el de realizar una restauración de la copia de seguridad. Cuando queramos restaurar una copia de seguridad en una máquina nueva simplemente tenemos que cambiar el estado de la propiedad enable_restore a true, y decidir qué copia de seguridad queremos utilizar para levantar la base de datos y ponerla en la variable backup_filename. El conjunto de tareas encargado de hacer el restore son:
roles/database/tasks/main.yml
--- # file: roles/dropbox_backup/tasks/restore_backup.yml - name: Get backup from dropbox command: "{{dropbox_backup.uploader_script_folder}}/dropbox_uploader.sh -f {{dropbox_backup.configuration_folder}}/.dropbox_uploader download {{dropbox_backup.backup_filename}} /tmp" tags: - restore - name: Change backup permission file: path=/tmp/{{dropbox_backup.backup_filename}} mode=666 tags: - restore - name: Decompress backup file command: tar -xzvf /tmp/{{dropbox_backup.backup_filename}} chdir=/tmp tags: - restore - name: Restore database shell: sudo -u postgres psql {{datasource.dbname}} < /tmp/home/backup/{{datasource.dbname}}.sql tags: - restore - name: Delete backups files file: path={{item}} state=absent with_items: - /tmp/home - "/tmp/{{dropbox_backup.backup_filename}}" tags: - restore
- En primer lugar obtenemos el backup de Dropbox y lo mantenemos en el directorio /tmp.
- Descomprimimos el backup y restauramos la base de datos.
- Por último eliminamos estos ficheros temporales y ya tendremos el mismo estado que teníamos en la base de datos.
4.3. Comprobación
Ahora vamos a comprobar como se crea la base de datos y como se genera una copia de seguridad a lo largo del tiempo.
Para comprobar que se ejecuta desde la creación de la máquina vamos a cargarnos la máquina que hemos creado antes y volver a crearla desde cero:
cd maquinas-virtuales/tutorial-backup vagrant destroy (y) vagrant up
Ejecutamos el playbook de Ansible para que ejecute el playbook en la máquina virtual. Para ello ejecutaremos el siguiente comando desde dentro de la carpeta ansible:
cd /Users/aortiz/maquinas-virtuales/tutorial-backup/ansible ansible-playbook -i environment/development/inventory --private-key=/Users/aortiz/maquinas-virtuales/tutorial-backup/.vagrant/machines/default/virtualbox/private_key playbook.yml -vv -u vagrant
NOTA: Para que no te de un problema de autenticación tienes que quitar la línea correspondiente a la IP del fichero localizado en /home/{usuario}/.ssh/known_hosts antes de ejecutar el playbook otra vez.
Justo al comenzar os pedirá verificación por ser un host desconocido. Aceptamos y dejamos a Ansible realizar las tareas. Una vez finalizado deberíamos ver algo así:
Es importante notar como las últimas tareas están en color azul. Esto quiere decir que estas tareas se han omitido gracias al módulo when del que hablábamos antes. Queremos hacer una copia de seguridad, no una restauración del sistema.
Ahora mismo ya tenemos nuestras copias de seguridad funcionando y generándose automáticamente. Para comprobarlo vamos a ver los log del proceso de backup que están localizados en el directorio /var/log/backup.log
Vemos como en un principio comprime los datos (solo el .sql) y lo sube a Dropbox:
[03-12-2015-12:00:01] Upload to dropbox... > Uploading "/tmp/vagrant-ubuntu-trusty-64-03-12-2015.tar.gz" to "/vagrant-ubuntu-trusty-64-03-12-2015.tar.gz"... DONE
Destacar como al final elimina los backups remotos:
[03-12-2015-12:05:09] Delete old remote backups... > Deleting "/vagrant-ubuntu-trusty-64-03-11-2015.tar.gz"... FAILED
Este último falla ya que no tenemos ninguna copia de seguridad del día 3/11/2015.
NOTA: Hay que destacar que solo se genera una copia de seguridad por día ya que se va sutituyendo la previa cada 5 minutos. Lo suyo es hacer una copia de seguridad a media noche, pero aquí hemos acelerado el periodo del cron para ver el funcionamiento.
Comprobamos que el archivo está subido a Dropbox:
Por último vamos a hacer una restauración del sistema, de modo que al instalar nuestros datos en la nueva máquina, queremos conservar el estado de la base de datos de la copia de seguridad que queramos. Para ello, eliminaremos la máquina virtual de nuevo, y utilizaremos el mismo playbook para que se quede todo tal y como estaba cambiando en las propiedades de Ansible el enable_restore a true y en backup_filename ponemos el nombre de nuestra copia de seguridad.
environment/development/group_vars/development
--- postgres: version: 9.3 datasource: dbname: booktown dbuser: booktown dbpassword: booktownpass dropbox_backup: days_to_keep: 30 directory: /home/backup uploader_script_folder: /opt/dropbox_uploader configuration_folder: /opt/dropbox_uploader/config enable_restore: true backup_filename: vagrant-ubuntu-trusty-64-03-12-2015.tar.gz
Volvemos a ejecutar el playbook que habíamos hecho antes.
NOTA: Repito que para que no te de un problema de autenticación tienes que quitar la línea correspondiente a la IP del fichero localizado en /home/{usuario}/.ssh/known_hosts antes de ejecutar el playbook otra vez.
Comprobamos dos cosas:
- No se ha ejecutado la tarea que introducía los datos de la base de datos previamente.
- Al meternos en la base de datos, los datos del .sql están presentes.
5. Conclusiones
Esta es una forma sencilla de tener una copia de seguridad de nuestras aplicaciones, o simplemente de nuestros archivos (cambiando un poco el script) en Dropbox de una manera automática y sencilla en Linux. Sobretodo lo importante es coger el concepto de que si es algo que vas a repetir en varios sistemas, lo mejor es tener un playbook automatizado que te ayude a replicar exactamente el mismo comportamiento en otras máquinas. 😀
Puedes ver la configuración de Vagrant y Ansible desde el repositorio GitHub https://github.com/JuananOc/tutorial-backup.
6. Referencias
- howopensource.com/2014/09/simple-linux-backup-to-dropbox/
- https://github.com/andreafabrizi/Dropbox-Uploader