数据库负载均衡
- Tier: Free, Premium, Ultimate
- Offering: GitLab Self-Managed
通过数据库负载均衡,只读查询可以分布在多个 PostgreSQL 节点上以提高性能。
此功能在 GitLab Rails 和 Sidekiq 中原生提供,可以将它们配置为以轮询方式平衡其数据库读取查询,无需任何外部依赖:
@startuml
!theme plain
card "**Internal Load Balancer**" as ilb
skinparam linetype ortho
together {
collections "**GitLab Rails** x3" as gitlab
collections "**Sidekiq** x4" as sidekiq
}
collections "**Consul** x3" as consul
card "Database" as database {
collections "**PGBouncer x3**\n//Consul//" as pgbouncer
card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary
collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary
pgbouncer --> postgres_primary
postgres_primary .r-> postgres_secondary
}
gitlab --> ilb
gitlab -[hidden]-> pgbouncer
gitlab .[norank]-> postgres_primary
gitlab .[norank]-> postgres_secondary
sidekiq --> ilb
sidekiq -[hidden]-> pgbouncer
sidekiq .[norank]-> postgres_primary
sidekiq .[norank]-> postgres_secondary
ilb --> pgbouncer
consul -r-> pgbouncer
consul .[norank]r-> postgres_primary
consul .[norank]r-> postgres_secondary
@enduml
启用数据库负载均衡的要求
要启用数据库负载均衡,请确保:
- HA PostgreSQL 设置有一个或多个复制主节点的辅助节点。
- 每个 PostgreSQL 节点都使用相同的凭据和相同的端口连接。
对于 Linux 软件包安装,您还需要在每个 PostgreSQL 节点上配置 PgBouncer,以便在配置多节点设置时池化所有负载均衡连接。
配置数据库负载均衡
数据库负载均衡可以通过以下两种方式之一进行配置:
主机
要配置主机列表,请在所有 GitLab Rails 和 Sidekiq 节点上为您想要平衡的每个环境执行以下步骤:
-
编辑
/etc/gitlab/gitlab.rb文件。 -
在
gitlab_rails['db_load_balancing']中,创建您想要平衡的数据库主机数组。例如,在 PostgreSQL 运行在主机primary.example.com、secondary1.example.com、secondary2.example.com上的环境中:gitlab_rails['db_load_balancing'] = { 'hosts' => ['primary.example.com', 'secondary1.example.com', 'secondary2.example.com'] }这些主机必须在配置了
gitlab_rails['db_port']的相同端口上可访问。 -
保存文件并重新配置 GitLab。
将主节点添加到主机列表是可选的,但建议这样做。 这使得主节点有资格进行负载均衡的读取查询,当主节点有处理这些查询的容量时,可以提高系统性能。 非常高流量的实例可能在主节点上没有足够的容量来充当读取副本。 无论主节点是否在此列表中,都将用于写入查询。
服务发现
服务发现允许 GitLab 自动检索要使用的 PostgreSQL 主机列表。它会定期检查 DNS A 记录,使用此记录返回的 IP 作为辅助节点的地址。要使服务发现工作,您只需要一个 DNS 服务器和一个包含辅助节点 IP 地址的 A 记录。
使用 Linux 软件包安装时,提供的 Consul 服务作为 DNS 服务器工作,并通过 postgresql-ha.service.consul 记录返回 PostgreSQL 地址。例如:
-
在每个 GitLab Rails / Sidekiq 节点上,编辑
/etc/gitlab/gitlab.rb并添加以下内容:gitlab_rails['db_load_balancing'] = { 'discover' => { 'nameserver' => 'localhost' 'record' => 'postgresql-ha.service.consul' 'record_type' => 'A' 'port' => '8600' 'interval' => '60' 'disconnect_timeout' => '120' } } -
保存文件并重新配置 GitLab 以使更改生效。
| 选项 | 描述 | 默认值 |
|---|---|---|
nameserver |
用于查找 DNS 记录的名称服务器。 | localhost |
record |
要查找的记录。此选项是服务发现工作所必需的。 | |
record_type |
要查找的可选记录类型。可以是 A 或 SRV。 |
A |
port |
名称服务器的端口。 | 8600 |
interval |
检查 DNS 记录之间的最小时间(秒)。 | 60 |
disconnect_timeout |
主机列表更新后,旧连接关闭的时间(秒)。 | 120 |
use_tcp |
使用 TCP 而不是 UDP 查找 DNS 资源 | false |
max_replica_pools |
每个 Rails 进程连接到的最大副本数。如果您运行大量的 Postgres 副本和大量的 Rails 进程,这很有用,因为如果没有此限制,默认情况下每个 Rails 进程都会连接到每个副本。如果未设置,默认行为是无限制的。 | nil |
如果 record_type 设置为 SRV,则 GitLab 继续使用轮询算法并忽略记录中的 weight 和 priority。因为 SRV 记录通常返回主机名而不是 IP,GitLab 需要在 SRV 响应的附加部分中查找返回主机名的 IP。如果找不到主机名的 IP,GitLab 需要查询配置的 nameserver 以获取每个此类主机名的 ANY 记录,查找 A 或 AAAA 记录,如果无法解析其 IP,则最终从轮换中删除此主机名。
interval 值指定检查之间的最小时间。如果 A 记录的 TTL 大于此值,则服务发现遵守所述 TTL。例如,如果 A 记录的 TTL 为 90 秒,则服务发现至少等待 90 秒才再次检查 A 记录。
当主机列表更新时,可能需要一段时间才能终止旧连接。disconnect_timeout 设置可用于强制执行终止所有旧数据库连接所需时间的上限。
处理过时读取
为了防止从过时的辅助节点读取数据,负载均衡器会检查它是否与主节点同步。如果数据足够新,则使用辅助节点,否则忽略它。为了减少这些检查的开销,我们只在特定时间间隔执行它们。
有三个配置选项会影响此行为:
| 选项 | 描述 | 默认值 |
|---|---|---|
max_replication_difference |
当辅助节点一段时间未复制数据时,允许滞后的数据量(字节)。 | 8 MB |
max_replication_lag_time |
在我们停止使用辅助节点之前,允许其滞后的最大秒数。 | 60 秒 |
replica_check_interval |
在检查辅助节点状态之前,我们必须等待的最小秒数。 | 60 秒 |
默认值应该足以满足大多数用户的需求。
要使用主机列表配置这些选项,请使用以下示例:
gitlab_rails['db_load_balancing'] = {
'hosts' => ['primary.example.com', 'secondary1.example.com', 'secondary2.example.com'],
'max_replication_difference' => 16777216, # 16 MB
'max_replication_lag_time' => 30,
'replica_check_interval' => 30
}日志记录
负载均衡器在 database_load_balancing.log 中记录各种事件,例如
- 当主机被标记为离线时
- 当主机重新上线时
- 当所有辅助节点都离线时
- 当由于查询冲突而在不同主机上重试读取时
日志是结构化的,每个条目都是一个 JSON 对象,至少包含:
- 用于过滤的
event字段。 - 人类可读的
message字段。 - 一些特定于事件的元数据。例如,
db_host - 始终记录的上下文信息。例如,
severity和time。
例如:
{"severity":"INFO","time":"2019-09-02T12:12:01.728Z","correlation_id":"abcdefg","event":"host_online","message":"Host came back online","db_host":"111.222.333.444","db_port":null,"tag":"rails.database_load_balancing","environment":"production","hostname":"web-example-1","fqdn":"gitlab.example.com","path":null,"params":null}实现细节
平衡查询
只读的 SELECT 查询在所有给定的主机之间平衡。其他所有内容(包括事务)都在主节点上执行。诸如 SELECT ... FOR UPDATE 之类的查询也在主节点上执行。
预处理语句
预处理语句在负载均衡时效果不佳,当启用负载均衡时会自动禁用。这不应该影响响应时间。
主节点粘性
执行写入后,GitLab 在特定时间内坚持使用主节点,范围限定为执行写入的用户。当辅助节点赶上或 30 秒后,GitLab 恢复使用辅助节点。
故障转移处理
在发生故障转移或数据库无响应的情况下,负载均衡器尝试使用下一个可用的主机。如果没有可用的辅助节点,则在主节点上执行操作。
如果在写入数据时发生连接错误,操作会使用指数退避重试最多 3 次。
使用负载均衡时,您应该能够安全地重启数据库服务器,而不会立即导致向用户显示错误。
开发指南
有关数据库负载均衡的详细开发指南,请参阅开发文档。