Ай Лайн
Связаться
Отдел продаж +7 (812) 385-74-84
Время работы пн-пт с 9:00 до 18:00

Резервное копирование всех баз 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
Оставить заявку