lunes, 14 de diciembre de 2020

Copias de seguridad periódica en PythonAnywhere

¿Como hacer copias de seguridad  de la base de datos de forma periódica y automática en pythonanywhere.com?

A continuación se muestra el código de forma detallada de como poder realizar nuestros backups de la base de datos mediante la ejecución de tareas.

De manera general lo que haremos es crear un archivo Python dentro de algún directorio de nuestra cuenta en Pythonanywhere, y luego hacer que dicho archivo sea ejecutado de forma periódica mediante la programación de tareas en el mismo Pythonanywhere.

Para empezar creamos nuestro archivo .py importamos los módulos que usaremos:

import os
import datetime
from zipfile import ZipFile

Este es el listado de los nombres de todas nuestras base de datos de las que queremos hacer backups.

# Nombres de las base de datos.
dbnames = []


Variables globales que usaremos en todo el archivo.

# Nombre de usuario de su cuenta.
USERNAME = "[username]"

# Directorio donde se almacenarán los backups
# (este directorio debe estar previamente creado).
BACKUP_DIR_NAME = f"/home/{USERNAME}/backups"

# Como nombre de archivo será backup_[fechayhora].
FILE_SUFFIX_DATE_FORMAT = "%Y%m%d%H%M%S"

# Se eliminarán los archivos que tengan más de 3 días de antigüedad.
DAYS_TO_KEEP_BACKUP = 3

back_date = datetime.datetime.now() - datetime.timedelta(days=DAYS_TO_KEEP_BACKUP)
back_date = back_date.strftime(FILE_SUFFIX_DATE_FORMAT)


El siguiente bloque de código va a recorrer cada nombre de la base de datos establecido en dbnames, y eliminará todos los archivos de base de datos que tengan más de 3 días de haberse creados.

# Eliminamos primero para asegurarnos de que casi siempre haya espacio
# disponible para los nuevos backups que serán creados.
print(f"Eliminando backups anteriores a {back_date}:")

# Recorremos cada nombre de base de datos.
# Para cada base de datos habrá un directorio exclusivo para sus backups.
for name in dbnames:
path = BACKUP_DIR_NAME + "/" + name
FILE_PREFIX = name + "_"
# Listamos todos los archivos de esta base de datos.
list_files = os.listdir(path)
length = len(FILE_PREFIX)

# Eliminamos los archivos con más de DAYS_TO_KEEP_BACKUP de antiguedad.
for f in list_files:
filename = f.split(".")[0]
if "zip" == f.split(".")[1]:
suffix = filename[length:]
if suffix < back_date:
os.remove(path + "/" + f)
print(f"- {path}/{f} (Eliminado)")


Una vez eliminados los archivos de copias de seguridad anteriores, procedemos a crear las copias de seguridad del día de hoy. El proceso es similar al anterior, recorremos cada nombre de la base de datos establecido en dbname, creamos nuestro nombre de archivo dentro de la carpeta con el mismo nombre de la base de datos, generamos el backup y luego lo comprimimos en zip.

# Una vez eliminados los backups antiguos, procedemos a crear los de hoy.
print("Iniciando backups para:")
print(", ".join(dbnames))

# Recorremos cada nombre de base de datos.
for name in dbnames:
# El nombre de la base de datos en Pythonanywhere es [username]$[dbname].
dbname = USERNAME + "$" + name
FILE_PREFIX = name + "_"

# Obtenemos la fecha y hora actual.
timestamp = datetime.datetime.now().strftime(FILE_SUFFIX_DATE_FORMAT)
backup_filename = BACKUP_DIR_NAME + "/" + name + "/" + FILE_PREFIX + timestamp + ".sql"

os.system("mysqldump -u "+USERNAME+" -h "+USERNAME+
".mysql.pythonanywhere-services.com '"+dbname+"' > "+backup_filename)

# Creamos el archivo zip.
zip_filename = BACKUP_DIR_NAME + "/" + name + "/" + FILE_PREFIX + timestamp + ".zip"
with ZipFile(zip_filename, 'w') as zip:
zip.write(backup_filename, os.path.basename(backup_filename))

# Eliminamos el archivo sql.
os.remove(backup_filename)

print(zip_filename, "OK")


Código completo:

import os
import datetime
from zipfile import ZipFile

# Nombres de las base de datos.
dbnames = []

# Nombre de usuario de su cuenta.
USERNAME = "[username]"

# Directorio donde se almacenarán los backups
# (este directorio debe estar previamente creado).
BACKUP_DIR_NAME = f"/home/{USERNAME}/backups"

# Como nombre de archivo será backup_[fechayhora].
FILE_SUFFIX_DATE_FORMAT = "%Y%m%d%H%M%S"

# Se eliminarán los archivos que tengan más de 3 días de antiguedad.
DAYS_TO_KEEP_BACKUP = 3

back_date = datetime.datetime.now() - datetime.timedelta(days=DAYS_TO_KEEP_BACKUP)
back_date = back_date.strftime(FILE_SUFFIX_DATE_FORMAT)

# Eliminamos primero para asegurarnos de que casi siempre haya espacio
# disponible para los nuevos backups que serán creados.
print(f"Eliminando backups anteriores a {back_date}:")

# Recorremos cada nombre de base de datos.
# Para cada base de datos habrá un directorio exclusivo para sus backups.
for name in dbnames:
path = BACKUP_DIR_NAME + "/" + name
FILE_PREFIX = name + "_"
# Listamos todos los archivos de esta base de datos.
list_files = os.listdir(path)
length = len(FILE_PREFIX)

# Eliminamos los archivos con más de DAYS_TO_KEEP_BACKUP de antiguedad.
for f in list_files:
filename = f.split(".")[0]
if "zip" == f.split(".")[1]:
suffix = filename[length:]
if suffix < back_date:
os.remove(path + "/" + f)
print(f"- {path}/{f} (Eliminado)")


# Una vez eliminados los backups antiguos, procedemos a crear los de hoy.
print("Iniciando backups para:")
print(", ".join(dbnames))

# Recorremos cada nombre de base de datos.
for name in dbnames:
# El nombre de la base de datos en Pythonanywhere es [username]$[dbname].
dbname = USERNAME + "$" + name
FILE_PREFIX = name + "_"

# Obtenemos la fecha y hora actual.
timestamp = datetime.datetime.now().strftime(FILE_SUFFIX_DATE_FORMAT)
backup_filename = BACKUP_DIR_NAME + "/" + name + "/" + FILE_PREFIX + timestamp + ".sql"

os.system("mysqldump -u "+USERNAME+" -h "+USERNAME+
".mysql.pythonanywhere-services.com '"+dbname+"' > "+backup_filename)

# Creamos el archivo zip.
zip_filename = BACKUP_DIR_NAME + "/" + name + "/" + FILE_PREFIX + timestamp + ".zip"
with ZipFile(zip_filename, 'w') as zip:
zip.write(backup_filename, os.path.basename(backup_filename))

# Eliminamos el archivo sql.
os.remove(backup_filename)

print(zip_filename, "OK")


Luego de tener nuestro archivo creado, procedemos a incluirlo en una tarea programada en Pythonanywhere. Para ello nos dirigimos a www.pythonanywhere.com e ingresamos a nuestra cuenta.

Nos vamos al la página de tareas programadas haciendo click en el enlace Tasks de la barra de navegación.


Luego procedemos a incluir una nueva tarea, haciendo uso del siguiente formulario en el que establecemos la ruta de nuestro archivo Python que queremos ejecutar de forma periódica.


Nota: Si deseamos que nuestro scripts se ejecute usando un virtualenv deberíamos utilizar la siguiente estructura de ruta en su lugar: 

/home/myusername/.virtualenvs/myvenv/bin/python /home/myusername/myproject/mytask.py

Para más información de como ejecutar nuestro archivo en un virtualenv puedes consultar Usando un virtualenv en una tarea programada.


¡Eso es todo de momento!

No hay comentarios.:

Publicar un comentario