Резервное копирование всех баз PostgreSQL — Linux
Скрипт не выполняет чистку в удаленном хранилище, для чистки удаленного хранилища используется скрипт непосредственно на хранилище.
Пользователь, под которым происходит подключение к postgres, должен быть также добавлен в систему, так как используется по умолчанию метод авторизации по сокету (local) и peer
На время резервного копирования происходит временное подключение smb-шары, после завершения происходит отключение
Для монтирования smb требуется установка: apt-get install cifs-utils
Вариант с монтированием smb-шары (cifs-utils)
Скрипт выполняет следующие задачи:
- получает список всех баз
- выполняет резервное копирование globals
- выполняет резервное копирование всех баз данных
- пишет лог резервного копирования по каждой базе
- выполняет копирование всех данных (globals, базы, логи) на другой сервер через smbclient
- выполняется проверка количества текущих резервных копий, если количество больше чем допустимый лимит, то выполняется проверка возраста файлов и чистка
Параметры в скрипте:
- pg_bak_dir="/pg_dump/pg_bak_files" - директория расположения резервных копий
- pg_log_dir="/pg_dump/pg_log_files" - директория расположения журналов резервных копий
- max_age_file="" - количество хранимых дней копий
- smb_share="" - smb шара
- smb_user="" - имя пользователя smb
- smb_pass="" - пароль пользователя smb
Содержимое файла /root/.smbclient-cred:
username=xxxxx
password=xxxxxxxxxxxx
Подготовка
Создаем папку для хранения скрипта и файл скрипта:
sudo mkdir -p /scripts/pg && touch /scripts/pg/pg_bak_all.sh
Делаем задачу в cron (лучше под root):
00 23 * * * /scripts/pg/./pg_bak_all.sh
Важный момент - скрипт при очистке использует выборку на основе знака - , поэтому если в названии базы есть данный символ, то могут быть проблем в случае если есть несколько баз с данным символом и одинаковым началом в названии. Нужно в конструкции (date +-D%Y-%m-%dT%H%M%S) поменять перед D разделительный знак на другой и поставить его в cut -d "новый_знак_разделителя" -f 1
bash скрипт:
Новая версия, только локальные копии, с логированием для последующего мониторинга
#! /bin/bash
# var
pg_bak_dir="/mnt/data-bak-postgres/pg-bak-files"
pg_log_dir="/var/log/pg-dump/by-base"
max_age_file="7"
pg_user="postgres_backup"
# create log folder
if [ ! -d "/var/log/pg-dump/by-base" ]
then
sudo mkdir -p $pg_log_dir
fi
# bak global
echo "$(date +"%Y-%m-%d %T") - start - pg_dump of globals" >> $pg_log_dir/globals.log
if ! sudo -Hiu $pg_user /usr/bin/pg_dumpall --globals-only >$pg_bak_dir/globals$(date +-D%Y-%m-%dT%H%M%S).dump 2>>$pg_log_dir/globals.log
then
echo "$(date +"%Y-%m-%d %T") - error - pg_dump of globals" >> $pg_log_dir/globals.log
else
echo "$(date +"%Y-%m-%d %T") - success - pg_dump of globals" >> $pg_log_dir/globals.log
fi
# bak bases and create backup logs
for base in $(sudo -Hiu $pg_user psql -d postgres -c "SELECT datname FROM pg_database;" | head --line=-2 | tail +3)
do
# Для исключения баз postgres, template0, template1 и баз содержащих в названии 'test' необходимо
# заменить строку условия
#if [[ $base =~ postgres ]] || [[ $base =~ test ]] || [[ $base =~ ^template ]];
if [[ $base =~ ^template ]];
then
continue
fi
bak_datetime=$(date +-D%Y-%m-%dT%H%M%S)
bak_name=$base$bak_datetime.dump
log_name=$base.log
echo "$(date +"%Y-%m-%d %T") - start - pg_dump of $base" >> $pg_log_dir/$log_name
if ! sudo -Hiu $pg_user /usr/bin/pg_dump -Fc $base >$pg_bak_dir/$bak_name 2>>$pg_log_dir/$log_name
then
echo "$(date +"%Y-%m-%d %T") - error - pg_dump of $base" >> $pg_log_dir/$log_name
else
echo "$(date +"%Y-%m-%d %T") - success - pg_dump of $base" >> $pg_log_dir/$log_name
fi
done
# get number of current bak files and remove old files
num_file_bak=$(ls -l $pg_bak_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 1 | head -n 1)
num_file_count_bak=$(ls -l $pg_bak_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 1 | grep $num_file_bak | wc -l)
if [ $max_age_file -le $num_file_count_bak ]
then
find $pg_bak_dir -name "*.dump" -type f -mtime +$max_age_file -delete
fi
# get number of current log files and remove old files
num_file_log=$(ls -l $pg_log_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 1 | head -n 1)
num_file_count_log=$(ls -l $pg_log_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 2 | grep $num_file_log | wc -l)
if [ $max_age_file -le $num_file_count_log ]
then
find $pg_log_dir -name "*.log" -type f -mtime +$max_age_file -delete
fi
Новая версия, с монтированием smb без локальных копий, с логированием для последующего мониторинга
#! /bin/bash
# var
smb_mount="/mnt/data-bak-postgres"
pg_bak_dir="/mnt/data-bak-postgres/pg-bak-files"
pg_log_dir="/var/log/pg-dump/by-base"
max_age_file="7"
smb_share="//uxxxxx-sub1.your-storagebox.de/share"
credfile="/root/.smbclient-cred"
pg_user="postgres_backup"
# create log folder
if [ ! -d "/var/log/pg-dump/by-base" ]
then
sudo mkdir -p $pg_log_dir
fi
# mount smb
sudo mount -t cifs -o credentials=$credfile $smb_share $smb_mount
# bak global
echo "$(date +"%Y-%m-%d %T") - start - pg_dump of globals" >> $pg_log_dir/globals.log
if ! sudo -Hiu $pg_user /usr/bin/pg_dumpall --globals-only >$pg_bak_dir/globals$(date +-D%Y-%m-%dT%H%M%S).dump 2>>$pg_log_dir/globals.log
then
echo "$(date +"%Y-%m-%d %T") - error - pg_dump of globals" >> $pg_log_dir/globals.log
else
echo "$(date +"%Y-%m-%d %T") - success - pg_dump of globals" >> $pg_log_dir/globals.log
fi
# bak bases and create backup logs
for base in $(sudo -Hiu $pg_user psql -d postgres -c "SELECT datname FROM pg_database;" | head --line=-2 | tail +3)
do
if [[ $base == 'template'* ]]
then
continue
fi
bak_datetime=$(date +-D%Y-%m-%dT%H%M%S)
bak_name=$base$bak_datetime.dump
log_name=$base.log
echo "$(date +"%Y-%m-%d %T") - start - pg_dump of $base" >> $pg_log_dir/$log_name
if ! sudo -Hiu $pg_user /usr/bin/pg_dump -Fc $base >$pg_bak_dir/$bak_name 2>>$pg_log_dir/$log_name
then
echo "$(date +"%Y-%m-%d %T") - error - pg_dump of $base" >> $pg_log_dir/$log_name
else
echo "$(date +"%Y-%m-%d %T") - success - pg_dump of $base" >> $pg_log_dir/$log_name
fi
done
# get number of current bak files and remove old files
num_file_bak=$(ls -l $pg_bak_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 1 | head -n 1)
num_file_count_bak=$(ls -l $pg_bak_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 1 | grep $num_file_bak | wc -l)
if [ $max_age_file -le $num_file_count_bak ]
then
find $pg_bak_dir -name "*.dump" -type f -mtime +$max_age_file -delete
fi
# get number of current log files and remove old files
num_file_log=$(ls -l $pg_log_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 1 | head -n 1)
num_file_count_log=$(ls -l $pg_log_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 2 | grep $num_file_log | wc -l)
if [ $max_age_file -le $num_file_count_log ]
then
find $pg_log_dir -name "*.log" -type f -mtime +$max_age_file -delete
fi
# umount smb
sudo umount /mnt/data-bak-postgres
Старая версия с копированием бэкапов на smb через smbclient
#! /bin/bash
#var
pg_bak_dir="/pg_dump/pg_bak_files"
pg_log_dir="/pg_dump/pg_log_files"
max_age_file="17"
smb_share="//10.4.148.10/pg-backup-host02/"
smb_user="user"
smb_pass="password"
pg_glob_name="pg_globals-$(date +D%Y%m%d-T%H%M%S).gz"
#bak global and put to smb share
sudo -Hiu postgres pg_dumpall -U postgres --globals-only | gzip > $pg_bak_dir/$pg_glob_name
###smbclient start
smbclient --quiet $smb_share -U $smb_user%$smb_pass << EOF
cd /pg_bak_files
put $pg_bak_dir/$pg_glob_name $pg_glob_name
exit
EOF
###smbclient stop
#bak bases and put to smb share + backup logs
for base in $(sudo -Hiu postgres psql -c "SELECT datname FROM pg_database;" | head --line=-2 | tail +3)
do
bak_datetime_echo=$(date +"%D %T")
bak_datetime=$(date +D%Y%m%d-T%H%M%S)
bak_name=$base-$bak_datetime.gz
log_name=log-$base-$bak_datetime
echo "$bak_datetime_echo backuping $base ..."
sudo -Hiu postgres pg_dump -U postgres --verbose $base 2>$pg_log_dir/$log_name | gzip > $pg_bak_dir/$bak_name
###smbclient start
smbclient --quiet $smb_share -U $smb_user%$smb_pass << EOF
cd /pg_bak_files
put $pg_bak_dir/$bak_name $bak_name
cd /pg_log_files
put $pg_log_dir/$log_name $log_name
exit
EOF
###smbclient stop
done
#get number of current bak files and remove old files
num_file_bak=$(ls -l $pg_bak_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 1 | head -n 1)
num_file_count_bak=$(ls -l $pg_bak_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 1 | grep $num_file_bak | wc -l)
if [ $max_age_file -le $num_file_count_bak ]
then
find $pg_bak_dir -name "*.gz" -type f -mtime +$max_age_file -delete
fi
#get number of current log files and remove old files
num_file_log=$(ls -l $pg_log_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 1 | head -n 1)
num_file_count_log=$(ls -l $pg_log_dir | tail +2 | awk '{print $9}' | cut -d "-" -f 2 | grep $num_file_log | wc -l)
if [ $max_age_file -le $num_file_count_log ]
then
find $pg_log_dir -name "*.gz" -type f -mtime +$max_age_file -delete
fi