PostgreSQL copia de seguridad con Docker SDK para Python
Evite los scripts Bash complejos. ¡Escribe tus scripts en Python!
Este es un breve post sobre la copia de seguridad de una base de datos Dockerized PostgreSQL . Para acceder a la base de datos, normalmente ejecutamos un script Bash en el host, con comandos como:
docker exec -t <container> bash -c '<command>'
En este post sustituimos nuestro script Bash por un script Python . ¿Por qué? Porque conocemos Python y programar en Bash puede llevarnos mucho tiempo. Aunque podemos usar 'subprocess' para ejecutar todos los comandos del sistema aquí, incluimos el SDK Docker para Python y obtenemos un montón de extras. Como siempre, estoy ejecutando esto en Ubuntu (22.04).
Operación de copia de seguridad
Vamos a realizar una copia de seguridad en los pasos:
- Copia de seguridad de la base de datos a un archivo temporal dentro del contenedor.
- Copiar el fichero de copia de seguridad fuera del contenedor.
- Eliminar el fichero temporal.
- Mostrar la hora de la operación de copia de seguridad.
El script Bash de copia de seguridad
A continuación se muestra el script Bash de copia de seguridad. Comprueba si hay errores durante la operación de copia de seguridad y la operación de copia. El script comienza con nuestros parámetros de contenedor y base de datos.
#!/usr/bin/bash
# backup.sh
pg_container=8c49633bda68
pg_user=postgres
db_name=my_db
db_user=postgres
# start
SECONDS=0
# backupfile inside docker container
tmp_backupfile=/tmp/${db_name}.pg_dump
# backupfile on the host
backupfile=${db_name}.pg_dump.`date +%Y%m%d"_"%H%M%S`.sql
echo "tmp_backupfile = ${tmp_backupfile}"
echo "backupfile = ${backupfile}"
echo "removing previous tmp_backupfile ..."
docker exec -t ${pg_container} bash -c 'rm -f -- ${0}' ${tmp_backupfile}
echo "dumping database to tmp_backupfile ..."
docker exec -t --user ${pg_user} ${pg_container} bash -c 'pg_dump ${0} -Fc -U ${1} -f ${2}' ${db_name} ${pg_user} ${tmp_backupfile}
exit_code=$?
if [ $exit_code -ne 0 ] ; then
echo "dump error, exit_code = ${exit_code}"
exit $exit_code
fi
echo "copying tmp_backupfile to backupfile ..."
docker cp "${pg_container}":${tmp_backupfile} ${backupfile}
exit_code=$?
if [ $exit_code -ne 0 ] ; then
echo "copy error, exit_code = ${exit_code}"
exit $exit_code
fi
echo "removing tmp_backupfile ..."
docker exec -t ${pg_container} bash -c 'rm -f -- ${0}' ${tmp_backupfile}
fsize=`stat --printf="%s" ${backupfile}`
elapsed_secs=$SECONDS
echo "ready, backupfile = ${backupfile}, size = ${fsize}, seconds = ${elapsed_secs}"
El script de copia de seguridad de Python
Para el script Python primero instalamos el SDK Python para Docker (en un virtual environment):
pip install docker
A continuación se muestra el script de copia de seguridad de Python . Comprueba si hay errores durante la operación de copia de seguridad y la operación de copia.
# backup.py
import datetime
import logging
import os
import subprocess
import sys
import time
import docker
def get_logger():
logger_format = '[%(asctime)s] [%(levelname)s] %(message)s'
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# console
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(logging.Formatter(logger_format))
logger.addHandler(console_handler)
return logger
logger = get_logger()
class DockerUtils:
def __init__(
self,
container=None,
):
self.client = docker.from_env()
self.container = self.client.containers.get(container)
def docker_exec(self, cmd, **kwargs):
return self.container.exec_run(cmd, **kwargs)
def remove_container_file(self, f):
r = self.docker_exec('ls ' + f)
if r.exit_code == 0:
r = self.docker_exec('rm ' + f)
def docker_cp_from_container(self, src, dst):
r = subprocess.run(['docker', 'cp', self.container.short_id + ':' + src, dst])
return r
def main():
pg_container = '8c49633bda68'
pg_user = 'postgres'
db_name = 'my_db'
db_user = 'postgres'
# start
dt_start = datetime.datetime.now()
# backupfile inside docker container
tmp_backupfile = os.path.join('/tmp/', db_name + '.pg_dump')
# backupfile on the host
backupfile = './' + db_name + '.pg_dump.' + datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
logger.debug('tmp_backupfile = {}'.format(tmp_backupfile))
logger.debug('backupfile = {}'.format(backupfile))
# instantiate and set container
du = DockerUtils(container=pg_container)
logger.debug('removing previous tmp_backupfile ...')
du.remove_container_file(tmp_backupfile)
logger.debug('dumping database to tmp_backupfile ...')
cmd = 'pg_dump {0} -Fc -U {1} -f {2}'.format(db_name, db_user, tmp_backupfile)
kwargs = {'user': pg_user}
r = du.docker_exec(cmd, **kwargs)
if r.exit_code != 0:
logger.error('dump error: {}'.format(str(r.output.decode('utf-8'))))
sys.exit()
logger.debug('copying tmp_backupfile to backupfile ...')
r = du.docker_cp_from_container(tmp_backupfile, backupfile)
if r.returncode != 0:
logger.error('copy error = {}'.format(r.returncode))
sys.exit()
logger.debug('removing tmp_backupfile ...')
du.remove_container_file(tmp_backupfile)
fsize = os.stat(backupfile).st_size
elapsed_secs = int((datetime.datetime.now() - dt_start).total_seconds())
logger.info('ready, backupfile = {}, size = {}, seconds = {}'.format(backupfile, fsize, elapsed_secs))
if __name__ == "__main__":
main()
Ejecutando sin virtual environment
Pero espera, con Python necesitamos un virtual environment. Para evitar esto, podemos crear un ejecutable utilizando PyInstaller. Primero instala PyInstaller:
pip install pyinstaller
Luego crea el ejecutable con el siguiente comando:
pyinstaller --onefile backup.py
Esto creará un ejecutable en el directorio './dist':
dist/backup
Podemos ejecutarlo y poner este ejecutable en nuestra ruta.
Resumen
Realmente no necesitamos el SDK Docker para Python aquí, podríamos haber usado 'subprocess', pero incluyéndolo podemos hacer muchas otras cosas también.
No hay mucha diferencia entre el script Bash y el script Python . Escribir algunas líneas en Bash es fácil. Pero tan pronto como queremos un poco más de funcionalidad y control, puede llegar a consumir mucho tiempo. A menos que seas un gurú de Bash, tienes que buscar comandos, estudiar cómo escribir funciones, probar cosas. Al escribir un script Python , todavía necesitamos conocimientos sobre los comandos Linux pero podemos limitar esto a las pocas funciones que necesitamos. Para el resto, podemos utilizar nuestros conocimientos de Python .
Enlaces / créditos
Docker - docker cp
https://docs.docker.com/engine/reference/commandline/cp
Docker SDK for Python
https://docker-py.readthedocs.io
Dockerize & Backup A Postgres Database
https://dev.to/mattcale/dockerize-backup-a-postgres-database-2b1l
PostgreSQL - pg_dump
https://www.postgresql.org/docs/current/app-pgdump.html
PyInstaller
https://pyinstaller.org
Why is pg_restore segfaulting in Docker?
https://stackoverflow.com/questions/63934856/why-is-pg-restore-segfaulting-in-docker
Leer más
Docker
Recientes
- Cómo ocultar las claves primarias de la base de datos UUID de su aplicación web
- Don't Repeat Yourself (DRY) con Jinja2
- SQLAlchemy, PostgreSQL, número máximo de filas por user
- Mostrar los valores en filtros dinámicos SQLAlchemy
- Transferencia de datos segura con cifrado de Public Key y pyNaCl
- rqlite: una alternativa de alta disponibilidad y dist distribuida SQLite
Más vistos
- Usando Python's pyOpenSSL para verificar los certificados SSL descargados de un host
- Usando UUIDs en lugar de Integer Autoincrement Primary Keys con SQLAlchemy y MariaDb
- Conectarse a un servicio en un host Docker desde un contenedor Docker
- Usando PyInstaller y Cython para crear un ejecutable de Python
- SQLAlchemy: Uso de Cascade Deletes para eliminar objetos relacionados
- Flask RESTful API validación de parámetros de solicitud con esquemas Marshmallow