PostgreSQL 的大版本之间,数据库的结构以及元数据保存方式可能会有一些区别,所以不能直接把数据拷过去用,直接把 volume 挂载到新的 container 里当然也是不行的。
官方提供了 pg_upgrade 命令,它要求你至少提供这四样东西:
- 旧版本的
psql命令 - 新版本的
psql命令 - 旧版数据库
- 用新版本命令初始化的数据库
但在 container 里,我们通常只有一个版本的 PostgresSQL 命令,也不方便临时安装其他版本的 PostgreSQL。软件工程师 Thomas Bandt 在他的博客里介绍了升级的方法,我按他的方法从 v16 升级到 v18 获得了成功。
我的环境是群晖的 NAS 系统,可以 SSH 进去使用 sudo docker 命令。下面的步骤假设使用官方/默认版本的镜像 postgres
备份数据库
我已经把数据和配置文件都备份到云上了,如果之前没有备份过的话,可以打开当前运行数据库的一个终端,用 pg_dumpall 备份。
建立新数据库的文件夹
PostgreSQL 17 之后,要求镜像的用户把数据挂载到容器的 /var/lib/postgresql 这个路径,PostgreSQL 在 ${VER}/docker 这个子路径里找数据库文件,也就是说数据库文件所在的完整路径是 /var/lib/postgresql/${VER}/docker。所以,先要给新版数据库建立对应的文件夹。
初始化新版数据库
拉取新版本的镜像,然后进入对应 /var/lib/postgresql 这个挂载点的本地路径,也就是如果你要升级到 v18 的话,当前路径下有 18/docker 这样的子路径。
docker run -it -v .:/var/lib/postgresql postgres:18 /bin/bash # 换成适合你的镜像 tag
$ su postgres # 切换用户
> cd /var/lib/postgresql/18/docker
> initdb -U wordpress -W # 假设数据库的 superuser 叫 wordpress
> pg_ctl -U wordpress start
# 可选,给数据库改名
> psql -U wordpress -d template1 # 不能给当前数据库改名
psql> ALTER DATABASE postgres RENAME TO wordpress;
psql> \q
> pg_ctl -U wordpress stop
> exit
$ exit # 退出和停止容器
导出旧版数据库
用旧版镜像启动一个容器,挂载原来对应 /var/lib/postgresql/data 也就是旧版数据库的路径。
docker run -it -v .:/var/lib/postgresql/data postgres:16 /bin/bash
$ su postgres
> cd /var/lib/postgresql/data
> pg_ctl -U wordpress start # 如果之前使用的容器已经停掉
> pg_dumpall -U wordpress > upgrade_backup.sql # 注意保存在对应持久化存储的位置
> pg_ctl -U wordpress stop
> exit
$ exit
处理导出文件
把下面脚本保存为 pg_extract.sh:
#!/bin/bash
[ $# -lt 2 ] && { echo "Usage: $0 <postgresql dump> <dbname>"; exit 1; }
sed "/connect.*$2/,\$!d" $1 | sed "/PostgreSQL database dump complete/,\$d"
在旧版或新版容器中运行,还是注意文件挂载位置和新文件保存位置:
./pg_extract.sh upgrade_backup.sql wordpress >> upgrade_backup_mydb.sql
导入数据
退出旧版容器,进入新版容器,还是切换成 postgres 用户,并启动数据库。
> cd /var/lib/postgresql # 假设导出 SQL 文件保存的位置对应这个路径
> cat upgrade_backup_mydb.sql | psql -U wordpress
...
> pg_ctl -U wordpress stop
> exit
$ exit
重启数据库和应用
cd /volume1/docker/wordpress
sudo docker-compose up -d
或者在 Web 管理界面用 Container Manager 重启项目也可。