今天分享一下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
这样就可以实现快速复制数据库到开发环境。
总结
通过流式备份和定时备份,我们可以保证数据库的安全性,通过快速复制数据库,我们可以方便的在开发环境中使用生产环境的数据用作测试。
我们通过这种方式尽可能的保证了数据库的安全性和可用性,当然,凡是不能绝对,但愿我们不会遇到最不利工况。
这里只是简单的介绍了备份的方法,具体的备份策略还需要根据实际情况进行调整。