浅析systemd管理postgres
背景
高可用能力是公认的数据库必备能力,也是一款数据库产品的核心竞争力之一。资源充沛的时候,我们可以配置一主一备或者一主多备,通过主备复制同步数据,实现故障场景的快速切换。再结合代理,能够自动实现读写均衡和对主节点的跟踪,这似乎是一个较为完美的高可用方案。
但是,切换不是一个简单的动作,对于pg来说还要维护新的流复制关系。一主多备场景下,主节点的切换需要每个备库重新配置primary_info等参数并重启。主备存在延迟情况下,切换还会丢失数据。虽然可以配置强同步复制模式,但是对性能影响较大,同时备库挂掉还会影响主库可用。另外,虽然备库可以作为只读节点提供服务,但是由于复制延迟等原因,难以保证全局数据一致性,只读节点的利用率很难提升,浪费了很多资源。更极端的场景下,有时候受条件所迫只有一台主机,那主备切换的高可用方案就毫无用武之地了。
综上所述,主备切换不是完美的方案,给pg的运维增加了很多难度,如果能在故障场景下不需要切换就快速原地恢复数据库,那无疑是更好的选择。
自然地我们想到了用一个守护进程来管理postgres,能在postgres主进程退出的时候重新拉起数据库,最好能在机器重启之后也能把数据库自动启动。 systemd 就是其中一个方案。
systemd
systemd是Linux的系统工具,它的设计目标是为系统的启动和管理提供一套完整的解决方案。 根据 Linux 惯例,字母d是守护进程(daemon)的缩写。 Systemd 这个名字的含义,就是它要守护整个系统。为了用systemd管理postgres,我们可以完成以下几步:
Step 1: 创建 **systemd**
单元文件
首先,创建一个新的 systemd
单元文件。这个文件通常会被放在 /etc/systemd/system
目录下。
sudo nano /etc/systemd/system/postgresql.service
文件中添加以下内容:
[Unit]
Description=PostgreSQLdatabaseserver
After=network.target
[Service]
Type=forking
User=postgres
Group=postgres
# Environment variables
Environment=PGDATA=/var/lib/postgresql/data
# Commands to manage the service
ExecStart=/usr/pgsql/bin/pg_ctlstart-D${PGDATA}-l${PGDATA}/logfile
ExecStop=/usr/pgsql/bin/pg_ctlstop-D${PGDATA}-s-mfast
ExecReload=/usr/pgsql/bin/pg_ctlreload-D${PGDATA}
# Restart settings
Restart=on-failure
RestartSec=5
# Limits
LimitNOFILE=65536
LimitNPROC=65536
[Install]
WantedBy=multi-user.target
Step 2: 重新加载 **systemd**
配置
保存并关闭文件后,重新加载 systemd
配置:
sudo systemctl daemon-reload
Step 3: 启动并启用 PostgreSQL 服务
使用以下命令启动并启用 PostgreSQL 服务:
# 启动服务
sudo systemctl start postgresql
# 设置为开机自启
sudo systemctl enable postgresql
Step 4: 检查服务状态
你可以使用以下命令检查服务的状态,确保它正在运行:
sudo systemctl status postgresql
示例解释
[Unit] 部分:
Description
: 描述功能。After=network.target
: 确保网络服务启动后才启动此服务。[Service] 部分:
Type=forking
: 指定服务会在启动时派生一个新的进程。User
和Group
: 指定运行该服务的用户和组,这里设置为postgres
。Environment
: 定义环境变量,指定数据目录位置。ExecStart
: 启动服务的命令。ExecStop
: 停止服务的命令。ExecReload
: 重新加载配置的命令。Restart
和RestartSec
: 指定服务在失败后的重启策略。LimitNOFILE
和LimitNPROC
: 设置打开文件和进程的限制。[Install] 部分:
WantedBy=multi-user.target
: 指定该服务在多用户模式下启动。
这个示例假设 PostgreSQL 安装在 /usr/pgsql/bin/
目录下,数据目录为 /var/lib/postgresql/data
。请根据你的实际情况调整路径。
通过这个 systemd
配置文件,你可以使用 systemctl
命令方便地管理 PostgreSQL 服务。例如:
启动服务:
sudo systemctl start postgresql停止服务:
sudo systemctl stop postgresql重新加载配置:
sudo systemctl reload postgresql查看状态:
sudo systemctl status postgresql
缺点
systemd 将被守护的程序进程维护在其 cgroups 层级结构中,一旦进程退出可以重新拉起;但是这要求被守护的程序只能被 systemd 来启动和停止,一旦它被其他程序重启,就脱离了守护。而用户习惯使用 pg_ctl 来管理数据库,一旦用户使用 pg_ctl 停止或者重启了数据库,就会导致数据库守护状态与实际运行状态不一致,从而脱离守护。最好有一个支持通过 pid 文件(如 pg 的 postmaster.pid)来守护被托管程序的方案。(尽管 systemd 也支持指定 PIDFile,但是它不以此作为守护状态的依据,仅会在停止被守护程序的时候清理它)
因为在 docker 容器中使用 systemd 需要特权模式(privileged)启动,但是开启了 privileged 的容器可以访问宿主上所有设备(几乎享有宿主上运行的进程的所有访问权限),这显然不符合一般的安全策略规范,如果为了安全考虑,那systemd的确不是一个在主机和容器场景下通用的组件守护方案。
supervisord
此外,基于开源项目 ochinchina/supervisord也可以实现以上需求,主要优势与缺点如下:
优势
既支持如 systemd 的子进程守护,又提供了 pidproxy 工具来支持对 pid 文件中的进程号进行状态守护(兼容 postmaster.pid 格式)
使用 golang 实现,可以通过交叉编译支持多 CPU 架构/操作系统、容器中使用无需特权模式、对安装环境无依赖、体积较小。
通过对其中 supervisord 和 pidproxy 组件的修改,可以支持一键停止 supervisord 和它托管的进程、兼容
缺点
supervisord 本身没有被守护,被误杀后进程会脱离守护。
用户机器重启后程序无法自动拉起,这一点也非常致命,尤其是对于经常有重启需求但又不想手动运维的用户。
supervisor需要额外的安装,而systemd是Linux本身提供的工具。
总结
对于云上数据库来说,只要探活机制足够可靠,守护进程+主备切换是能最快恢复的办法,也能覆盖更对异常场景。毕竟大部分故障不是通过重启就能解决的,机器断电、宕机、网卡故障、磁盘写满,太多的故障场景都需要备库来容灾。云厂商一般都能提供强大的管控能力,切换后流复制、节点重建等等也都不需要使用者关注。但是对于线下小型数据库系统来说,systemd等提供的守护能力也不失为日常运维的简单有效方案。很难找到完美的高可用方案,适合自身需要的就是好方案。




