恢复RDS数据库至Docker容器时遇postgres角色及权限错误的解决方法
问题概述
从AWS RDS PostgreSQL导出全局角色与数据库备份后,恢复到本地Docker PostgreSQL容器时,出现角色已存在、权限不足、RDS专属对象无法创建等错误。
导出命令
全局信息导出
pg_dumpall \ --globals-only \ --no-password \ --no-role-passwords \ --encoding="${encoding}" \ --file="${backup_dirname}/${globals_filename}" \ --host="${host}" \ --port="${port}" \ --username="${username}"
数据库导出
pg_dump \ --blobs \ --create \ --no-password \ --dbname="${dbname}" \ --encoding="${encoding}" \ --file="${backup_dirname}/${dump_filename}" \ --format="directory" \ --host="${host}" \ --jobs="${workers}" \ --port="${port}" \ --username="${username}"
恢复命令(原错误版本)
docker ps -aq --filter "name=${container_name}" | grep -q . && docker stop "${container_name}" && docker rm -fv "${container_name}" docker run --detach --name "${container_name}" -p 5432:5432 -e POSTGRES_PASSWORD=mypassword postgres until docker exec -it "${container_name}" psql -U postgres -c "SELECT 1 AS working"; do sleep 1 done docker cp "globals.dump" "${container_name}:/home/globals.dump" docker cp "pg_ch_api_db" "${container_name}:/home/pg_ch_api_db" docker exec -it "${container_name}" sh -c "psql --no-password --quiet --host="${host}" --port="${port}" --username="${username}" --file=/home/globals.dump"
核心错误信息
psql:/home/globals.dump:18: ERROR: role "postgres" already exists
psql:/home/globals.dump:21: ERROR: must be superuser to alter superuser roles or change superuser attribute
psql:/home/globals.dump:202: ERROR: permission denied to create tablespace "rds_temp_tablespace"
HINT: Must be superuser to create a tablespace.
...(其他权限类错误省略)
解决方案
1. 使用超级用户执行全局备份恢复
Docker容器默认的postgres用户是超级用户,恢复全局备份时必须以该用户身份执行,而非RDS中的普通用户。修改恢复命令中的psql执行部分:
docker exec -it "${container_name}" psql --no-password -U postgres --file=/home/globals.dump
此操作将赋予足够权限修改角色、设置参数和处理表空间。
2. 过滤全局备份中的RDS专属对象
RDS生成的全局备份包含rdsadmin、rdstopmgr等RDS专属角色,以及rds_temp_tablespace专属表空间,这些对象在普通PostgreSQL环境中无需保留。在宿主机上预处理备份文件:
# 移除RDS专属角色相关语句 sed -i '/^CREATE ROLE rdsadmin/d' globals.dump sed -i '/^ALTER ROLE rdsadmin/d' globals.dump sed -i '/^CREATE ROLE rdstopmgr/d' globals.dump sed -i '/^ALTER ROLE rdstopmgr/d' globals.dump # 移除RDS专属表空间相关语句 sed -i '/^CREATE TABLESPACE rds_temp_tablespace/d' globals.dump sed -i '/^ALTER ROLE.*SET.*rds_temp_tablespace/d' globals.dump
处理完成后再将文件复制到容器中恢复。
3. 导出时排除postgres角色
如果不想事后修改备份文件,可以在导出全局信息时直接排除已存在的postgres角色:
pg_dumpall \ --globals-only \ --no-password \ --no-role-passwords \ --exclude-role=postgres \ --encoding="${encoding}" \ --file="${backup_dirname}/${globals_filename}" \ --host="${host}" \ --port="${port}" \ --username="${username}"
4. 完整修正后的恢复流程
结合上述方案,完整的恢复命令如下:
# 清理旧容器 docker ps -aq --filter "name=${container_name}" | grep -q . && docker stop "${container_name}" && docker rm -fv "${container_name}" # 启动新PostgreSQL容器 docker run --detach --name "${container_name}" -p 5432:5432 -e POSTGRES_PASSWORD=mypassword postgres # 等待容器服务就绪(静默检测) until docker exec "${container_name}" psql -U postgres -c "SELECT 1" >/dev/null 2>&1; do sleep 1 done # 预处理全局备份文件(宿主机执行) sed -i '/^CREATE ROLE rdsadmin/d' globals.dump sed -i '/^ALTER ROLE rdsadmin/d' globals.dump sed -i '/^CREATE ROLE rdstopmgr/d' globals.dump sed -i '/^ALTER ROLE rdstopmgr/d' globals.dump sed -i '/^CREATE TABLESPACE rds_temp_tablespace/d' globals.dump sed -i '/^ALTER ROLE.*SET.*rds_temp_tablespace/d' globals.dump # 复制备份文件到容器 docker cp "globals.dump" "${container_name}:/home/globals.dump" docker cp "pg_ch_api_db" "${container_name}:/home/pg_ch_api_db" # 以超级用户恢复全局角色与配置 docker exec "${container_name}" psql --no-password -U postgres --file=/home/globals.dump # 恢复数据库文件 docker exec "${container_name}" pg_restore --no-password -U postgres --dbname=postgres --format=directory /home/pg_ch_api_db
内容的提问来源于stack exchange,提问作者PirateApp

