0%

更新 Docker 运行的 PostgreSQL 实例的版本

背景

服务器的 PostgreSQL 实例通过 docker-compose 运行在 docker 环境中, 使用的官方镜像;

PostgreSQL 发布了新的主要的版本, 兴冲冲的修改镜像准备升级, 运行后报错:

2023-10-11 16:34:29.913 CST [1] FATAL:  database files are incompatible with server
2023-10-11 16:34:29.913 CST [1] DETAIL:  The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 16.0 (Debian 16.0-1.pgdg120+1).

这是 PostgreSQL 16 发布时候的时候的报错.

postgres-1  | 2024-10-10 01:57:26.786 UTC [1] FATAL:  database files are incompatible with server
postgres-1  | 2024-10-10 01:57:26.786 UTC [1] DETAIL:  The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.0 (Debian 17.0-1.pgdg120+1).

这是 PostgreSQL 17 发布时候的时候的报错.

原因是 PostgreSQL 数据库数据不同主要版本之间不兼容, 需要手动升级数据.

解决办法

阅读官方文档, 说可以使用 pg_dumpallpg_upgrade 工具进行处理:

  • pg_dumpall
    pg_dumpallPostgreSQL 的转储工具, 通过再老版本转储后再新版本导入的方式可以实现数据升级;
  • pg_upgrade
    pg_upgradePostgreSQL 的专用升级工具, 大量数据的情况下比 pg_dumpall 性能更好; 本文使用此工具进行操作;

升级数据

步骤说明

pg_upgrade 命令需要传入至少 4 个参数:

pg_upgrade -b oldbindir [-B newbindir] -d oldconfigdir -D newconfigdir [option...]
  • -b: 旧版本的 PostgreSQL 程序文件目录;
  • -B: 新版本的 PostgreSQL 程序文件目录;
  • -d: 旧版本的数据目录;
  • -D: 新版本的数据目录;

整个操作过程需要准备以下材料:

  1. 老版本的 PostgreSQL 数据目录;
  2. 老版本的 PostgreSQL 程序文件目录;
  3. 新版本的 PostgreSQL 数据目录;
  4. 新版本的 PostgreSQL 程序文件目录;

由于使用的是 docker 容器, 因此数据目录通过挂载不同的本地文件夹到容器中即可; 程序文件则通过 pull 不同版本的镜像即可得到.

下面开始操作.

操作前务必停止正在运行的实例, 否则可能导致数据损坏!!!

准备数据目录

# sudo rm -rf ~/data/postgres/data-old/
sudo cp -rf ~/data/postgres/data/ ~/data/postgres/data-backup/
sudo mv ~/data/postgres/data/ ~/data/postgres/data-old/
sudo mkdir -p ~/data/postgres/data/

直接将老版本的数据目录重命名, 并创建一个新的目录存放新版本数据库数据.
操作数据库事关重大, 请务必备份!!!

准备程序文件

docker pull postgres:16
docker pull postgres:17

将当前版本和新版本的 docker 镜像 pull 下来, 以便提取程序文件.

docker run --rm -ti -v ~/data/postgres/upgrade/:/upgrade/ postgres:16 bash

启动老版本的 PostgreSQL 容器, 并挂载一个本地目录用以存放老版本程序数据以便后续升级使用;
镜像版本只要主要版本号一致都能用, 比如 16 / 16.4; 但最好和数据文件保持一致;

mkdir -p /upgrade/16/
cp -rf /usr/lib/postgresql/16/ /upgrade/16/lib/
cp -rf /usr/share/postgresql/16/ /upgrade/16/share/

将老版本的程序文件拷贝到本地目录中;

# 检查一下文件
ls -lh /upgrade/
ls -lh /upgrade/16/
ls -lh /upgrade/16/lib/
ls -lh /upgrade/16/share/
# 完事退出容器
exit

拷贝完之后检查一下, 正常会有低版本的文件夹.

升级

docker run --rm -ti \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=123456 \
-v ~/data/postgres/data/:/var/lib/postgresql/data/ \
postgres:17

首先需要完成对新数据目录的初始化, 启动这个容器快速完成.
打印 PostgreSQL init process complete; ready for start up. 这个之后就可以退出了.
这里的初始密码只是临时使用, 在数据升级完成之后会变成老数据的值.

docker run --rm -ti \
-v ~/data/postgres/upgrade/16/lib/:/usr/lib/postgresql/16/ \
-v ~/data/postgres/upgrade/16/share/:/usr/share/postgresql/16/ \
-v ~/data/postgres/data-old/:/var/lib/postgresql/data-old/ \
-v ~/data/postgres/data/:/var/lib/postgresql/data/ \
postgres:17 bash

挂载老版本程序目录 / 老版本数据目录 / 新版本数据目录准备升级;

# 先切到 postgres 用户, 并进入有写权限的目录, pg_upgrade 不允许以 root 用户执行
su postgres
cd ~
# 检查
pg_upgrade -b /usr/lib/postgresql/16/bin/ -B /usr/lib/postgresql/17/bin/ -d /var/lib/postgresql/data-old/ -D /var/lib/postgresql/data/ -c
# 升级
pg_upgrade -b /usr/lib/postgresql/16/bin/ -B /usr/lib/postgresql/17/bin/ -d /var/lib/postgresql/data-old/ -D /var/lib/postgresql/data/
# 完事退出容器
exit

pg_upgrade 检查和真正执行完会有一堆的 ok 表示没有异常, 如果出现错误请通过日志分析.

清理数据

sudo rm -rf ~/data/postgres/data-old/
sudo rm -rf ~/data/postgres/data-backup/
sudo rm -rf ~/data/postgres/upgrade/

可选清理数据.

参考: