データベースの負荷分散

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

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

データベース負荷分散を有効にするための要件

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

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

Linuxパッケージインストールの場合、複数ノードの設定時にすべての負荷分散接続をプールするために、各PostgreSQLノードで設定されたPgBouncerも必要です。

データベースの負荷分散の設定

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

  • (推奨)Hosts: PostgreSQLホストのリスト。
  • Service Discovery: PostgreSQLホストのリストを返すDNSレコード。

ホスト

ホストのリストを設定するには、バランスを取りたい環境ごとに、すべてのGitLab RailsとSidekiqノードで以下の手順を実行します:

  1. /etc/gitlab/gitlab.rb ファイルを編集します。
  2. gitlab_rails['db_load_balancing'] に、バランスを取りたいデータベースホストの配列を作成します。例えば、PostgreSQLがprimary.example.comsecondary1.example.comsecondary2.example.com のホスト上で動作している環境の場合です:

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

    これらのホストは、gitlab_rails['db_port'] で設定された同じポートで到達可能でなければなりません。

  3. ファイルを保存し、GitLabを再設定してください。
note
プライマリをhostsリストに追加するのはオプションですが、推奨します。これにより、プライマリは負荷分散された読み込みクエリの対象となり、プライマリにクエリのキャパシティがある場合にはシステムのパフォーマンスが向上します。非常にトラフィックの多いインスタンスでは、プライマリに読み込み用レプリカとしてのキャパシティがない場合があります。プライマリはこのリストにあるかどうかにかかわらず、書き込みクエリに使用されます。

サービス・ディスカバリー

GitLab 11.0で導入されました

サービスディスカバリーによって、GitLabは自動的に使用するPostgreSQLホストのリストを取得することができます。DNSA レコードを A定期的にチェックA し、このレコードが返すIPをセカンダリのアドレスとして使用 Aします。サービスA ディスカバリーが動作するために必要なのは、DNSサーバーと AセカンダリのIPアドレスを含むA レコードだけ 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'
  }
}
  1. ファイルを保存し、変更を有効にするためにGitLab を再設定してください。
オプション説明デフォルト
nameserverDNS レコードの検索に使用するネームサーバー。ローカルホスト
record検索するレコード。このオプションはサービス発見が機能するために必要です。 
record_type検索するオプションのレコードタイプ。A またはSRV (GitLab 12.3 以降) のいずれかになります。A
portネームサーバーのポート。8600
intervalDNSレコードをチェックする最短時間(秒)。60
disconnect_timeoutホスト一覧を更新した後、古い接続を閉じるまでの時間 (秒単位)。120
use_tcpUDP ではなく TCP を使った DNS リソースの検索false
max_replica_pools各Railsプロセスが接続するレプリカの最大数。この制限がないと、デフォルトですべてのRailsプロセスがすべてのレプリカに接続するからです。設定しない場合のデフォルトの動作は無制限です。無制限

record_typeSRV に設定されている場合、GitLab は引き続きラウンドロビンアルゴリズムを使用し、レコード内のweightpriority は無視します。SRV レコードは通常IPの代わりにホスト名を返すので SRVSRV GitLabは SRVレスポンスのSRV 追加セクションで返されたホスト名のIPを探す必要が SRVあります。ホスト名のIPが見つからない場合、GitLabは設定されたnameserver forANY レコードをクエリし、そのようなホスト名ごとにA またはAAAA レコードを探し、最終的にIPを解決できなければそのホスト名をローテーションから外す必要があります。

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

ホストのリストが更新されると、古い接続が終了するまでしばらく時間がかかることがあります。disconnect_timeout 、すべての古いデータベース接続の終了にかかる時間の上限を設定することができます。

古い読み込みの処理

14.0でGitLab PremiumからGitLab Freeに移行

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

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

オプション説明デフォルト
max_replication_differenceセカンダリがしばらくの間データをレプリケートしていないときに、セカンダリが遅れてもよいデータ量(バイト数)。8 MB
max_replication_lag_timeセカンダリの使用を停止するまでの最大遅延秒数。60秒
replica_check_intervalセカンダリのステータスをチェックするまでの最低待機時間。60秒

ほとんどのユーザーにはデフォルトで十分でしょう。

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

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回まで再試行します。

ロードバランシングを使用する場合、データベースサーバーを安全に再起動することができるはずです。

開発者向けガイド

データベースの負荷分散に関する詳細な開発ガイドについては、開発ドキュメントを参照してください。