angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

PostgreSQL Sicherung mit Docker SDK für Python

Vermeiden Sie komplexe Bash-Skripte. Schreiben Sie Ihre Skripte in Python!

9 März 2023
post main image
https://www.pexels.com/nl-nl/@pixabay/

Dies ist ein kurzer Beitrag über die Sicherung einer Dockerized PostgreSQL Datenbank. Um auf die Datenbank zuzugreifen, führen wir normalerweise ein Bash-Skript auf dem Host aus, mit Befehlen wie:

docker exec -t <container> bash -c '<command>'

In diesem Beitrag ersetzen wir unser Bash-Skript durch ein Python -Skript. Warum? Weil wir Python kennen und die Programmierung in Bash zeitaufwändig sein kann. Während wir hier 'subprocess' verwenden können, um alle Systembefehle auszuführen, binden wir das Docker SDK für Python ein und erhalten eine Menge Extras. Wie immer laufe ich dies auf Ubuntu (22.04).

Backup-Vorgang

Wir werden in den folgenden Schritten ein Backup durchführen:

  • Sicherung der Datenbank in eine temporäre Datei innerhalb des Containers.
  • Kopieren der Sicherungsdatei außerhalb des Containers.
  • Entfernen Sie die temporäre Datei.
  • Anzeige des Zeitpunkts des Sicherungsvorgangs.

Das Bash-Sicherungsskript

Nachfolgend sehen Sie das Bash-Sicherungsskript. Es prüft auf Fehler während des Sicherungs- und Kopiervorgangs. Das Skript beginnt mit unseren Container- und Datenbankparametern.

#!/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}"

Das Python -Sicherungsskript

Für das Python Skript installieren wir zunächst das Python SDK für Docker (in einem virtual environment):

pip install docker

Nachfolgend finden Sie das Python -Sicherungsskript. Es prüft auf Fehler während des Sicherungs- und Kopiervorgangs.

# 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()

Ausführung ohne virtual environment

Aber halt, mit Python brauchen wir einen virtual environment. Um dies zu vermeiden, können wir eine ausführbare Datei mit PyInstaller erstellen. Installieren Sie zunächst PyInstaller:

pip install pyinstaller

Dann erstellen Sie die ausführbare Datei mit dem folgenden Befehl:

pyinstaller --onefile backup.py

Dadurch wird eine ausführbare Datei im Verzeichnis './dist' erstellt:

dist/backup

Wir können dies ausführen und diese ausführbare Datei in unseren Pfad aufnehmen.

Zusammenfassung

Wir brauchten das Docker SDK für Python hier nicht wirklich, wir hätten auch 'subprocess' verwenden können, aber indem wir es einbinden, können wir auch eine Menge anderer Dinge tun.

Es gibt keinen großen Unterschied zwischen dem Bash-Skript und dem Skript Python . Ein paar Zeilen in der Bash zu schreiben ist einfach. Aber sobald wir ein wenig mehr Funktionalität und Kontrolle wollen, kann es sehr zeitaufwändig werden. Wenn man kein Bash-Guru ist, muss man Befehle nachschlagen, lernen, wie man Funktionen schreibt, und Dinge testen. Wenn wir ein Python -Skript schreiben, brauchen wir immer noch Kenntnisse über Linux -Befehle, aber wir können dies auf die wenigen Funktionen beschränken, die wir brauchen. Für die restlichen Funktionen können wir unsere Python -Kenntnisse nutzen.

Links / Impressum

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

Mehr erfahren

Docker

Einen Kommentar hinterlassen

Kommentieren Sie anonym oder melden Sie sich zum Kommentieren an.

Kommentare

Eine Antwort hinterlassen

Antworten Sie anonym oder melden Sie sich an, um zu antworten.