Linux下postgresql.service和postgresql@.service的一点观察

Catalogue

在Ubuntu下用apt安装postgresql后,一般会生成两个systemctl服务(通常位于/lib/system/systemd),一个是postgresql.service,另一个是带参数的postgresql@.service,其中参数是Postgresql的大版本号。后者可以通过systemctl start postgresql@18-main.service启动,其中18是版本号,可以换成其他安装的版本。

笔者遇到的问题是,笔者将Postgresql版本从16升级到了18(用的同样的配置同样的端口号),但想保留16一段时间以防万一。于是在升级时简单用systemctl stop postgresql@16-main.servicesystemctl stop postgresql@18-main.service简单地切换了下。但笔者的机器断电了一次,重启后一些依赖数据库的其他服务都挂了。于是进行了研究,发现开机自动重启的是16版本。

经过仔细研究,笔者发现

  • Ubuntu安装Postgresql后,注册的开机启动服务其实是postgresql.service,并不是具体指向版本的postgresql@16-main.service或者postgresql@18-main.service;
    • 查看开机启动状态systemctl is-enabled postgresql.service返回enabled, systemctl is-enabled postgresql@16-main.servicesystemctl is-enabled postgresql@18-main.service返回enabled-runtime
    • 这篇翻译官方文档的博客给出enabled-runtime是“已经通过 /run/systemd/system/ 目录下的 Alias= 别名、 .wants/ 或 .requires/ 软连接被临时启用”;
    • 实际可以发现postgresql@16-main.servicepostgresql@18-main.service均存在于/run/systemd/generator/postgresql.service.wants
  • 直接运行systemctl stop postgresql.service会尝试启动所有存在的Postgresql版本;并在/run/systemd/generator/postgresql.service.wants创建对应所有版本的postgresql@<版本号>-main.service
    • 这样因为端口冲突,16启动后18就启动失败了。

于是问题转化为如何让postgresql.service只启动18而不启动16。上网搜索和询问AI均建议删除原postgresql.service并将postgresql@18-main.service直接软连接到postgresql.service上面,但这显然违背了官方的服务管理的框架。

于是笔者在/lib/systemd下查找了下所有postgres开头的文件,果然有所发现,在/lib/systemd/system-generators下还有一个postgresql-generator文件,打开后内容如下

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
#!/bin/sh

# This systemd generator creates dependency symlinks that make all PostgreSQL
# clusters with "auto" in their start.conf file be started/stopped/reloaded
# when postgresql.service is started/stopped/reloaded.

set -eu

gendir="$1"
wantdir="$1/postgresql.service.wants"
bindir="/usr/lib/postgresql/"
#redhat# bindir="/usr/pgsql-"
pgservice="/lib/systemd/system/postgresql@.service"

mkdir -p "$wantdir"

for conf in /etc/postgresql/*/*/postgresql.conf; do
# abort loop if glob was not expanded (but accept dead symlinks)
if ! test -e "$conf" && ! test -L "$conf"; then continue; fi

dir="${conf%/*}"

# evaluate start.conf
if [ -e "$dir/start.conf" ]; then
start=$(sed 's/#.*$//; /^[[:space:]]*$/d; s/^\s*//; s/\s*$//' "$dir/start.conf")
else
start=auto
fi
[ "$start" = "auto" ] || continue

verdir="${dir%/*}"
version="${verdir##*/}"
test -x "$bindir$version/bin/postgres" || continue # package got removed
cluster="${dir##*/}"
ln -s "$pgservice" "$wantdir/postgresql@$version-$cluster.service"
done

exit 0

可见在/run/systemd/generator/postgresql.service.wants生成启动服务是根据Postgresql配置文件里面的start.conf决定的。于是官方的解决方法应该是把16版本配置文件目录/etc/postgresql/16/main里面的start.conf里面的内容从auto改为manual(注释中有给出内容的选项和含义)。