背景:因项目需求,需要基于k8s部署高可用postgres数据库。
采用方案:Patroni
Patroni介绍
patroni 是一款运用 dcs 存储集群来存储信息、主备状态与配置,通过 patroni 来检测并且实 现主备库自动切换的软件。使用一套模板化的配置文件来自动搭建初始化数据库流复制集群 以及配置数据库。
patroni 高可用集群由 postgresql,patroni,dcs 存储组成
patroni:通过参数文件来配置自动初始化数据库搭建流复制(配置 pg 参数 文件、创建用户、可以配置预加脚本),指定 etcd 节点等。负责通过一个 api 接口连接到 dcs(分布式存储系统:etcd 集群),向其
插入键值记录 patroni 参数、数据库参数、主备信息以及连接信息。平常通过 etcd 对其 它节点做心跳检测。与数据库的主备切换或者做恢复时通过向 etcd 拿取键 值中储存的主备信息来判断各节点的状态进行切换。
etcd:最少需要三个节点且为奇数来进行 leader 选举(脑裂发生时会 etcd 集群会僵死等待恢复,不会发生都认为自己是主的情况)。在各个节点上同 步健康状态信息以及数据库节点的主备状态与连接、配置信息。平常会对
其余节点做心跳检测。
组件说明:
patroni:通过参数文件来配置自动初始化数据库搭建流复制(配置 pg 参数文件、创建用户、可以配置预加脚本),指定 etcd 节点等。负责通过一个 api 接口连接到 dcs(分布式存储系统集群),向其插入键值记录 patroni
参数、数据库参数、主备信息以及连接信息。平时通过对 etcd 中的信息进行更新、读取来判断集群的健康状态。在主备切换或者做恢复时通过向etcd 读取主备信息来判断各节点的状态进行切换。(此处用etcd举例,还可以选择zookeeper、consul)
etcd最少需要三个节点且为奇数来进行 leader 选举(脑裂发生时 etcd 集群会僵死等待恢复,不会发生都认为自己是主的情况)。存储 并在各个节点上同步键值信息。
patroni工作流程图:
流程文字描述:
-
Patroni 自动创建主备流复制集群通过 api 接口往 etcd 记录键值来储存主备信息与连接 信息以及配置信息
-
Etcd 进行心跳检测(etcd 之间的心态检测)与存储键值信息
-
patroni 通过连接 etcd 对其它节点做心跳检测,每 loop_wait 秒一次
- patroni 通过连接到 etcd 集群,向其插入键值记录 patroni 参数、数据库参数、主备信 息以及连接信息。进行数据库的主备切换时通过向 etcd 拿取键值中储存的主备信息来
- 判断各节点的状态来切换。各节点会在 data 目录下生成 recovery.done(与 recovery.conf 一样,里面的 primary_conninfo 记录是上一次主节点的连接信息),原主节点发生切换
-
时自动改变后缀为 recovery.conf,原备节点会删除掉自身的 recovery.conf 文件,再通 过 pg_rewind 来快速恢复节点,不需要做基础备份。
-
异步流复制时主从之间延时:主从之间 wal 日志延时超过 maximum_lag_on_failover(byte)的大小,主备有可能会重启但不会发生切换。 数据丢失量通过 maximum_lag_on_failover,ttl,loop_wait 三个参数控制。
-
最坏的情况下的丢失量: maximum_lag_on_failover 字节+最后的 TTL 秒时间内写入的日志量(loop_wait /2 在平 均情况下)。
- haproxy+keeplived 保持对外的访问 ip 端口不变
优势:
-
自动检测主备状态进行切换、统一模板配置
-
在上图最基本的架构中,任意 down 一个 etcd 节点或者任意一个 patroni 节点、数据节点,通过转换都能使集群继续运行下去。测试中有针对于三个 etcd 网络互不通做了脑 裂测试,故障发生后 etcd 集群会僵死等待恢复,
-
不会发生都认为自己是主的情况。主 库会变成只读状态,恢复网络后,主、备继续用 etcd 的数据信息恢复到故障前的状态,etcd 也恢复正常。
-
在线添加 etcd、patroni 节点以及数据节点
-
支持同步异步流复制,级联流复制
-
异步流复制可设置最小丢失数据量
-
使用 pg_rewind 进行恢复,缩短恢复时间
- haproxy+keeplived 可以保持在主备切换或者节点故障后,实现 ip 漂移。对外的 ip+端口不变。
限制:
-
patroni 对数据库操作需要普通用户
-
需要至少三个以上且为奇数的 etcd 节点
-
底层基于的是流复制
-
大部分参数都需要通过更改 etcd 中键值来修改
- 因故障发生的连接会回滚,但是需要客户端重新发起连接
使用过程中发现的疑点:
-
使用patroni部署高可用postgres主库发生故障时,如何实现无感知恢复(迁移服务)
答:使用官网helm chart patroni部署高可用集群,DCS组件默认使用etcd分布式键值(key-value)数据库,通过 etcd 对节点服务做心跳检测。与数据库的主备切换或者做恢复时通过向 etcd 拿取键值中储存的主备信息来判断各节点的状态进行切换。patroni values.yaml 内容如下
# Distribution Configuration stores
# Please note that only one of the following stores should be enabled.
# 请注意,应该只启用以下存储中的一个,这里因为我们是基于k8s部署所以选择kubernetes中的dcs。etcd也可以单独部署,将enable改为true同时将上面dcs改为false即可。
kubernetes:
dcs:
enable: true
configmaps:
enable: false
etcd:
enable: false
deployChart: false
# If not deploying etcd chart, fill-in value for etcd service
# <service>.<namespace>.svc.cluster.local
host:
# Leave blank to use vendored etcd chart
discovery:
基于k8s部署的patroni,etcd是集成在DCS还是单独启动?为什么说是用etcd分布式键值库而不是Zookeeper或者是其他。
答:从官方的相关文档看,DCS可以是Etcd,也可以是Consul、Zookeeper、Exhibitor等等、使用不同的模块对应不同的分布式键值库。通过查看patroni的Dockerfile发现在build patroni镜像时将etcd相关安装包已经打在镜像中了,
并且进入pod查看有python的进程,所以判断DCS使用的etcd。
主库所在的pod发生故障时,该pod会被k8s杀死,那新建后这个pod的角色是什么?
答:经过多次测试,当主库pod发生故障,k8s将这个pod杀死,为了满足副本数,会新建一个pod,此时这个新建的pod不会参与竞选leader。具体竞选的策略使用etcd raft算法。相关配置在pod中postgres.yml中配置。
举例:现在有五个postgres的副本数,其中一个发生故障被杀死,在新建过程中,剩余的4个pod节点会开始竞选leader,竞选成功后为主库节点,其它pod为从库。且只有主库拥有所有权限,从库只有读权限。
DCS使用的是k8s集群的etcd。是自身的还是k8s集群的etcd?
答:经过测试调研验证DCS使用的etcd集群是k8s的etcd集群,官网解释如下:
测试过程:
pod正常情况下: etcd 数据如下,可以看到状态为running的
如果某个pod启动失败或者是服务有异常,etcd 数据如下,
基本流程:
Patroni 自动创建主备流复制集群通过 api 接口往 etcd 记录键值来储存主备信息与连接信息以及配置信息。
Etcd 进行心跳检测(etcd 之间的心态检测)与存储键值信息
patroni 通过连接 etcd 对其它节点做心跳检测,每 loop_wait 秒一次
patroni 通过连接到 etcd 集群,向其插入键值记录 patroni 参数、数据库参数、主备信 息以及连接信息。进行数据库的主备切换时通过向 etcd 拿取键值中储存的主备信息来 判断各节点的状态来切换。
各节点会在 data 目录下生成 recovery.done(与recovery.conf 一样,里面的 primary_conninfo 记录是上一次主节点的连接信息),原主节点发生切换 时自动改变后缀为 recovery.conf,原备节点会删除掉自身的 recovery.conf 文件,再通 过 pg_rewind 来快速恢复节点,不需要做基础备份。
etcdctl 工具使用方法:
#查看etcd根路径的key
etcdctl get / --prefix --keys-only=true | grep patroni
#查看某个key的value
etcdctl get /registry/events/default/test-patroni-0.165076b4d481f7d7 --prefix --keys-only=false -w=json | python -m json.tool