InfoGrab DocsInfoGrab Docs

Linux 패키지 설치를 위한 PostgreSQL 복제 및 장애 조치

요약

GitLab Self-Managed의 Free 사용자라면 클라우드 호스팅 솔루션 사용을 고려합니다. 복제 및 장애 조치가 포함된 설정을 찾는 것이 아니라면 Linux 패키지의 데이터베이스 구성 문서를 참고합니다. GitLab에서 복제 및 장애 조치와 함께 PostgreSQL을 구성하기 전에 이 문서를 전부 읽는 것을 권장합니다.

GitLab Self-Managed의 Free 사용자라면 클라우드 호스팅 솔루션 사용을 고려합니다. 이 문서는 소스 컴파일 설치를 다루지 않습니다.

복제 및 장애 조치가 포함된 설정을 찾는 것이 아니라면 Linux 패키지의 데이터베이스 구성 문서를 참고합니다.

GitLab에서 복제 및 장애 조치와 함께 PostgreSQL을 구성하기 전에 이 문서를 전부 읽는 것을 권장합니다.

운영 체제 업그레이드#

다른 운영 체제를 사용하는 시스템으로 장애 조치를 수행하는 경우 PostgreSQL 운영 체제 업그레이드 문서를 참고합니다. 운영 체제 업그레이드 시 로컬 변경 사항을 고려하지 않으면 데이터가 손상될 수 있습니다.

아키텍처#

복제 장애 조치가 포함된 PostgreSQL 클러스터에 대해 Linux 패키지가 권장하는 구성에는 다음이 필요합니다:

  • 최소 세 개의 PostgreSQL 노드
  • 최소 세 개의 Consul 서버 노드
  • 기본 데이터베이스의 읽기 및 쓰기를 추적하고 처리하는 최소 세 개의 PgBouncer 노드
    • PgBouncer 노드 간 요청을 분산하는 내부 로드 밸런서(TCP)
  • 데이터베이스 로드 밸런싱 활성화
    • 각 PostgreSQL 노드에 구성된 로컬 PgBouncer 서비스. 이는 기본 노드를 추적하는 메인 PgBouncer 클러스터와는 별개입니다.
PlantUML 다이어그램 (37줄)
소스 코드 보기
@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에 직접 연결하거나, 여러 PgBouncer를 처리하도록 구성된 내부 로드 밸런서(TCP)를 통해 연결합니다.
  • PgBouncer는 기본 데이터베이스 서버의 PostgreSQL 기본 포트에 연결합니다.

데이터베이스 로드 밸런싱#

최근에 변경되지 않아 모든 데이터베이스 노드에서 최신 상태인 데이터에 대한 읽기 쿼리의 경우:

  • 애플리케이션 서버는 라운드 로빈 방식으로 각 데이터베이스 노드의 로컬 PgBouncer 서비스에 기본 포트를 통해 연결합니다.
  • 로컬 PgBouncer는 로컬 데이터베이스 서버의 PostgreSQL 기본 포트에 연결합니다.

복제#

  • 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
    
  • 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는 복제를 시작할 때 노드당 하나의 추가 슬롯을 사용합니다.
  • 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 값을 지정하지 않습니다.

데이터베이스 노드 구성#

  1. Consul 노드를 구성합니다.
  2. 다음 단계를 실행하기 전에 CONSUL_SERVER_NODES, PGBOUNCER_PASSWORD_HASH, POSTGRESQL_PASSWORD_HASH, db 노드 수, 네트워크 주소를 수집합니다.

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'] 구성 키로 설정해야 합니다.

예시는 다음과 같습니다:

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

모든 데이터베이스 노드는 동일한 구성을 사용합니다. 리더 노드는 구성으로 결정되지 않으며, 리더 노드와 복제본 노드에 대해 추가적이거나 다른 구성도 없습니다.

노드 구성이 완료되면 변경 사항을 적용하려면 각 노드에서 GitLab을 재구성해야 합니다.

일반적으로 Consul 클러스터가 준비되면 가장 먼저 재구성한 노드가 리더가 됩니다. 노드 재구성 순서를 지정할 필요는 없습니다. 병렬로 또는 임의의 순서로 실행할 수 있습니다. 임의의 순서를 선택하면 미리 정해진 리더는 없습니다.

모니터링 활성화#

모니터링을 활성화하는 경우 모든 데이터베이스 서버에서 활성화해야 합니다.

  1. /etc/gitlab/gitlab.rb를 생성 또는 편집하고 다음 구성을 추가합니다:

    # Enable service discovery for Prometheus
    consul['monitoring_service_discovery'] = true
    
    # Set the network addresses that the exporters must 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 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 (default): 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_NODES, CONSUL_PASSWORD_HASH, PGBOUNCER_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
    # Set the real values as explained in Required Information section
    # Replace CONSUL_PASSWORD_HASH with a generated md5 value
    # Replace PGBOUNCER_PASSWORD_HASH 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. Consul이 PgBouncer를 다시 로드할 수 있도록 .pgpass 파일을 생성합니다. 요청 시 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 # Supply PGBOUNCER_PASSWORD when prompted
    

    비밀번호를 입력한 후 psql: ERROR: Auth failed 오류가 발생하면 올바른 형식으로 MD5 비밀번호 해시를 생성했는지 확인합니다. 올바른 형식은 비밀번호와 사용자 이름을 연결하는 것입니다: PASSWORDUSERNAME. 예를 들어 pgbouncer 사용자의 MD5 비밀번호 해시를 생성하려면 Sup3rS3cr3tpgbouncer라는 텍스트가 필요합니다.

  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을 재구성합니다.

애플리케이션 노드 사후 구성#

모든 마이그레이션이 실행되었는지 확인합니다:

gitlab-rake gitlab:db:configure
Note

PgBouncer가 PostgreSQL에 연결하지 못한다는 rake aborted! 오류가 발생하면 데이터베이스 노드의 gitlab.rb에 있는 PostgreSQL의 trust_auth_cidr_addresses에 PgBouncer 노드의 IP 주소가 누락된 것일 수 있습니다. 진행하기 전에 PgBouncer 오류 ERROR: pgbouncer cannot connect to server를 참고합니다.

백업#

PgBouncer 연결을 통해 GitLab을 백업하거나 복원하지 않습니다. 이렇게 하면 GitLab 중단이 발생합니다.

이에 대한 자세한 내용과 백업 재구성 방법을 참고합니다.

GitLab 실행 확인#

이 시점에서 GitLab 인스턴스가 실행 중이어야 합니다. 로그인하고 이슈 및 머지 리퀘스트를 생성할 수 있는지 확인합니다. 자세한 내용은 복제 및 장애 조치 문제 해결을 참고합니다.

예시 구성#

이 섹션에서는 완전히 확장된 몇 가지 예시 구성을 설명합니다.

권장 설정 예시#

이 예시는 세 개의 Consul 서버, 세 개의 PgBouncer 서버(연결된 내부 로드 밸런서 포함), 세 개의 PostgreSQL 서버, 하나의 애플리케이션 노드를 사용합니다.

이 설정에서 모든 서버는 동일한 10.6.0.0/16 사설 네트워크 범위를 공유합니다. 서버들은 이 주소를 통해 자유롭게 통신합니다.

다른 네트워크 설정을 사용할 수도 있지만, 클러스터 전체에서 동기 복제가 이루어지도록 허용하는 설정을 권장합니다. 일반적으로 지연 시간이 2ms 미만이면 복제 작업의 성능이 보장됩니다.

GitLab 참조 아키텍처는 애플리케이션 데이터베이스 쿼리가 세 노드에 모두 분산된다고 가정하여 크기가 산정됩니다. 통신 지연이 2ms를 초과하면 데이터베이스 잠금이 발생할 수 있고 복제본이 제때 읽기 전용 쿼리를 처리하는 능력에 영향을 줄 수 있습니다.

  • 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_url은 http://gitlab.example.com입니다.

초기 구성 후 장애 조치가 발생하면 PostgreSQL 리더 노드는 장애가 복구될 때까지 사용 가능한 세컨더리 중 하나로 변경됩니다.

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

변경 사항을 적용하려면 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

변경 사항을 적용하려면 GitLab을 재구성합니다.

내부 로드 밸런서 설정#

이렇게 하면 각 PgBouncer 노드를 처리하도록 내부 로드 밸런서(TCP)를 설정해야 합니다(이 예시에서는 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

변경 사항을 적용하려면 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 에이전트에 연결하여 클러스터에 참여합니다. 이 시점 이후에야 노드가 기본인지 복제본인지 결정됩니다. 이 결정을 기반으로 Unix 소켓을 통해 직접 통신하는 PostgreSQL을 구성하고 시작합니다. 즉 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 필드는 어떤 WAL(Write-Ahead Log) 세그먼트가 복제되었는지와 관련됩니다. 현재 LSN(Log Sequence Number)을 확인하려면 리더에서 다음을 실행합니다:

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

복제본이 동기화되지 않은 경우 gitlab-ctl patroni members가 누락된 데이터의 양을 나타내며, lag 필드는 경과 시간을 나타냅니다.

리더가 반환하는 데이터에 대한 자세한 내용은 PostgreSQL 문서를 참고합니다. 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 문서를 주의 깊게 검토합니다. 구성한 복제 모드에 따라 허용 가능한 데이터 손실량이 결정됩니다.

Warning

복제는 백업 전략이 아닙니다. 신중하게 설계되고 테스트된 백업 솔루션을 대체할 수 있는 것은 없습니다.

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 수동 장애 조치 절차#

Patroni는 자동 장애 조치를 지원하지만, 다음과 같이 약간 다른 두 가지 옵션으로 수동 장애 조치를 수행할 수도 있습니다:

  • 장애 조치(Failover): 정상 노드가 없을 때 수동 장애 조치를 수행할 수 있습니다. 이 작업은 모든 PostgreSQL 노드에서 수행할 수 있습니다:

    sudo gitlab-ctl patroni failover
    
  • 전환(Switchover): 클러스터가 정상일 때만 작동하며 전환을 예약할 수 있습니다(즉시 실행할 수도 있음). 이 작업은 모든 PostgreSQL 노드에서 수행할 수 있습니다:

    sudo gitlab-ctl patroni switchover
    

이 주제에 대한 자세한 내용은 Patroni 문서를 참고합니다.

Geo 보조 사이트 고려 사항#

Patroni와 PgBouncer를 사용하는 기본 사이트에서 Geo 보조 사이트가 복제하는 경우 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 업그레이드를 신중하게 계획해야 합니다:

  1. 어떤 노드가 리더이고 어떤 노드가 복제본인지 확인합니다:

    gitlab-ctl patroni members
    

    [!note] Geo 보조 사이트에서는 Patroni 리더 노드를 standby leader라고 부릅니다.

  2. 복제본에서만 Patroni를 중지합니다.

    sudo gitlab-ctl stop patroni
    
  3. 애플리케이션 노드에서 유지 관리 모드를 활성화합니다:

    sudo gitlab-ctl deploy-page up
    
  4. 리더 노드에서 PostgreSQL을 업그레이드하고 업그레이드가 성공적으로 완료되었는지 확인합니다:

    # Default command timeout is 600s, configurable with '--timeout'
    sudo gitlab-ctl pg-upgrade
    

    [!note] gitlab-ctl pg-upgrade는 노드의 역할을 자동으로 감지하려고 시도합니다. 어떤 이유로 자동 감지가 작동하지 않거나 역할을 올바르게 감지하지 못했다고 판단되면 --leader 또는 --replica 인수를 사용하여 수동으로 재정의할 수 있습니다. 사용 가능한 옵션에 대한 자세한 내용은 gitlab-ctl pg-upgrade --help를 사용합니다.

  5. 리더와 클러스터의 상태를 확인합니다. 정상 상태인 리더가 있는 경우에만 다음 단계로 진행할 수 있습니다:

    gitlab-ctl patroni check-leader
    
    # OR
    
    gitlab-ctl patroni members
    
  6. 이제 애플리케이션 노드에서 유지 관리 모드를 비활성화할 수 있습니다:

    sudo gitlab-ctl deploy-page down
    
  7. 복제본에서 PostgreSQL을 업그레이드합니다(모든 복제본에서 병렬로 수행할 수 있음):

    sudo gitlab-ctl pg-upgrade
    
  8. 백업 또는 복원을 수행할 때 버전 불일치 오류가 발생하지 않도록 GitLab Rails 인스턴스에서 호환되는 버전의 pg_dump와 pg_restore를 사용해야 합니다. Rails 인스턴스의 /etc/gitlab/gitlab.rb에서 PostgreSQL 버전을 지정하여 이를 수행할 수 있습니다:

    postgresql['version'] = 16
    

복제본을 업그레이드하는 중 문제가 발생하면 문제 해결 섹션에서 해결책을 찾을 수 있습니다.

Note

gitlab-ctl revert-pg-upgrade로 PostgreSQL 업그레이드를 되돌리는 작업은 gitlab-ctl pg-upgrade와 동일한 사항을 고려해야 합니다. 먼저 복제본을 중지하고, 그다음 리더를 되돌린 후, 마지막으로 복제본을 되돌리는 동일한 절차를 따라야 합니다.

Patroni 클러스터에서 PostgreSQL의 다운타임이 거의 없는 업그레이드#

Patroni를 사용하면 클러스터를 종료하지 않고도 주 버전 PostgreSQL 업그레이드를 실행할 수 있습니다. 다만 이 경우 업그레이드된 PostgreSQL을 실행할 새 Patroni 노드를 호스팅할 추가 리소스가 필요합니다. 실제로 이 절차에서는 다음을 수행합니다:

  • 새 버전의 PostgreSQL로 새 Patroni 클러스터를 생성합니다.
  • 기존 클러스터에서 데이터를 마이그레이션합니다.

이 절차는 침습적이지 않으며, 기존 클러스터를 전환하기 전까지는 영향을 미치지 않습니다. 다만 시간과 리소스가 모두 소모될 수 있습니다. 가용성과의 트레이드오프를 고려합니다.

단계는 다음 순서로 진행합니다:

  1. 새 클러스터를 위한 리소스 프로비저닝
  2. 사전 점검
  3. 새 클러스터의 리더 구성
  4. 기존 리더에서 퍼블리셔 시작
  5. 기존 클러스터에서 데이터 복사
  6. 기존 클러스터에서 데이터 복제
  7. 새 클러스터 확장
  8. 새 클러스터를 사용하도록 애플리케이션 전환
  9. 정리

새 클러스터를 위한 리소스 프로비저닝#

Patroni 노드를 위한 새 리소스 세트가 필요합니다. 새 Patroni 클러스터가 기존 클러스터와 정확히 같은 수의 노드를 가질 필요는 없습니다. 요구 사항에 따라 다른 수의 노드를 선택할 수 있습니다. 새 클러스터는 (다른 patroni['scope']를 사용하는) 기존 Consul 클러스터와 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. 장애 조치를 트리거하지 않고 PostgreSQL을 재시작하여 새 wal_level을 적용하려면 gitlab-ctl patroni restart를 실행합니다. 재시작 주기 동안에는 클러스터 리더를 사용할 수 없습니다.

  4. gitlab-psql로 SHOW wal_level을 실행하여 변경 사항을 확인합니다.

새 클러스터의 리더 구성#

새 클러스터의 첫 번째 노드를 구성합니다. 이 노드가 새 클러스터의 리더가 됩니다. 새 PostgreSQL 버전과 호환된다면 기존 클러스터의 구성을 사용할 수 있습니다. Patroni 클러스터 구성 문서를 참고합니다.

공통 구성 외에도 gitlab.rb에 다음을 적용해야 합니다:

  1. 새 Patroni 클러스터가 다른 스코프를 사용하는지 확인합니다. 스코프는 Consul에서 Patroni 설정을 네임스페이스로 구분하는 데 사용되며, 이를 통해 기존 클러스터와 새 클러스터에 동일한 Consul 클러스터를 사용할 수 있습니다.

    patroni['scope'] = 'postgresql_new-ha'
    
  2. Consul 에이전트가 기존 Patroni 클러스터와 새 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_dump와 pg_dumpall 명령은 /opt/gitlab/embedded/bin에 있습니다. 이 명령들에서 EXISTING_CLUSTER_LEADER는 기존 클러스터의 리더 노드 호스트 주소입니다.

Note

gitlab-psql 사용자는 새 리더 노드에서 기존 리더를 인증할 수 있어야 합니다.

기존 클러스터에서 데이터 복제#

초기 데이터 덤프를 가져온 후에는 기존 클러스터의 최신 변경 사항과 새 리더를 동기화된 상태로 유지해야 합니다. 새 리더에서 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 클러스터의 리소스를 정리할 수 있습니다. 더 이상 필요하지 않기 때문입니다. 다만 리소스를 제거하기 전에 gitlab-psql로 DROP SUBSCRIPTION patroni_upgrade를 실행하여 새 리더의 논리적 복제 구독을 제거합니다.

Linux 패키지 설치를 위한 PostgreSQL 복제 및 장애 조치

GitLab v19.5
Tier: Premium, Ultimate
Offering: GitLab Self-Managed
원문 보기

요약

GitLab Self-Managed의 Free 사용자라면 클라우드 호스팅 솔루션 사용을 고려합니다. 복제 및 장애 조치가 포함된 설정을 찾는 것이 아니라면 Linux 패키지의 데이터베이스 구성 문서를 참고합니다. GitLab에서 복제 및 장애 조치와 함께 PostgreSQL을 구성하기 전에 이 문서를 전부 읽는 것을 권장합니다.

GitLab Self-Managed의 Free 사용자라면 클라우드 호스팅 솔루션 사용을 고려합니다. 이 문서는 소스 컴파일 설치를 다루지 않습니다.

복제 및 장애 조치가 포함된 설정을 찾는 것이 아니라면 Linux 패키지의 데이터베이스 구성 문서를 참고합니다.

GitLab에서 복제 및 장애 조치와 함께 PostgreSQL을 구성하기 전에 이 문서를 전부 읽는 것을 권장합니다.

운영 체제 업그레이드#

다른 운영 체제를 사용하는 시스템으로 장애 조치를 수행하는 경우 PostgreSQL 운영 체제 업그레이드 문서를 참고합니다. 운영 체제 업그레이드 시 로컬 변경 사항을 고려하지 않으면 데이터가 손상될 수 있습니다.

아키텍처#

복제 장애 조치가 포함된 PostgreSQL 클러스터에 대해 Linux 패키지가 권장하는 구성에는 다음이 필요합니다:

  • 최소 세 개의 PostgreSQL 노드
  • 최소 세 개의 Consul 서버 노드
  • 기본 데이터베이스의 읽기 및 쓰기를 추적하고 처리하는 최소 세 개의 PgBouncer 노드
    • PgBouncer 노드 간 요청을 분산하는 내부 로드 밸런서(TCP)
  • 데이터베이스 로드 밸런싱 활성화
    • 각 PostgreSQL 노드에 구성된 로컬 PgBouncer 서비스. 이는 기본 노드를 추적하는 메인 PgBouncer 클러스터와는 별개입니다.
PlantUML 다이어그램 (37줄)
소스 코드 보기
@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에 직접 연결하거나, 여러 PgBouncer를 처리하도록 구성된 내부 로드 밸런서(TCP)를 통해 연결합니다.
  • PgBouncer는 기본 데이터베이스 서버의 PostgreSQL 기본 포트에 연결합니다.

데이터베이스 로드 밸런싱#

최근에 변경되지 않아 모든 데이터베이스 노드에서 최신 상태인 데이터에 대한 읽기 쿼리의 경우:

  • 애플리케이션 서버는 라운드 로빈 방식으로 각 데이터베이스 노드의 로컬 PgBouncer 서비스에 기본 포트를 통해 연결합니다.
  • 로컬 PgBouncer는 로컬 데이터베이스 서버의 PostgreSQL 기본 포트에 연결합니다.

복제#

  • 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
    
  • 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는 복제를 시작할 때 노드당 하나의 추가 슬롯을 사용합니다.
  • 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 값을 지정하지 않습니다.

데이터베이스 노드 구성#

  1. Consul 노드를 구성합니다.
  2. 다음 단계를 실행하기 전에 CONSUL_SERVER_NODES, PGBOUNCER_PASSWORD_HASH, POSTGRESQL_PASSWORD_HASH, db 노드 수, 네트워크 주소를 수집합니다.

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'] 구성 키로 설정해야 합니다.

예시는 다음과 같습니다:

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

모든 데이터베이스 노드는 동일한 구성을 사용합니다. 리더 노드는 구성으로 결정되지 않으며, 리더 노드와 복제본 노드에 대해 추가적이거나 다른 구성도 없습니다.

노드 구성이 완료되면 변경 사항을 적용하려면 각 노드에서 GitLab을 재구성해야 합니다.

일반적으로 Consul 클러스터가 준비되면 가장 먼저 재구성한 노드가 리더가 됩니다. 노드 재구성 순서를 지정할 필요는 없습니다. 병렬로 또는 임의의 순서로 실행할 수 있습니다. 임의의 순서를 선택하면 미리 정해진 리더는 없습니다.

모니터링 활성화#

모니터링을 활성화하는 경우 모든 데이터베이스 서버에서 활성화해야 합니다.

  1. /etc/gitlab/gitlab.rb를 생성 또는 편집하고 다음 구성을 추가합니다:

    # Enable service discovery for Prometheus
    consul['monitoring_service_discovery'] = true
    
    # Set the network addresses that the exporters must 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 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 (default): 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_NODES, CONSUL_PASSWORD_HASH, PGBOUNCER_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
    # Set the real values as explained in Required Information section
    # Replace CONSUL_PASSWORD_HASH with a generated md5 value
    # Replace PGBOUNCER_PASSWORD_HASH 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. Consul이 PgBouncer를 다시 로드할 수 있도록 .pgpass 파일을 생성합니다. 요청 시 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 # Supply PGBOUNCER_PASSWORD when prompted
    

    비밀번호를 입력한 후 psql: ERROR: Auth failed 오류가 발생하면 올바른 형식으로 MD5 비밀번호 해시를 생성했는지 확인합니다. 올바른 형식은 비밀번호와 사용자 이름을 연결하는 것입니다: PASSWORDUSERNAME. 예를 들어 pgbouncer 사용자의 MD5 비밀번호 해시를 생성하려면 Sup3rS3cr3tpgbouncer라는 텍스트가 필요합니다.

  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을 재구성합니다.

애플리케이션 노드 사후 구성#

모든 마이그레이션이 실행되었는지 확인합니다:

gitlab-rake gitlab:db:configure
Note

PgBouncer가 PostgreSQL에 연결하지 못한다는 rake aborted! 오류가 발생하면 데이터베이스 노드의 gitlab.rb에 있는 PostgreSQL의 trust_auth_cidr_addresses에 PgBouncer 노드의 IP 주소가 누락된 것일 수 있습니다. 진행하기 전에 PgBouncer 오류 ERROR: pgbouncer cannot connect to server를 참고합니다.

백업#

PgBouncer 연결을 통해 GitLab을 백업하거나 복원하지 않습니다. 이렇게 하면 GitLab 중단이 발생합니다.

이에 대한 자세한 내용과 백업 재구성 방법을 참고합니다.

GitLab 실행 확인#

이 시점에서 GitLab 인스턴스가 실행 중이어야 합니다. 로그인하고 이슈 및 머지 리퀘스트를 생성할 수 있는지 확인합니다. 자세한 내용은 복제 및 장애 조치 문제 해결을 참고합니다.

예시 구성#

이 섹션에서는 완전히 확장된 몇 가지 예시 구성을 설명합니다.

권장 설정 예시#

이 예시는 세 개의 Consul 서버, 세 개의 PgBouncer 서버(연결된 내부 로드 밸런서 포함), 세 개의 PostgreSQL 서버, 하나의 애플리케이션 노드를 사용합니다.

이 설정에서 모든 서버는 동일한 10.6.0.0/16 사설 네트워크 범위를 공유합니다. 서버들은 이 주소를 통해 자유롭게 통신합니다.

다른 네트워크 설정을 사용할 수도 있지만, 클러스터 전체에서 동기 복제가 이루어지도록 허용하는 설정을 권장합니다. 일반적으로 지연 시간이 2ms 미만이면 복제 작업의 성능이 보장됩니다.

GitLab 참조 아키텍처는 애플리케이션 데이터베이스 쿼리가 세 노드에 모두 분산된다고 가정하여 크기가 산정됩니다. 통신 지연이 2ms를 초과하면 데이터베이스 잠금이 발생할 수 있고 복제본이 제때 읽기 전용 쿼리를 처리하는 능력에 영향을 줄 수 있습니다.

  • 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_url은 http://gitlab.example.com입니다.

초기 구성 후 장애 조치가 발생하면 PostgreSQL 리더 노드는 장애가 복구될 때까지 사용 가능한 세컨더리 중 하나로 변경됩니다.

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

변경 사항을 적용하려면 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

변경 사항을 적용하려면 GitLab을 재구성합니다.

내부 로드 밸런서 설정#

이렇게 하면 각 PgBouncer 노드를 처리하도록 내부 로드 밸런서(TCP)를 설정해야 합니다(이 예시에서는 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

변경 사항을 적용하려면 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 에이전트에 연결하여 클러스터에 참여합니다. 이 시점 이후에야 노드가 기본인지 복제본인지 결정됩니다. 이 결정을 기반으로 Unix 소켓을 통해 직접 통신하는 PostgreSQL을 구성하고 시작합니다. 즉 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 필드는 어떤 WAL(Write-Ahead Log) 세그먼트가 복제되었는지와 관련됩니다. 현재 LSN(Log Sequence Number)을 확인하려면 리더에서 다음을 실행합니다:

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

복제본이 동기화되지 않은 경우 gitlab-ctl patroni members가 누락된 데이터의 양을 나타내며, lag 필드는 경과 시간을 나타냅니다.

리더가 반환하는 데이터에 대한 자세한 내용은 PostgreSQL 문서를 참고합니다. 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 문서를 주의 깊게 검토합니다. 구성한 복제 모드에 따라 허용 가능한 데이터 손실량이 결정됩니다.

Warning

복제는 백업 전략이 아닙니다. 신중하게 설계되고 테스트된 백업 솔루션을 대체할 수 있는 것은 없습니다.

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 수동 장애 조치 절차#

Patroni는 자동 장애 조치를 지원하지만, 다음과 같이 약간 다른 두 가지 옵션으로 수동 장애 조치를 수행할 수도 있습니다:

  • 장애 조치(Failover): 정상 노드가 없을 때 수동 장애 조치를 수행할 수 있습니다. 이 작업은 모든 PostgreSQL 노드에서 수행할 수 있습니다:

    sudo gitlab-ctl patroni failover
    
  • 전환(Switchover): 클러스터가 정상일 때만 작동하며 전환을 예약할 수 있습니다(즉시 실행할 수도 있음). 이 작업은 모든 PostgreSQL 노드에서 수행할 수 있습니다:

    sudo gitlab-ctl patroni switchover
    

이 주제에 대한 자세한 내용은 Patroni 문서를 참고합니다.

Geo 보조 사이트 고려 사항#

Patroni와 PgBouncer를 사용하는 기본 사이트에서 Geo 보조 사이트가 복제하는 경우 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 업그레이드를 신중하게 계획해야 합니다:

  1. 어떤 노드가 리더이고 어떤 노드가 복제본인지 확인합니다:

    gitlab-ctl patroni members
    

    [!note] Geo 보조 사이트에서는 Patroni 리더 노드를 standby leader라고 부릅니다.

  2. 복제본에서만 Patroni를 중지합니다.

    sudo gitlab-ctl stop patroni
    
  3. 애플리케이션 노드에서 유지 관리 모드를 활성화합니다:

    sudo gitlab-ctl deploy-page up
    
  4. 리더 노드에서 PostgreSQL을 업그레이드하고 업그레이드가 성공적으로 완료되었는지 확인합니다:

    # Default command timeout is 600s, configurable with '--timeout'
    sudo gitlab-ctl pg-upgrade
    

    [!note] gitlab-ctl pg-upgrade는 노드의 역할을 자동으로 감지하려고 시도합니다. 어떤 이유로 자동 감지가 작동하지 않거나 역할을 올바르게 감지하지 못했다고 판단되면 --leader 또는 --replica 인수를 사용하여 수동으로 재정의할 수 있습니다. 사용 가능한 옵션에 대한 자세한 내용은 gitlab-ctl pg-upgrade --help를 사용합니다.

  5. 리더와 클러스터의 상태를 확인합니다. 정상 상태인 리더가 있는 경우에만 다음 단계로 진행할 수 있습니다:

    gitlab-ctl patroni check-leader
    
    # OR
    
    gitlab-ctl patroni members
    
  6. 이제 애플리케이션 노드에서 유지 관리 모드를 비활성화할 수 있습니다:

    sudo gitlab-ctl deploy-page down
    
  7. 복제본에서 PostgreSQL을 업그레이드합니다(모든 복제본에서 병렬로 수행할 수 있음):

    sudo gitlab-ctl pg-upgrade
    
  8. 백업 또는 복원을 수행할 때 버전 불일치 오류가 발생하지 않도록 GitLab Rails 인스턴스에서 호환되는 버전의 pg_dump와 pg_restore를 사용해야 합니다. Rails 인스턴스의 /etc/gitlab/gitlab.rb에서 PostgreSQL 버전을 지정하여 이를 수행할 수 있습니다:

    postgresql['version'] = 16
    

복제본을 업그레이드하는 중 문제가 발생하면 문제 해결 섹션에서 해결책을 찾을 수 있습니다.

Note

gitlab-ctl revert-pg-upgrade로 PostgreSQL 업그레이드를 되돌리는 작업은 gitlab-ctl pg-upgrade와 동일한 사항을 고려해야 합니다. 먼저 복제본을 중지하고, 그다음 리더를 되돌린 후, 마지막으로 복제본을 되돌리는 동일한 절차를 따라야 합니다.

Patroni 클러스터에서 PostgreSQL의 다운타임이 거의 없는 업그레이드#

Patroni를 사용하면 클러스터를 종료하지 않고도 주 버전 PostgreSQL 업그레이드를 실행할 수 있습니다. 다만 이 경우 업그레이드된 PostgreSQL을 실행할 새 Patroni 노드를 호스팅할 추가 리소스가 필요합니다. 실제로 이 절차에서는 다음을 수행합니다:

  • 새 버전의 PostgreSQL로 새 Patroni 클러스터를 생성합니다.
  • 기존 클러스터에서 데이터를 마이그레이션합니다.

이 절차는 침습적이지 않으며, 기존 클러스터를 전환하기 전까지는 영향을 미치지 않습니다. 다만 시간과 리소스가 모두 소모될 수 있습니다. 가용성과의 트레이드오프를 고려합니다.

단계는 다음 순서로 진행합니다:

  1. 새 클러스터를 위한 리소스 프로비저닝
  2. 사전 점검
  3. 새 클러스터의 리더 구성
  4. 기존 리더에서 퍼블리셔 시작
  5. 기존 클러스터에서 데이터 복사
  6. 기존 클러스터에서 데이터 복제
  7. 새 클러스터 확장
  8. 새 클러스터를 사용하도록 애플리케이션 전환
  9. 정리

새 클러스터를 위한 리소스 프로비저닝#

Patroni 노드를 위한 새 리소스 세트가 필요합니다. 새 Patroni 클러스터가 기존 클러스터와 정확히 같은 수의 노드를 가질 필요는 없습니다. 요구 사항에 따라 다른 수의 노드를 선택할 수 있습니다. 새 클러스터는 (다른 patroni['scope']를 사용하는) 기존 Consul 클러스터와 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. 장애 조치를 트리거하지 않고 PostgreSQL을 재시작하여 새 wal_level을 적용하려면 gitlab-ctl patroni restart를 실행합니다. 재시작 주기 동안에는 클러스터 리더를 사용할 수 없습니다.

  4. gitlab-psql로 SHOW wal_level을 실행하여 변경 사항을 확인합니다.

새 클러스터의 리더 구성#

새 클러스터의 첫 번째 노드를 구성합니다. 이 노드가 새 클러스터의 리더가 됩니다. 새 PostgreSQL 버전과 호환된다면 기존 클러스터의 구성을 사용할 수 있습니다. Patroni 클러스터 구성 문서를 참고합니다.

공통 구성 외에도 gitlab.rb에 다음을 적용해야 합니다:

  1. 새 Patroni 클러스터가 다른 스코프를 사용하는지 확인합니다. 스코프는 Consul에서 Patroni 설정을 네임스페이스로 구분하는 데 사용되며, 이를 통해 기존 클러스터와 새 클러스터에 동일한 Consul 클러스터를 사용할 수 있습니다.

    patroni['scope'] = 'postgresql_new-ha'
    
  2. Consul 에이전트가 기존 Patroni 클러스터와 새 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_dump와 pg_dumpall 명령은 /opt/gitlab/embedded/bin에 있습니다. 이 명령들에서 EXISTING_CLUSTER_LEADER는 기존 클러스터의 리더 노드 호스트 주소입니다.

Note

gitlab-psql 사용자는 새 리더 노드에서 기존 리더를 인증할 수 있어야 합니다.

기존 클러스터에서 데이터 복제#

초기 데이터 덤프를 가져온 후에는 기존 클러스터의 최신 변경 사항과 새 리더를 동기화된 상태로 유지해야 합니다. 새 리더에서 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 클러스터의 리소스를 정리할 수 있습니다. 더 이상 필요하지 않기 때문입니다. 다만 리소스를 제거하기 전에 gitlab-psql로 DROP SUBSCRIPTION patroni_upgrade를 실행하여 새 리더의 논리적 복제 구독을 제거합니다.