Automatización de copias de seguridad con Dropbox y Ansible

0
6292

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

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

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad