GitLab開発に必須?PostgreSQLとの連携方法を解説

はい、承知いたしました。GitLab開発におけるPostgreSQL連携の詳細な解説を含む、約5000語の記事を作成します。


GitLab開発に必須?PostgreSQLとの連携方法を徹底解説

GitLabは、ソフトウェア開発ライフサイクル全体をカバーする包括的なDevOpsプラットフォームです。バージョン管理システムとしてのGit機能に加え、CI/CDパイプライン、イシュー管理、コードレビュー、モニタリング、セキュリティスキャンなど、多岐にわたる機能を提供します。これらの機能の心臓部として、GitLabは膨大なデータを管理しています。プロジェクト情報、ユーザーデータ、コミット履歴、パイプラインの実行ログ、マージリクエストの議論、イシューの状態など、プラットフォーム上で発生するあらゆる活動はデータベースに記録されます。

このデータベースとして、GitLabが第一選択肢として推奨し、そして内部的に利用しているのがPostgreSQLです。GitLab開発に携わる者、あるいはGitLabを運用する者にとって、PostgreSQLとの連携、その仕組み、そして最適な利用方法を理解することは、安定した、高性能な、そしてスケーラブルなGitLab環境を構築・維持するために不可欠です。

この記事では、なぜGitLabがPostgreSQLを選ぶのかという基本的な問いから始まり、PostgreSQLのインストール、GitLabとの連携設定、開発者が知っておくべきデータベース操作のベストプラクティス、そして運用・保守の側面まで、GitLabとPostgreSQLの連携について詳細に解説します。

1. はじめに:GitLabとデータベースの役割

GitLabは、Gitリポジトリを核としたアプリケーションですが、その機能は単なるコードホスティングにとどまりません。前述のように、イシュー管理、マージリクエスト、CI/CD、Wiki、スニペットなど、様々な機能が統合されています。これらの機能は、ユーザー、プロジェクト、リポジトリのメタデータ、活動ログ、設定情報など、膨大な構造化データに依存しています。

例えば、

  • プロジェクト情報: リポジトリのパス、可視性設定、フィーチャーフラグなど
  • ユーザー情報: ユーザー名、メールアドレス、権限、SSHキーなど
  • イシュー/マージリクエスト: タイトル、説明、ステータス、担当者、コメント、ラベルなど
  • CI/CD: パイプライン定義、ジョブのログ、実行履歴、アーティファクト情報など
  • 権限: ユーザーとプロジェクト、グループ間のアクセスレベル

これらのデータはすべて、GitLabアプリケーションが適切に機能するために、常に整合性が保たれ、迅速にアクセス可能である必要があります。このデータの永続化と管理を担っているのがデータベースです。

GitLabは、その設計当初からPostgreSQLを主要なデータベースとして採用しています。これは単なる偶然ではなく、PostgreSQLが持つ様々な特性がGitLabのような複雑で大規模なアプリケーションの要件を満たすためです。

なぜPostgreSQLなのか?

GitLabがPostgreSQLを選択する主な理由は以下の通りです。

  • 堅牢性と信頼性: PostgreSQLは長い歴史を持ち、ACID特性(原子性、一貫性、独立性、永続性)を厳密に保証する、非常に信頼性の高いリレーショナルデータベースシステムです。データ損失や破損のリスクを最小限に抑え、トランザクションの整合性を保証することは、GitLabのような重要なプラットフォームにおいて極めて重要です。
  • 豊富な機能と標準への準拠: PostgreSQLはSQL標準に高く準拠しており、多様なデータ型(JSON, HSTORE, 配列など)、インデックスタイプ(B-tree, GIN, GiSTなど)、外部キー制約、トリガー、ストアドプロシージャなど、非常に豊富な機能を備えています。これにより、GitLabは複雑なデータ構造やロジックをデータベース層で効率的に扱うことができます。
  • 拡張性 (Extensibility): PostgreSQLは拡張機能(Extensions)によって機能を容易に追加できます。GitLabは、例えばpg_stat_statements(クエリ統計情報の収集)やbtree_gist(GiSTインデックスの機能拡張)など、様々な拡張機能を活用してパフォーマンス監視や機能を実現しています。
  • パフォーマンス: 適切な設計とチューニングにより、PostgreSQLは大量のデータと高い同時接続数に対しても優れたパフォーマンスを発揮します。複雑なクエリを効率的に実行するための高度なクエリプランナーを備えています。
  • コミュニティとエコシステム: PostgreSQLは活発なオープンソースコミュニティによって開発されており、豊富なドキュメント、ツール、情報が利用可能です。これにより、問題解決や運用が容易になります。
  • ライセンス: PostgreSQLは寛容なBSDライセンスの下で提供されており、商用利用や組み込みが容易です。

これらの理由から、PostgreSQLはGitLabのバックエンドデータベースとして最適な選択肢とされています。GitLab開発者は、自身が書くコードがどのようにデータベースとやり取りし、それが全体のパフォーマンスや安定性にどう影響するかを理解するために、PostgreSQLの知識が不可欠となります。

2. GitLabとPostgreSQLの関係性の基礎

GitLabは複数のコンポーネントから成るマイクロサービスアーキテクチャに近い構成を持っていますが、主要なRailsアプリケーション、Sidekiq(バックグラウンドジョブワーカ)、そしてGitaly(Gitリポジトリ操作サービス)などがPostgreSQLデータベースに接続してデータを読み書きします。

  • Railsアプリケーション: WebインターフェースやAPIリクエストを処理します。ユーザー操作(イシュー作成、MR承認、設定変更など)の結果をデータベースに永続化したり、表示のためにデータを読み出したりします。ActiveRecord ORM(Object-Relational Mapping)を通じてデータベースとやり取りします。
  • Sidekiq: 非同期タスクやバックグラウンドジョブ(CI/CDパイプライン実行、メール送信、データクリーンアップなど)を実行します。これらのジョブも、その状態や結果をデータベースに記録したり、必要な情報をデータベースから読み出したりします。
  • Gitaly: Gitリポジトリの操作(クローン、プッシュ、プル、ブランチ作成など)を専門に扱います。リポジトリ自体はファイルシステム上に存在しますが、リポジトリに関連するメタデータ(プロジェクト情報、ブランチ保護設定など)はデータベースに格納されています。GitalyはGitLab ShellやWorkhorseといった他のコンポーネントからリクエストを受け、必要に応じてデータベースからも情報を取得します。

これらのコンポーネントは、それぞれPostgreSQLへの接続プールを持ち、データベースに対してSQLクエリを発行します。データベースは、これらのクエリを実行し、データの読み書きやトランザクション処理を行います。

PostgreSQLは、以下の種類の情報を主に格納します。

  • 認証・認可データ: ユーザー情報、グループ、プロジェクト、ロール、権限
  • プロジェクト管理データ: プロジェクト、マージリクエスト、イシュー、コメント、ラベル、マイルストーン、エピック
  • CI/CDデータ: パイプライン、ジョブ、ステージ、変数、Runner情報
  • 設定データ: アプリケーション設定、プロジェクト設定、機能フラグ
  • アクティビティデータ: イベントログ、監査ログ
  • Wiki/スニペットのメタデータ
  • Container Registry, Package Registry, Dependency Proxyなどのメタデータ

これらのデータは、PostgreSQLのリレーショナルモデルに従ってテーブル構造として格納されます。GitLabの複雑な機能は、これらのテーブル間のリレーションシップと、それらを操作するクエリによって実現されています。

3. PostgreSQLのインストールと基本的な設定

GitLabをインストールする方法によって、PostgreSQLのセットアップ方法は異なります。

  • Omnibus Package: GitLabの推奨インストール方法です。これには、PostgreSQLサーバーが同梱されており、特別な設定なしに利用できます。デフォルトでは、GitLabと同じサーバー上で稼働するこの内部PostgreSQLインスタンスが利用されます。
  • Source Installation: GitLabをソースコードからビルドしてインストールする場合です。この場合は、外部のPostgreSQLインスタンスを別途準備し、設定する必要があります。

ここでは、外部PostgreSQLを利用するケースを想定して、基本的なインストールと設定について解説します。

3.1. PostgreSQLのインストール

各OSのパッケージマネージャを使用してインストールするのが最も一般的です。

Debian/Ubuntu:

bash
sudo apt update
sudo apt install postgresql postgresql-contrib

RHEL/CentOS/Rocky Linux/AlmaLinux:

bash
sudo dnf install postgresql-server postgresql-contrib # または yum install
sudo /usr/bin/postgresql-setup --initdb # 初回のみデータベースクラスターを初期化
sudo systemctl enable postgresql
sudo systemctl start postgresql

インストール後、PostgreSQLサービスが起動していることを確認します。

3.2. 基本的な設定ファイル postgresql.conf

PostgreSQLの主要な設定ファイルは通常 /etc/postgresql/<version>/main/postgresql.conf (Debian/Ubuntu) や /var/lib/pgsql/data/postgresql.conf (RHEL系) にあります。このファイルで、サーバーの挙動、リソース割り当て、ロギングなどが設定されます。GitLabのパフォーマンスに影響を与える主要な設定項目をいくつか紹介します。

  • listen_addresses: PostgreSQLが接続を受け付けるIPアドレスを指定します。GitLabサーバーから接続できるよう、GitLabサーバーのIPアドレスまたは ‘‘ を指定します。セキュリティのため、 ‘‘ の場合はファイアウォールでアクセス元を制限することが推奨されます。
    ini
    listen_addresses = 'localhost, 192.168.1.10' # または '*'
  • port: PostgreSQLがリッスンするポート番号。デフォルトは5432です。
  • max_connections: 同時接続の最大数。GitLabのWebプロセス、Sidekiqワーカ、Gitalyなどがそれぞれ接続プールを持つため、十分な数を確保する必要があります。GitLabの推奨値を参考に設定します。
  • shared_buffers: PostgreSQLがデータをキャッシュするために使用するメモリ容量。システムメモリの25%程度が目安とされますが、適切な値はワークロードに依存します。
  • work_mem: 各バックエンドプロセスがソートやハッシュテーブルなどの内部操作に使用できるメモリ容量。複雑なクエリが多い場合は、この値を増やすことでディスクへの一時ファイル書き込みを減らし、パフォーマンスを向上できます。ただし、同時実行クエリ数 * work_mem がシステムメモリ総量を超えないように注意が必要です。
  • maintenance_work_mem: VACUUM, CREATE INDEX, ALTER TABLEなどのメンテナンス操作に使用されるメモリ容量。これらの操作を高速化するために、通常 work_mem よりも大きな値を設定します。
  • wal_level: Write-Ahead Logging (WAL) の詳細レベル。レプリケーションやPoint-in-Time Recovery (PITR) を行う場合は replica または logical に設定します。
  • archive_mode, archive_command: PITRのためにWALファイルをアーカイブする設定。on にして、archive_command で適切なコマンドを指定します。
  • max_wal_senders: ストリーミングレプリケーションのために接続できるレプリケーションスロットの最大数。レプリカサーバーの数に合わせて設定します。
  • wal_keep_segments: pg_wal ディレクトリに保持するWALファイルの最小数。レプリカが必要とするWALファイルが削除されないように設定します。
  • random_page_cost, seq_page_cost: クエリプランナーが、ディスクシークを伴うランダムアクセスとシーケンシャルアクセスにかかるコストを評価するためのパラメータ。SSDを使用している場合は random_page_cost を小さく設定することで、インデックススキャンがより優先されるようになります。
  • effective_cache_size: オペレーティングシステムのページキャッシュとPostgreSQLの shared_buffers を含めた、利用可能な合計メモリ量をクエリプランナーにヒントとして与えるパラメータ。システムメモリ総量の50-75%程度を目安に設定します。
  • ロギング設定 (log_destination, logging_collector, log_directory, log_filename, log_min_duration_statementなど): サーバーログの出力先、ファイル名、ログに記録するイベント(例えば、一定時間以上かかるクエリ)などを設定します。トラブルシューティングやパフォーマンス分析に非常に役立ちます。log_min_duration_statement = 250 (250ミリ秒以上かかるクエリをログに出力) のように設定すると、スロークエリの特定に役立ちます。

設定変更後は、PostgreSQLサービスを再起動して変更を反映させます。

bash
sudo systemctl restart postgresql

3.3. 認証設定 pg_hba.conf

pg_hba.conf ファイルは、PostgreSQLへのクライアント認証方法を制御します。どのホストから、どのデータベースに、どのユーザーが、どの認証方式で接続できるかを定義します。GitLabサーバーからPostgreSQLへの接続を許可するように設定する必要があります。

ファイルは通常 /etc/postgresql/<version>/main/pg_hba.conf/var/lib/pgsql/data/pg_hba.conf にあります。

典型的なエントリは以下のようになります。

“`ini

TYPE DATABASE USER ADDRESS METHOD

Allow GitLab server to connect to the gitlabhq_production database

host gitlabhq_production gitlab 192.168.1.10/32 md5

Allow local connections for administration

local all postgres peer
host all all 127.0.0.1/32 md5
“`

各カラムの意味:

  • TYPE: 接続タイプ (local はUnixドメインソケット、host はTCP/IPソケット、hostssl はSSL経由のTCP/IP、hostnossl はSSLなしのTCP/IP)
  • DATABASE: 接続を許可するデータベース名 (all は全てのデータベース、カンマ区切りで複数指定可能)
  • USER: 接続を許可するユーザー名 (all は全てのユーザー、カンマ区切りで複数指定可能)
  • ADDRESS: 接続元のアドレス。host の場合のみ指定。IPアドレス、IPアドレス範囲 (CIDR形式)、ホスト名、または alllocal の場合は不要。
  • METHOD: 認証方式 (md5 はMD5ハッシュ化されたパスワード、password は平文パスワード、trust はパスワード不要、peer はOSユーザー名を使用、ident はidentサービスを使用など)。md5 が推奨されます。

GitLabが使用するデータベースとユーザーに対して、GitLabサーバーのIPアドレスからの host 接続を md5 方式で許可する設定を追加します。設定変更後、pg_ctl reload コマンドを実行するか、PostgreSQLサービスを再起動して変更を反映させます。

bash
sudo systemctl reload postgresql # または restart

3.4. PostgreSQLユーザーとデータベースの作成

GitLabはデータベースに接続するために専用のPostgreSQLユーザーを必要とします。また、GitLabが使用するデータベース(デフォルトでは gitlabhq_production)も作成する必要があります。

postgres ユーザーでログインし、psql クライアントを使用してコマンドを実行します。

bash
sudo -u postgres psql

PostgreSQLプロンプト (#) で以下のコマンドを実行します。

“`sql
— GitLab用のユーザーを作成(パスワードを安全なものに変更してください)
CREATE USER gitlab WITH PASSWORD ‘your-strong-password’;

— GitLabが使用するデータベースを作成
CREATE DATABASE gitlabhq_production OWNER gitlab;

— 接続をテストするために、作成したユーザーでデータベースに接続できるか確認
— \connect gitlabhq_production gitlab

— \q でpsqlを終了
“`

これで、GitLabから接続するための基本的なPostgreSQL側の準備が整いました。

4. GitLabとの連携設定

いよいよ、GitLabをPostgreSQLに接続するための設定を行います。これも、Omnibus PackageかSource Installationかで方法が異なります。

4.1. Omnibus Packageでの設定

Omnibus Packageの場合、設定は /etc/gitlab/gitlab.rb ファイルで行います。外部PostgreSQLを利用する場合、デフォルトで有効になっている内部PostgreSQLの設定を無効にし、外部データベースの設定を記述します。

/etc/gitlab/gitlab.rb を編集します。

“`ruby

Disable the built-in database

これはデフォルトでコメントアウトされていることが多いですが、明示的に指定する場合は以下のようにします

postgresql[‘enable’] = false # 内部PostgreSQLを使用しない場合はコメントを外してfalseに

Configure the external database connection

gitlab_rails[‘db_adapter’] = ‘postgresql’
gitlab_rails[‘db_encoding’] = ‘unicode’
gitlab_rails[‘db_database’] = ‘gitlabhq_production’ # 作成したデータベース名
gitlab_rails[‘db_pool’] = 10 # 接続プールサイズ (推奨値を参考に)
gitlab_rails[‘db_username’] = ‘gitlab’ # 作成したユーザー名
gitlab_rails[‘db_password’] = ‘your-strong-password’ # 設定したパスワード
gitlab_rails[‘db_host’] = ‘192.168.1.20’ # PostgreSQLサーバーのIPアドレスまたはホスト名
gitlab_rails[‘db_port’] = 5432 # PostgreSQLがリッスンしているポート

Optional: Configure SSL connection (recommended)

gitlab_rails[‘db_sslmode’] = ‘require’ # または verify-full

gitlab_rails[‘db_sslrootcert’] = ‘/path/to/ca.crt’

“`

設定変更後、GitLabの設定を再構築します。

bash
sudo gitlab-ctl reconfigure

このコマンドは、/etc/gitlab/gitlab.rb の設定を読み込み、様々な設定ファイルを生成し、GitLabの各コンポーネント(Nginx, Puma, Sidekiqなど)を適切に設定します。PostgreSQLの接続設定もこの時に反映されます。gitlab-ctl reconfigure 実行中にデータベース接続テストが行われ、問題があればエラーが表示されます。

4.2. Source Installationでの設定

Source Installationの場合、データベース設定は database.yml ファイル(通常 /home/git/gitlab/config/database.yml など)で行います。

“`yaml

database.yml

production:
adapter: postgresql
encoding: unicode
database: gitlabhq_production # 作成したデータベース名
pool: 10 # 接続プールサイズ
username: gitlab # 作成したユーザー名
password: your-strong-password # 設定したパスワード
host: 192.168.1.20 # PostgreSQLサーバーのIPアドレスまたはホスト名
port: 5432 # PostgreSQLがリッスンしているポート
# Optional: Configure SSL connection
# sslmode: require # または verify-full
# sslrootcert: /path/to/ca.crt

development:

test:

“`

または、環境変数を使用して設定することも可能です。これにより、認証情報などを設定ファイルから分離できます。

“`bash
export DATABASE_URL=”postgresql://gitlab:[email protected]:5432/gitlabhq_production”

必要に応じてSSLモードなども環境変数で指定

“`

設定後、GitLabサービスを再起動して変更を反映させます。

4.3. 接続テストとトラブルシューティング

設定が完了したら、GitLabがデータベースに正しく接続できるか確認します。

Omnibus Packageの場合は、gitlab-ctl reconfigure が成功すれば基本的な接続は確立されています。さらに詳細なテストやトラブルシューティングが必要な場合は、以下のコマンドが役立ちます。

“`bash

データベース接続状態を確認する (Omnibus)

sudo gitlab-ctl psql -d gitlabhq_production -U gitlab -h 192.168.1.20 -p 5432

接続できればpsqlプロンプトが表示される

\conninfo で接続情報を確認

\q で終了

“`

Source Installationの場合は、GitLabのRailsコンソールから接続を試みるのが一般的です。

“`bash

GitLabのルートディレクトリへ移動

cd /home/git/gitlab

Railsコンソールを起動

bundle exec rails c production

データベース接続をテスト

ActiveRecord::Base.connection.active?

=> true が返れば接続成功

簡単なクエリを実行してみる

User.count

=> ユーザー数が返ればOK

“`

接続できない場合、考えられる原因と対策:

  • ファイアウォール: GitLabサーバーとPostgreSQLサーバー間のポート (デフォルト 5432) がファイアウォールで閉じられていないか確認します。
  • pg_hba.conf: GitLabサーバーのIPアドレス、GitLabユーザー、データベース名が正しく設定され、適切な認証方式 (md5 が推奨) が指定されているか確認します。設定変更後はリロードまたは再起動が必要です。
  • postgresql.conf: listen_addresses が適切に設定され、GitLabサーバーからの接続を受け付けるようになっているか確認します。設定変更後は再起動が必要です。
  • ユーザー/パスワード: gitlab.rb または database.yml に設定したユーザー名とパスワードがPostgreSQLで作成したものと一致しているか確認します。
  • PostgreSQLサービス: PostgreSQLサービスが起動しているか確認します (systemctl status postgresql)。
  • GitLab設定の反映: gitlab-ctl reconfigure または GitLabサービスの再起動が正しく行われたか確認します。
  • ログファイルの確認: PostgreSQLサーバーのログ (log_directory で設定した場所) やGitLabのログを確認し、接続エラーに関する詳細情報を探します。

5. GitLab開発におけるデータベース操作のベストプラクティス

GitLab開発者がPostgreSQLとの連携において最も頻繁に触れるのは、データベーススキーマの変更と、アプリケーションコードからのデータアクセスです。GitLabはRuby on Railsフレームワークを使用しており、データベース操作には主にActiveRecord ORMを利用します。

5.m. ActiveRecordとデータベース操作

ActiveRecordは、オブジェクト指向プログラミングのパラダイムでリレーショナルデータベースを操作するためのツールです。Rubyのクラスがデータベースのテーブルに対応し、インスタンスがテーブルの行に対応します。

  • モデル (Models): app/models ディレクトリにあるRubyクラスがActiveRecordモデルとして定義されます。これらのモデルは、対応するテーブルとやり取りするためのメソッド(データ取得、保存、更新、削除、関連付けなど)を提供します。
  • リレーション (Associations): has_many, belongs_to, has_one, has_and_belongs_to_many などのメソッドを使用して、モデル間の関係(テーブル間のリレーションシップ)を定義します。ActiveRecordはこれらの定義に基づいて、関連するデータを効率的にロードするクエリを生成します。
  • クエリインターフェース: Model.where(...), Model.find(...), instance.save, instance.destroy など、ActiveRecordは直感的で高レベルなクエリインターフェースを提供します。これにより、SQLを直接書くことなくデータベースを操作できます。

ActiveRecordは開発を迅速化する一方で、その裏側で生成されるSQLや、それがデータベースのパフォーマンスにどう影響するかを理解することは、高性能なGitLabを開発する上で非常に重要です。

5.2. データベースマイグレーション (Database Migrations)

データベーススキーマ(テーブル構造、カラム、インデックス、制約など)を変更する際は、マイグレーションファイルを作成します。マイグレーションは、データベーススキーマの変更履歴をコードとして管理し、開発環境、テスト環境、本番環境で一貫性のある方法でスキーマ変更を適用できるようにするための仕組みです。

GitLabのマイグレーションファイルは通常 db/migrate ディレクトリにあります。ファイル名にはタイムスタンプが含まれており、実行順序が保証されます。

マイグレーションファイルの例 (add_description_to_issues.rb):

“`ruby

db/migrate/YYYYMMDDHHMMSS_add_description_to_issues.rb

class AddDescriptionToIssues < ActiveRecord::Migration[6.1] # Railsのバージョンによる
def change
add_column :issues, :description, :text
# 必要に応じてインデックスを追加
# add_index :issues, :description_checksum
end
end
“`

マイグレーションを実行するには、GitLabのRakeタスクを使用します。

“`bash

GitLabのルートディレクトリで実行 (Source Installation)

bundle exec rake db:migrate

Omnibus Packageの場合は gitlab-rake コマンドを使用

sudo gitlab-rake db:migrate
“`

マイグレーションは、up メソッド(変更を適用)と down メソッド(変更を取り消すロールバック)を持ちます。change メソッドを使用すると、Railsが updown の操作を自動的に推測してくれますが、複雑な操作や不可逆な操作(カラム削除など)の場合は updown を明示的に定義する必要があります。

“`ruby
class RemoveOldColumnFromProjects < ActiveRecord::Migration[6.1]
def up
remove_column :projects, :old_column_name
end

def down
# ロールバック時にカラムを復元することは通常困難または不可能なので、
# ここでは例外を発生させるなど、ロールバックを許可しない旨を示すことが多い
raise ActiveRecord::IrreversibleMigration
# もし復元可能なら add_column などを記述
end
end
“`

5.3. Zero-Downtime Migrations (ダウンタイムを最小限に抑えるマイグレーション)

GitLab.comのような大規模環境では、サービス停止なしにデータベーススキーマを変更することが求められます。これを実現するために、GitLab開発ではZero-Downtime Migrationsの手法を積極的に採用しています。ActiveRecordの標準的なマイグレーションコマンドは、大きなテーブルに対して ALTER TABLE などの操作を行う際に、テーブル全体にロックをかけてしまい、その間アプリケーションからのアクセスがブロックされる可能性があります。

ダウンタイムを避けるための主なテクニック:

  • disable_ddl_transaction!: マイグレーション全体を単一のトランザクションとして実行しないようにします。これにより、大きな操作を複数のステップに分割し、各ステップの間にアプリケーションがデータベースにアクセスできるようになります。
  • コンカレントインデックス (Concurrent Indexes): add_index の代わりに add_concurrent_index を使用します。これにより、インデックス作成中にテーブルが読み書き可能になります(ただし、インデックス作成に時間がかかる場合があり、失敗する可能性もあります)。
  • コンカレントカラム追加: add_column の際に defaultNOT NULL 制約を同時に追加するとロックが発生しやすいです。
    1. まず null: true でカラムを追加します。
    2. 次に、バックグラウンドマイグレーションなどを利用して、既存のデータに新しいカラムの値を投入します。
    3. 値の投入が完了したら、NOT NULL 制約を validate_not_nulladd_not_null_constraint の2段階で追加します。
    4. 最後に、default 値が必要な場合は、データベースレベルで設定します。
  • バックグラウンドマイグレーション (Background Migrations): 大量のデータを更新する必要がある場合(例: 新しいカラムに既存データから計算した値をセットする)、通常のマイグレーションで実行するとタイムアウトやロックを引き起こす可能性があります。GitLabではSidekiqを利用したバックグラウンドマイグレーションの仕組みが導入されています。これは、マイグレーションを小さなバッチに分割し、バックグラウンドでゆっくりと実行するものです。開発者は queue_background_migration などのヘルパーメソッドを使用してこれをスケジューリングします。
  • リネーム操作: テーブル名やカラム名のリネームはロックを伴います。GitLabでは、古い名前と新しい名前の両方で読み書き可能な状態を一時的に作る、といったより複雑な段階的アプローチを採用することがあります。

GitLabの開発ガイドラインには、これらのZero-Downtime Migrationsに関する詳細なベストプラクティスが記載されています。大規模サービスでの開発においては、これらの手法を理解し、適用することが必須です。

5.4. データベースクエリの最適化

アプリケーションのパフォーマンスの多くは、データベースクエリの効率に依存します。遅いクエリは、Webリクエストの応答時間の増加、Sidekiqジョブの遅延、データベースサーバーのリソース枯渇などを引き起こします。

  • N+1問題の回避: ActiveRecordで関連オブジェクトをロードする際に発生しやすい問題です。例えば、プロジェクト一覧を表示する際に、各プロジェクトのオーナー情報も表示する場合、プロジェクトをN件取得した後、各プロジェクトに対して個別にオーナー情報を取得するクエリ(合計N+1クエリ)が発行されてしまうことがあります。これを回避するには、includes, preload, eager_load などのメソッドを使用して、関連オブジェクトをまとめてロードします。
    “`ruby
    # Bad (N+1):
    projects = Project.all
    projects.each { |project| puts project.owner.username }

    Good (Eager loading):

    projects = Project.includes(:owner)
    projects.each { |project| puts project.owner.username }
    * **`EXPLAIN` / `EXPLAIN ANALYZE` の活用:** PostgreSQLの `EXPLAIN` コマンドは、指定したクエリがどのように実行されるか(クエリプラン)を表示します。どのテーブルがどのような順序でアクセスされ、どのインデックスが使用され、結合がどのように行われるかなどが分かります。`EXPLAIN ANALYZE` は実際にクエリを実行し、各ステップの実行時間や返された行数などの統計情報も表示します。遅いクエリを特定したら、`EXPLAIN ANALYZE` を使ってクエリプランを分析し、パフォーマンスボトルネックを特定します。sql
    EXPLAIN ANALYZE SELECT * FROM issues WHERE project_id = 123 AND state = ‘opened’ ORDER BY created_at DESC;
    ``
    この結果を見て、期待したインデックスが使われているか、Seq Scan (シーケンシャルスキャン、全件検索) になっていないか、不要なソートが発生していないかなどを確認します。
    * **インデックスの適切な使用:** インデックスは、特定のカラムでの検索、ソート、結合操作を高速化します。頻繁にWHERE句やORDER BY句で使用されるカラム、JOINの結合条件となるカラムにはインデックスを考慮します。
    *
    add_index :table_name, :column_name* 複合インデックス (add_index :table_name, [:column1, :column2]):複数のカラムを組み合わせて検索する場合に有効です。複合インデックスは、左端のカラムから順に使用されます。
    * 部分インデックス (
    add_index :table_name, :column_name, where: ‘condition’):テーブル全体ではなく、特定の条件を満たす行にのみインデックスを作成します。例えば、ステータスが 'open' のイシューは少なく、ほとんどのクエリが 'open' イシューを対象とする場合などに有効です。
    * GitLabではPostgreSQLの様々なインデックスタイプ(GIN for full-text search, GiST for spatial data or rangesなど)も活用しています。
    * **
    COUNT()の注意点:** PostgreSQLでは、COUNT()はテーブルの全ての行をスキャンする必要があるため、非常に高コストになることがあります。おおよその行数で十分な場合は、より高速な方法(例えば、別のキャッシュテーブルに件数を保持するなど)を検討します。
    * **適切なデータ型の選択:** カラムに格納するデータに最適なデータ型(
    integer,bigint,varchar,text,boolean,timestamp,jsonb` など)を選択します。これにより、ストレージ効率が向上し、クエリパフォーマンスが改善されることがあります。
    * 複雑なJOINの回避: 多数のテーブルをJOINする複雑なクエリは、クエリプランナーにとって最適化が難しく、パフォーマンス問題を引き起こしやすいです。必要最小限のJOINに留めたり、非正規化を検討したりします。

5.5. トランザクション管理

トランザクションは、一連のデータベース操作を不可分な単一の単位として扱う仕組みです。全ての操作が成功するか、あるいは全てが失敗してロールバックされるか、のいずれかになります。これにより、データベースの一貫性が保たれます。

ActiveRecordでは、ActiveRecord::Base.transaction do ... end ブロックを使用してトランザクションを定義できます。

ruby
ActiveRecord::Base.transaction do
user = User.create!(name: 'New User')
project = user.projects.create!(name: 'New Project')
# ここで何らかのエラーが発生した場合、userとprojectの作成はロールバックされる
raise "Something went wrong"
end rescue ActiveRecord::Rollback

GitLab開発では、特に複数の関連オブジェクトを作成、更新、削除する場合に、データの整合性を保つためにトランザクションを適切に使用することが重要です。

トランザクション分離レベルは、複数の同時実行トランザクションが互いにどの程度影響を与え合うかを定義します。PostgreSQLのデフォルトは Read Committed です。これは、トランザクション内で実行されるクエリが、そのクエリが開始された時点までにコミットされたデータを「だけ」見ることを保証します。GitLabでは、特定の処理で異なる分離レベル(例えば Repeatable Read)が必要な場合がありますが、これは慎重に検討する必要があります。

5.6. スキーマ設計の考慮事項

GitLabのような複雑なアプリケーションでは、データベーススキーマ設計が非常に重要です。

  • 正規化と非正規化: データの冗長性を排除し、更新時の不整合を防ぐために正規化は重要ですが、極端な正規化はJOINを増やし、読み取りパフォーマンスを低下させる可能性があります。パフォーマンス要件によっては、意図的に非正規化(データの重複を許容)を行うこともあります。
  • Large Objects (LOB): ファイルコンテンツや大きなテキストデータ(例えば、大きなCIログやWikiページの内容)をデータベースに直接格納するか、ファイルシステムに格納してデータベースにはパスだけを格納するかは検討が必要です。PostgreSQLには bytea 型や oid (ラージオブジェクト) を格納する機能がありますが、ファイルシステムの方が一般的にスケールしやすく、Gitリポジトリの内容はファイルシステムに格納されています。
  • 主キーと外部キー: 全てのテーブルに適切な主キーを設定し、関連するテーブル間には外部キー制約を設定することがデータの整合性を保つ上で重要です。ActiveRecordは外部キー制約を直接強制しませんが、データベースレベルでの制約はデータの完全性を保証します。

6. 運用と保守

GitLabを安定稼働させ続けるためには、PostgreSQLデータベースの適切な運用と保守が不可欠です。

6.1. バックアップとリストア

データベースのバックアップは最も重要な運用タスクの一つです。GitLabのデータが失われることは壊滅的な影響を与えます。

  • pg_dump / pg_restore: PostgreSQLの標準ツールです。
    • pg_dump: データベースのスキーマとデータをダンプファイルに出力します。通常、カスタマイズされたバイナリ形式 (-Fc) で出力します。
      bash
      pg_dump -Fc gitlabhq_production > gitlab_db_backup.dump -U gitlab -h localhost
    • pg_restore: pg_dump -Fc で作成したダンプファイルからデータベースをリストアします。
      bash
      pg_restore -d gitlabhq_production gitlab_db_backup.dump -U gitlab -h localhost -c -O

      -c: リストア前にデータベースオブジェクトを全て削除します。-O: オブジェクトのオーナーシップをリストア元のままにせず、接続ユーザーに設定します。
  • Point-in-Time Recovery (PITR): 特定の任意の時点の状態にデータベースを復旧する高度なバックアップ手法です。これには、ベースバックアップ (pg_basebackup) と継続的なWALアーカイブが必要です。WALアーカイブを設定することで、直近のトランザクションまで復旧することが可能になります。
  • GitLab Omnibusのバックアップ機能: Omnibus Packageには、GitLab全体(リポジトリ、データベース、設定ファイルなど)をバックアップ・リストアする組み込み機能があります。これは gitlab-backup create コマンドを使用します。デフォルトでは、このコマンドは内部PostgreSQLのデータもバックアップに含めます(外部PostgreSQLの場合は別途バックアップが必要です)。
    bash
    sudo gitlab-backup create

    リストアは gitlab-backup restore DIR=timestamp_of_backup で行います。

バックアップ戦略では、バックアップの頻度、保存場所(リモートストレージなど)、保存期間、そして何よりリストアが正しく機能することの定期的なテストが重要です。

6.2. 監視とパフォーマンスチューニング

データベースの状態とパフォーマンスを継続的に監視し、必要に応じてチューニングを行うことが重要です。

  • PostgreSQLの統計情報: PostgreSQLは豊富な統計情報を提供します。
    • pg_stat_activity: 現在実行中のクエリ、接続元、待機中のロックなどを確認できます。遅いクエリやブロックしているクエリの特定に役立ちます。
    • pg_stat_statements (contribモジュール): 実行されたクエリとその実行時間、呼び出し回数などの統計情報を集計します。最も時間のかかっているクエリや、頻繁に実行されているクエリを特定するのに非常に有用です。利用するには postgresql.confshared_preload_libraries = 'pg_stat_statements' と設定し、拡張機能を有効にする必要があります。
    • pg_stat_user_tables, pg_stat_user_indexes: テーブルやインデックスのアクセス統計情報を提供します(スキャン回数、タプル読み取り数など)。
  • 監視ツールとの連携: Prometheus, Grafana, Zabbix, Nagiosなどの監視ツールを使用して、CPU使用率、メモリ使用率、ディスクIO、コネクション数、QPS (Query Per Second)、スロークエリ数などを監視します。PostgreSQLエクスポーターなどが利用可能です。
  • GitLabの組み込み監視: GitLab自身もPrometheusやGrafanaと連携し、GitLabアプリケーションや付属サービスのメトリクス(Sidekiqキュー長、Pumaワーカー数など)を収集・表示できます。これらの情報とDBのメトリクスを関連付けて分析することで、問題の根本原因を特定しやすくなります。
  • スロークエリの特定と対策: log_min_duration_statement を設定したログファイルや pg_stat_statements を分析して、遅いクエリを特定します。特定したクエリに対して EXPLAIN ANALYZE を実行し、ボトルネック(インデックスの不足/不適切、非効率なJOIN、大量の行スキャンなど)を特定します。その後、インデックスの追加/変更、クエリのリファクタリング、スキーマ変更などで対策を行います。
  • 設定パラメータのチューニング: 前述の postgresql.conf の設定項目(shared_buffers, work_mem, effective_cache_size など)は、サーバーのリソースとワークロードに合わせて適切に調整することで、パフォーマンスが大きく向上する可能性があります。ただし、変更は慎重に行い、効果を測定しながら進める必要があります。
  • VACUUMとANALYZE: PostgreSQLでは、UPDATEやDELETE操作で古いバージョンの行(タプル)が残存します(これらは「デッドタプル」と呼ばれます)。これらのデッドタプルを再利用可能な領域に回収し、テーブルの断片化を解消するのが VACUUM の役割です。また、クエリプランナーが最適な実行計画を選択するために、テーブルやインデックスの統計情報が必要です。これを更新するのが ANALYZE です(VACUUM ANALYZE で両方実行可能)。PostgreSQLには自動的にこれらの処理を行うオートバキュームデーモンがあります。オートバキュームの設定(autovacuum, autovacuum_vacuum_scale_factor, autovacuum_analyze_scale_factor など)が適切に行われているか確認し、必要に応じてチューニングします。特に書き込み負荷が高いテーブルでは、オートバキュームが追いつかないと、テーブルが肥大化し、パフォーマンスが劣化する可能性があります。
  • インデックスのメンテナンス: 使用されていない冗長なインデックスは、書き込みパフォーマンスを低下させ、ディスク容量を消費します。pg_stat_user_indexes などの統計情報や、pg_extra のようなツールを使用して、使用されていないインデックスを特定し、削除を検討します。

6.3. レプリケーションと高可用性 (High Availability)

本番環境では、データベースの単一障害点をなくし、高可用性を実現するためにレプリケーション構成を組むことが一般的です。

  • ストリーミングレプリケーション (Streaming Replication): マスターサーバーのWALをリアルタイムに近い形でスタンバイサーバーに送信し、適用する物理レプリケーションです。最も一般的なレプリケーション方法で、リードレプリカ(参照用レプリカ)の構築や、マスター障害時のフェイルオーバーに利用できます。
  • 論理レプリケーション (Logical Replication): テーブルやデータベースの論理的な変更(INSERT, UPDATE, DELETE)をパブリッシュ/サブスクライブ形式で複製します。PostgreSQLのメジャーバージョンを跨いだレプリケーションや、特定のテーブルのみをレプリケーションする場合などに利用できます。
  • フェイルオーバー: マスターデータベースに障害が発生した場合、スタンバイデータベースの一つを新しいマスターに昇格させるプロセスです。手動で行うことも可能ですが、Patroni, Repmgr, Keepalived+Repmgrのような自動フェイルオーバーツールを導入することで、ダウンタイムを最小限に抑えることができます。GitLabはこれらの外部ツールと連携して高可用性構成を構築できます。
  • GitLab Geo: 複数の地理的に離れた場所にGitLabインスタンスをデプロイし、リポジトリやデータベースなどを非同期にレプリケーションする機能です。ディザスターリカバリーや、遠隔地のユーザーのアクセス速度向上に利用されます。Geoのデータベースレプリケーションは、PostgreSQLの論理レプリケーションや外部ツールを利用して行われます。

大規模なGitLabインスタンスでは、データベースの負荷分散のためにリードレプリカを追加したり、さらに進んでデータベースをシャーディング(分割)することもあります。

6.4. アップグレード

  • PostgreSQLのメジャーバージョンアップグレード: PostgreSQLはメジャーバージョンアップグレード(例: 13から14)の際に、データベースのデータファイルを物理的に互換性のない形式で保存することがあります。この場合、pg_upgrade ツールを使用するか、pg_dumppg_restore を組み合わせて論理的にデータを移行する必要があります。計画的なダウンタイムが必要になることが多いですが、pg_upgrade はデータコピーを伴わないため、より高速にアップグレードできる可能性があります。
  • GitLabのバージョンアップグレードとデータベースマイグレーション: GitLabのバージョンアップグレード時には、必ず関連するデータベースマイグレーションが実行されます。gitlab-rake db:migrate コマンドは、アプリケーションコードに含まれる未実行のマイグレーションファイルを順番に実行します。前述のZero-Downtime Migrationsが考慮されていれば、多くの場合、サービスを停止せずにマイグレーションを実行できますが、大規模なマイグレーションの場合は計画的なメンテナンスウィンドウが必要になることもあります。

7. セキュリティ

データベースセキュリティは、GitLab全体のセキュリティにとって極めて重要です。

  • 認証と認可:
    • pg_hba.conf で、信頼できるホストからの接続のみを許可し、安全な認証方式 (md5, scram-sha-256) を使用します。trust 認証は、ローカル接続以外では使用すべきではありません。
    • GitLabがデータベースに接続するためのPostgreSQLユーザーには、GitLabが必要とする最小限の権限のみを付与します。例えば、gitlabhq_production データベースに対する CONNECT 権限と、そのデータベース内のテーブルに対する SELECT, INSERT, UPDATE, DELETE 権限などです。SUPERUSER 権限をGitLabユーザーに与えることは絶対に避けるべきです。
  • SSL接続: GitLabサーバーとPostgreSQLサーバー間の接続はSSL/TLSを使用して暗号化することを強く推奨します。これにより、ネットワーク上で認証情報やデータが傍受されるリスクを防げます。gitlab.rb または database.ymlsslmoderequire または verify-full に設定し、PostgreSQL側でSSL接続を受け付けるように設定します。
  • ネットワークアクセス制限: ファイアウォールやセキュリティグループ(クラウド環境の場合)を使用して、GitLabサーバーからのPostgreSQLポートへのアクセスのみを許可します。信頼できないネットワークからのアクセスをブロックします。
  • 機密データの取り扱い: データベースに格納される機密情報(APIキー、パスワードハッシュなど)は、適切に暗号化またはハッシュ化して保存する必要があります。GitLab自身がこれらの対策を講じていますが、カスタム開発を行う場合は注意が必要です。
  • 定期的なセキュリティパッチの適用: PostgreSQLおよびGitLabのセキュリティアップデートは速やかに適用します。これにより、既知の脆弱性から保護されます。

8. GitLab.comとPostgreSQL

世界最大のGitLabインスタンスであるGitLab.comは、PostgreSQLデータベースを大規模に運用しています。GitLabチームは、スケーラビリティ、パフォーマンス、高可用性、そして運用の効率化のために、PostgreSQLに対して様々な最適化と技術的な取り組みを行っています。

GitLab.comでは、単一の巨大なPostgreSQLデータベースではなく、機能や負荷に応じて複数のデータベースクラスターに分割(シャーディング)しています。例えば、CI/CD関連のデータ、Issue/MR関連のデータなど、特定の種類のデータを専用のデータベースクラスターに格納することで、各クラスターの負荷を管理し、独立してスケーリングできるようにしています。

また、多数のリードレプリカを配置して読み取り負荷を分散したり、Patroniのようなツールを使用して高可用性構成を維持したりしています。日々の開発においても、前述のZero-Downtime Migrationsは厳密に守られ、データベースに大きな影響を与える変更は時間をかけて段階的に適用されます。

GitLabの開発チーム自身がPostgreSQLのエキスパートであり、パフォーマンス改善、新機能の活用(例: パーティショニング、JSONB)、そしてデータベース運用の自動化に取り組んでいます。GitLabのIssueトラッカーやブログ記事には、GitLab.comでのPostgreSQL運用に関する貴重な情報が多く公開されています。

9. まとめ

この記事では、GitLab開発におけるPostgreSQL連携について、その基礎から高度なトピックまで幅広く解説しました。

GitLabにとって、PostgreSQLは単なるデータストアではなく、プラットフォームの安定性、パフォーマンス、そしてスケーラビリティを支える心臓部です。PostgreSQLが持つ堅牢性、豊富な機能、拡張性といった特性が、GitLabのような複雑で進化し続けるアプリケーションの要件を満たしています。

GitLab開発に携わる者は、Ruby on Rails/ActiveRecordを通じたデータベース操作だけでなく、マイグレーションのベストプラクティス(特にZero-Downtime Migrations)やクエリ最適化の手法を深く理解する必要があります。自身が書いたコードがデータベースにどのような負荷をかけ、どのようにパフォーマンスに影響するかを意識することは、高品質なコードを書く上で不可欠です。

また、GitLabの運用に携わる者にとっては、PostgreSQLのインストールと設定、バックアップとリストア、監視とパフォーマンスチューニング、そしてレプリケーションによる高可用性構成の構築と維持といったスキルが必須です。データベースが健全な状態になければ、GitLabサービス全体が不安定になり、最悪の場合は停止してしまいます。

PostgreSQLは奥深いデータベースシステムであり、GitLab開発/運用におけるその重要性は今後も増していくでしょう。継続的に学習し、公式ドキュメントやコミュニティのリソースを活用することで、GitLab環境を最大限に活かすことができるはずです。

GitLab開発を成功させる鍵の一つは、まさにこのPostgreSQLとの密接な連携を理解し、最大限に活用することにあると言えるでしょう。

さらなる学習リソース:


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール