Linux 软件包安装的 PostgreSQL 复制与故障转移
- 层级:Premium、Ultimate
- 提供:GitLab 自管理版
如果您是 GitLab 自管理版的免费用户,请考虑使用云托管解决方案。 本文档不涵盖自行编译的安装。
如果带有复制和故障转移的设置不是您想要的,请参阅 Linux 软件包的数据库配置文档。
建议在尝试为 GitLab 配置带复制和故障转移的 PostgreSQL 之前,完整阅读本文档。
操作系统升级
如果您要故障转移到不同操作系统的系统,请阅读PostgreSQL 操作系统升级文档。 未考虑操作系统升级时的本地更改可能导致数据损坏。
架构
PostgreSQL 集群(带复制故障转移)的 Linux 软件包推荐配置需要:
- 至少三个 PostgreSQL 节点。
- 至少三个 Consul 服务器节点。
- 至少三个跟踪并处理主数据库读写操作的 PgBouncer 节点。
- 用于在 PgBouncer 节点之间平衡请求的内部负载均衡器(TCP)。
- 启用数据库负载均衡。
- 在每个 PostgreSQL 节点上配置的本地 PgBouncer 服务。这与跟踪主节点的主体 PgBouncer 集群分开。
@startuml
card "**Internal Load Balancer**" as ilb #9370DB
skinparam linetype ortho
together {
collections "**GitLab Rails** x3" as gitlab #32CD32
collections "**Sidekiq** x4" as sidekiq #ff8dd1
}
collections "**Consul** x3" as consul #e76a9b
card "Database" as database {
collections "**PGBouncer x3**\n//Consul//" as pgbouncer #4EA7FF
card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary #4EA7FF
collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary #4EA7FF
pgbouncer -[#4EA7FF]-> postgres_primary
postgres_primary .[#4EA7FF]r-> postgres_secondary
}
gitlab -[#32CD32]-> ilb
gitlab -[hidden]-> pgbouncer
gitlab .[#32CD32,norank]-> postgres_primary
gitlab .[#32CD32,norank]-> postgres_secondary
sidekiq -[#ff8dd1]-> ilb
sidekiq -[hidden]-> pgbouncer
sidekiq .[#ff8dd1,norank]-> postgres_primary
sidekiq .[#ff8dd1,norank]-> postgres_secondary
ilb -[#9370DB]-> pgbouncer
consul -[#e76a9b]r-> pgbouncer
consul .[#e76a9b,norank]r-> postgres_primary
consul .[#e76a9b,norank]r-> postgres_secondary
@enduml
您还需要考虑底层网络拓扑结构,确保所有数据库和 GitLab 实例之间有冗余连接,以避免网络成为单点故障。
数据库节点
每个数据库节点运行四个服务:
PostgreSQL:数据库本身。Patroni:与集群中的其他 Patroni 服务通信,并在主服务器出现问题时处理故障转移。故障转移流程包括:- 选择新集群领导者。
- 将新节点提升为主节点。
- 指示剩余服务器跟随新主节点。
PgBouncer:节点的本地池化器。作为数据库负载均衡的一部分,用于读取查询。Consul代理:与存储当前 Patroni 状态的 Consul 集群通信。该代理监控数据库集群中每个节点的状态,并在 Consul 集群的服务定义中跟踪其健康状态。
Consul 服务器节点
Consul 服务器节点运行 Consul 服务器服务。这些节点必须在 Patroni 集群引导前达到法定人数并选举出领导者;否则,数据库节点会等待直到选出 Consul 领导者。
PgBouncer 节点
每个 PgBouncer 节点运行两个服务:
PgBouncer:数据库连接池化器本身。Consul代理:监视 Consul 集群上的 PostgreSQL 服务定义的状态。如果该状态发生变化,Consul 会运行一个脚本,更新 PgBouncer 配置以指向新的 PostgreSQL 主节点,并重新加载 PgBouncer 服务。
连接流
软件包中的每个服务都附带一组默认端口。您可能需要为以下列出的连接制定特定的防火墙规则:
此设置中有多个连接流:
主节点(Primary)
- 应用服务器通过其默认端口直接连接到PgBouncer,或通过配置的内部负载均衡器(TCP)连接,该负载均衡器服务于多个PgBouncer。
- PgBouncer连接到主数据库服务器的PostgreSQL默认端口。
数据库负载均衡(Database Load Balancing)
对于针对最近未更改且在所有数据库节点上保持最新的数据的读查询:
- 应用服务器以轮询方式连接到每个数据库节点上的本地PgBouncer服务的默认端口。
- 本地PgBouncer连接到本地数据库服务器的PostgreSQL默认端口。
复制(Replication)
- Patroni主动管理正在运行的PostgreSQL进程和配置。
- PostgreSQL从库连接到主数据库服务器的PostgreSQL默认端口。
- Consul服务器和代理相互连接到对方的Consul默认端口。
设置步骤
所需信息
在进行配置之前,您需要收集所有必要的信息。
网络信息
默认情况下,PostgreSQL不会监听任何网络接口。它需要知道监听哪个IP地址才能被其他服务访问。同样,PostgreSQL的访问是基于网络源进行控制的。
因此,您需要:
- 每个节点的网络接口的IP地址。可以将其设置为
0.0.0.0以监听所有接口。不能设置为回环地址127.0.0.1。 - 网络地址。可以是子网形式(即
192.168.0.0/255.255.255.0)或无类域间路由(CIDR)形式(192.168.0.0/24)。
Consul信息
使用默认设置时,最小配置要求如下:
-
CONSUL_USERNAME。Linux包安装的默认用户是gitlab-consul。 -
CONSUL_DATABASE_PASSWORD。数据库用户的密码。 -
CONSUL_PASSWORD_HASH。这是由Consul用户名/密码对生成的哈希值。可以使用以下命令生成:sudo gitlab-ctl pg-password-md5 CONSUL_USERNAME
关于该服务本身的几点说明:
- 该服务在系统账户下运行,默认为
gitlab-consul。 - 如果您使用不同的用户名,必须通过
CONSUL_USERNAME变量指定。 - 密码存储在以下位置:
/etc/gitlab/gitlab.rb:哈希值/var/opt/gitlab/pgbouncer/pg_auth:哈希值/var/opt/gitlab/consul/.pgpass:明文
PostgreSQL信息
配置PostgreSQL时,我们执行以下操作:
- 将
max_replication_slots设置为数据库节点数量的两倍。Patroni在初始化复制时会为每个节点额外使用一个槽位。 - 将
max_wal_senders设置为集群中分配的复制槽数量加一。这可防止复制耗尽所有可用的数据库连接。
在本文档中,我们假设有3个数据库节点,因此配置如下:
patroni['postgresql']['max_replication_slots'] = 6
patroni['postgresql']['max_wal_senders'] = 7如前所述,准备需要权限来认证数据库的网络子网。您还需要准备好Consul服务器节点的IP地址或DNS记录。
您需要应用程序数据库用户的以下密码信息:
-
POSTGRESQL_USERNAME。Linux包安装的默认用户是gitlab。 -
POSTGRESQL_USER_PASSWORD。数据库用户的密码。 -
POSTGRESQL_PASSWORD_HASH。这是由用户名/密码对生成的哈希值。可以使用以下命令生成:sudo gitlab-ctl pg-password-md5 POSTGRESQL_USERNAME
Patroni信息
您需要Patroni API的以下密码信息:
PATRONI_API_USERNAME。API基本身份验证的用户名。PATRONI_API_PASSWORD。API基本身份验证的密码。
PgBouncer 信息
使用默认设置时,最小配置要求如下:
-
PGBOUNCER_USERNAME。Linux 包安装的默认用户是pgbouncer -
PGBOUNCER_PASSWORD。这是 PgBouncer 服务的密码。 -
PGBOUNCER_PASSWORD_HASH。这是基于 PgBouncer 用户名/密码对生成的哈希值。可通过以下命令生成:sudo gitlab-ctl pg-password-md5 PGBOUNCER_USERNAME -
PGBOUNCER_NODE,是运行 PgBouncer 节点的 IP 地址或完全限定域名(FQDN)。
关于该服务本身需记住以下几点:
- 该服务与数据库使用相同的系统账户运行。在包中,默认为
gitlab-psql - 如果为 PgBouncer 服务使用了非默认的用户账户(默认为
pgbouncer),则需要指定此用户名。 - 密码存储在以下位置:
/etc/gitlab/gitlab.rb:以哈希形式和明文形式存储/var/opt/gitlab/pgbouncer/pg_auth:以哈希形式存储
安装 Linux 包
首先,确保在每个节点上 下载并安装 Linux 包。
请确保从第 1 步安装必要的依赖项,
从第 2 步添加 GitLab 包仓库。
安装 GitLab 包时,不要提供 EXTERNAL_URL 值。
配置数据库节点
- 确保已 配置 Consul 节点。
- 在执行下一步之前,请确保收集了
CONSUL_SERVER_NODES、PGBOUNCER_PASSWORD_HASH、POSTGRESQL_PASSWORD_HASH、数据库节点数量 以及 网络地址。
配置 Patroni 集群
必须显式启用 Patroni 才能使用它(通过 patroni['enable'] = true)。
任何控制复制的 PostgreSQL 配置项,例如 wal_level、max_wal_senders 或其他,均由 Patroni 严格管理。这些配置会覆盖您使用 postgresql[...] 配置键所做的原始设置。因此,它们全部被分离并放置在 patroni['postgresql'][...] 下。此行为仅限于复制功能。Patroni 会尊重您使用 postgresql[...] 配置键所做的任何其他 PostgreSQL 配置。例如,max_wal_senders 默认设置为 5。如果您希望更改此值,则必须使用 patroni['postgresql']['max_wal_senders'] 配置键进行设置。
示例如下:
# 禁用所有组件,仅保留 Patroni、PgBouncer 和 Consul
roles(['patroni_role', 'pgbouncer_role'])
# PostgreSQL 配置
postgresql['listen_address'] = '0.0.0.0'
# 禁用自动数据库迁移
gitlab_rails['auto_migrate'] = false
# 配置 Consul 代理
consul['services'] = %w(postgresql)
# START 用户配置
# 根据必需信息部分所述设置实际值
#
# 将 PGBOUNCER_PASSWORD_HASH 替换为生成的 md5 值
postgresql['pgbouncer_user_password'] = 'PGBOUNCER_PASSWORD_HASH'
# 将 POSTGRESQL_REPLICATION_PASSWORD_HASH 替换为生成的 md5 值
postgresql['sql_replication_password'] = 'POSTGRESQL_REPLICATION_PASSWORD_HASH'
# 将 POSTGRESQL_PASSWORD_HASH 替换为生成的 md5 值
postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH'
# 将 PATRONI_API_USERNAME 替换为用于 Patroni REST API 调用的用户名(所有节点使用相同用户名)
patroni['username'] = 'PATRONI_API_USERNAME'
# 将 PATRONI_API_PASSWORD 替换为用于 Patroni REST API 调用的密码(所有节点使用相同密码)
patroni['password'] = 'PATRONI_API_PASSWORD'
# 将 `max_replication_slots` 设置为数据库节点数量的两倍。
# 当 Patroni 初始化复制时,每个节点会额外使用一个槽位。
patroni['postgresql']['max_replication_slots'] = X
# 将 `max_wal_senders` 设置为比集群中复制槽位数量多一。
# 这用于防止复制占用所有可用的数据库连接。
patroni['postgresql']['max_wal_senders'] = X+1
# 将 XXX.XXX.XXX.XXX/YY 替换为您其他 patroni 节点的网络地址
patroni['allowlist'] = %w(XXX.XXX.XXX.XXX/YY 127.0.0.1/32)
# 将 XXX.XXX.XXX.XXX/YY 替换为网络地址
postgresql['trust_auth_cidr_addresses'] = %w(XXX.XXX.XXX.XXX/YY 127.0.0.1/32)
# 本地 PgBouncer 服务用于数据库负载均衡
pgbouncer['databases'] = {
gitlabhq_production: {
host: "127.0.0.1",
user: "PGBOUNCER_USERNAME",
password: 'PGBOUNCER_PASSWORD_HASH'
}
}
# 替换占位符:
#
# Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
# 为 CONSUL_SERVER_NODES 收集到的地址
consul['configuration'] = {
retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z)
}
#最终用户配置
所有数据库节点都使用相同的配置。领导节点的确定不在配置中进行,且领导和副本节点之间没有额外或不同的配置。
当一个节点的配置完成后,你必须在每个节点上重新配置GitLab,以使更改生效。
通常,当Consul集群就绪时,第一个执行重新配置的节点会成为领导节点。你无需按顺序重新配置节点,可以并行运行或在任何顺序下运行。如果你选择任意顺序,就不会有预先确定的领导节点。
启用监控
如果启用了监控,则必须在所有数据库服务器上启用它。
-
创建/编辑
/etc/gitlab/gitlab.rb并添加以下配置:# 启用Prometheus的服务发现 consul['monitoring_service_discovery'] = true # 设置导出器必须监听的地址 node_exporter['listen_address'] = '0.0.0.0:9100' postgres_exporter['listen_address'] = '0.0.0.0:9187' -
运行
sudo gitlab-ctl reconfigure以编译配置。
为Patroni API启用TLS支持
默认情况下,Patroni REST API 通过HTTP提供服务。你可以选择启用TLS并使用HTTPS(通过同一端口)。
要启用TLS,你需要PEM格式的证书和私钥文件。这两个文件必须可被PostgreSQL用户(默认为gitlab-psql,或由postgresql['username']设置的)读取:
patroni['tls_certificate_file'] = '/path/to/server/certificate.pem'
patroni['tls_key_file'] = '/path/to/server/key.pem'如果服务器的私钥已加密,请指定解密密码:
patroni['tls_key_password'] = 'private-key-password' # 这是明文密码。如果你使用自签名证书或内部CA,则需要禁用TLS验证或传递内部CA的证书,否则在使用gitlab-ctl patroni ...命令时可能会遇到意外错误。Linux包确保Patroni API客户端遵守此配置。
TLS证书验证默认启用。若要禁用它:
patroni['tls_verify'] = false或者,你可以传递内部CA的PEM格式证书。同样,该文件必须可被PostgreSQL用户读取:
patroni['tls_ca_file'] = '/path/to/ca.pem'启用TLS后,所有端点都可以进行API服务器与客户端的双向认证,其范围取决于patroni['tls_client_mode']属性:
none(默认):API不检查任何客户端证书。optional:对于所有不安全的API调用,需要客户端证书。required:所有API调用都需要客户端证书。
客户端证书会根据patroni['tls_ca_file']指定的CA证书进行验证。因此,该属性对于双向TLS认证是必需的。你还需要指定PEM格式的客户端证书和私钥文件。这两个文件必须可被PostgreSQL用户读取:
patroni['tls_client_mode'] = 'required'
patroni['tls_ca_file'] = '/path/to/ca.pem'
patroni['tls_client_certificate_file'] = '/path/to/client/certificate.pem'
patroni['tls_client_key_file'] = '/path/to/client/key.pem'你可以在不同的Patroni节点上使用不同的证书和密钥,只要它们可以被验证即可。但是,CA证书(patroni['tls_ca_file'])、TLS证书验证(patroni['tls_verify'])以及客户端TLS认证模式(patroni['tls_client_mode']),必须在所有节点上具有相同的值。
配置PgBouncer节点
-
执行下一步前,请确保已收集
CONSUL_SERVER_NODES、CONSUL_PASSWORD_HASH和PGBOUNCER_PASSWORD_HASH。 -
在每个节点上,编辑
/etc/gitlab/gitlab.rb配置文件,并按如下所示替换# START user configuration部分的值:# 禁用除PgBouncer和Consul代理之外的所有组件 roles(['pgbouncer_role']) # 配置PgBouncer pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul) # 配置Consul代理 consul['watchers'] = %w(postgresql) # START 用户配置 # 按照必需信息部分说明设置真实值 # 将CONSUL_PASSWORD_HASH替换为生成的md5值 # 将PGBOUNCER_PASSWORD_HASH替换为生成的md5值 pgbouncer['users'] = { 'gitlab-consul': { password: 'CONSUL_PASSWORD_HASH' }, 'pgbouncer': { password: 'PGBOUNCER_PASSWORD_HASH' } } # 替换占位符: # # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z # 为CONSUL_SERVER_NODES中收集到的地址 consul['configuration'] = { retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z) } # # END 用户配置 -
运行
gitlab-ctl reconfigure -
创建
.pgpass文件,使Consul能够重新加载PgBouncer。当被提示时,输入PGBOUNCER_PASSWORD两次:gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
PgBouncer检查点
-
确保每个节点与当前节点领导者通信:
gitlab-ctl pgb-console # 输入PGBOUNCER_PASSWORD(当被提示时)如果输入密码后出现
psql: ERROR: Auth failed错误,请确认之前是否使用正确的格式生成了MD5密码哈希。正确格式是将密码和用户名拼接:PASSWORDUSERNAME。例如,Sup3rS3cr3tpgbouncer是为pgbouncer用户生成MD5密码哈希所需的文本。 -
控制台提示符可用后,运行以下查询:
show databases ; show clients ;输出应类似如下:
名称 | 主机 | 端口 | 数据库 | force_user | 连接池大小 | 预留池大小 | 连接池模式 | 最大连接数 | 当前连接数 ---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+--------------------- gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production | | 20 | 0 | | 0 | 0 pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0 (2 行) 类型 | 用户 | 数据库 | 状态 | 地址 | 端口 | 本地地址 | 本地端口 | 连接时间 | 请求时间 | 指针 | 链接 | 远程进程ID | TLS ------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+----- C | pgbouncer | pgbouncer | active | 127.0.0.1 | 56846 | 127.0.0.1 | 6432 | 2017-08-21 18:09:59 | 2017-08-21 18:10:48 | 0x22b3880 | | 0 | (1 行)
配置内部负载均衡器
如果运行了多个PgBouncer节点(建议如此),则必须设置TCP内部负载均衡器以正确服务每个节点。任何可靠的TCP负载均衡器均可完成此操作。
以 HAProxy 为例,配置如下:
global
log /dev/log local0
log localhost local1 notice
log stdout format raw local0
defaults
log global
default-server inter 10s fall 3 rise 2
balance leastconn
frontend internal-pgbouncer-tcp-in
bind *:6432
mode tcp
option tcplog
default_backend pgbouncer
backend pgbouncer
mode tcp
option tcp-check
server pgbouncer1 <ip>:6432 check
server pgbouncer2 <ip>:6432 check
server pgbouncer3 <ip>:6432 check参考您首选负载均衡器的文档获取进一步指导。
配置应用节点
应用节点运行 gitlab-rails 服务。你可能设置了其他属性,但以下必须进行配置:
-
编辑
/etc/gitlab/gitlab.rb:# 禁用应用节点的 PostgreSQL postgresql['enable'] = false gitlab_rails['db_host'] = 'PGBOUNCER_NODE' 或 'INTERNAL_LOAD_BALANCER' gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = 'POSTGRESQL_USER_PASSWORD' gitlab_rails['auto_migrate'] = false gitlab_rails['db_load_balancing'] = { 'hosts' => ['POSTGRESQL_NODE_1', 'POSTGRESQL_NODE_2', 'POSTGRESQL_NODE_3'] } -
重新配置 GitLab,使更改生效。
应用节点后续配置
确保所有迁移已执行:
gitlab-rake gitlab:db:configure如果遇到 rake aborted! 错误,提示 PgBouncer 无法连接到 PostgreSQL,可能是数据库节点的 gitlab.rb 中 trust_auth_cidr_addresses 缺少 PgBouncer 节点的 IP 地址。在继续操作前,请参阅 PgBouncer 错误 ERROR: pgbouncer cannot connect to server。
备份
不要通过 PgBouncer 连接备份或恢复 GitLab:这会导致 GitLab 停机。
确保 GitLab 运行
此时你的 GitLab 实例应已启动并运行。验证能否登录,创建问题(issues)和合并请求(merge requests)。更多信息请参见 故障排除:复制与故障转移。
示例配置
本节介绍几个完整的示例配置。
推荐配置示例
此示例使用三台 Consul 服务器、三台 PgBouncer 服务器(带内部负载均衡器)、三台 PostgreSQL 服务器和一个应用节点。
在此配置中,所有服务器共享同一 10.6.0.0/16 私有网络范围。服务器可通过这些地址自由通信。
虽然你可以使用不同的网络配置,但建议确保集群内能实现同步复制。一般来说,延迟小于 2ms 可保证复制操作的性能。
GitLab 参考架构 的设计假设应用数据库查询由所有三个节点分担。通信延迟超过 2ms 可能导致数据库锁,影响副本及时处理只读查询的能力。
10.6.0.22:PgBouncer 210.6.0.23:PgBouncer 310.6.0.31:PostgreSQL 110.6.0.32:PostgreSQL 210.6.0.33:PostgreSQL 310.6.0.41:GitLab 应用节点
所有密码均设为 toomanysecrets。不要使用此密码或其衍生哈希值,且 GitLab 的 external_url 为 http://gitlab.example.com。
初始配置后,若发生故障转移,PostgreSQL 主节点会切换至可用从节点之一,直至故障回切。
Consul 服务器推荐配置
在每个服务器上编辑 /etc/gitlab/gitlab.rb:
# 仅启用 Consul 角色(禁用其他组件)
roles(['consul_role'])
consul['configuration'] = {
server: true,
retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13)
}
consul['monitoring_service_discovery'] = true重新配置 GitLab 使更改生效。
PgBouncer 服务器推荐配置
在每个服务器上编辑 /etc/gitlab/gitlab.rb:
# 仅启用 PgBouncer 和 Consul agent 角色(禁用其他组件)
roles(['pgbouncer_role'])
# 配置 PgBouncer
pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
pgbouncer['users'] = {
'gitlab-consul': {
password: '5e0e3263571e3704ad655076301d6ebe'
},
'pgbouncer': {
password: '771a8625958a529132abe6f1a4acb19c'
}
}
consul['watchers'] = %w(postgresql)
consul['configuration'] = {
retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13)
}
consul['monitoring_service_discovery'] = true重新配置 GitLab 使更改生效。
内部负载均衡器配置
需设置内部负载均衡器(TCP),以服务每个 PgBouncer 节点(本例中为 10.6.0.20 的 IP)。配置方法可参考 PgBouncer 配置内部负载均衡器 部分。
PostgreSQL 服务器推荐配置
在数据库节点上编辑 /etc/gitlab/gitlab.rb:
禁用除 Patroni、PgBouncer 和 Consul 之外的所有组件
roles([‘patroni_role’, ‘pgbouncer_role’])
PostgreSQL 配置
postgresql[‘监听地址’] = ‘0.0.0.0’
postgresql[‘热备模式’] = ‘on’
postgresql[‘WAL级别’] = ‘replica’
禁用自动数据库迁移
gitlab_rails[‘自动迁移’] = false
postgresql[‘pgbouncer用户密码’] = ‘771a8625958a529132abe6f1a4acb19c’
postgresql[‘SQL用户密码’] = ‘450409b85a0223a214b5fb1484f34d0f’
patroni[‘用户名’] = ‘PATRONI_API_USERNAME’
patroni[‘密码’] = ‘PATRONI_API_PASSWORD’
patroni[‘postgresql’][‘最大复制槽位’] = 6
patroni[‘postgresql’][‘最大WAL发送者’] = 7
patroni[‘允许列表’] = %w(10.6.0.0/16 127.0.0.1/32)
postgresql[‘信任认证CIDR地址’] = %w(10.6.0.0/16 127.0.0.1/32)
本地 PgBouncer 服务用于数据库负载均衡
pgbouncer[‘数据库’] = {
gitlabhq_production: {
host: “127.0.0.1”,
user: “pgbouncer”,
password: ‘771a8625958a529132abe6f1a4acb19c’
}
}
配置 Consul 代理
consul[‘服务’] = %w(postgresql)
consul[‘配置’] = {
retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13)
}
consul[‘监控服务发现’] = true
重新配置 GitLab,使更改生效。
示例推荐设置手动步骤
部署配置后,请执行以下步骤:
-
找到主数据库节点:
gitlab-ctl get-postgresql-primary -
在我们的应用服务器
10.6.0.41上:
将gitlab-consul用户的 PgBouncer 密码设置为toomanysecrets:gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul运行数据库迁移:
gitlab-rake gitlab:db:configure
Patroni
Patroni 是一个针对 PostgreSQL 高可用性的固执己见的解决方案。它控制 PostgreSQL,覆盖其配置,并管理其生命周期(启动、停止、重启)。Patroni 是 PostgreSQL 12+ 集群化和地理部署级联复制的唯一选项。
Patroni 的基本 架构 没有改变。在准备数据库节点时,您不需要对 Patroni 做任何特殊考虑。Patroni 严重依赖 Consul 来存储集群状态并选举领导者。Consul 集群及其领导者选举中的任何故障都会传播到 Patroni 集群中。
Patroni 监控集群并处理任何故障转移。当主节点发生故障时,它会与 Consul 协作通知 PgBouncer。发生故障时,Patroni 会将旧的主节点转换为副本并自动将其重新加入集群。
使用 Patroni 时,连接流程略有不同。每个节点上的 Patroni 连接到 Consul 代理以加入集群。只有在这之后,它才会决定该节点是主节点还是副本。基于此决策,它配置并启动 PostgreSQL,并通过 Unix 套接字直接与其通信。这意味着如果 Consul 集群不可用或没有领导者,Patroni 以及由此延伸的 PostgreSQL 将不会启动。Patroni 还提供了一个 REST API,可以通过每个节点上的 默认端口 访问。
检查复制状态
运行 gitlab-ctl patroni members 向 Patroni 查询集群状态的摘要:
+ Cluster: postgresql-ha (6970678148837286213) ------+---------+---------+----+-----------+
| 成员 | 主机 | 角色 | 状态 | TL | MB 中的延迟 |
+-------------------------------------+--------------+---------+---------+----+-----------+
| gitlab-database-1.example.com | 172.18.0.111 | 副本 | 运行中 | 5 | 0 |
| gitlab-database-2.example.com | 172.18.0.112 | 副本 | 运行中 | 5 | 100 |
| gitlab-database-3.example.com | 172.18.0.113 | 主节点 | 运行中 | 5 | |
+-------------------------------------+--------------+---------+---------+----+-----------+若要验证复制状态:
echo -e 'select * from pg_stat_wal_receiver\x\g\x \n select * from pg_stat_replication\x\g\x' | gitlab-psql相同的命令可在所有三台数据库服务器上运行。它会根据服务器执行的角色返回任何可用的复制相关信息。
主节点应为每个副本返回一条记录:
-[ RECORD 1 ]----+------------------------------
pid | 371
usesysid | 16384
usename | gitlab_replicator
application_name | gitlab-database-1.example.com
client_addr | 172.18.0.111
client_hostname |
client_port | 42900
backend_start | 2021-06-14 08:01:59.580341+00
backend_xmin |
state | streaming
sent_lsn | 0/EA13220
write_lsn | 0/EA13220
flush_lsn | 0/EA13220
replay_lsn | 0/EA13220
write_lag |
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
reply_time | 2021-06-18 19:17:14.915419+00若出现以下情况,请进一步调查:
- 存在缺失或多余的记录。
reply_time不是最新的。
lsn 字段与已复制的预写日志(WAL)段相关。在主节点上运行以下命令以获取当前的日志序列号(LSN):
echo 'SELECT pg_current_wal_lsn();' | gitlab-psql若副本未同步,gitlab-ctl patroni members 会显示缺失数据的量,而 lag 字段则表示经过的时间。
阅读主节点返回的数据更多信息(包括 state 字段的其他值),请参阅 PostgreSQL 文档。
副本应返回:
-[ RECORD 1 ]---------+-------------------------------------------------------------------------------------------------
pid | 391
status | streaming
receive_start_lsn | 0/D000000
receive_start_tli | 5
received_lsn | 0/EA13220
received_tli | 5
last_msg_send_time | 2021-06-18 19:16:54.807375+00
last_msg_receipt_time | 2021-06-18 19:16:54.807512+00
latest_end_lsn | 0/EA13220
latest_end_time | 2021-06-18 19:07:23.844879+00
slot_name | gitlab-database-1.example.com
sender_host | 172.18.0.113
sender_port | 5432
conninfo | user=gitlab_replicator host=172.18.0.113 port=5432 application_name=gitlab-database-1.example.com阅读副本返回的数据更多信息,请参阅 PostgreSQL 文档。
选择合适的 Patroni 复制方法
在修改前请仔细阅读 Patroni 文档,因为某些选项若未被充分理解,可能会导致数据丢失的风险。配置的 复制模式 决定了可容忍的数据丢失量。
复制不是备份策略!没有替代经过深思熟虑和测试的备份方案。
Linux 软件包安装默认将 synchronous_commit 设为 on。
postgresql['synchronous_commit'] = 'on'
gitlab['geo-postgresql']['synchronous_commit'] = 'on'自定义Patroni故障转移行为
Linux包安装提供了多个选项,可让您对Patroni恢复过程进行更精细的控制。
以下每个选项及其默认值均显示在 /etc/gitlab/gitlab.rb 中:
patroni['use_pg_rewind'] = true
patroni['remove_data_directory_on_rewind_failure'] = false
patroni['remove_data_directory_on_diverged_timelines'] = false上游文档始终是最新的,但下表应提供功能的最小概述。
| 设置 | 概述 |
|---|---|
use_pg_rewind |
在前集群领导者重新加入数据库集群之前,尝试在其上运行 pg_rewind。 |
remove_data_directory_on_rewind_failure |
如果 pg_rewind 失败,删除本地PostgreSQL数据目录并从当前集群领导者重新复制。 |
remove_data_directory_on_diverged_timelines |
如果无法使用 pg_rewind 且前领导者的时间线与当前时间线不同步,则删除本地数据目录并从当前集群领导者重新复制。 |
Patroni的数据库授权
Patroni使用Unix套接字管理PostgreSQL实例。因此,来自 local 套接字的连接必须受信任。
副本使用复制用户(默认为 gitlab_replicator)与领导者通信。对于此用户,您可以选择 trust 或 md5 认证。如果设置了 postgresql['sql_replication_password'],Patroni会使用 md5 认证;否则回退到 trust。
根据选择的认证方式,您必须在 postgresql['md5_auth_cidr_addresses'] 或 postgresql['trust_auth_cidr_addresses'] 设置中指定集群CIDR。
与Patroni集群交互
您可以使用 gitlab-ctl patroni members 检查集群成员的状态。若要检查各节点状态,gitlab-ctl patroni 提供两个额外的子命令:check-leader 和 check-replica,用于指示节点是否为主节点或副本。
启用Patroni后,它会独占控制PostgreSQL的启动、关闭和重启。这意味着,若要在特定节点关闭PostgreSQL,必须在该节点上停止Patroni:
sudo gitlab-ctl stop patroni在主节点上停止或重启Patroni服务会触发自动故障转移。如果您需要Patroni重新加载其配置或重启PostgreSQL进程而不触发故障转移,必须改用 gitlab-ctl patroni 的 reload 或 restart 子命令。这两个子命令是对相同 patronictl 命令的封装。
Patroni的手动故障转移流程
在GitLab 16.5及更早版本中,Pgbouncer节点不会随Patroni节点自动故障转移。Pgbouncer服务必须手动重启才能成功完成切换。
虽然Patroni支持自动故障转移,但您也可以执行手动故障转移,有两种略有不同的选项:
-
故障转移:当没有健康节点时,允许您执行手动故障转移。您可以在任何PostgreSQL节点上执行此操作:
sudo gitlab-ctl patroni failover -
切换:仅适用于集群健康时,允许您计划切换(可立即发生)。您可以在任何PostgreSQL节点上执行此操作:
sudo gitlab-ctl patroni switchover
有关此主题的更多详细信息,请参阅Patroni文档。
Geo二级站点注意事项
当Geo二级站点从使用 Patroni 和 Pgbouncer 的主站点复制时,不支持通过Pgbouncer复制。有一个添加支持的特性请求,见问题 #8832。
建议:在主站点引入负载均衡器以自动处理 Patroni 集群中的故障转移。更多信息,请参见步骤2:在主站点配置内部负载均衡器。
处理从主节点直接复制时的Patroni故障转移
如果你的次要站点配置为从Patroni集群的主节点直接复制,那么Patroni集群中的故障转移会停止向次要站点的复制,即使原始节点被重新添加为从节点。
在这种情况下,你必须在Patroni集群发生故障转移后手动将次要站点指向从新主节点复制:
sudo gitlab-ctl replicate-geo-database --host=<new_leader_ip> --replication-slot=<slot_name>这会重新同步你的次要站点数据库,根据要同步的数据量可能需要很长时间。如果在重新同步后复制仍无法工作,你可能还需要运行gitlab-ctl reconfigure。
恢复Patroni集群
若要恢复旧主节点并将其重新加入集群作为副本,你可以通过以下方式启动Patroni:
sudo gitlab-ctl start patroni无需额外配置或干预。
Patroni的维护流程
启用Patroni后,你可以对节点执行计划内维护。若要在未启用Patroni时对一个节点进行维护,可将其置于维护模式:
sudo gitlab-ctl patroni pause当Patroni以暂停模式运行时,它不会改变PostgreSQL的状态。完成操作后,你可以恢复Patroni:
sudo gitlab-ctl patroni resume更多详情,请参阅此主题的Patroni文档。
在Patroni集群中升级PostgreSQL主版本
有关捆绑的PostgreSQL版本列表以及每个发行版的默认版本,请参阅《Linux软件包的PostgreSQL版本》。
在升级PostgreSQL之前,你需要考虑以下几个关键事实:
-
主要的一点是你必须关闭Patroni集群。这意味着你的GitLab部署会在数据库升级期间处于停机状态,或者至少在领导者节点升级时处于停机状态。具体停机时间取决于你的数据库大小。
-
升级PostgreSQL会创建一个包含新控制数据的新数据目录。从Patroni的角度来看,这是一个需要重新引导的新集群。因此,作为升级流程的一部分,存储在Consul中的集群状态会被清除。升级完成后,Patroni会引导一个新的集群。这会改变你的集群ID。
-
升级领导者和从节点的流程不同。这就是为什么在每个节点上使用正确的流程很重要。
-
升级从节点会删除数据目录,并使用配置的复制方法(仅可用选项为
pg_basebackup)从领导者重新同步数据。从节点追上领导者所需的时间取决于你的数据库大小。 -
升级流程概述可参考《Patroni文档》。你仍然可以使用
gitlab-ctl pg-upgrade,它在一些调整后实现了此流程。
基于这些考虑,你应该仔细规划PostgreSQL升级:
-
找出哪个节点是领导者,哪个节点是从节点:
gitlab-ctl patroni members在Geo二级站点上,Patroni领导者节点被称为
standby leader。 -
仅在从节点上停止Patroni。
sudo gitlab-ctl stop patroni -
在应用节点上启用维护模式:
sudo gitlab-ctl deploy-page up -
在领导者节点上升级PostgreSQL,并确保升级成功完成:
# 默认命令超时时间为600秒,可通过'--timeout'配置 sudo gitlab-ctl pg-upgradegitlab-ctl pg-upgrade尝试检测节点的角色。如果由于某种原因自动检测不起作用,或者你认为它未正确检测到角色,你可以使用--leader或--replica参数手动覆盖。更多可用选项的详细信息,请运行gitlab-ctl pg-upgrade --help。 -
检查领导者和集群的状态。只有当领导者健康时才能继续:
gitlab-ctl patroni check-leader # 或者 gitlab-ctl patroni members -
现在你可以在应用节点上禁用维护模式:
sudo gitlab-ctl deploy-page down -
在从节点上升级PostgreSQL(可以并行对所有从节点执行):
sudo gitlab-ctl pg-upgrade -
确保
pg_dump和pg_restore的兼容版本用于GitLab Rails实例,以避免备份或恢复时出现版本不匹配错误。可以通过在Rails实例上的/etc/gitlab/gitlab.rb中指定PostgreSQL版本来实现:postgresql['version'] = 16
如果在升级从节点时遇到问题,故障排除部分可能有解决方案。
使用gitlab-ctl revert-pg-upgrade回滚PostgreSQL升级与gitlab-ctl pg-upgrade有相同的注意事项。你应该遵循相同的流程:先停止从节点,然后回滚领导者,最后回滚从节点。
在Patroni集群中实现接近零停机的PostgreSQL升级
- 状态:实验
Patroni允许你在不停机的情况下执行主要的PostgreSQL升级。然而,这需要额外的资源来托管带有升级后PostgreSQL的新Patroni节点。在实践中,通过此流程,你将:
- 创建一个包含新版本PostgreSQL的新Patroni集群。
- 将数据从现有集群迁移过来。
该流程是非侵入式的,在切换关闭之前不会影响现有集群。但是,它既耗时又消耗资源。请考虑其与可用性的权衡。
按顺序的步骤如下:
- 为新集群准备资源。
- 预检。
- 配置新集群的leader节点。
- 在现有leader上启动publisher。
- 从现有集群复制数据。
- 从现有集群复制数据(逻辑复制)。
- 扩展新集群。
- 将应用切换到使用新集群。
- 清理。
为新集群准备资源
你需要一组新的资源用于Patroni节点。新Patroni集群不一定需要与现有集群完全相同数量的节点。你可以根据需求选择不同数量的节点。新集群使用现有的Consul集群(具有不同的patroni['scope'])和PgBouncer节点。
确保现有集群的至少leader节点可从新集群的节点访问。
预检
我们依赖PostgreSQL 逻辑复制 来支持Patroni集群的接近零停机升级。逻辑复制的限制条件 必须满足。特别是,wal_level 必须设为 logical。要检查 wal_level,请在现有集群的任意节点上使用 gitlab-psql 运行以下命令:
SHOW wal_level;默认情况下,Patroni将 wal_level 设为 replica。你必须将其提升至 logical。更改 wal_level 需要重启PostgreSQL,因此这一步会导致短暂停机(故称为接近零停机)。在Patroni leader节点上执行此操作:
-
编辑
gitlab.rb并设置:patroni['postgresql']['wal_level'] = 'logical' -
运行
gitlab-ctl reconfigure。这将写入配置但不重启PostgreSQL服务。 -
运行
gitlab-ctl patroni restart以重启PostgreSQL并应用新的wal_level,同时避免触发故障转移。在重启周期内,集群leader不可用。 -
使用
gitlab-psql运行SHOW wal_level验证变更。
配置新集群的leader节点
配置新集群的第一个节点。它将成为新集群的leader。如果现有集群的配置与新PostgreSQL版本兼容,可以使用现有集群的配置。参考关于配置Patroni集群 的文档。
除了通用配置外,你必须在 gitlab.rb 中应用以下设置:
-
确保新Patroni集群使用不同的scope。Scope用于在Consul中对Patroni设置进行命名空间划分,使得可以在同一Consul集群中使用现有和新集群。
patroni['scope'] = 'postgresql_new-ha' -
确保Consul代理不会混淆现有和新Patroni集群提供的PostgreSQL服务。为此,你必须使用内部属性:
consul['internal']['postgresql_service_name'] = 'postgresql_new'
在现有leader上启动publisher
在现有leader上,使用 gitlab-psql 运行以下SQL语句以启动逻辑复制publisher:
CREATE PUBLICATION patroni_upgrade FOR ALL TABLES;从现有集群复制数据
要从现有集群转储当前数据库,在新集群的leader上运行以下命令:
-
可选。复制全局数据库对象:
pg_dumpall -h ${EXISTING_CLUSTER_LEADER} -U gitlab-psql -g | gitlab-psql你可以忽略关于现有数据库对象的错误,例如角色。这些对象在节点首次配置时会被创建。
-
复制当前数据库:
pg_dump -h ${EXISTING_CLUSTER_LEADER} -U gitlab-psql -d gitlabhq_production -s | gitlab-psql根据你的数据库大小,此命令可能需要一段时间才能完成。
pg_dump 和 pg_dumpall 命令位于 /opt/gitlab/embedded/bin 中。在这些命令中,EXISTING_CLUSTER_LEADER 是现有集群leader节点的地址。
gitlab-psql 用户必须能够从新集群的leader节点认证现有leader。
从现有集群复制数据
在执行初始数据转储后,你必须让新leader与现有集群的最新更改保持同步。在新leader上,使用 gitlab-psql 运行以下SQL语句以订阅现有leader的发布:
CREATE SUBSCRIPTION patroni_upgrade
CONNECTION 'host=EXISTING_CLUSTER_LEADER dbname=gitlabhq_production user=gitlab-psql'
PUBLICATION patroni_upgrade;在此语句中,EXISTING_CLUSTER_LEADER 是现有集群leader节点的地址。你也可以使用 其他参数 来修改连接字符串。例如,你可以传递认证密码。
要检查复制状态,运行以下查询:
- 在现有leader(发布者)上运行
SELECT * FROM pg_replication_slots WHERE slot_name = 'patroni_upgrade'。 - 在新leader(订阅者)上运行
SELECT * FROM pg_stat_subscription。
扩展新集群
按照你 配置leader的方式 配置新集群的其他节点。确保使用相同的 patroni['scope'] 和 consul['internal']['postgresql_service_name']。
此处发生的情况:
- 应用程序仍然使用现有leader作为其数据库后端。
- 逻辑复制确保新leader保持同步。
- 当其他节点被添加到新集群时,Patroni会处理这些节点之间的复制。
等待新集群的副本节点初始化并赶上复制延迟是一个好主意。
将应用程序切换到新集群
到目前为止,你可以在不丢失现有集群数据的情况下停止升级过程。当你将应用程序的数据库后端切换并指向新集群时,旧集群不再接收新的更新。它会落后于新集群。在此之后,任何恢复都必须从新集群的节点进行。
要在所有PgBouncer节点上进行切换:
-
编辑
gitlab.rb并设置:consul['watchers'] = %w(postgresql_new) consul['internal']['postgresql_service_name'] = 'postgresql_new' -
运行
gitlab-ctl reconfigure。
清理
完成这些步骤后,你可以清理旧Patroni集群的资源。它们不再需要。但是,在移除资源之前,请通过运行 DROP SUBSCRIPTION patroni_upgrade 使用 gitlab-psql 删除新leader上的逻辑复制订阅。