{{< details >}}

  • Tier: 专业版,旗舰版
  • Offering: 私有化部署

{{< /details >}}

如果你是极狐GitLab 私有化部署的基础版用户,建议使用云托管解决方案。本文档不涉及自行编译安装。

如果您寻找的不是复制和故障转移的设置,请参阅 Linux 软件包的数据库配置文档

建议在尝试配置极狐GitLab 的 PostgreSQL 复制和故障转移之前完整阅读本文档。

操作系统升级

如果您要故障转移到一个不同操作系统的系统,请阅读PostgreSQL 操作系统升级文档。 如果在操作系统升级时未考虑本地更改,可能会导致数据损坏。

架构

Linux 软件包推荐的 PostgreSQL 集群配置,其复制故障转移要求:

  1. 至少三个 PostgreSQL 节点。
  2. 至少三个 Consul 服务器节点。
  3. 至少三个 PgBouncer 节点,负责跟踪和处理主数据库的读写操作。
    • 内部负载均衡器 (TCP) 在 PgBouncer 节点之间平衡请求。
  4. 启用数据库负载均衡
    • 在每个 PostgreSQL 节点上配置一个本地 PgBouncer 服务。这与跟踪主节点的主 PgBouncer 集群是分开的。

您还需要考虑底层网络拓扑,确保在所有数据库和极狐GitLab 实例之间有冗余连接,以避免网络成为单点故障。

数据库节点

每个数据库节点运行四个服务:

  1. PostgreSQL:数据库本身。
  2. Patroni:与集群中的其他 Patroni 服务通信,并在领导服务器出现问题时进行故障转移。故障转移过程包括:
    • 为集群选择新的领导者。
    • 提升新的节点为领导者。
    • 指示其余服务器跟随新的领导节点。
  3. PgBouncer:节点的本地连接池。用于_读取_查询,作为数据库负载均衡的一部分。
  4. Consul 代理:与存储当前 Patroni 状态的 Consul 集群通信。代理监控数据库集群中每个节点的状态,并在 Consul 集群中跟踪其健康状态。

Consul 服务器节点

Consul 服务器节点运行 Consul 服务器服务。这些节点在 Patroni 集群引导之前必须达到法定人数并选出领导者;否则,数据库节点会等待选出这样的 Consul 领导者。

PgBouncer 节点

每个 PgBouncer 节点运行两个服务:

  1. PgBouncer:数据库连接池本身。
  2. Consul 代理:监视 Consul 集群中的 PostgreSQL 服务定义状态。如果状态发生变化,Consul 运行一个脚本,将 PgBouncer 配置更新为指向新的 PostgreSQL 领导节点并重新加载 PgBouncer 服务。

连接流程

软件包中的每个服务都附带一组默认端口。您可能需要为下面列出的连接制定特定的防火墙规则:

在此设置中有几种连接流程:

  1. Primary
  2. Database Load Balancing
  3. Replication

Primary

  1. 应用服务器通过其默认端口或通过配置的内部负载均衡器 (TCP) 直接连接到 PgBouncer,该负载均衡器服务于多个 PgBouncers。
  2. PgBouncer 连接到主数据库服务器的 PostgreSQL 默认端口

Database Load Balancing

对于最近未更改且在所有数据库节点上都已更新的读取查询:

  1. 应用服务器通过其默认端口以轮询方式连接到每个数据库节点上的本地 PgBouncer 服务。
  2. 本地 PgBouncer 连接到本地数据库服务器的 PostgreSQL 默认端口

Replication

  1. Patroni 主动管理正在运行的 PostgreSQL 进程和配置。
  2. PostgreSQL 从服务器连接到主数据库服务器的 PostgreSQL 默认端口
  3. Consul 服务器和代理通过 Consul 默认端口相互连接。

设置

所需信息

在进行配置之前,您需要收集所有必要的信息。

网络信息

PostgreSQL 默认情况下不监听任何网络接口。它需要知道要监听的 IP 地址,以便可被其他服务访问。同样,PostgreSQL 访问是基于网络源进行控制的。

这就是为什么您需要:

  1. 每个节点网络接口的 IP 地址。可以设置为 0.0.0.0 以监听所有接口。不能设置为回环地址 127.0.0.1
  2. 网络地址。可以以子网形式(即 192.168.0.0/255.255.255.0)或无类域间路由 (CIDR)(192.168.0.0/24)形式表示。

Consul 信息

使用默认设置时,最低配置要求:

  1. CONSUL_USERNAME。Linux 软件包安装的默认用户是 gitlab-consul
  2. CONSUL_DATABASE_PASSWORD。数据库用户的密码。
  3. CONSUL_PASSWORD_HASH。这是根据 Consul 用户名/密码对生成的哈希。可以通过以下方式生成:

    sudo gitlab-ctl pg-password-md5 CONSUL_USERNAME
    
  4. CONSUL_SERVER_NODES。Consul 服务器节点的 IP 地址或 DNS 记录。

关于服务本身的一些说明:

  1. 服务在系统账户下运行,默认情况下为 gitlab-consul
  2. 如果您使用不同的用户名,则必须通过 CONSUL_USERNAME 变量指定。
  3. 密码存储在以下位置:
    • /etc/gitlab/gitlab.rb:哈希
    • /var/opt/gitlab/pgbouncer/pg_auth:哈希
    • /var/opt/gitlab/consul/.pgpass:明文

PostgreSQL 信息

配置 PostgreSQL 时,我们执行以下操作:

  1. max_replication_slots 设置为数据库节点数量的两倍。Patroni 在启动复制时会使用每个节点的一个额外插槽。
  2. max_wal_senders 设置为集群中分配的复制插槽数量加一。这可以防止复制使用所有可用的数据库连接。

在本文档中,我们假设有 3 个数据库节点,这使得此配置为:

patroni['postgresql']['max_replication_slots'] = 6
patroni['postgresql']['max_wal_senders'] = 7

如前所述,准备需要身份验证才能与数据库进行身份验证的网络子网。 您还需要掌握 Consul 服务器节点的 IP 地址或 DNS 记录。

您需要以下应用程序数据库用户的密码信息:

  1. POSTGRESQL_USERNAME。Linux 软件包安装的默认用户是 gitlab
  2. POSTGRESQL_USER_PASSWORD。数据库用户的密码。
  3. POSTGRESQL_PASSWORD_HASH。这是根据用户名/密码对生成的哈希。可以通过以下方式生成:

    sudo gitlab-ctl pg-password-md5 POSTGRESQL_USERNAME
    

Patroni 信息

您需要以下 Patroni API 的密码信息:

  1. PATRONI_API_USERNAME。用于 API 的基本身份验证用户名。
  2. PATRONI_API_PASSWORD。用于 API 的基本身份验证密码。

PgBouncer 信息

使用默认设置时,最低配置要求:

  1. PGBOUNCER_USERNAME。Linux 软件包安装的默认用户是 pgbouncer
  2. PGBOUNCER_PASSWORD。这是 PgBouncer 服务的密码。
  3. PGBOUNCER_PASSWORD_HASH。这是根据 PgBouncer 用户名/密码对生成的哈希。可以通过以下方式生成:

    sudo gitlab-ctl pg-password-md5 PGBOUNCER_USERNAME
    
  4. PGBOUNCER_NODE,是运行 PgBouncer 的节点的 IP 地址或 FQDN。

关于服务本身需要记住的几点:

  1. 服务以与数据库相同的系统账户运行。在软件包中,这默认情况下为 gitlab-psql
  2. 如果您为 PgBouncer 服务使用非默认用户账户(默认情况下为 pgbouncer),则需要指定此用户名。
  3. 密码存储在以下位置:
    • /etc/gitlab/gitlab.rb:哈希和明文
    • /var/opt/gitlab/pgbouncer/pg_auth:哈希

安装 Linux 软件包

首先,请确保在每个节点下载并安装 Linux 软件包。

确保从步骤 1 安装必要的依赖项, 从步骤 2 添加极狐GitLab 软件包库。 安装极狐GitLab 软件包时,不要提供 EXTERNAL_URL 值。

配置数据库节点

  1. 确保配置 Consul 节点
  2. 确保在执行下一个步骤之前收集CONSUL_SERVER_NODESPGBOUNCER_PASSWORD_HASHPOSTGRESQL_PASSWORD_HASH数据库节点数量网络地址

配置 Patroni 集群

必须显式启用 Patroni 才能使用它(通过 patroni['enable'] = true)。

任何控制复制的 PostgreSQL 配置项,例如 wal_levelmax_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 用户配置
# 根据所需信息部分中解释的设置真实值
#
# 用生成的 md5 值替换 PGBOUNCER_PASSWORD_HASH
postgresql['pgbouncer_user_password'] = 'PGBOUNCER_PASSWORD_HASH'
# 用生成的 md5 值替换 POSTGRESQL_REPLICATION_PASSWORD_HASH
postgresql['sql_replication_password'] = 'POSTGRESQL_REPLICATION_PASSWORD_HASH'
# 用生成的 md5 值替换 POSTGRESQL_PASSWORD_HASH
postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH'

# 用 Patroni Rest API 调用的用户名替换 PATRONI_API_USERNAME(在所有节点上使用相同的用户名)
patroni['username'] = 'PATRONI_API_USERNAME'
# 用 Patroni Rest API 调用的密码替换 PATRONI_API_PASSWORD(在所有节点上使用相同的密码)
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

# 用您的其他 patroni 节点的网络地址替换 XXX.XXX.XXX.XXX/YY
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)
}
#
# END 用户配置

所有数据库节点使用相同的配置。领导节点在配置中未确定, 对于领导节点或副本节点没有额外或不同的配置。

节点配置完成后,您必须在每个节点上重新配置极狐GitLab,以使更改生效。

通常,当 Consul 集群准备好时,第一个重新配置的节点成为领导者。您无需排序节点重新配置。您可以并行或以任何顺序运行它们。 如果您选择任意顺序,则没有预定的领导者。

启用监控

如果启用监控,则必须在所有数据库服务器上启用。

  1. 创建/编辑 /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'
    
  2. 运行 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 节点上为 API 服务器和客户端使用不同的证书和密钥,只要它们可以验证。但是,CA 证书(patroni['tls_ca_file'])、TLS 证书验证(patroni['tls_verify'])和客户端 TLS 认证模式(patroni['tls_client_mode'])必须在所有节点上具有相同的值。

配置 PgBouncer 节点

  1. 确保在执行下一个步骤之前收集CONSUL_SERVER_NODESCONSUL_PASSWORD_HASHPGBOUNCER_PASSWORD_HASH

  2. 在每个节点上,编辑 /etc/gitlab/gitlab.rb 配置文件并替换 # START 用户配置 部分中记录的值,如下所示:

    # 禁用除 PgBouncer 和 Consul 代理外的所有组件
    roles(['pgbouncer_role'])
    
    # 配置 PgBouncer
    pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
    
    # 配置 Consul 代理
    consul['watchers'] = %w(postgresql)
    
    # START 用户配置
    # 根据所需信息部分中解释的设置真实值
    # 用生成的 md5 值替换 CONSUL_PASSWORD_HASH
    # 用生成的 md5 值替换 PGBOUNCER_PASSWORD_HASH
    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 用户配置
    
  3. 运行 gitlab-ctl reconfigure

  4. 创建 .pgpass 文件,以便 Consul 能够重新加载 PgBouncer。被询问时输入 PGBOUNCER_PASSWORD 两次:

    gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
    
  5. 启用监控

PgBouncer 检查点

  1. 确保每个节点正在与当前节点领导者通信:

    gitlab-ctl pgb-console # 提示时提供 PGBOUNCER_PASSWORD
    

    如果在输入密码后出现 psql: ERROR: Auth failed 错误,请确保您之前已使用正确格式生成 MD5 密码哈希。 正确的格式是将密码和用户名连接起来:PASSWORDUSERNAME。例如,Sup3rS3cr3tpgbouncer 是为 pgbouncer 用户生成 MD5 密码哈希所需的文本。

  2. 控制台提示可用后,运行以下查询:

    show databases ; show clients ;
    

    输出应类似于以下内容:

            name         |  host       | port |      database       | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections
    ---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+---------------------
     gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production |            |        20 |            0 |           |               0 |                   0
     pgbouncer           |             | 6432 | pgbouncer           | pgbouncer  |         2 |            0 | statement |               0 |                   0
    (2 rows)
    
     type |   user    |      database       |  state  |   addr         | port  | local_addr | local_port |    connect_time     |    request_time     |    ptr    | link | remote_pid | 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 |
    (2 rows)
    

配置内部负载均衡器

如果您按推荐运行多个 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 服务。您可能设置了其他属性,但需要设置以下属性。

  1. 编辑 /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'] }
    
  2. 重新配置极狐GitLab,以使更改生效。

应用节点后配置

确保所有迁移均已运行:

gitlab-rake gitlab:db:configure

注意:如果您遇到 rake aborted! 错误,表明 PgBouncer 无法连接到 PostgreSQL,可能是由于您的 PgBouncer 节点的 IP 地址在数据库节点上的 gitlab.rb 中的 PostgreSQL 的 trust_auth_cidr_addresses 中缺失。请参阅PgBouncer 错误 ERROR: pgbouncer cannot connect to server 后再继续。

备份

不要通过 PgBouncer 连接备份或恢复极狐GitLab:这会导致极狐GitLab 中断。

了解有关此内容的更多信息以及如何重新配置备份

确保极狐GitLab 正在运行

此时,您的极狐GitLab 实例应该已启动并运行。验证您能够登录并创建议题和合并请求。有关更多信息,请参阅复制和故障转移故障排除

示例配置

本节描述了一些完全展开的示例配置。

示例推荐设置

此示例使用三个 Consul 服务器、三个 PgBouncer 服务器(以及相关的内部负载均衡器)、三个 PostgreSQL 服务器和一个应用节点。

在此设置中,所有服务器共享相同的 10.6.0.0/16 私有网络范围。 服务器通过这些地址自由通信。

虽然您可以使用不同的网络设置,但建议确保允许集群之间进行同步复制。 一般规则是,延迟小于 2 毫秒可确保复制操作的性能。

极狐GitLab 参考架构的大小假设应用程序数据库查询由所有三个节点共享。 通信延迟超过 2 毫秒可能会导致数据库锁定,并影响副本及时提供只读查询的能力。

  • 10.6.0.22:PgBouncer 2
  • 10.6.0.23:PgBouncer 3
  • 10.6.0.31:PostgreSQL 1
  • 10.6.0.32:PostgreSQL 2
  • 10.6.0.33:PostgreSQL 3
  • 10.6.0.41:极狐GitLab 应用

所有密码均设置为 toomanysecrets。请勿使用此密码或派生哈希,极狐GitLab 的 external_urlhttp://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 代理外的所有组件
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 节点(在此示例中,IP 为 10.6.0.20)。如何实现此功能的示例可以在 PgBouncer 配置内部负载均衡器部分找到。

PostgreSQL 服务器的示例推荐设置

在数据库节点上编辑 /etc/gitlab/gitlab.rb

# 禁用除 Patroni、PgBouncer 和 Consul 外的所有组件
roles(['patroni_role', 'pgbouncer_role'])

# PostgreSQL 配置
postgresql['listen_address'] = '0.0.0.0'
postgresql['hot_standby'] = 'on'
postgresql['wal_level'] = 'replica'

# 禁用自动数据库迁移
gitlab_rails['auto_migrate'] = false

postgresql['pgbouncer_user_password'] = '771a8625958a529132abe6f1a4acb19c'
postgresql['sql_user_password'] = '450409b85a0223a214b5fb1484f34d0f'
patroni['username'] = 'PATRONI_API_USERNAME'
patroni['password'] = 'PATRONI_API_PASSWORD'
patroni['postgresql']['max_replication_slots'] = 6
patroni['postgresql']['max_wal_senders'] = 7

patroni['allowlist'] = = %w(10.6.0.0/16 127.0.0.1/32)
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/16 127.0.0.1/32)

# 用于数据库负载均衡的本地 PgBouncer 服务
pgbouncer['databases'] = {
  gitlabhq_production: {
    host: "127.0.0.1",
    user: "pgbouncer",
    password: '771a8625958a529132abe6f1a4acb19c'
  }
}

# 配置 Consul 代理
consul['services'] = %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以使更改生效。

示例推荐设置手动步骤

部署配置后,请执行以下步骤:

  1. 找到主数据库节点:

    gitlab-ctl get-postgresql-primary
    
  2. 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+ 集群和 Geo 部署级联复制的唯一选项。

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) ------+---------+---------+----+-----------+
| Member                              | Host         | Role    | State   | TL | Lag in MB |
+-------------------------------------+--------------+---------+---------+----+-----------+
| gitlab-database-1.example.com       | 172.18.0.111 | Replica | running |  5 |         0 |
| gitlab-database-2.example.com       | 172.18.0.112 | Replica | running |  5 |       100 |
| gitlab-database-3.example.com       | 172.18.0.113 | Leader  | running |  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 字段与已复制的预写日志段有关。 在领导者上运行以下命令以找出当前日志序列号 (LSN):

echo 'SELECT pg_current_wal_lsn();' | gitlab-psql

如果副本不同步,gitlab-ctl patroni members 指示缺失数据的数量,并且 lag 字段指示经过的时间。

副本应该返回:

-[ 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

选择适当的 Patroni 复制方法

仔细查看 Patroni 文档。在进行更改之前,因为某些选项如果不充分理解,会有潜在的数据丢失风险。确定可容忍的数据丢失量。

{{< alert type=”warning” >}}

复制不是备份策略!没有替代经过深思熟虑和测试的备份解决方案。

{{< /alert >}}

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)与领导者进行通信。对于此用户,您可以选择 trustmd5 身份验证。如果设置了 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-leadercheck-replica,用于指示节点是否为主节点或副本。

启用 Patroni 后,它将专门控制 PostgreSQL 的启动、关闭和重启。这意味着要关闭某个节点上的 PostgreSQL,您必须在同一节点上关闭 Patroni:

sudo gitlab-ctl stop patroni

在领导节点上停止或重新启动 Patroni 服务会触发自动故障转移。如果需要 Patroni 重新加载其配置或重新启动 PostgreSQL 进程而不触发故障转移,必须使用 gitlab-ctl patronireloadrestart 子命令。这两个子命令是相同 patronictl 命令的包装器。

Patroni 的手动故障转移过程

{{< alert type=”warning” >}}

在极狐GitLab 16.5 和更早版本中,PgBouncer 节点不会自动与 Patroni 节点一起故障转移。PgBouncer 服务 必须手动重启 才能成功切换。

{{< /alert >}}

虽然 Patroni 支持自动故障转移,但您也可以执行手动故障转移,并且有两种稍有不同的选择:

  • 故障转移:在没有健康节点时允许您进行手动故障转移。您可以在任何 PostgreSQL 节点上执行此操作:

    sudo gitlab-ctl patroni failover
    
  • 切换:仅在集群健康时有效,并允许您安排切换(可以立即发生)。您可以在任何 PostgreSQL 节点上执行此操作:

    sudo gitlab-ctl patroni switchover
    

Geo 次要站点注意事项

当 Geo 次要站点从使用 PatroniPgBouncer 的主站点进行复制时,通过 PgBouncer 进行复制是不支持的。

建议在主站点引入负载均衡器以自动处理 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 集群中升级 PostgreSQL 主版本

有关捆绑的 PostgreSQL 版本以及每个版本的默认版本的列表,请参阅 Linux 软件包的 PostgreSQL 版本

在升级 PostgreSQL 之前,您必须考虑以下几点关键事实:

  • 主要的一点是您必须 关闭 Patroni 集群。这意味着您的极狐GitLab 部署在数据库升级期间或至少在领导节点升级期间会停机。这可能会根据您的数据库大小 造成显著的停机时间

  • 升级 PostgreSQL 会创建一个新的数据目录和新的控制数据。从 Patroni 的角度来看,这是一个需要重新引导的新集群。因此,作为升级程序的一部分,集群状态(存储在 Consul 中)将被清除。升级完成后,Patroni 将引导一个新的集群。这将更改您的 集群 ID

  • 升级领导节点和副本节点的过程不同。这就是为什么在每个节点上使用正确的过程很重要。

  • 升级副本节点 删除数据目录并从领导节点重新同步,使用配置的复制方法(pg_basebackup 是唯一可用的选项)。根据您的数据库大小,副本可能需要一些时间才能赶上领导节点。

  • 升级过程概述在 Patroni 文档中。您仍然可以使用 gitlab-ctl pg-upgrade,它实现了该过程并进行了少量调整。

考虑到这些因素,您应该仔细规划您的 PostgreSQL 升级:

  1. 找出哪个节点是领导节点,哪个节点是副本:

    gitlab-ctl patroni members
    

    {{< alert type=”note” >}}

    在 Geo 次要站点上,Patroni 领导节点被称为 standby leader

    {{< /alert >}}

  2. 仅在副本上停止 Patroni。

    sudo gitlab-ctl stop patroni
    
  3. 应用节点上启用维护模式:

    sudo gitlab-ctl deploy-page up
    
  4. 升级 领导节点上的 PostgreSQL,并确保升级成功完成:

    # 默认命令超时为 600s,可通过 '--timeout' 配置
    sudo gitlab-ctl pg-upgrade
    

    {{< alert type=”note” >}}

    gitlab-ctl pg-upgrade 试图检测节点的角色。如果由于任何原因自动检测不起作用,或者您认为它没有正确检测角色,可以使用 --leader--replica 参数手动覆盖。有关可用选项的更多详细信息,请使用 gitlab-ctl pg-upgrade --help

    {{< /alert >}}

  5. 检查领导节点和集群的状态。只有在您有健康的领导节点时才能继续:

    gitlab-ctl patroni check-leader
    
    # 或者
    
    gitlab-ctl patroni members
    
  6. 您现在可以在 应用节点上禁用维护模式:

    sudo gitlab-ctl deploy-page down
    
  7. 在副本上升级 PostgreSQL(您可以在所有副本上并行进行):

    sudo gitlab-ctl pg-upgrade
    

如果在升级副本时遇到问题,有一个故障排除部分可能是解决方案。

{{< alert type=”note” >}}

使用 gitlab-ctl revert-pg-upgrade 还原 PostgreSQL 升级与 gitlab-ctl pg-upgrade 有相同的考虑。您应该遵循相同的程序,首先停止副本,然后还原领导节点,最后还原副本。

{{< /alert >}}

在 Patroni 集群中几乎零停机时间升级 PostgreSQL

{{< details >}}

  • 状态:实验

{{< /details >}}

Patroni 使您能够在不关闭集群的情况下运行 PostgreSQL 的重大升级。然而,这需要额外的资源来托管具有升级后 PostgreSQL 的新 Patroni 节点。实际上,使用此过程,您正在:

  • 创建一个新的 Patroni 集群,具有新版本的 PostgreSQL。
  • 从现有集群迁移数据。

该过程是非侵入性的,在关闭之前不会影响现有集群。然而,它可能耗时且资源消耗大。考虑它们与可用性之间的权衡。

步骤,按顺序:

  1. 为新集群准备资源
  2. 飞行前检查
  3. 配置新集群的领导节点
  4. 在现有领导节点上启动发布者
  5. 从现有集群复制数据
  6. 从现有集群复制数据
  7. 扩展新集群
  8. 切换应用程序以使用新集群
  9. 清理

为新集群准备资源

您需要为 Patroni 节点准备一组新资源。新的 Patroni 集群不需要与现有集群完全相同数量的节点。您可以根据需要选择不同数量的节点。新集群使用现有的 Consul 集群(具有不同的 patroni['scope'])和 PgBouncer 节点。

确保现有集群的领导节点至少可以从新集群的节点访问。

飞行前检查

我们依赖 PostgreSQL 逻辑复制支持 Patroni 集群的几乎零停机时间升级。逻辑复制要求必须满足。特别是,wal_level 必须是 logical。要检查 wal_level,请在现有集群的任何节点上使用 gitlab-psql 运行以下命令:

SHOW wal_level;

默认情况下,Patroni 将 wal_level 设置为 replica。您必须将其提高到 logical。更改 wal_level 需要重新启动 PostgreSQL,因此此步骤导致短暂的停机时间(因此接近零停机时间)。要在 Patroni 领导节点上执行此操作:

  1. 编辑 gitlab.rb 设置:

    patroni['postgresql']['wal_level'] = 'logical'
    
  2. 运行 gitlab-ctl reconfigure。这会写入配置但不重新启动 PostgreSQL 服务。
  3. 运行 gitlab-ctl patroni restart 以重新启动 PostgreSQL 并应用新的 wal_level 而不触发故障转移。在重启周期期间,集群领导节点不可用。
  4. 使用 gitlab-psql 运行 SHOW wal_level 验证更改。

配置新集群的领导节点

配置新集群的第一个节点。它成为新集群的领导节点。您可以使用现有集群的配置,如果它与新的 PostgreSQL 版本兼容。请参阅 配置 Patroni 集群 文档。

除了通用配置之外,您必须在 gitlab.rb 中应用以下内容,以确保新的 Patroni 集群使用不同的作用域。作用域用于在 Consul 中对 Patroni 设置进行命名空间,使得可以对现有和新集群使用相同的 Consul 集群。

patroni['scope'] = 'postgresql_new-ha'

确保 Consul 代理不会混淆现有和新 Patroni 集群提供的 PostgreSQL 服务。为此,您必须使用内部属性:

consul['internal']['postgresql_service_name'] = 'postgresql_new'

在现有领导节点上启动发布者

在现有领导节点上,使用 gitlab-psql 运行此 SQL 语句以启动逻辑复制发布者:

CREATE PUBLICATION patroni_upgrade FOR ALL TABLES;

从现有集群复制数据

要从现有集群转储当前数据库,请在新集群的 领导节点上运行以下命令:

  1. 可选。复制全局数据库对象:

    pg_dumpall -h ${EXISTING_CLUSTER_LEADER} -U gitlab-psql -g | gitlab-psql
    

    您可以忽略有关现有数据库对象(例如角色)的错误。它们是在第一次配置节点时创建的。

  2. 复制当前数据库:

    pg_dump -h ${EXISTING_CLUSTER_LEADER} -U gitlab-psql -d gitlabhq_production -s | gitlab-psql
    

    根据您的数据库大小,此命令可能需要一段时间才能完成。

pg_dumppg_dumpall 命令位于 /opt/gitlab/embedded/bin。在这些命令中,EXISTING_CLUSTER_LEADER 是现有集群领导节点的主机地址。

{{< alert type=”note” >}}

gitlab-psql 用户必须能够从新领导节点进行现有领导的身份验证。

{{< /alert >}}

从现有集群复制数据

在进行初始数据转储后,您必须保持新领导节点与现有集群的最新更改同步。在新领导节点上,使用 gitlab-psql 运行此 SQL 语句以订阅现有领导节点的发布:

CREATE SUBSCRIPTION patroni_upgrade
  CONNECTION 'host=EXISTING_CLUSTER_LEADER dbname=gitlabhq_production user=gitlab-psql'
  PUBLICATION patroni_upgrade;

在此语句中,EXISTING_CLUSTER_LEADER 是现有集群领导节点的主机地址。您还可以使用其他参数更改连接字符串。例如,您可以传递身份验证密码。

要检查复制状态,请运行以下查询:

  • 在现有领导节点(发布者)上运行 SELECT * FROM pg_replication_slots WHERE slot_name = 'patroni_upgrade'
  • 在新领导节点(订阅者)上运行 SELECT * FROM pg_stat_subscription

扩展新集群

按照您 配置领导节点 的方式配置新集群的其他节点。确保您使用相同的 patroni['scope']consul['internal']['postgresql_service_name']

这里发生了什么:

  • 应用程序仍然使用现有领导节点作为其数据库后端。
  • 逻辑复制确保新领导节点保持同步。
  • 当其他节点添加到新集群时,Patroni 处理节点的复制。

最好等待新集群的副本节点初始化并赶上复制滞后。

切换应用程序以使用新集群

到目前为止,您可以停止升级过程而不会丢失现有集群中的数据。当您切换应用程序的数据库后端并将其指向新集群时,旧集群不会接收新的更新。它会落后于新集群。在此之后,任何恢复都必须从新集群的节点进行。

要在 所有 PgBouncer 节点上进行切换:

  1. 编辑 gitlab.rb 设置:

    consul['watchers'] = %w(postgresql_new)
    consul['internal']['postgresql_service_name'] = 'postgresql_new'
    
  2. 运行 gitlab-ctl reconfigure

清理

完成这些步骤后,您可以清理旧 Patroni 集群的资源。它们不再需要。但是,在删除资源之前,请在新领导节点上运行 DROP SUBSCRIPTION patroni_upgrade 并使用 gitlab-psql 删除逻辑复制订阅。