本文主要從以下幾個(gè)方面介紹PostgreSQL高可用集群在360的落地實(shí)戰(zhàn)
背景介紹
為什么選擇Patroni + Etcd + PostgreSQL高可用集群方案
Patroni + Etcd 高可用集群架構(gòu)解析
Patroni + Etcd + PostgreSQL 部署實(shí)戰(zhàn)
Patroni 日常運(yùn)維管理
PostgreSQL 監(jiān)控實(shí)現(xiàn)
PostgreSQL 應(yīng)用連接方式
PostgreSQL 備份恢復(fù)方式選擇
背景
? ? 最近線上重要業(yè)務(wù)容器云的鏡像倉(cāng)庫(kù)需要部署一套postgresql 高可用集群,涉及到數(shù)據(jù)庫(kù)選型,最終選擇了postgresql,為什么不選擇mysql呢,postgresql是功能最強(qiáng)大的開(kāi)源數(shù)據(jù)庫(kù),主要考慮pg 支持使用函數(shù)索引和條件索引,text 沒(méi)有限制,可以索引,還可以全文檢索,不用再接一套es,并且postgresql 開(kāi)源協(xié)議好,開(kāi)源軟件原生支持好,特別對(duì)開(kāi)發(fā)來(lái)說(shuō)比較友好,最重要的是kube-bench 只支持postgresql??
為什么選擇Patroni + Etcd 方案
PostgreSQL 比較流行的高可用解決方案有很多,常用的主要包含repmgr和patroni等,也是github star前幾的高可用組件,并且文檔更新比較及時(shí),都可最高支持postgresql 13,repmgr 相對(duì)來(lái)說(shuō)功能沒(méi)有patroni全面以及不能檢測(cè)備機(jī)是否被錯(cuò)誤配置為未知或不存在的節(jié)點(diǎn)、不能檢測(cè)遠(yuǎn)程節(jié)點(diǎn)的狀態(tài)(不具備分布式解決方案)和不能處理單個(gè)節(jié)點(diǎn)的恢復(fù),本文主要基于patroni 實(shí)現(xiàn)。
此方案使用Patroni管理本地庫(kù),并結(jié)合Etcd作為數(shù)據(jù)存儲(chǔ)和主節(jié)點(diǎn)選舉,具有以下優(yōu)勢(shì)
o健壯性: 使用分布式key-value數(shù)據(jù)庫(kù)作為數(shù)據(jù)存儲(chǔ),主節(jié)點(diǎn)故障時(shí)進(jìn)行主節(jié)點(diǎn)重新選舉,具有很強(qiáng)的健壯性
o支持多種復(fù)制方式: 基于內(nèi)置流復(fù)制,支持同步流復(fù)制、異步流復(fù)制、級(jí)聯(lián)復(fù)制
o支持主備延遲設(shè)置: 可以設(shè)置備庫(kù)延遲主庫(kù)WAL的字節(jié)數(shù),當(dāng)備庫(kù)延遲大于指定值時(shí)不做故障切換
o自動(dòng)化程度高:
①支持自動(dòng)化初始PostgreSQL實(shí)例并部署流復(fù)制
②當(dāng)備庫(kù)實(shí)例關(guān)閉后,支持自動(dòng)拉起
③當(dāng)主庫(kù)實(shí)例關(guān)閉后,首先會(huì)嘗試自動(dòng)拉起
④支持switchover命令,能自動(dòng)將老的主庫(kù)進(jìn)行角色轉(zhuǎn)換? ? ?
o避免腦裂: 數(shù)據(jù)庫(kù)信息記錄到 ETCD 中,通過(guò)優(yōu)化部署策略(多機(jī)房部署、增加實(shí)例數(shù))可以避免腦裂
Patroni + Etcd 高可用架構(gòu)
Patroni 是一個(gè)開(kāi)源工具套件,它是用 Python編寫(xiě)的,可確保 PostgreSQL HA 集群的端到端設(shè)置,包括流復(fù)制。它的功能通過(guò)REST API顯示,也通過(guò)一個(gè)名為 Patronictl 的命令行實(shí)用程序顯示。它通過(guò)使用其運(yùn)行狀況檢查API來(lái)處理負(fù)載平衡來(lái)支持與 HAProxy 的集成。在此 HA 解決方案中,etcd 用于分布式配置存儲(chǔ) (DCS),以實(shí)現(xiàn)最大的可訪問(wèn)性,下面是官方高可用集群方案的架構(gòu)圖展示,我們基于此完成postgresql 高可用架構(gòu)集群方案設(shè)計(jì),不過(guò)并沒(méi)有使用HAproxy 進(jìn)行負(fù)載均衡,而是使用公司內(nèi)部的LVS 來(lái)實(shí)現(xiàn)的,一主兩副本,副本部署在不同的idc 實(shí)現(xiàn)異地災(zāi)備,etcd 也是三節(jié)點(diǎn)集群分別部署在三臺(tái)機(jī)器, 如果資源有限,也可以和postgresql/patroni部署在相同機(jī)器

Etcd、Patroni 和PostgreSQL是如何一起工作的
etcd/patroni/postgresql 都是部署的3節(jié)點(diǎn)集群
Etcd: 分布式的Key-Value數(shù)據(jù)庫(kù)
etcd1、etcd2、 etcd3作為分布式的Key-Value數(shù)據(jù)庫(kù),被partroni1、 patroni2、 patroni3讀/寫(xiě),用于共享/傳遞信息。每一個(gè) Patroni都能讀/寫(xiě)etcd中的數(shù)據(jù).
Paroni: 控制/監(jiān)控本地的PostgreSQL, 把本地PostgreSQL信息/狀態(tài)寫(xiě)入etcd
每一個(gè) Patroni實(shí)例監(jiān)控/控制本地的PostgreSQL,把本地本地PostgreSQL信息/狀態(tài)寫(xiě)入etcd , 一個(gè)Patroni實(shí)例能夠通過(guò)讀取etcd獲取外地PostgreSQL的信息/狀態(tài).
PostreSQL主節(jié)點(diǎn)的選舉
Patroni判斷本地PostgreSQL是否可以作為Primary庫(kù)。如果可以,Paroni試圖選舉本地PostgreSQL作為Primary(Leader) , 選舉方式是:把etcd中的某個(gè)key更新成為本地PostgreSQL的名字, 如果多個(gè)Paroni同時(shí)更改同一個(gè)key,只有一個(gè)能改成功,然后成為Primary(Leader)。
部署篇
系統(tǒng)/軟件/版本
?CentOS 7.4
?PostgreSQL 12.6
?etcd: 3.2.18
?python: Python 3.6.5
?Patroni: 2.1.0
主機(jī)信息
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臺(tái)機(jī)器,也可以用3臺(tái)機(jī)器組件全部安裝在一起
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運(yùn)行環(huán)境到虛擬環(huán)境
source venv4archery/bin/activate
創(chuàng)建PG目錄和用戶
useradd postgres #編譯安裝的,yum 安裝不用創(chuàng)建
mkdir -p /data04/pg15432
mkdir -p /data04/pg15432/data
mkdir -p /data04/pg15432/scripts
chown -R postgres:postgres /data04/pg15432/
創(chuàng)建歸檔目錄
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 每個(gè)節(jié)點(diǎn)都不一樣,etcd-cluster-1 是集群名字統(tǒng)一
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/
#啟動(dòng)patroni集群需非root用戶
集群進(jìn)行初始化(所有節(jié)點(diǎn)執(zhí)行)
patroni /etc/patroni.yml? > patroni_member_1.log 2>&1 &
可以使用centos7的service啟動(dòng):
[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 (可根據(jù)自己需求定制化)
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
重點(diǎn)關(guān)注上面標(biāo)紅的參數(shù)設(shè)置,如果初始化未設(shè)置也沒(méi)關(guān)系,部署完成一定驗(yàn)證下重要參數(shù)設(shè)置,未設(shè)置的可以通過(guò)patronictl 進(jìn)行設(shè)置
PG集群信息查看

數(shù)據(jù)庫(kù)賬戶創(chuàng)建
CREATE USER docker WITH PASSWORD 'xxxx';
CREATE DATABASE docker OWNER docker;?
GRANT ALL PRIVILEGES ON DATABASE docker to docker;
遠(yuǎn)程連接數(shù)據(jù)庫(kù)
psql -U docker -d docker -p xxxx-h xxxx
嘗試對(duì)主節(jié)點(diǎn)進(jìn)行讀寫(xiě)

以上完成對(duì)整個(gè)集群的部署并確保數(shù)據(jù)庫(kù)可以正常進(jìn)行讀寫(xiě)
Patronictl 日常運(yùn)維
參數(shù)設(shè)置查看
shared_buffers
類(lèi)似mysql innodb_buffer_pool_size,該參數(shù)主要設(shè)置數(shù)據(jù)庫(kù)服務(wù)器將使用的共享內(nèi)存緩沖區(qū)量,建議是設(shè)置系統(tǒng)內(nèi)存的25%,過(guò)高也會(huì)造成一些工作負(fù)載,還是需要跟業(yè)務(wù)溝通獲取預(yù)估的數(shù)據(jù)量以及估算熱數(shù)據(jù)量來(lái)針對(duì)性制定參數(shù)值
修改shared_buffers
patronictl -c /etc/patroni.yml edit-config -p "shared_buffers='32GB'"

patronictl -c /etc/patroni.yml restart postgresql (重啟生效)
max_connections
? 決定數(shù)據(jù)庫(kù)的最大并發(fā)連接數(shù)。默認(rèn)值通常是 100 個(gè)連接,但是如果內(nèi)核設(shè)置不支持(initdb時(shí)決定),可能會(huì)比這個(gè) 數(shù)少。這個(gè)參數(shù)只能在服務(wù)器啟動(dòng)時(shí)設(shè)置。
當(dāng)運(yùn)行一個(gè)后備服務(wù)器時(shí),你必須設(shè)置這個(gè)參數(shù)等于或大于主服務(wù)器上的參數(shù)。否則,后備服務(wù)器上可能無(wú)法允許查詢
修改max_connections
patronictl -c /etc/patroni.yml edit-config -p 'max_connections=6000'

patronictl -c /etc/patroni.yml restart postgresql (重啟生效)
查看當(dāng)前歸檔日志
select pg_walfile_name(pg_current_wal_lsn());

手動(dòng)切換歸檔
select pg_walfile_name(pg_current_wal_lsn());

可以看到歸檔已經(jīng)由...0000B 切換到...0000C
設(shè)置開(kāi)啟慢日志
閾值為200ms
patronictl -c /etc/patroni.yml edit-config -p 'log_min_duration_statement=200'?

查看慢日志設(shè)置閾值

高可用故障轉(zhuǎn)移測(cè)試
Kill 主節(jié)點(diǎn)進(jìn)程

會(huì)發(fā)現(xiàn)patroni 會(huì)自動(dòng)拉起postgresql進(jìn)程,并且還是主節(jié)點(diǎn),并未進(jìn)行切主
手工切主
假設(shè)切主到postgresql2 155節(jié)點(diǎn)
patronictl -d etcd://10.16.75.17:2379 switchover postgresql

切主期間會(huì)出現(xiàn)中間狀態(tài)unknown,瞬間會(huì)恢復(fù)正常如下,切主成功到155

重啟集群

可看到主節(jié)點(diǎn)也是沒(méi)切主,拓?fù)浜鸵郧笆且粯拥?/p>
主/從節(jié)點(diǎn)機(jī)器重啟
機(jī)器宕機(jī)是常有的事情,當(dāng)postgresql 某個(gè)節(jié)點(diǎn)所在機(jī)器宕機(jī)恢復(fù)后,需要手動(dòng)拉起節(jié)點(diǎn)
patroni /etc/patroni.yml? > patroni_member_1.log 2>&1 &
禁用開(kāi)啟故障轉(zhuǎn)移
當(dāng)pause 后,這時(shí)候集群是不會(huì)進(jìn)行自動(dòng)故障轉(zhuǎn)移的,可以在某些特定場(chǎng)景使用,然后用resume 恢復(fù)就可以了

還有其它一些功能可以參考patronictl --help
PostgreSQL監(jiān)控
監(jiān)控是眼睛,對(duì)于運(yùn)維來(lái)說(shuō)非常重要,這邊主要采用的是Grafana + Prometheus 監(jiān)控PostgreSQL,自帶PG監(jiān)控指標(biāo)模版,也可自己定制詳盡指標(biāo),結(jié)合內(nèi)部告警平臺(tái)編寫(xiě)api 實(shí)現(xiàn)告警短信發(fā)送以及推推內(nèi)部溝通軟件推送,具體部署這里就不詳細(xì)闡述了,監(jiān)控界面如下:



監(jiān)控參考:
https://www.bbsmax.com/A/pRdBKG0Pzn/
dashboard json 下載模版
https://grafana.com/grafana/dashboards?search=postgresql&orderBy=reviewsCount&direction=desc
下載好json 文件直接在dashboard倒入模版即可
監(jiān)控指標(biāo)rules 可自己定制,可選擇常用的,比較重要的指標(biāo)即可
應(yīng)用連接方式
? 這邊采用的是公司內(nèi)部的lvs機(jī)制,通過(guò)掛載vip的方式來(lái)實(shí)現(xiàn)讀寫(xiě)分離,也可以通過(guò)haproxy 等組件來(lái)實(shí)現(xiàn)負(fù)載均衡,多一層組件就會(huì)多一層故障點(diǎn),同樣需要保證高可用,可根據(jù)業(yè)務(wù)重要程度進(jìn)行抉擇
PostgreSQL 備份
PostgreSQL流復(fù)制備份可選擇物理備份或者邏輯備份,邏輯備份可選擇pg_dump 進(jìn)行單表備份或者pg_dumpall進(jìn)行全庫(kù)備份,物理備份可選擇pg_basebackup 或者pg_rman 備份,在此我選擇pg_rman 進(jìn)行備份,下面重點(diǎn)介紹下強(qiáng)大的pg_rman 備份工具
pg_rman是一個(gè)開(kāi)源的PostgreSQL備份管理軟件,類(lèi)似Oracle的RMAN,使用的是pg_start_backup(), copy, pg_stop_backup()的備份模式,pg_rman跑的不是流復(fù)制協(xié)議,而是文件拷貝,所以pg_rman必須和數(shù)據(jù)庫(kù)節(jié)點(diǎn)跑在一起,如果在standby節(jié)點(diǎn)運(yùn)行pg_rman,pg_rman則需要通過(guò)網(wǎng)絡(luò)連接到主節(jié)點(diǎn)執(zhí)行pg_start_backup/pg_stop_backup
pg_rman可支持的主要功能如下:
◎全量備份
◎增量備份
◎檢驗(yàn)備份集
◎列出備份集
◎按指定時(shí)間從catalog 刪除備份集
◎物理刪除已從catalog刪除的備份集
相信用過(guò)oracle的同學(xué)了解oracle rman 備份的強(qiáng)大就清楚pg_rman 的功能強(qiáng)大之處,具體可參考http://mysql.taobao.org/monthly/2016/09/05/
未來(lái)展望:
????PostgreSQL?在最新的數(shù)據(jù)庫(kù)DB-Engines?9月榜單高居第四,是功能最強(qiáng)大的開(kāi)源數(shù)據(jù)庫(kù),增勢(shì)迅速,穩(wěn)定性極強(qiáng),特別是在GIS領(lǐng)域處于優(yōu)勢(shì)地位,有豐富統(tǒng)計(jì)函數(shù)和統(tǒng)計(jì)語(yǔ)法支持對(duì)開(kāi)發(fā)特別友好,而且高可用復(fù)制架構(gòu)越來(lái)越完善,相信在不久的將來(lái)在國(guó)內(nèi)應(yīng)用會(huì)越來(lái)越廣泛