データベースの負荷分散
- GitLab Premium9.0で導入されました。
- 14.0でGitLab PremiumからGitLab Freeに移行。
- GitLab 14.1でSidekiqに導入。
データベースロードバランシングにより、読み取り専用のクエリを複数のPostgreSQLノードに分散し、パフォーマンスを向上させることができます。
この機能はGitLab RailsとSidekiqでネイティブに提供されており、外部依存なしにラウンドロビン方式でデータベースの読み込みクエリのバランスを取るように設定できます:
データベース負荷分散を有効にするための要件
データベースのロードバランシングを有効にするには、以下を確認してください:
- HAのPostgreSQLセットアップで、プライマリノードをレプリケートするセカンダリノードが1つ以上あること。
- それぞれのPostgreSQLノードは同じ認証情報と同じポートで接続されています。
Linuxパッケージインストールの場合、複数ノードの設定時にすべての負荷分散接続をプールするために、各PostgreSQLノードで設定されたPgBouncerも必要です。
データベースの負荷分散の設定
データベースのロードバランシングは、2つの方法のいずれかで設定できます:
- (推奨)Hosts: PostgreSQLホストのリスト。
- Service Discovery: PostgreSQLホストのリストを返すDNSレコード。
ホスト
ホストのリストを設定するには、バランスを取りたい環境ごとに、すべてのGitLab RailsとSidekiqノードで以下の手順を実行します:
-
/etc/gitlab/gitlab.rb
ファイルを編集します。 -
gitlab_rails['db_load_balancing']
に、バランスを取りたいデータベースホストの配列を作成します。例えば、PostgreSQLがprimary.example.com
、secondary1.example.com
、secondary2.example.com
のホスト上で動作している環境の場合です:gitlab_rails['db_load_balancing'] = { 'hosts' => ['primary.example.com', 'secondary1.example.com', 'secondary2.example.com'] }
これらのホストは、
gitlab_rails['db_port']
で設定された同じポートで到達可能でなければなりません。 - ファイルを保存し、GitLabを再設定してください。
サービス・ディスカバリー
GitLab 11.0で導入されました。
サービスディスカバリーによって、GitLabは自動的に使用するPostgreSQLホストのリストを取得することができます。DNSA
レコードを A
定期的にチェックA
し、このレコードが返すIPをセカンダリのアドレスとして使用 A
します。サービスA
ディスカバリーが動作するために必要なのは、DNSサーバーと A
セカンダリのIPアドレスを含むA
レコードだけ A
です。
Linuxパッケージインストールを使用している場合、提供されているConsulサービスがDNSサーバとして動作し、postgresql-ha.service.consul
レコードを介してPostgreSQLアドレスを返します。例えば
- 各 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'
}
}
- ファイルを保存し、変更を有効にするためにGitLab を再設定してください。
オプション | 説明 | デフォルト |
---|---|---|
nameserver | DNS レコードの検索に使用するネームサーバー。 | ローカルホスト |
record | 検索するレコード。このオプションはサービス発見が機能するために必要です。 | |
record_type | 検索するオプションのレコードタイプ。A またはSRV (GitLab 12.3 以降) のいずれかになります。 | A |
port | ネームサーバーのポート。 | 8600 |
interval | DNSレコードをチェックする最短時間(秒)。 | 60 |
disconnect_timeout | ホスト一覧を更新した後、古い接続を閉じるまでの時間 (秒単位)。 | 120 |
use_tcp | UDP ではなく TCP を使った DNS リソースの検索 | false |
max_replica_pools | 各Railsプロセスが接続するレプリカの最大数。この制限がないと、デフォルトですべてのRailsプロセスがすべてのレプリカに接続するからです。設定しない場合のデフォルトの動作は無制限です。 | 無制限 |
record_type
がSRV
に設定されている場合、GitLab は引き続きラウンドロビンアルゴリズムを使用し、レコード内のweight
とpriority
は無視します。SRV
レコードは通常IPの代わりにホスト名を返すので SRV
、SRV
GitLabは SRV
レスポンスのSRV
追加セクションで返されたホスト名のIPを探す必要が SRV
あります。ホスト名のIPが見つからない場合、GitLabは設定されたnameserver
forANY
レコードをクエリし、そのようなホスト名ごとにA
またはAAAA
レコードを探し、最終的にIPを解決できなければそのホスト名をローテーションから外す必要があります。
interval
値は、チェック間の_最小_時間を指定します。A
レコードのTTLがこの値より大きい場合、サービスディスカバリーはそのTTLを優先 A
します。たとえば、レコードA
のTTLが A
90秒の場合、サービスディスカバリーは、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
- 常に記録されるコンテキスト情報。例えば、
severity
やtime
。
使用例:
{"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回まで再試行します。
ロードバランシングを使用する場合、データベースサーバーを安全に再起動することができるはずです。
開発者向けガイド
データベースの負荷分散に関する詳細な開発ガイドについては、開発ドキュメントを参照してください。