使用 Linux 软件包安装进行 PostgreSQL 复制和故障转移

如果您是极狐GitLab 私有化部署版的免费用户,请考虑使用云托管解决方案。本文档不适用于从源代码安装。

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

建议在尝试为 GitLab 配置具有复制和故障转移功能的 PostgreSQL 之前完整阅读本文档。

架构

Linux 软件包安装推荐的具有复制故障转移功能的 PostgreSQL 集群配置需要:

  • 至少三个 PostgreSQL 节点。
  • 至少三个 Consul 服务器节点。
  • 至少三个 PgBouncer 节点,用于跟踪和处理主数据库读取和写入。
    • 一个内部负载均衡器 (TCP) 来平衡 PgBouncer 节点之间的请求。
  • 数据库负载均衡已启用。
    • 在每个 PostgreSQL 节点上配置的本地 PgBouncer 服务。请注意,与跟踪主集群的主 PgBouncer 集群是分开的。

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

note从 13.3 版本开始,PostgreSQL 12 随 Linux 软件包安装一起提供。只有 Patroni 支持 PostgreSQL 12 的集群。有关更多详细信息,请参阅 Patroni 部分。从 14.0 版本开始,只有 PostgreSQL 12 随 Linux 软件包安装一起提供,因此 Patroni 成为复制和故障转移所必需的。

数据库节点

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

  • PostgreSQL - 数据库本身。
  • Patroni - 与集群中的其它 Patroni 服务通信,并在 leader 服务器出现问题时处理故障转移。故障转移过程包括:
    • 为集群选择一个新的 leader。
    • 将新节点提升为 leader。
    • 指示剩余服务器跟随新的 leader 节点。
  • PgBouncer:节点的本地 pooler。 用于 read 查询,作为数据库负载均衡的一部分。
  • Consul 代理 - 与存储当前 Patroni 状态的 Consul 集群通信。代理监控数据库集群中每个节点的状态,并在 Consul 集群上的服务定义中跟踪其健康状况。

Consul 服务器节点

Consul 服务器节点运行 Consul 服务器服务。这些节点必须在 Patroni 集群引导之前达到法定人数并选举了一个 leader,否则数据库节点会等到这样的 Consul leader 被选举出来。

PgBouncer 节点

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

PgBouncer - 数据库连接池本身。

Consul 代理 - 监视 Consul 集群上 PostgreSQL 服务定义的状态。如果该状态发生变化,Consul 会运行一个脚本来更新 PgBouncer 配置以指向新的 PostgreSQL leader 节点并重新加载 PgBouncer 服务。

连接流

此设置中有几个连接流:

Primary

  • 应用程序服务器通过其默认端口,或通过为多个 PgBouncer 提供服务的已配置内部负载均衡器 (TCP) 直接连接到 PgBouncer。
  • PgBouncer 连接到主数据库服务器的 PostgreSQL 默认端口。

数据库负载均衡

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

  • 应用服务器以循环方式通过每个数据库节点上的默认端口连接到本地 PgBouncer 服务。
  • 本地 PgBouncer 连接到本地数据库服务器的 PostgreSQL 默认端口。

Replication

  • Patroni 主动管理正在运行的 PostgreSQL 进程和配置。
  • PostgreSQL secondaries 连接到主数据库服务器的 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
    
  • CONSUL_SERVER_NODES。Consul 服务器节点的 IP 地址或 DNS 记录。

关于服务本身的几点说明:

  • 该服务在系统账号下运行,默认情况下为 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 在启动复制时为每个节点使用一个额外的 slot。
  • max_wal_senders 设置为比集群中分配的 replication slot 多一个。可以防止复制用完所有可用的数据库连接。

在本文档中,我们假设有 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 值。

配置数据库节点

  1. 确保配置了 consul 节点
  2. 确保收集了 CONSUL_SERVER_NODESPGBOUNCER_PASSWORD_HASHPOSTGRESQL_PASSWORD_HASHdb 节点数网络地址,然后再执行下一步。

配置 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'] 配置键来设置它。

notePatroni 节点的配置与 repmgr 非常相似,但更短。启用 Patroni 后,首先您可以忽略 PostgreSQL 的任何复制设置(无论如何它都会被覆盖)。然后可以删除任何 repmgr[...] 或 repmgr 特定的配置。特别是,请确保您删除了 postgresql['shared_preload_libraries'] = 'repmgr_funcs'

示例如下:

# Disable all components except Patroni, PgBouncer and Consul
roles(['patroni_role', 'pgbouncer_role'])

# PostgreSQL configuration
postgresql['listen_address'] = '0.0.0.0'

# Disable automatic database migrations
gitlab_rails['auto_migrate'] = false

# Configure the Consul agent
consul['services'] = %w(postgresql)

# START user configuration
# Please set the real values as explained in Required Information section
#
# Replace PGBOUNCER_PASSWORD_HASH with a generated md5 value
postgresql['pgbouncer_user_password'] = 'PGBOUNCER_PASSWORD_HASH'
# Replace POSTGRESQL_REPLICATION_PASSWORD_HASH with a generated md5 value
postgresql['sql_replication_password'] = 'POSTGRESQL_REPLICATION_PASSWORD_HASH'
# Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value
postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH'

# Replace PATRONI_API_USERNAME with a username for Patroni Rest API calls (use the same username in all nodes)
patroni['username'] = 'PATRONI_API_USERNAME'
# Replace PATRONI_API_PASSWORD with a password for Patroni Rest API calls (use the same password in all nodes)
patroni['password'] = 'PATRONI_API_PASSWORD'

# Sets `max_replication_slots` to double the number of database nodes.
# Patroni uses one extra slot per node when initiating the replication.
patroni['postgresql']['max_replication_slots'] = X

# Set `max_wal_senders` to one more than the number of replication slots in the cluster.
# This is used to prevent replication from using up all of the
# available database connections.
patroni['postgresql']['max_wal_senders'] = X+1

# Replace XXX.XXX.XXX.XXX/YY with Network Addresses for your other patroni nodes
patroni['allowlist'] = %w(XXX.XXX.XXX.XXX/YY 127.0.0.1/32)

# Replace XXX.XXX.XXX.XXX/YY with Network Address
postgresql['trust_auth_cidr_addresses'] = %w(XXX.XXX.XXX.XXX/YY 127.0.0.1/32)

# Local PgBouncer service for Database Load Balancing
pgbouncer['databases'] = {
  gitlabhq_production: {
    host: "127.0.0.1",
    user: "PGBOUNCER_USERNAME",
    password: 'PGBOUNCER_PASSWORD_HASH'
  }
}

# Replace placeholders:
#
# Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
# with the addresses gathered for CONSUL_SERVER_NODES
consul['configuration'] = {
  retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z)
}
#
# END user configuration

所有数据库节点都使用相同的配置。Leader 节点不是在配置中确定的,leader 节点或副本节点都没有额外的或不同的配置。

节点配置完成后,必须在每个节点上重新配置 Omnibus GitLab才能生效。

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

启用监控

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

  1. 创建/编辑 /etc/gitlab/gitlab.rb 并添加以下配置:

    # Enable service discovery for Prometheus
    consul['monitoring_service_discovery'] = true
    
    # Set the network addresses that the exporters will listen on
    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' # This is the plain-text password.

如果您使用的是自签名证书或内部 CA,则需要禁用 TLS 验证或通过内部 CA 的证书,否则在使用 gitlab-ctl parenti ... . 命令遇到无法预料的错误。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 不会检查任何客户端证书。
  • 可选:所有不安全 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 user configuration 部分中注明的值,如下所示:

    # Disable all components except PgBouncer and Consul agent
    roles(['pgbouncer_role'])
    
    # Configure PgBouncer
    pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
    
    # Configure Consul agent
    consul['watchers'] = %w(postgresql)
    
    # START user configuration
    # Please set the real values as explained in Required Information section
    # Replace CONSUL_PASSWORD_HASH with with a generated md5 value
    # Replace PGBOUNCER_PASSWORD_HASH with with a generated md5 value
    pgbouncer['users'] = {
      'gitlab-consul': {
        password: 'CONSUL_PASSWORD_HASH'
      },
      'pgbouncer': {
        password: 'PGBOUNCER_PASSWORD_HASH'
      }
    }
    # Replace placeholders:
    #
    # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
    # with the addresses gathered for CONSUL_SERVER_NODES
    consul['configuration'] = {
      retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z)
    }
    #
    # END user configuration
    
  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. 确保每个节点都在与当前 leader 节点通信:

    gitlab-ctl pgb-console # Supply PGBOUNCER_PASSWORD when prompted
    

    如果在输入密码后出现错误 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

    # Disable PostgreSQL on the application node
    postgresql['enable'] = false
    
    gitlab_rails['db_host'] = 'PGBOUNCER_NODE' or '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使更改生效。

应用节点后配置

确保所有 migration 都运行:

gitlab-rake gitlab:db:configure

Note: 如果您遇到一个 rake aborted! 错误,指出 PgBouncer 无法连接到 PostgreSQL,可能是您的 PgBouncer 节点的 IP 地址,在数据库节点上的 gitlab.rb 中的 PostgreSQL trust_auth_cidr_addresses 中丢失。在继续之前,请参阅故障排查部分中的 PgBouncer 错误 ERROR: pgbouncer cannot connect to server

备份

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

确保 GitLab 正在运行

此时,您的 GitLab 实例应该已启动并正在运行。验证您是否能够登录,并创建议题和合并请求。如果遇到问题,请参阅故障排查部分

示例配置

本节介绍几个完全扩展的示例配置。

示例推荐设置

此示例使用三台 Consul 服务器、三台 PgBouncer 服务器(带有关联的内部负载均衡器)、三台 PostgreSQL 服务器和一个应用程序节点。

我们从同一 10.6.0.0/16 专用网络范围内的所有服务器开始,它们可以在这些地址上自由连接。

以下是每台机器和分配的 IP 的列表和描述:

  • 10.6.0.11: Consul 1
  • 10.6.0.12: Consul 2
  • 10.6.0.13: Consul 3
  • 10.6.0.20: 内部负载均衡器
  • 10.6.0.21: PgBouncer 1
  • 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 leader 节点将更改为可用的 secondaries 节点之一,直到故障恢复。

Consul 服务器的推荐设置示例

在每个服务器上编辑 /etc/gitlab/gitlab.rb

# Disable all components except 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

重新配置 Omnibus GitLab 使更改生效。

PgBouncer 服务器的推荐设置示例

在每个服务器上编辑/etc/gitlab/gitlab.rb

# Disable all components except Pgbouncer and Consul agent
roles(['pgbouncer_role'])

# Configure 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

重新配置 Omnibus GitLab 使更改生效。

内部负载均衡器设置

需要设置一个内部负载均衡器 (TCP) 来为每个 PgBouncer 节点提供服务(在此示例中,IP 为 10.6.0.20)。可以在 PgBouncer 配置内部负载均衡器部分中找到如何执行此操作的示例。

PostgreSQL 服务器的推荐设置示例

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

# Disable all components except Patroni, PgBouncer and Consul
roles(['patroni_role', 'pgbouncer_role'])

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

# Disable automatic database migrations
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)

# Local PgBouncer service for Database Load Balancing
pgbouncer['databases'] = {
  gitlabhq_production: {
    host: "127.0.0.1",
    user: "pgbouncer",
    password: '771a8625958a529132abe6f1a4acb19c'
  }
}

# Configure the Consul agent
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

重新配置 Omnibus 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

notePostgreSQL 11 支持使用 Patroni 而不是 Repmgr,PostgreSQL 12 需要它。从 14.0 版本开始,只有 PostgreSQL 12 可用,因此 Patroni 是实现故障转移和复制所必需的。

Patroni 是 PostgreSQL 高可用性的一个固定的解决方案。它控制 PostgreSQL,覆盖其配置,并管理其生命周期(启动、停止、重启)。Patroni 是 PostgreSQL 12 集群和 Geo 部署级联复制的唯一选择。

Patroni 的基本架构(如上所述)不会改变。在配置数据库节点时,您不需要对 Patroni 进行任何特殊考虑。Patroni 严重依赖 Consul 来存储集群的状态并选举一个 leader。Consul 集群中的任何故障及其 leader 选举也会传播到 Patroni 集群。

Patroni 监控集群并处理任何故障转移。当主节点出现故障时,它会与 Consul 一起通知 PgBouncer。出现故障时,Patroni 会处理旧主服务器到副本的转换,并自动将其重新加入集群。

使用 Patroni,连接流程略有不同。每个节点上的 Patroni 连接到 Consul 代理以加入集群。只有在这之后,它才会决定节点是主节点还是副本节点。基于这个决定,它配置并启动 PostgreSQL,直接通过 Unix 套接字与之通信。这意味着如果 Consul 集群不起作用或没有 leader,则 Patroni 和扩展 PostgreSQL 不会启动。Patroni 还公开了一个 REST API,可以通过每个节点上的默认端口访问该 API。

检查复制状态

运行 gitlab-ctlsponi 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

可以在所有三个数据库服务器上运行相同的命令。它根据服务器正在执行的角色返回有关可用复制的任何信息。

leader 应该为每个副本返回一条记录:

-[ 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 字段与已复制的预写日志段相关。在 leader 上运行以下命令以找出当前的 LSN:

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

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

在 PostgreSQL 文档中阅读有关 leader 返回的数据的更多信息,包括其他值 state 字段。

副本应该返回:

-[ 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 文档,因为如果不完全理解某些选项可能会导致潜在的数据丢失。配置的复制模式决定了可容忍的数据丢失量。

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

Linux 软件包安装默认 synchronous_commiton

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 在重新加入数据库集群之前,尝试在前集群 leader 上运行 pg_rewind
remove_data_directory_on_rewind_failure 如果 pg_rewind 失败,删除本地 PostgreSQL 数据目录并从当前集群 leader 重新复制。
remove_data_directory_on_diverged_timelines 如果不能使用 pg_rewind,并且前任 leader 的时间线与当前的有分歧,则删除本地数据目录并从当前集群领导者重新复制。

Patroni 的数据库授权

此外,副本使用复制用户(默认为gitlab_replicator)与 leader 通信。对于此用户,您可以在 trustmd5 身份验证之间进行选择。如果您设置了postgresql['sql_replication_password'],Patroni 使用 md5 身份验证,否则回退到 trust。您必须分别在 postgresql['md5_auth_cidr_addresses']postgresql['trust_auth_cidr_addresses'] 中指定集群 CIDR。

与 Patroni 集群交互

您可以使用 gitlab-ctlsponi members 来查看集群成员的状态。为了检查每个节点的状态,gitlab-ctlsponi 提供了两个额外的子命令,check-leadercheck-replica,它们指示一个节点是主节点还是副本节点。

当启用 Patroni 时,它专门控制 PostgreSQL 的启动、关闭和重启。这意味着,要在某个节点上关闭 PostgreSQL,您必须使用以下命令在同一节点上关闭 Patroni:

sudo gitlab-ctl stop patroni

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

Patroni 的手动故障转移程序

虽然 Patroni 支持自动故障转移,您还可以执行手动故障转移,其中有两个略有不同的选项:

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

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

    sudo gitlab-ctl patroni switchover
    

有关此主题的更多详细信息,请参阅 Patroni 文档

Geo 次要站点注意事项

当 Geo 次要站点从使用 PatroniPgBouncer 的主站点复制时,不支持通过 PgBouncer 复制。次要节点必须直接从 Patroni 集群中的 leader 节点复制。当 Patroni 集群中出现自动或手动故障转移时,您可以手动重新指向您的次要站点以从新的 leader 复制:

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 关于此主题的文档

从 repmgr 切换到 Patroni

caution从 repmgr 切换到 Patroni 很简单,反之则。从 Patroni 回滚到 repmgr 可能很复杂,可能涉及删除数据目录。

您可以通过以下步骤将现有数据库集群切换为使用 Patroni 而不是 repmgr:

  1. 在所有副本节点上停止 repmgr,最后在主节点上停止:

    sudo gitlab-ctl stop repmgrd
    
  2. 在所有副本节点上停止 PostgreSQL:

    sudo gitlab-ctl stop postgresql
    
    note确保主节点上没有运行 walsender 进程。ps aux | grep walsender 不显示任何正在运行的进程。
  3. 在主节点上,配置 Patroni。删除 repmgr 和任何其它特定于 repmgr 的配置。还要删除与 PostgreSQL 复制相关的任何配置。
  4. 在主节点上重新配置 Omnibus GitLab。 这使它成为 leader。您可以通过以下方式检查:

    sudo gitlab-ctl tail patroni
    
  5. 对所有副本节点重复最后两个步骤。 gitlab.rb 应该在所有节点上看起来都一样。
  6. 如果存在,请删除主数据库上的 gitlab_repmgr 数据库和角色。如果不删除 gitlab_repmgr 数据库,将 PostgreSQL 11 升级到 12 会失败:

    could not load library "$libdir/repmgr_funcs": ERROR:  could not access file "$libdir/repmgr_funcs": No such file or directory
    

在 Patroni 集群中升级 PostgreSQL 主要版本

从 14.1 版本开始,PostgreSQL 12.6 和 13.3 默认都随 Linux 软件包一起提供。从 15.0 版本开始,PostgreSQL 13 是默认值。如果想在 15.0 之前的版本中升级到 PostgreSQL 13,您必须明确要求。

caution在 Patroni 集群中升级 PostgreSQL 的过程与使用 repmgr 升级时不同。以下概述了升级 PostgreSQL 时需要考虑的主要差异和重要注意事项。

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

  • 重点是您必须关闭 Patroni 集群。这意味着您的 GitLab 部署在数据库升级期间或至少在您的领导节点升级期间处于关闭状态。根据您的数据库的大小,这可能是相当长的停机时间

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

  • 升级 leader 和副本的过程是不一样的。这就是为什么在每个节点上使用正确的程序很重要的原因。

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

  • Patroni 的文档 概述了升级过程。您仍然可以使用gitlab-ctl pg-upgrade,它通过一些调整来实现这个过程。

考虑到这些,您应该仔细计划 PostgreSQL 升级:

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

    gitlab-ctl patroni members
    
    note在 Geo 次要站点上,Patroni leader 节点称为 standby leader
  2. 仅在副本上停止 Patroni。

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

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

    sudo gitlab-ctl pg-upgrade -V 13
    
    notegitlab-ctl pg-upgrade 尝试检测节点的角色。如果出于某种原因自动检测不起作用,或者您认为它没有正确检测到角色,您可以使用 --leader--replica 参数手动覆盖它。
  5. 检查 leader 和集群的状态。只有当您有一个健康的 leader 时,才能继续:

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

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

    sudo gitlab-ctl pg-upgrade -V 13
    
note使用 gitlab-ctl revert-pg-upgrade 恢复 PostgreSQL 升级与 gitlab-ctl pg-upgrade 具有相同的注意事项。您应该遵循相同的过程,首先停止副本,然后恢复 leader,最后恢复副本。

故障排查

Consul 和 PostgreSQL 更改未生效

由于潜在的影响,gitlab-ctl reconfigure 只会重新加载 Consul 和 PostgreSQL,不会重启服务。但是,并非所有更改都可以通过重新加载来激活。

要重新启动任一服务,请运行 gitlab-ctl restart SERVICE

对于 PostgreSQL,默认情况下重启 leader 节点通常是安全的。自动故障转移默认为 1 分钟超时。如果数据库在此之前返回,则无需执行任何其他操作。

在 Consul 服务器节点上,以受控方式重启 Consul 服务 很重要。

PgBouncer 错误 ERROR: pgbouncer cannot connect to server

您可能会在运行 gitlab-rake gitlab:db:configure 时收到此错误,或者您可能会在 PgBouncer 日志文件中看到该错误。

PG::ConnectionBad: ERROR:  pgbouncer cannot connect to server

问题可能是您的 PgBouncer 节点的 IP 地址未包含在数据库节点上 /etc/gitlab/gitlab.rb 中的 trust_auth_cidr_addresses 设置中。

您可以通过检查 leader 数据库节点上的 PostgreSQL 日志来确认这是问题所在。如果您看到以下错误,则问题是 trust_auth_cidr_addresses

2018-03-29_13:59:12.11776 FATAL:  no pg_hba.conf entry for host "123.123.123.123", user "pgbouncer", database "gitlabhq_production", SSL off

要解决此问题,请将 IP 地址添加到 /etc/gitlab/gitlab.rb

postgresql['trust_auth_cidr_addresses'] = %w(123.123.123.123/32 <other_cidrs>)

重新配置极狐GitLab 以使更改生效。

重新初始化副本

如果副本无法启动或重新加入集群,或者当它落后且无法同步时,可能需要重新初始化副本:

  1. 检查复制状态,确认需要重新初始化哪个服务器。例如:

    + Cluster: postgresql-ha (6970678148837286213) ------+---------+--------------+----+-----------+
    | Member                              | Host         | Role    | State        | TL | Lag in MB |
    +-------------------------------------+--------------+---------+--------------+----+-----------+
    | gitlab-database-1.example.com       | 172.18.0.111 | Replica | running      | 55 |         0 |
    | gitlab-database-2.example.com       | 172.18.0.112 | Replica | start failed |    |   unknown |
    | gitlab-database-3.example.com       | 172.18.0.113 | Leader  | running      | 55 |           |
    +-------------------------------------+--------------+---------+--------------+----+-----------+
    
  2. 登录到损坏的服务器并重新初始化数据库和副本。Patroni 将关闭该服务器上的 PostgreSQL,删除数据目录,然后从头开始重新初始化它:

    sudo gitlab-ctl patroni reinitialize-replica --member gitlab-database-2.example.com
    

    可以在任何 Patroni 节点上运行,但请注意,不带 --membersudo gitlab-ctl patroni reinitialize-replica 将重新初始化运行它的服务器。建议在损坏的服务器上本地运行它,减少意外数据丢失的风险。

  3. 监控日志:

    sudo gitlab-ctl tail patroni
    

在 Consul 中重置 Patroni 状态

caution这是一个破坏性的过程,可能会导致集群进入不良状态。在运行此过程之前,请确保您有一个健康的备份。

作为最后的手段,如果您的 Patroni 集群处于未知/坏状态并且没有节点可以启动,您可以在 Consul 中完全重置 Patroni 状态,从而在第一个 Patroni 节点启动时重新初始化 Patroni 集群。

要在 Consul 中重置 Patroni 状态:

  1. 如果当前状态显示不止一个,或者没有,记下作为 leader 的 Patroni 节点,或者应用程序认为的当前 leader。一种方法是查看 /var/opt/gitlab/consul/databases.ini 中的 PgBouncer 节点,其中包含当前 leader 的主机名。
  2. 在所有节点上停止 Patroni:

    sudo gitlab-ctl stop patroni
    
  3. 在 Consul 中重置状态:

    /opt/gitlab/embedded/bin/consul kv delete -recurse /service/postgresql-ha/
    
  4. 启动一个 Patroni 节点,它初始化 Patroni 集群并选举为 leader。强烈建议启动前一个 leader(在第一步中注明),以免丢失由于集群状态损坏而可能尚未复制的现有写入:

    sudo gitlab-ctl start patroni
    
  5. 启动所有其它加入 Patroni 集群的 Patroni 节点作为副本:

    sudo gitlab-ctl start patroni
    

如果您仍然看到问题,下一步是恢复上次运行正常的备份。

Patroni 日志中关于 127.0.0.1pg_hba.conf 条目的错误

Patroni 日志中的以下日志条目表明复制不起作用,需要更改配置:

FATAL:  no pg_hba.conf entry for replication connection from host "127.0.0.1", user "gitlab_replicator"

要解决此问题,请确保环回接口包含在 CIDR 地址列表中:

  1. 编辑 /etc/gitlab/gitlab.rb

    postgresql['trust_auth_cidr_addresses'] = %w(<other_cidrs> 127.0.0.1/32)
    
  2. 重新配置极狐GitLab 以使更改生效。
  3. 检查所有副本都已同步

Patroni 日志中的错误:the requested start point is ahead of the WAL flush position

此错误表明数据库未在复制:

FATAL:  could not receive data from WAL stream: ERROR:  requested starting point 0/5000000 is ahead of the WAL flush position of this server 0/4000388

此示例错误来自最初配置错误且从未复制的副本。

通过重新初始化副本修复它。

Patroni 因 MemoryError 无法启动

Patroni 可能无法启动,记录错误和堆栈跟踪:

MemoryError
Traceback (most recent call last):
  File "/opt/gitlab/embedded/bin/patroni", line 8, in <module>
    sys.exit(main())
[..]
  File "/opt/gitlab/embedded/lib/python3.7/ctypes/__init__.py", line 273, in _reset_cache
    CFUNCTYPE(c_int)(lambda: None)

如果堆栈跟踪以 CFUNCTYPE(c_int)(lambda: None)结尾,如果 Linux 服务器已进行安全加固,则此代码触发 MemoryError

该代码导致 Python 写入临时可执行文件,如果它找不到执行此操作的文件系统。例如,如果在 /tmp 文件系统上设置了 noexec,它会因 MemoryError 失败。

解决方法:

  • /tmp/var/tmp 等文件系统的挂载选项中删除noexec
  • 如果设置为 enforcing,SELinux 也可能会阻止这些操作。通过将 SELinux 设置为 permissive 来验证问题是否已解决。

自 13.1 以来,Patroni 一直随 Linux 软件包以及 Python 3.7 版本一起提供。当 GitLab 14.x 开始与更高版本的 Python 一起发布时,应该不再需要变通方法,因为导致此问题的代码已从 Python 3.8 中删除。

其它组件的问题

如果您遇到此处未列出的组件问题,请务必查看其特定文档页面的故障排查部分: