Backups automáticos con PostgreSql, bash y cronjobs — en español

Sin duda, saber que tienes un backup a la mano es una de las mayores esperanzas en casos de emergencia.

Backup o respaldo, en español, es una copia de los datos originales, generalmente de una base de datos, que se crean con la finalidad de poder restaurar la base de datos en cierto momento en caso de que se necesite tener la información en otro entorno, o, en la mayoría de los casos, luego de que la base de datos principal haya sido corrupta o vulnerada, hablando en cristiano, que hayas sido víctima de un ataque.

Antes de continuar, comparto con el lector mi posición ante este tema, y es que no soy un experto, al igual que tú, me encuentro aprendiendo y sólo comparto lo que me funcionó a mí en específico, si tienes alguna aportación o correción, siéntete libre de publicarla en comentarios.


Si quieres ir a la práctica, obvia esta parte, pero si prefieres empaparte un poco, puedes darle un vistazo a estos conceptos previos:

PostgreSql: Es un motor de base de datos bastante popular en el entorno web, sobre todo del lado open source, personalmente lo uso para rails en producción y algunas veces estando en el entorno de desarrollo. Sitio web oficial https://www.postgresql.org/.

Bash: Es el intérprete de comandos por defecto de linux, básicamente lo que hace posible que puedas ejecutar comandos como ‘ls’ ‘cd’ ‘dpkg’, y también otros más lógicos como ‘if’, ‘for’, ‘while’, aunque no se usen estos últimos muy a menudo, mayoritariamente en desarrollo de scripts para automatizar tareas.

Cronjobs: Como lo dice su nombre, un cronjob es una tarea cronológica, es decir tareas programadas que se desarrollan cada cierto tiempo. ¿Vamos captando la idea? Lo usaremos para generar los backup todos los días a la hora que sea más conveniente.


¡Manos a la obra!

Lo que haremos será que todos los días a las 2:30 am se cree un backup en nuestro server y avise por medio de correo el peso del actual backup, y si hay mas de 4 backups, que se borre el más antiguo, para ahorrar espacio.

  1. Iniciar sesión con el usuario de postgres, para asegurarnos que el script de backup tenga los permisos necesarios para ejecutarse correctamente desde el cronjob.
sudo su postgres

2. Ubicar en donde generaremos los backup, para asegurarnos de que estaremos en una dirección donde tengamos permisos de crear los backups, iremos a la carpeta del usuario postgres, sencillamente con:

cd

3. Creamos la estructura necesario, primero nos aseguramos de que estamos en la carpeta de postgres, con un ls, y nos debe aparecer algo así:

ls
# 9.4
# Es decir debe de haber por lo menos una carpeta con la versión de postgres, es donde se encuentra la carpeta main, en donde están distintos archivos de configuración, pero no nos preocupemos por ello ahora.

Creamos la carpeta cron, para construir ahí el script de backup y la carpeta con backups:

mkdir cron
cd cron
touch backup.sh
chmod +x backup.sh
mkdir backups

Básicamente lo que hemos hecho es crear la carpeta cron, dentro un archivo para el script, backup.sh, le dimos permisos de ejecutable, y luego creamos una carpeta para guardar los backups.

4. Ahora la parte divertida, el script, colocaré aquí el código y lo explicaré mediante comentarios, para posteriormente dar una explicación general del funcionamiento.

#!/bin/bash
# vars
backups_path="/var/lib/postgresql/cron/backups"
database="my_database_name"
current_date_time="`date +%Y%m%d%H%M%S`";
# dump
pg_dump $database > $backups_path/$current_date_time.sql;
# get size of dump
size="`wc -c $backups_path/$current_date_time.sql`";
# get oldest backup
first_backup="`ls $backups_path | sort -n | head -1`"
echo 'first backup '$first_backup
# get size of oldest backup
size_first="`wc -c $backups_path/$first_backup`"
echo 'size_first '$size_first
# get backups count
backups_count="`find $backups_path/*.sql -type f | wc -l`"
echo 'backups_count '$backups_count
# printing subject for email
echo 'Subject:'$size > $backups_path/$current_date_time.txt
# condition for remove if there is more than 4 backups
if [ $backups_count -ge 5 ] ; then
echo 'Greather than 5'
# removing backup
rm $backups_path/$first_backup
first_text="`ls $backups_path | sort -n | head -1`"
# removing text of backup
rm $backups_path/$first_text
# printing body explaining removed backup
printf "\nArchivo borrado: "$first_backup >> $backups_path/$current_date_time.txt
printf "\nPeso: "$size_first >> $backups_path/$current_date_time.txt
fi
# sending email
curl --url 'smtps://smtp.gmail.com:465' --ssl-reqd --mail-from 'senderemail@example.com' --mail-rcpt 'receiveremail@example.com' --upload-file $backups_path/$current_date_time.txt --user 'senderemail@example.com:myawesomepassword555sincorriente' --insecure

Antes de explicar a detalle, aclaro que el script de arriba es código de mi autoría, y si bien el post está titulado “en español” cuando se trata de código no recomiendo nunca hacer en español, o “espanglish”, más bien, netamente en inglés, si, hasta los comentarios.

Explicación a detalle.

Esta línea se encarga de que el programa sea ejecutado por bash, no por sh, como es por defecto, cada programa tiene diferencias sutiles.

#!/bin/bash

Estas líneas son variables globales para el script, por decirlo de una manera, primero, es la dirección exacta en donde se encuentran o encontrarán los backups, y segundo el nombre de la base de datos, para tener la ruta de una dirección podemos usar el comando pwd.

backups_path="/var/lib/postgresql/cron/backups"
database="my_database_name"

Esta línea consigue la fecha y hora exacta, con segundos, del momento que se ejecuta el script, lo usaremos para asegurarnos de que nuestro backup tiene un nombre distinto en cada instancia. Nótese que para guardar el prompt de un comando en una variable, además de guardar dentro de comillas “”, también debe estar dentro de comillas simples hacia la izquierda ``.

current_date_time="`date +%Y%m%d%H%M%S`";

Este comando se encarga de crear el backup de la manera mas simple que nos brinda el comando pg_dump, obviamente se pueden crear otros tipos de backups o el mismo con diferentes opciones, pero este comando sólo genera el backup de una base de datos en específico en cierto archivo en una ruta en específico. Como vemos, nos valemos de las variables previamente creadas.

pg_dump $database > $backups_path/$current_date_time.sql;

Creamos una variable para obtener el ancho del archivo backup, ¿Por qué? pues porque lo utilizaremos luego para saber el peso del archivo en un solo vistazo en un correo.

size="`wc -c $backups_path/$current_date_time.sql`";

Ubicamos el backup más antiguo, ya que iremos borrando los backup más antiguos para ahorrar espacio, en mi caso el servidor que tengo no tiene más de 20gb, en la mayoría de los casos, ahorrar espacio en los backups será vital.

first_backup="`ls $backups_path | sort -n | head -1`"

Igualmente obtenemos el peso del archivo más antiguo, para enviarlo en el correo, para saber exactamente qué estamos borrando.

size_first="`wc -c $backups_path/$first_backup`"

Guardamos en una variable el total de backups existentes, para tener en cuenta la cantidad que guardaremos, por ejemplo, si tenemos más de cuatro, entonces el script debe borrar el más antiguo.

backups_count="`find  $backups_path/*.sql -type f | wc -l`"

Esta parte es una personalización y preferencia de mi persona, en mi caso lo primero y más importante que deseo saber de mi backup es cuanto pesa, pues si veo algo raro entonces dispongo a tomar las medidas necesarias. Aquí coloco el peso del backup en el subject, así, cuando esté desde navegador o teléfono, voy a enterarme del status en el primer momento.

echo 'Subject:'$size >  $backups_path/$current_date_time.txt

Esta es la parte que en pocas palabras, borra el backup más antiguo en caso hayan más de 4 backups, podemos obviar esta parte si disponemos de espacio, o limitar al número que queramos. Primero hacemos la comparación con un ‘if’, luego borramos el último backup con un rm, después ubicamos el archivo de texto creado del respectivo backup ‘*.txt’ y lo borramos con un rm, finalmente agregamos al archivo de texto del backup actual la información del backup que estamos borrando, para que sepamos en el correo que es lo que estamos borrando.

if [ $backups_count -ge 5 ] ; then
echo 'Greather than 5'
# removing backup
rm $backups_path/$first_backup
first_text="`ls $backups_path | sort -n | head -1`"
# removing text of backup
rm $backups_path/$first_text
# printing body explaining removed backup
printf "\nArchivo borrado: "$first_backup >> $backups_path/$current_date_time.txt
printf "\nPeso: "$size_first >> $backups_path/$current_date_time.txt
fi

Por último ejecutamos el comando de envío de mail, mediante el programa curl, que no hace más que enviar peticiones con algunas opciones, en este caso, la url, que vendría a ser el servicio de envío de mails smtp de gmail, el mail remitente, el mail receptor y en upload file el archivo que estamos creando, con la información previamente mencionada, por último las credenciales del mail de envío, seguido de la opción ‘insecure’.

Podríamos instalar un servidor smtp dentro de nuestro server y no valernos de un servicio externo, pero es una tarea compleja para un fin tan simple, queda para criterio del desarrollador.

curl --url 'smtps://smtp.gmail.com:465' --ssl-reqd --mail-from 'senderemail@example.com' --mail-rcpt 'receiveremail@example.com' --upload-file $backups_path/$current_date_time.txt --user 'senderemail@example.com:myawesomepassword555sincorriente' --insecure

Es necesario activar el acceso de aplicaciones inseguras desde la cuenta remitente, para eso vamos a https://www.google.com/settings/security/lesssecureapps desde la cuenta de google que utilizaremos de remitente y marcamos la casilla de ‘Activar’.

Tal cuál.

Es todo con respecto al script, para testear el funcionamiento puedes ubicarte en donde está el backup.sh y ejecutar:

sh backup.sh

E ir testeando en caso haya errores en nuestro script.

5. Finalmente el cronjob.

Para eso ejecutamos el comando:

crontab -e

Lo cuál abrirá un archivo en donde dejaremos todas las tareas cronológicas, vemos que hay muchas indicaciones de configuración. Esta guía me sirvió mucho para entender como guardar correctamente estas tareas. https://www.cyberciti.biz/faq/how-do-i-add-jobs-to-cron-under-linux-or-unix-oses/

Al ejecutar crontab por primera vez nos preguntará con que queremos editar el archivo de tareas, escojamos nano porque es fácil de usar.

Si has leído el manual de como usar cronjobs entendiste que el primer espacio corresponde a minutos, luego horas, después días, meses, semanas, y finalmente el comando a ejecutar, es importante que nuestras rutas al script o comando a ejecutar sean absolutas.

Como queremos que nuestra tarea se ejecute en este caso todos los días a las 2:30 am, entonces la línea a agregar al archivo de tareas sería:

30 2 * * * /var/lib/postgresql/cron/backup.sh

Eso hará que se ejecute todos los días a las 2:30 am, ahora guardamos con Ctrl + X, confirmamos con Y, y cerramos con Enter.


Hemos terminado con la guía de como hacer backups automatizados a mi parecer, puede que para otras realidades o contextos la configuración sea diferente o tal vez cambie en demasía, en ese caso podrías comentar cual es tu caso para todos aprender en comunidad. Aclaro que no soy un experto en estas tareas, si ves algún error o mala práctica siéntete libre de colocar la corrección en el artículo.

Espero te haya sido de ayuda :) si es así, recomienda.

Show your support

Clapping shows how much you appreciated Fabian Peña’s story.