Patroni

Patroni是从compose/governor发展而来的开源项目,旨在帮助PostgreSQL用户快速搭建一个基于Streaming Replication特性的高可用PG集群。

Patroni基于Python3开发,运行时需要一个分布式配置存储服务(称为DCS)来分发不同Patroni实例之间的配置。

目前Patroni支持以下DCS:

  • etcd or etcd3
  • consul
  • zookeeper
  • exhibitor
  • kubernetes
  • raft
  • aws

上述DCS中需要着重说明的是raft,Patroni可以基于pysyncobj库在内部通过Raft协议实现Leader选举功能,此时Patroni不在依赖外部的DCS

Configuration

Patroni本质上是Postgres的配置同步工具,所有配置项由以下三个部分组成:

  • Dynamic configuration:保存在DCS上的配置,用户可以随时修改上述配置Patroni发现配置变化后,同步修改到所有的Postgres节点。
  • Local configuration (patroni.yml):Patroni进程启动时会加载配置文件,配置文件中的配置有限级高于Dynamic configuration。Patroni进程运行过程中,用户可以通过发送SIGHUP或者调用REST API重新加载配置文件。
  • Environment configuration:Environment 配置覆盖配置文件中的配置,有最高的优先级。

出于以下考虑,Patroni将一下Postgres的配置规定为动态配置,用户只能在DCS上设置:

  • PostgreSQL某些参数必须在所有实例上保持相同的值
  • PostgreSQL不要求Master和Replicas之间配置一致,但由于可能发生主备切换

同时Partroni还接管了PostgreSQL一些配置,在postgresql.conf直接修改这些配置没有作用,包括:

  • listen_addresses:通过postgresql.listen或者PATRONI_SCOPE指定
  • port:通过postgresql.listen或者PATRONI_SCOPE指定
  • cluster_name:通过scope或者PATRONI_SCOPE指定
  • hot_standby:强制为on

上述配置Patroni在启动Postgres时通过pg_ctl start传递,因此具备最高的优先级。

每个节点有以下三个配置文件:

  • postgresql.base.conf:Partroni进程生成,每个节点可能不同,用户可以用custom_conf配置指定改文件,或者在数据库目录中创建同名文件。如果用户没有指定custom_conf或者放置同名文件,那么原生postgresql.conf配置的内容将会被写入该文件中。
  • postgresql.conf:include了postgresql.base.conf文件,并且包含DCS中的动态设置。因此该文件是所有节点都相同的。
  • postgresql.auto.conf:Postgres原生配置,用于ALTER SYSTEM操作。

postgresql.conf修改后执行 SELECT pg_reload_conf() 或者 pg_ctl reload 就可以使配置生效

每次更新DCS配置是Patroni节点还会在数据目录下生成patroni.dynamic.json文件。当DCS上的配置不可用时,Master会通过该文件恢复DCS上的配置。

RestAPI

Patroni自身包含一个REST API,用于进行PostgreSQL的Leader选举,通过patronictl工具通过REST API执行failovers/switchovers/reinitialize/restarts/reloads,用户可以通过该REST API执行监控等工作。

1.健康检查

在Master节点上,以下Endpoint返回200:

  • GET /
  • GET /master
  • GET /primary
  • GET /read-write
  • GET /standby-leader:只有在Standby Cluster集群中返回200
  • GET /leader: 当Patroni节点有leader锁时,返回HTTP状态码200,即无论PostgresSQL是否是standby cluster或者正常集群

在Replica节点上,以下Endpoint返回200:

  • GET /replica:role是replica并且没有设置noloadbalance标签
  • GET /replica?lag=:检查服务延迟,并且在低于指定值时返回200。max-lag的值可以是整数或者16kB, 64MB, 1GB
  • GET /replica?tag_key1=value1&tag_key2=value2:检查replica的同时检查节点的label是否存在,不满足条件时返回503。PS:Master相关的状态检查不支持tag_key

其他Endpoint:

  • GET /read-only
  • GET /synchronous或者GET /sync:判断集群是否运行在synchronous状态
  • GET /asynchronous或者GET /async:判断集群是否运行在asynchronous状态
  • GET /asynchronous?lag=:类似GET /replica?lag=
  • GET /health:PostgreSQL是否启动并运行
  • GET /liveness:Patroni启动并运行
  • GET /readiness:Patroni 节点作为领导者运行或 PostgreSQL 启动并运行时,返回 HTTP 状态代码 200。

2. 监控相关

Patroni虽然提供比较丰富的监控信息采集接口,但是其返回结果是json格式的,Promethues无法直接scrap。

为了和Promethues更好适配,官方提供了metrics端点,但是能获取的信息有限。

第三方服务:patroni-exporter

官方返回JSON采集信息的API参考:https://patroni.readthedocs.io/en/latest/rest_api.html#config-endpoint

3. 控制面API

主备切换

API允许通过leader/candidate参数,将将节点配置切换为Master/Replicas,包括:

POST /switchover:指定节点健康的状态下,Master立即/定时释放Lock, 下面请求中rccp-tiny-1是Master, 执行命令后rccp-tiny-1将释放Leader Lock。

1
curl -s http://localhost:8008/switchover -XPOST -d '{"leader":"rccp-tiny-1","scheduled_at":"2021-12-20T11:20+08"}'

不指定scheduled_at时,会生成一个同步请求立即发生切换。

1
curl -s http://localhost:8008/switchover -XPOST -d '{"leader":"rccp-tiny-1"}'

立即释放leader,并且指定候选人为rccp-tiny-2,即rccp-tiny-1和rccp-tiny-2角色互换

1
curl -s http://localhost:8008/switchover -XPOST -d '{"leader":"rccp-tiny-1","candidate参数":"rccp-tiny-2"}'

通过DELETE /switchover可以清除节点切换调度。

POST /failover:允许执行手工立即执行一次主备切换,此时Master节点不是健康状态的。

1
curl -s http://localhost:8009/failover -XPOST -d '{"candidate":"rccp-tiny-0"}'

Restart endpoint

POST /restart 可以用于重启Postgres服务,请求接受一个JSON Body参数如下:

  • restart_pending:只有当PostgresSQL的配置文件变更,且需要重启生效时重启。
  • role:指定重启的角色
  • postgres_version:当postgres版本低于指定时才会重启
  • timeout:请求被相应的超时时间
  • schedule:重启调度时间

重启Master配置文件变更时重启Master节点:

1
2
3
4
5
{
  "restart_pending":true,
  "role": "Master"
  
}

Reload endpoint

  • POST /reload:更新配置文件执行pg_ctl reload,相当于向 Patroni 进程发送 SIGHUP 信号。
  • POST /reinitialize:只允许在replicas上执行,此时会删除节点的数据目录,并开始如pg_basebackup等类似操作。

Replication-modes

参考:https://patroni.readthedocs.io/en/latest/replication_modes.html#replication-modes

Watchdog

为了避免裂脑Patroni需要确保PostgreSQL在DCS中的Leader Lock过期后不会接受任何事务。在正常情况下,如果主备切换失败,Patroni会尝试通过停止PostgreSQL服务避免出现多个Master。

但是以下情况依然可能发生:

  • Patroni意外退出
  • PostgreSQL没有及时关闭

Patroni为了避免上述情况,提供了Watchdog能力。

Linux软件Watchdog:

Linux内核模块自带了watchdog实现,在程序通过/dev/watchdog设备与watchdog交互。 用户空间程序一旦打开/dev/watchdog,会导致在内核中启动一个1分钟的定时器(系统默认时间),此后用户空间程序需要保证在1分钟之内向这个设备写入数据,每次写操作会导致重新设定定时器。 如果用户空间程序在1分钟之内没有写操作,定时器到期会导致一次系统 reboot 操作。

相关配置如下:

1
2
3
4
watchdog:
  mode: automatic # Allowed values: off, automatic, required
  device: /dev/watchdog
  safety_margin: 5

当Patroni将节点提升为Master前,会同步启动该节点的watchdog。默认配置中,watchdog的投喂时间为5s

如果启动Master失败,并且watchdog.mode为required,那么该节点不能被升级成Master。当Patroni将Master降级时,会禁用该节点的watchdog。

参考:https://patroni.readthedocs.io/en/latest/watchdog.html

DCS为Kubernetes时,建议不要启动watchdog功能!

Pause/Resume

Patroni允许在维护场景下,暂时将PostgreSQL和Patroni分离,同时仍保留DCS中的集群状态再需要时重新接管PostgreSQL集群。

pause 命令:

1
2
3
4
5
6
7
8
patronictl pause --help
Usage: patronictl pause [OPTIONS] [CLUSTER_NAME]

  Disable auto failover

Options:
  --wait  Wait until pause is applied on all nodes
  --help  Show this message and exit.

resume 命令:

1
2
3
4
5
6
7
8
patronictl resume --help
Usage: patronictl resume [OPTIONS] [CLUSTER_NAME]

  Resume auto failover

Options:
  --wait  Wait until pause is cleared on all nodes
  --help  Show this message and exit.

当Pause状态下,patroni的行为参考以下链接。

参考:https://patroni.readthedocs.io/en/latest/pause.html#the-implementation

参考

Patroni配置文件说明

Patroni环境变量配置说明