本文主要从以下几个方面介绍PostgreSQL高可用集群在360的落地实战
背景介绍
为什么选择Patroni + Etcd + PostgreSQL高可用集群方案
Patroni + Etcd 高可用集群架构解析
Patroni + Etcd + PostgreSQL 部署实战
Patroni 日常运维管理
PostgreSQL 监控实现
PostgreSQL 应用连接方式
PostgreSQL 备份恢复方式选择
背景
最近线上重要业务容器云的镜像仓库需要部署一套postgresql 高可用集群,涉及到数据库选型,最终选择了postgresql,为什么不选择mysql呢,postgresql是功能最强大的开源数据库,主要考虑pg 支持使用函数索引和条件索引,text 没有限制,可以索引,还可以全文检索,不用再接一套es,并且postgresql 开源协议好,开源软件原生支持好,特别对开发来说比较友好,最重要的是kube-bench 只支持postgresql
为什么选择Patroni + Etcd 方案
PostgreSQL 比较流行的高可用解决方案有很多,常用的主要包含repmgr和patroni等,也是github star前几的高可用组件,并且文档更新比较及时,都可最高支持postgresql 13,repmgr 相对来说功能没有patroni全面以及不能检测备机是否被错误配置为未知或不存在的节点、不能检测远程节点的状态(不具备分布式解决方案)和不能处理单个节点的恢复,本文主要基于patroni 实现。
此方案使用Patroni管理本地库,并结合Etcd作为数据存储和主节点选举,具有以下优势
o健壮性: 使用分布式key-value数据库作为数据存储,主节点故障时进行主节点重新选举,具有很强的健壮性
o支持多种复制方式: 基于内置流复制,支持同步流复制、异步流复制、级联复制
o支持主备延迟设置: 可以设置备库延迟主库WAL的字节数,当备库延迟大于指定值时不做故障切换
o自动化程度高:
①支持自动化初始PostgreSQL实例并部署流复制
②当备库实例关闭后,支持自动拉起
③当主库实例关闭后,首先会尝试自动拉起
④支持switchover命令,能自动将老的主库进行角色转换
o避免脑裂: 数据库信息记录到 ETCD 中,通过优化部署策略(多机房部署、增加实例数)可以避免脑裂
Patroni + Etcd 高可用架构
Patroni 是一个开源工具套件,它是用 Python编写的,可确保 PostgreSQL HA 集群的端到端设置,包括流复制。它的功能通过REST API显示,也通过一个名为 Patronictl 的命令行实用程序显示。它通过使用其运行状况检查API来处理负载平衡来支持与 HAProxy 的集成。在此 HA 解决方案中,etcd 用于分布式配置存储 (DCS),以实现最大的可访问性,下面是官方高可用集群方案的架构图展示,我们基于此完成postgresql 高可用架构集群方案设计,不过并没有使用HAproxy 进行负载均衡,而是使用公司内部的LVS 来实现的,一主两副本,副本部署在不同的idc 实现异地灾备,etcd 也是三节点集群分别部署在三台机器, 如果资源有限,也可以和postgresql/patroni部署在相同机器
Etcd、Patroni 和PostgreSQL是如何一起工作的
etcd/patroni/postgresql 都是部署的3节点集群
Etcd: 分布式的Key-Value数据库
etcd1、etcd2、 etcd3作为分布式的Key-Value数据库,被partroni1、 patroni2、 patroni3读/写,用于共享/传递信息。每一个 Patroni都能读/写etcd中的数据.
Paroni: 控制/监控本地的PostgreSQL, 把本地PostgreSQL信息/状态写入etcd
每一个 Patroni实例监控/控制本地的PostgreSQL,把本地本地PostgreSQL信息/状态写入etcd , 一个Patroni实例能够通过读取etcd获取外地PostgreSQL的信息/状态.
PostreSQL主节点的选举
Patroni判断本地PostgreSQL是否可以作为Primary库。如果可以,Paroni试图选举本地PostgreSQL作为Primary(Leader) , 选举方式是:把etcd中的某个key更新成为本地PostgreSQL的名字, 如果多个Paroni同时更改同一个key,只有一个能改成功,然后成为Primary(Leader)。
部署篇
系统/软件/版本
CentOS 7.4
PostgreSQL 12.6
etcd: 3.2.18
python: Python 3.6.5
Patroni: 2.1.0
主机信息
10.16.75.17 pg12/patroni
10.16.75.15 pg12/patroni
10.16.78.53 pg12/patroni
10.24.13.9 etcd
10.24.13.10 etcd
10.24.13.11 etcd
这里用了6台机器,也可以用3台机器组件全部安装在一起
Python3 安装
# 安装依赖
yum install wget gcc make zlib-devel openssl openssl-devel
wget "https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz"
tar -xvJf Python-3.6.5.tar.xz
# 编译
cd Python-3.6.5 && ./configure prefix=/usr/local/python3
make && make install
ln -fs /usr/local/python3/bin/python3 /usr/bin/python3
ln -fs /usr/local/python3/bin/pip3 /usr/bin/pip3
# virtualenv
pip3 install virtualenv -i https://mirrors.ustc.edu.cn/pypi/web/simple/
ln -fs /usr/local/python3/bin/virtualenv /usr/bin/virtualenv
# 编译安装python的使用
cd /data04 && virtualenv venv4archery --python=python3
# 切换python运行环境到虚拟环境
source venv4archery/bin/activate
创建PG目录和用户
useradd postgres #编译安装的,yum 安装不用创建
mkdir -p /data04/pg15432
mkdir -p /data04/pg15432/data
mkdir -p /data04/pg15432/scripts
chown -R postgres:postgres /data04/pg15432/
创建归档目录
mkdir -p /data04/pg15432/arch
源码安装postgresql12
yum -y install -y readline-devel
mkdir -p /usr/local/pgsql/
./configure --prefix=/usr/local/pgsql
make && make install
cp /usr/local/pgsql/bin/* /sbin/
Etcd 部署
yum install etcd -y
编辑/etc/etcd/etcd.conf 配置文件
Etcd node1配置
ETCD_DATA_DIR="/var/lib/etcd/codis.etcd"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_NAME="node1"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.24.13.9:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://10.24.13.9:2379"
ETCD_INITIAL_CLUSTER="node1=http://10.24.13.9:2380,node2=http://10.24.13.10:2380,node3=http://10.24.13.11:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
Etcd node2 配置
ETCD_DATA_DIR="/var/lib/etcd/codis.etcd"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_NAME="node2"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.24.13.10:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://10.24.13.10:2379"
ETCD_INITIAL_CLUSTER="node1=http://10.24.13.9:2380,node2=http://10.24.13.10:2380,node3=http://10.24.13.11:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
Etcd node3 配置
ETCD_DATA_DIR="/var/lib/etcd/codis.etcd"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_NAME="node3"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.24.13.11:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://10.24.13.11:2379"
ETCD_INITIAL_CLUSTER="node1=http://10.24.13.9:2380,node2=http://10.24.13.10:2380,node3=http://10.24.13.11:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
#注意etcdnode1 每个节点都不一样,etcd-cluster-1 是集群名字统一
systemctl daemon-reload
systemctl enable etcd
systemctl start etcd
Etcd 成员列表显示
etcdctl member list
Etcd集群健康检查
etcdctl --endpoints http://10.24.13.9:2379 cluster-health
Patronic 部署
cd /data04 && virtualenv venv4archery --python=python3
source venv4archery/bin/activate
pip3 install psycopg2-binary -i https://mirrors.aliyun.com/pypi/simple/
或者pip3 install psycopg2>=2.5.4 -i https://mirrors.aliyun.com/pypi/simple/
pip3 install patroni[etcd,consul,zookeeper,kubernetes] -i https://mirrors.aliyun.com/pypi/simple/
#启动patroni集群需非root用户
集群进行初始化(所有节点执行)
patroni /etc/patroni.yml > patroni_member_1.log 2>&1 &
可以使用centos7的service启动:
[Unit]
Description=Runners to orchestrate a high-availability PostgreSQL
After=syslog.target network.target
[Service]
Type=simple
User=postgres
Group=postgres
#StandardOutput=syslog
ExecStart=/sbin/patroni /etc/patroni.yml
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=process
TimeoutSec=30
Restart=no
[Install]
WantedBy=multi-user.target
/etc/patroni.yml (可根据自己需求定制化)
scope: postgresql
namespace: /service/
name: postgresql1
restapi:
listen: 10.16.75.17:8008
connect_address: 10.16.75.17:8008
etcd:
host: 10.24.13.9:2379
host: 10.24.13.10:2379
host: 10.24.13.11:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
listen_addresses: "0.0.0.0"
wal_level: logical
archive_mode: "on"
max_connections: 6000
Shared_buffers: 32GB
archive_command: 'DATE=`date +%Y%m%d`;DIR="/data04/pg15432/arch/$DATE";(test -d $DIR || mkdir -p $DIR)&& cp %p $DIR/%f'
hot_standby: "on"
wal_keep_segments: 100
max_wal_senders: 10
max_replication_slots: 10
wal_log_hints: "on"
initdb:
- encoding: UTF8
- data-checksums
pg_hba:
- host replication repl 127.0.0.1/32 md5
- host replication repl 10.16.75.17/0 md5
- host replication repl 10.16.75.15/0 md5
- host replication repl 10.206.87.218/0 md5
- host replication repl 0.0.0.0/0 md5
- host all all 0.0.0.0/0 md5
users:
admin:
password: admin
options:
- createrole
- createdb
postgresql:
listen: 10.16.75.17:15432
connect_address: 10.16.75.17:15432
bin_dir: /usr/local/pgsql/bin
data_dir: /data04/pg15432/data
pgpass: /tmp/pgpass1
authentication:
replication:
username: repl
password: "1a23s6c54f"
superuser:
username: postgres
password: "59687411134be622"
parameters:
unix_socket_directories: '.'
synchronous_commit: "on"
synchronous_standby_names: "*"
tags:
nofailover: false
noloadbalance: false
clonefrom: false
nosync: false
重点关注上面标红的参数设置,如果初始化未设置也没关系,部署完成一定验证下重要参数设置,未设置的可以通过patronictl 进行设置
PG集群信息查看
数据库账户创建
CREATE USER docker WITH PASSWORD 'xxxx';
CREATE DATABASE docker OWNER docker;
GRANT ALL PRIVILEGES ON DATABASE docker to docker;
远程连接数据库
psql -U docker -d docker -p xxxx-h xxxx
尝试对主节点进行读写
以上完成对整个集群的部署并确保数据库可以正常进行读写
Patronictl 日常运维
参数设置查看
shared_buffers
类似mysql innodb_buffer_pool_size,该参数主要设置数据库服务器将使用的共享内存缓冲区量,建议是设置系统内存的25%,过高也会造成一些工作负载,还是需要跟业务沟通获取预估的数据量以及估算热数据量来针对性制定参数值
修改shared_buffers
patronictl -c /etc/patroni.yml edit-config -p "shared_buffers='32GB'"
patronictl -c /etc/patroni.yml restart postgresql (重启生效)
max_connections
决定数据库的最大并发连接数。默认值通常是 100 个连接,但是如果内核设置不支持(initdb时决定),可能会比这个 数少。这个参数只能在服务器启动时设置。
当运行一个后备服务器时,你必须设置这个参数等于或大于主服务器上的参数。否则,后备服务器上可能无法允许查询
修改max_connections
patronictl -c /etc/patroni.yml edit-config -p 'max_connections=6000'
patronictl -c /etc/patroni.yml restart postgresql (重启生效)
查看当前归档日志
select pg_walfile_name(pg_current_wal_lsn());
手动切换归档
select pg_walfile_name(pg_current_wal_lsn());
可以看到归档已经由...0000B 切换到...0000C
设置开启慢日志
阈值为200ms
patronictl -c /etc/patroni.yml edit-config -p 'log_min_duration_statement=200'
查看慢日志设置阈值
高可用故障转移测试
Kill 主节点进程
会发现patroni 会自动拉起postgresql进程,并且还是主节点,并未进行切主
手工切主
假设切主到postgresql2 155节点
patronictl -d etcd://10.16.75.17:2379 switchover postgresql
切主期间会出现中间状态unknown,瞬间会恢复正常如下,切主成功到155
重启集群
可看到主节点也是没切主,拓扑和以前是一样的
主/从节点机器重启
机器宕机是常有的事情,当postgresql 某个节点所在机器宕机恢复后,需要手动拉起节点
patroni /etc/patroni.yml > patroni_member_1.log 2>&1 &
禁用开启故障转移
当pause 后,这时候集群是不会进行自动故障转移的,可以在某些特定场景使用,然后用resume 恢复就可以了
还有其它一些功能可以参考patronictl --help
PostgreSQL监控
监控是眼睛,对于运维来说非常重要,这边主要采用的是Grafana + Prometheus 监控PostgreSQL,自带PG监控指标模版,也可自己定制详尽指标,结合内部告警平台编写api 实现告警短信发送以及推推内部沟通软件推送,具体部署这里就不详细阐述了,监控界面如下:
监控参考:
https://www.bbsmax.com/A/pRdBKG0Pzn/
dashboard json 下载模版
https://grafana.com/grafana/dashboards?search=postgresql&orderBy=reviewsCount&direction=desc
下载好json 文件直接在dashboard倒入模版即可
监控指标rules 可自己定制,可选择常用的,比较重要的指标即可
应用连接方式
这边采用的是公司内部的lvs机制,通过挂载vip的方式来实现读写分离,也可以通过haproxy 等组件来实现负载均衡,多一层组件就会多一层故障点,同样需要保证高可用,可根据业务重要程度进行抉择
PostgreSQL 备份
PostgreSQL流复制备份可选择物理备份或者逻辑备份,逻辑备份可选择pg_dump 进行单表备份或者pg_dumpall进行全库备份,物理备份可选择pg_basebackup 或者pg_rman 备份,在此我选择pg_rman 进行备份,下面重点介绍下强大的pg_rman 备份工具
pg_rman是一个开源的PostgreSQL备份管理软件,类似Oracle的RMAN,使用的是pg_start_backup(), copy, pg_stop_backup()的备份模式,pg_rman跑的不是流复制协议,而是文件拷贝,所以pg_rman必须和数据库节点跑在一起,如果在standby节点运行pg_rman,pg_rman则需要通过网络连接到主节点执行pg_start_backup/pg_stop_backup
pg_rman可支持的主要功能如下:
◎全量备份
◎增量备份
◎检验备份集
◎列出备份集
◎按指定时间从catalog 删除备份集
◎物理删除已从catalog删除的备份集
相信用过oracle的同学了解oracle rman 备份的强大就清楚pg_rman 的功能强大之处,具体可参考http://mysql.taobao.org/monthly/2016/09/05/
未来展望:
PostgreSQL 在最新的数据库DB-Engines 9月榜单高居第四,是功能最强大的开源数据库,增势迅速,稳定性极强,特别是在GIS领域处于优势地位,有丰富统计函数和统计语法支持对开发特别友好,而且高可用复制架构越来越完善,相信在不久的将来在国内应用会越来越广泛