2SOMEone | PostgreSQL备份
by CUNOE, April 7, 2024
今天分享一下2SOMEone的PostgreSQL备份方案。
流式传输同步
泡泡树洞后端采用bitnami
提供的PostgreSQL
镜像,bitnami
为原版镜像提供了更丰富的定制化,包括流式备份等。
这里使用bitnami
的流式备份方案
services:
# master节点
postgres-2someone-master:
image: bitnami/postgresql:15
restart: always
environment:
- POSTGRESQL_USERNAME=uername
- POSTGRESQL_PASSWORD=password
- POSTGRESQL_DATABASE=2someone
- POSTGRESQL_POSTGRES_PASSWORD=postgres_password
- POSTGRESQL_TIMEZONE=Asia/Shanghai
- POSTGRESQL_LOG_TIMEZONE=Asia/Shanghai
- POSTGRESQL_ENABLE_TLS=yes
- POSTGRESQL_TLS_CERT_FILE=/bitnami/postgresql/ssl/fullchain.cer
- POSTGRESQL_TLS_KEY_FILE=/bitnami/postgresql/ssl/private.key
# 流式备份,设置为master节点,在这里设置的user和password是专用于复制的,不可直接用于连接数据库
- POSTGRESQL_REPLICATION_MODE=master
- POSTGRESQL_REPLICATION_USER=repl_user
- POSTGRESQL_REPLICATION_PASSWORD=repl_password
volumes:
- ./postgresql_data/ssl:/bitnami/postgresql/ssl
- ./postgresql_data/master:/bitnami/postgresql/data
- ./postgresql_data/scripts:/bitnami/postgresql/scripts
# slave节点
postgres-2someone-slave:
image: bitnami/postgresql:15
restart: always
environment:
# POSTGRESQL_PASSWORD 是该数据库超级用户 postgres 的密码 可以自行修改 登录该slave节点的时候使用 user: postgres password: ${POSTGRESQL_PASSWORD}
- POSTGRESQL_PASSWORD=postgres_password
- POSTGRESQL_TIMEZONE=Asia/Shanghai
- POSTGRESQL_LOG_TIMEZONE=Asia/Shanghai
# 流式备份,设置为slave节点,在这里使用master节点定义的repl专用的用户和密码
- POSTGRESQL_REPLICATION_MODE=slave
- POSTGRESQL_REPLICATION_USER=repl_user # 该用户用于主从同步,和主库的用户密码一致
- POSTGRESQL_REPLICATION_PASSWORD=repl_password
- POSTGRESQL_MASTER_HOST=postgres-2someone-master
- POSTGRESQL_MASTER_PORT_NUMBER=5432
volumes:
- ./postgresql_data:/bitnami/postgresql
- ./postgresql_data/scripts:/scripts
执行该docker-compose.yml
文件后,bitnami
的PostgreSQL
镜像会自动进行流式同步。
数据库定时备份
为了保证数据的安全,我们还需要定期对数据库进行备份。
这里使用pg_dump
工具进行备份,通过crontab
定时执行备份任务。
#!/bin/sh
# ./postgresql_data/scripts/backup_2someone_db.sh
##################################################
# 环境变量及系统参数
##################################################
# PostgreSQL安装目录
PGHOME=/opt/bitnami/postgresql
# Host
PGHOST=0.0.0.0
# 监听端口
PGPORT=5432
# 用户
PGUSER=postgres
# 密码
PGPASSWORD=postgres_password
# 数据库
DBNAME=2someone_db
# 编码
ENCODING=UTF8
# 备份文件存放目录
BACKUP_DIR=/bitnami/postgresql/backup
# 备份文件名称
BACKUP_FILE_NAME=${DBNAME}-$(date +%Y%m%d%H%M%S).bak
# 备份文件保留天数,默认30天
BACKUP_FILE_RESERVED_DAYS=30
# 脚本名称
SCRIPT_NAME="$(basename "${0}")"
# 备份执行日志存放目录
BACKUP_LOG_PATH=/bitnami/postgresql/backup/logs
# 备份执行日志文件名称
BACKUP_LOG_FILENAME=${BACKUP_LOG_PATH}/${SCRIPT_NAME}-$(date +%Y%m%d%H%M%S).log
# 准备
function prepare() {
if [ ! -d "${BACKUP_DIR}" ]; then
mkdir -p "${BACKUP_DIR}"
fi
if [ ! -d "${BACKUP_LOG_PATH}" ]; then
mkdir -p "${BACKUP_LOG_PATH}"
fi
if [ ! -f "${BACKUP_LOG_FILENAME}" ]; then
touch "${BACKUP_LOG_FILENAME}"
fi
}
# 记录INFO日志
function info() {
echo "$(date "+%Y-%m-%d %H:%M:%S") [ INFO] ${1}" >> "${BACKUP_LOG_FILENAME}"
}
# 记录ERROR日志
function error() {
echo -e "$(date "+%Y-%m-%d %H:%M:%S") \033[31m[ERROR]\033[0m ${1}" >> "${BACKUP_LOG_FILENAME}"
}
# 备份数据
function backup() {
info "备份数据库${DBNAME}开始"
export PGPASSWORD
${PGHOME}/bin/pg_dump --file ${BACKUP_DIR}/${BACKUP_FILE_NAME} --host ${PGHOST} --port ${PGPORT} --dbname ${DBNAME} --username ${PGUSER} --role postgres --format=c --blobs --encoding ${ENCODING} --verbose >> ${BACKUP_LOG_FILENAME} 2>&1
# 获取上一个命令的退出状态(0表示正常退出)
dump_status=$?
if [ ! ${dump_status} -eq 0 ]; then
error "备份数据库${DBNAME}失败"
exit 1
fi
info "数据库${DBNAME}备份文件:${BACKUP_DIR}/${BACKUP_FILE_NAME}"
info "备份数据库${DBNAME}结束"
}
# 删除历史数据库备份文件
function clear() {
info "删除数据库${DBNAME}历史备份文件开始"
# 获取更新时间大于保留天数以前的历史备份文件
history_bak_zip_files=$(find ${BACKUP_DIR} -type f -mtime +${BACKUP_FILE_RESERVED_DAYS} -name "*.bak")
if [ -z "${history_bak_zip_files}" ]; then
info "无${BACKUP_FILE_RESERVED_DAYS}天前的历史备份文件"
exit 0
fi
# 逐个删除历史备份文件
for history_bak_zip_file in ${history_bak_zip_files}
do
rm -f ${history_bak_zip_file}
info "删除数据库${DBNAME}历史备份文件${history_bak_zip_file}"
done
info "删除数据库${DBNAME}历史备份文件结束"
}
function doBackup() {
# 准备
prepare
# 备份数据库
backup
# 删除历史备份文件
clear
}
doBackup
exit 0
在crontab
中添加定时任务
# 每天凌晨2点执行备份任务
# crontab -e
0 2 * * * docker exec -d postgres-2someone-slave /bitnami/postgresql/scripts/backup_2someone_db.sh
这样就可以实现每天凌晨2点对数据库进行备份。
开发时复制数据库使用
在开发时,由于需要测试一些数据,为了方便开发,在允许的情况下,可以将生产环境的数据库复制到开发环境。
为此我们研究了使用docker-compose和bitnami的数据库初始化方法结合的方案来实现快速复制数据库。
services:
postgres-2someone-copy:
image: bitnami/postgresql:15
restart: always
environment:
# 该复制数据库的用户与密码
- POSTGRESQL_USERNAME=leaperone
- POSTGRESQL_PASSWORD=password
- POSTGRESQL_DATABASE=2someone
- POSTGRESQL_POSTGRES_PASSWORD=password
- POSTGRESQL_TIMEZONE=Asia/Shanghai
- POSTGRESQL_LOG_TIMEZONE=Asia/Shanghai
volumes:
# 将初始化脚本挂载到容器的/docker-entrypoint-initdb.d/目录下,以便容器进行初始化操作
- ./init-db.sh:/docker-entrypoint-initdb.d/init-2someone-db.sh
ports:
- "5432:5432"
#!/bin/bash
set -e
# 获取远程数据库数据
export REMOTE_HOST=postgre-2someone-slave # 使用master节点也是可以的,但我们建议使用slave节点以确保安全性和降低master的负载
export REMOTE_PORT=5432
export PGPASSWORD=postgres_password
pg_dump --file "/bitnami/postgresql/2someone.backup" --host ${REMOTE_HOST} --port ${REMOTE_PORT} --username "postgres" --dbname "2someone_db" --verbose --role "postgres" --format=c --blobs --encoding "UTF8"
pg_dump --file "/bitnami/postgresql/something.backup" --host ${REMOTE_HOST} --port ${REMOTE_PORT} --username "postgres" --dbname "something_db" --verbose --role "postgres" --format=c --blobs --encoding "UTF8"
export PGPASSWORD=password
# 你可能需要创建一些新数据库
psql -v ON_ERROR_STOP=1 --username "postgres" <<-EOSQL
CREATE DATABASE something_db;
GRANT ALL PRIVILEGES ON DATABASE something_db TO $POSTGRESQL_USERNAME;
ALTER DATABASE something_db OWNER TO $POSTGRESQL_USERNAME;
EOSQL
pg_restore --host "localhost" --port "5432" --username "postgres" --role "postgres" --dbname "2someone_db" --verbose /bitnami/postgresql/2someone.backup
pg_restore --host "localhost" --port "5432" --username "postgres" --role "postgres" --dbname "something_db" --verbose /bitnami/postgresql/something.backup
这样就可以实现快速复制数据库到开发环境。
总结
通过流式备份和定时备份,我们可以保证数据库的安全性,通过快速复制数据库,我们可以方便的在开发环境中使用生产环境的数据用作测试。
我们通过这种方式尽可能的保证了数据库的安全性和可用性,当然,凡是不能绝对,但愿我们不会遇到最不利工况。
这里只是简单的介绍了备份的方法,具体的备份策略还需要根据实际情况进行调整。