用 Docker 把 PostgreSQL 数据库从 9.4 升级到 9.5

杯具是如何开始的

由于使用 Atlassian 产品的缘故,一直在服务器上跑着一个 PostgreSQL。之前在配置服务的时候,直接用的是 latest 标签的镜像,然后手贱运行了 docker pull postgres 升级了 Docker 镜像。然后……然后……服务器就挂了,跑不起来了(懵逼脸)。看了一下,原来最新的官方 PostgreSQL 已经是 9.5 了,而我当时 pull 下来的 latest 实际上是 9.4。这个事情教育我们,生产环境的镜像最好使用版本号 tag 而不要偷懒使用 latest。

既然杯具已经发生了,那么该解决的问题还是要解决的。看了一下,PostgreSQL 官方的升级建议是使用 pg_upgrade 来处理新旧数据库,但是有个棘手的问题,就是必须同时有 9.4 和 9.5 两个版本的 PostgreSQL,否则无法升级数据库。

准备工作

因为我是使用 -v 来映射目录的,所以不用 --volumes-from 的方式来加载 Volume。因此,简单处理一下目录,把所有的 containers 都先 stop 掉,然后把待升级的数据库目录改个名,同时新建一个同名目录用于保存升级后的数据。

1
2
3
4
mv postgres postgres_old
mkdir postgres
chown 999.999 postgres
chmod 700 postgres

其中用户 999.999 对应的是 container 里面的用户 postgres:postgres

接下来就是用新版本的 PostgreSQL 来初始化数据库,以便后续数据库升级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
$ docker run --name="postgres_new" -v /path/to/postgres:/var/lib/postgresql/data -ti --rm postgres:9.5
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.utf8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /var/lib/postgresql/data ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting dynamic shared memory implementation ... posix
creating configuration files ... ok
creating template1 database in /var/lib/postgresql/data/base/1 ... ok
initializing pg_authid ... ok
initializing dependencies ... ok
creating system views ... ok
loading system objects' descriptions ... ok
creating collations ... ok
creating conversions ... ok
creating dictionaries ... ok
setting privileges on built-in objects ... ok
creating information schema ... ok
loading PL/pgSQL server-side language ... ok
vacuuming database template1 ... ok
copying template1 to template0 ... ok
copying template1 to postgres ... ok
syncing data to disk ... ok

WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

pg_ctl -D /var/lib/postgresql/data -l logfile start

****************************************************
WARNING: No password has been set for the database.
This will allow anyone with access to the
Postgres port to access your database. In
Docker's default configuration, this is
effectively any other container on the same
system.

Use "-e POSTGRES_PASSWORD=password" to set
it in "docker run".
****************************************************
waiting for server to start....LOG: database system was shut down at 2016-08-14 15:49:36 UTC
LOG: MultiXact member wraparound protections are now enabled
LOG: database system is ready to accept connections
LOG: autovacuum launcher started
done
server started
ALTER ROLE


/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*

LOG: received fast shutdown request
LOG: aborting any active transactions
waiting for server to shut down...LOG: autovacuum launcher shutting down
.LOG: shutting down
LOG: database system is shut down
done
server stopped

PostgreSQL init process complete; ready for start up.

LOG: database system was shut down at 2016-08-14 15:49:40 UTC
LOG: MultiXact member wraparound protections are now enabled
LOG: database system is ready to accept connections
LOG: autovacuum launcher started
^C
LOG: received fast shutdown request
LOG: aborting any active transactions
LOG: autovacuum launcher shutting down
LOG: shutting down
LOG: database system is shut down

这样,我们就得到了一个新的 9.5 版本的初始化数据库。

升级数据库

首先从待升级的 container 获得 9.4 版本 PostgreSQL 的运行文件和共享文件:

1
2
docker cp postgres_old:/usr/lib/postgresql/9.4 .
docker cp postgres_old:/usr/share/postgresql/9.4 9.4s

然后创建一个临时的 container 来升级数据库

1
$ docker run --name="tmp" -v $(pwd)/9.4:/usr/lib/postgresql/9.4 -v $(pwd)/9.4s:/usr/share/postgresql/9.4 -v /path/to/postgres:/data/new -v /path/to/postgres_old:/data/old -ti --rm postgres:9.5 bash
  • /path/to/postgres 是新数据库的路径
  • /path/to/postgres_old 是 9.4 版本的旧数据库的路径

在 container 中运行 su - postgres 把用户切换成 postgres,然后运行如下命令来升级数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
$ cd /tmp
$ /usr/lib/postgresql/9.5/bin/pg_upgrade -b /usr/lib/postgresql/9.4/bin -B /usr/lib/postgresql/9.5/bin -d /data/old -D /data/new
Performing Consistency Checks
-----------------------------
Checking cluster versions ok
Checking database user is the install user ok
Checking database connection settings ok
Checking for prepared transactions ok
Checking for reg* system OID user data types ok
Checking for contrib/isn with bigint-passing mismatch ok
Creating dump of global objects ok
Creating dump of database schemas
ok
Checking for presence of required libraries ok
Checking database user is the install user ok
Checking for prepared transactions ok

If pg_upgrade fails after this point, you must re-initdb the
new cluster before continuing.

Performing Upgrade
------------------
Analyzing all rows in the new cluster ok
Freezing all rows on the new cluster ok
Deleting files from new pg_clog ok
Copying old pg_clog to new server ok
Setting next transaction ID and epoch for new cluster ok
Deleting files from new pg_multixact/offsets ok
Copying old pg_multixact/offsets to new server ok
Deleting files from new pg_multixact/members ok
Copying old pg_multixact/members to new server ok
Setting next multixact ID and offset for new cluster ok
Resetting WAL archives ok
Setting frozenxid and minmxid counters in new cluster ok
Restoring global objects in the new cluster ok
Restoring database schemas in the new cluster
ok
Copying user relation files
ok
Setting next OID for new cluster ok
Sync data directory to disk ok
Creating script to analyze new cluster ok
Creating script to delete old cluster ok

Upgrade Complete
----------------
Optimizer statistics are not transferred by pg_upgrade so,
once you start the new server, consider running:
./analyze_new_cluster.sh

Running this script will delete the old cluster's data files:
./delete_old_cluster.sh

好了,经过上述几步,数据库就升级完毕了,接下来重启各种服务,完美!