PostgreSQL резервное копирование с Docker SDK для Python
Избегайте сложных сценариев Bash. Пишите свои сценарии на языке Python!
Это небольшая заметка о резервном копировании базы данных Dockerized PostgreSQL . Для доступа к базе данных мы обычно запускаем на хосте сценарий Bash с такими командами, как:
docker exec -t <container> bash -c '<command>'
В этом посте мы заменим наш сценарий Bash на сценарий Python . Почему? Потому что мы знаем Python и программирование на Bash может отнимать много времени. Хотя мы можем использовать 'subprocess' для запуска всех системных команд здесь, мы включаем Docker SDK для Python и получаем много дополнительных возможностей. Как всегда, я запускаю это на Ubuntu (22.04).
Операция резервного копирования
Мы будем выполнять резервное копирование по шагам:
- Резервное копирование базы данных во временный файл внутри контейнера.
- Скопируйте файл резервной копии за пределы контейнера.
- Удалите временный файл.
- Показать время выполнения операции резервного копирования.
Сценарий резервного копирования Bash
Ниже приведен сценарий резервного копирования Bash. Он проверяет наличие ошибок во время операции резервного копирования и операции копирования. Сценарий начинается с параметров нашего контейнера и базы данных.
#!/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}"
Сценарий резервного копирования Python
Для сценария Python мы сначала устанавливаем Python SDK для Docker (в virtual environment):
pip install docker
Ниже приведен сценарий резервного копирования Python . Он проверяет наличие ошибок во время операции резервного копирования и операции копирования.
# 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()
Запуск без virtual environment
Но подождите, с Python нам нужен virtual environment. Чтобы избежать этого, мы можем создать исполняемый файл с помощью PyInstaller. Сначала установите PyInstaller:
pip install pyinstaller
Затем создайте исполняемый файл с помощью следующей команды:
pyinstaller --onefile backup.py
Это создаст исполняемый файл в каталоге './dist':
dist/backup
Мы можем запустить его и поместить этот исполняемый файл в наш путь.
Резюме
Нам не очень нужен был здесь Docker SDK для Python , мы могли бы использовать 'subprocess', но включив его, мы можем сделать много других вещей.
Разница между сценарием Bash и сценарием Python невелика. Написать несколько строк в Bash очень просто. Но как только мы захотим немного больше функциональности и контроля, это может стать очень трудоемким. Если вы не гуру Bash, вам придется искать команды, изучать, как писать функции, тестировать. При написании скрипта Python нам все еще нужны знания о командах Linux , но мы можем ограничиться несколькими нужными нам функциями. Для остальных мы можем использовать наши знания Python .
Ссылки / кредиты
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
Подробнее
Docker
Недавний
- Скрытие первичных ключей базы данных UUID вашего веб-приложения
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- Использование PyInstaller и Cython для создания исполняемого файла Python
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу