正式なドキュメントは英語版であり、この日本語訳はAI支援翻訳により作成された参考用のものです。日本語訳の一部の内容は人間によるレビューがまだ行われていないため、翻訳のタイミングにより英語版との間に差異が生じることがあります。最新かつ正確な情報については、英語版をご参照ください。

データベースのロードバランシング

  • プラン: Free、Premium、Ultimate
  • 提供形態: GitLab Self-Managed

データベースのロードバランシングを使用すると、読み取り専用のクエリを複数のPostgreSQLノードに分散させ、パフォーマンスを向上させることができます。

この機能はGitLab RailsおよびSidekiqにネイティブで提供されており、外部依存関係なしに、ラウンドロビン方式でデータベースの読み取りクエリの負荷を分散するように設定できます:

@startuml
!theme plain
card "**Internal Load Balancer**" as ilb
skinparam linetype ortho

together {
  collections "**GitLab Rails** x3" as gitlab
  collections "**Sidekiq** x4" as sidekiq
}

collections "**Consul** x3" as consul

card "Database" as database {
  collections "**PGBouncer x3**\n//Consul//" as pgbouncer

  card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary
  collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary

  pgbouncer --> postgres_primary
  postgres_primary .r-> postgres_secondary
}

gitlab --> ilb
gitlab -[hidden]-> pgbouncer
gitlab .[norank]-> postgres_primary
gitlab .[norank]-> postgres_secondary

sidekiq --> ilb
sidekiq -[hidden]-> pgbouncer
sidekiq .[norank]-> postgres_primary
sidekiq .[norank]-> postgres_secondary

ilb --> pgbouncer

consul -r-> pgbouncer
consul .[norank]r-> postgres_primary
consul .[norank]r-> postgres_secondary
@enduml

データベースのロードバランシングを有効にするための要件

データベースのロードバランシングを有効にするには、以下を確認してください:

  • HA PostgreSQLセットアップには、プライマリをレプリケートする1つ以上のセカンダリノードが必要です。
  • 各PostgreSQLノードは、同じ認証情報とポートで接続されている必要があります。

Linuxパッケージインストールの場合、設定されたマルチノードセットアップのすべてのロードバランシングされた接続をプールするために、各PostgreSQLノードにPgBouncerを設定する必要があります。

データベースのロードバランシングの設定

データベースのロードバランシングは、次の2つの方法のいずれかで設定できます:

ホスト

ホストのリストを設定するには、負荷分散を行う各環境のすべてのGitLab RailsおよびSidekiqノードで以下の手順を実行します:

  1. /etc/gitlab/gitlab.rbファイルを編集します。

  2. gitlab_rails['db_load_balancing']で、負荷分散するデータベースホストの配列を作成します。たとえば、primary.example.comsecondary1.example.comsecondary2.example.comのホストでPostgreSQLが実行されている環境では:

    gitlab_rails['db_load_balancing'] = { 'hosts' => ['primary.example.com', 'secondary1.example.com', 'secondary2.example.com'] }

    これらのホストは、gitlab_rails['db_port']で設定された同じポートで到達可能である必要があります。

  3. ファイルを保存してGitLabを再設定します。

プライマリをホストリストに追加することは任意ですが、推奨されます。これにより、プライマリがこれらのクエリに対応できる場合、プライマリがロードバランシングされた読み取りクエリの対象となり、システムパフォーマンスが向上します。非常にトラフィックが多いインスタンスでは、プライマリが読み取りレプリカとして機能するための容量がない場合があります。このリストにプライマリが存在するかどうかにかかわらず、書き込みクエリにはプライマリが使用されます。

サービスディスカバリ

サービスディスカバリにより、GitLabは使用するPostgreSQLホストのリストを自動的に取得できます。これは定期的にDNS Aレコードをチェックし、このレコードによって返されるIPをセカンダリのアドレスとして使用します。サービスディスカバリを機能させるには、DNSサーバーと、セカンダリのIPアドレスを含むAレコードが必要です。

Linuxパッケージインストールを使用している場合、提供されているConsulサービスはDNSサーバーとして機能し、postgresql-ha.service.consulレコードを介してPostgreSQLアドレスを返します。例:

  1. 各GitLab Rails/Sidekiqノードで、/etc/gitlab/gitlab.rbを編集して以下を追加します:

    gitlab_rails['db_load_balancing'] = { 'discover' => {
        'nameserver' => 'localhost'
        'record' => 'postgresql-ha.service.consul'
        'record_type' => 'A'
        'port' => '8600'
        'interval' => '60'
        'disconnect_timeout' => '120'
      }
    }
  2. ファイルを保存して、GitLabを再設定し、変更を有効にします。

オプション説明デフォルト
nameserverDNSレコードを検索するために使用するネームサーバー。localhost
record検索するレコード。このオプションはサービスディスカバリが機能するために必要です。
record_type検索するオプションのレコードタイプ。AまたはSRVのいずれかになります。A
portネームサーバーのポート。8600
intervalDNSレコードをチェックする間の最小時間(秒)。60
disconnect_timeoutホストのリストが更新された後、古い接続が閉じられるまでの時間(秒)。120
use_tcpUDPではなくTCPを使用してDNSリソースを検索します。false
max_replica_pools各Railsプロセスが接続するレプリカの最大数。多数のPostgresレプリカと多数のRailsプロセスを実行している場合に役立ちます。この制限がないと、すべてのRailsプロセスがデフォルトで各レプリカに接続するためです。デフォルトの動作は、設定されていない場合、無制限です。nil

もしrecord_typeSRVに設定されている場合、GitLabは引き続きラウンドロビンアルゴリズムを使用し、レコード内のweightおよびpriorityを無視します。SRVレコードは通常、IPの代わりにホスト名を返すため、GitLabは返されたホスト名のIPをSRV応答の追加セクションで探す必要があります。ホスト名のIPが見つからない場合、GitLabは設定されたnameserverに対し、各ホスト名のANYレコードをクエリしてAまたはAAAAレコードを探し、IPを解決できない場合は最終的にこのホスト名をローテーションから除外します。

intervalの値は、チェック間の最小時間を示します。AレコードのTTLがこの値より大きい場合、サービスディスカバリはそのTTLを尊重します。たとえば、AレコードのTTLが90秒の場合、サービスディスカバリはAレコードを再度チェックする前に少なくとも90秒間待機します。

ホストのリストが更新されると、古い接続が終了するまでに時間がかかる場合があります。disconnect_timeout設定は、すべての古いデータベース接続を終了するのにかかる時間の上限を強制するために使用できます。

古い読み取りの処理

古くなったセカンダリからの読み取りを防ぐため、ロードバランサーはプライマリと同期しているかを確認します。データが十分に新しい場合、セカンダリが使用され、そうでなければ無視されます。これらのチェックのオーバーヘッドを減らすために、特定の期間でのみ実行します。

この動作に影響を与える3つの設定オプションがあります:

オプション説明デフォルト
max_replication_differenceセカンダリがデータをレプリケートしていない場合にラグを許容されるデータ量(バイト単位)。8 MB
max_replication_lag_timeセカンダリの使用を停止する前に許容される最大ラグ時間(秒)。60秒
replica_check_intervalセカンダリのステータスをチェックする前に待機する必要がある最小時間(秒)。60秒

デフォルトはほとんどのユーザーにとって十分です。

ホストリストでこれらのオプションを設定するには、以下の例を使用してください:

gitlab_rails['db_load_balancing'] = {
  'hosts' => ['primary.example.com', 'secondary1.example.com', 'secondary2.example.com'],
  'max_replication_difference' => 16777216, # 16 MB
  'max_replication_lag_time' => 30,
  'replica_check_interval' => 30
}

ロギング

ロードバランサーは、database_load_balancing.logに様々なイベントを記録します。

  • ホストがオフラインとマークされたとき
  • ホストがオンラインに戻ったとき
  • すべてのセカンダリがオフラインになったとき
  • クエリの競合により、読み取りが別のホストで再試行されたとき

ログは、各エントリが少なくとも以下を含むJSONオブジェクトとして構造化されています:

  • フィルタリングに便利なeventフィールド。
  • 人間が判読できるmessageフィールド。
  • いくつかのイベント固有のメタデータ。たとえば、db_host
  • 常にログに記録されるコンテキスト情報。たとえば、severitytime

例:

{"severity":"INFO","time":"2019-09-02T12:12:01.728Z","correlation_id":"abcdefg","event":"host_online","message":"Host came back online","db_host":"111.222.333.444","db_port":null,"tag":"rails.database_load_balancing","environment":"production","hostname":"web-example-1","fqdn":"gitlab.example.com","path":null,"params":null}

実装の詳細

クエリの負荷分散

読み取り専用のSELECTクエリは、指定されたすべてのホスト間で負荷分散されます。その他すべて(トランザクションを含む)はプライマリで実行されます。SELECT ... FOR UPDATEのようなクエリもプライマリで実行されます。

プリペアドステートメント

プリペアドステートメントはロードバランシングと相性が悪く、ロードバランシングが有効な場合は自動的に無効になります。これは応答時間に影響を与えないはずです。

プライマリへの固着

書き込みが実行された後、GitLabは、書き込みを実行したユーザーにスコープされた一定期間、プライマリの使用に固執します。GitLabは、セカンダリが追いついた場合、または30秒後にセカンダリの使用に戻ります。

フェイルオーバーの処理

フェイルオーバーまたは応答しないデータベースの場合、ロードバランサーは次に利用可能なホストの使用を試みます。セカンダリが利用できない場合、操作は代わりにプライマリで実行されます。

データの書き込み中に接続エラーが発生した場合、操作は指数関数的バックオフを使用して最大3回再試行されます。

ロードバランシングを使用している場合、データベースサーバーを安全に再起動でき、すぐにユーザーにエラーが表示されることはありません。

開発ガイド

データベースロードバランシングに関する詳細な開発ガイドについては、開発ドキュメントを参照してください。