RHEL 実践 #2 DB 運用: PostgreSQL on RHEL
RHEL 実践 #1 では nginx で Web 層を立ち上げました。今回はその後ろを支える データ層 に進みます。RHEL でデータベースを立ち上げる作業も、Web サーバと同じくパッケージ 1 つでは終わりません。ただ PostgreSQL には RHEL 固有のもう 1 段階が加わります。それが AppStream module でバージョンを選ぶ 過程です。
RHEL は同一パッケージの複数バージョンを module という単位でまとめて提供します。PostgreSQL のようにメジャーバージョンが速く上がるソフトウェアは、この module 構造を経てこそ目的のバージョンをきれいにインストールできます。今回はそのインストールから始めて、初期化・リモート接続・SELinux・バックアップまで運用 1 サイクルを辿ります。
AppStream module でバージョンを選ぶ #
まず、どの PostgreSQL バージョンが module として提供されているかを確認します。利用可能な stream (バージョンの流れ) とデフォルト値を一目で確認できます。
# 提供される postgresql module と stream の一覧
dnf module list postgresql出力で [d] が付いた stream がデフォルト、[e] が付いた stream はすでに有効化されたものです。目的のバージョンがデフォルトと違う場合は、明示的に有効化してからインストールします。ここでは 16 バージョンを例にします。
# 16 stream を有効化
sudo dnf module enable -y postgresql:16
# サーバパッケージをインストール
sudo dnf install -y postgresql-server postgresql-contribpostgresql-server がデータベース本体で、postgresql だけをインストールするとクライアント (psql など) だけが入ります。サーバを立ち上げるには必ず postgresql-server をインストールする必要があります。postgresql-contrib は pg_stat_statements のような実務でよく使う拡張モジュールを含むので、一緒にインストールしておくことをお勧めします。
一度 stream を有効化すると、以降のアップデートもそのバージョンの流れの中でだけ起こります。メジャーバージョンを上げるときは module を reset するか別の stream で enable する必要があるので、運用中のデータベースでは慎重に扱う必要があります。
初期化とサービス登録 #
インストール直後はデータディレクトリが空でサービスがすぐには立ち上がりません。RHEL パッケージは初期化専用のコマンドを提供します。
# データディレクトリの初期化
sudo postgresql-setup --initdbこのコマンドはデフォルトのデータディレクトリである /var/lib/pgsql/data に設定ファイルとシステムカタログを生成します。初期化が終わったら systemd に登録して即座に起動します。
# ブート時に自動起動 + 即時起動
sudo systemctl enable --now postgresql
# 状態確認
systemctl status postgresqlenable --now で一度に処理する流れは nginx のときと同じです。正常に立ち上がったら、まずローカルで接続を確認します。インストール時に postgres という OS ユーザが作られるので、このユーザに切り替えて接続するのが最も単純です。
# postgres ユーザに切り替えてから psql 接続
sudo -u postgres psqlpsql プロンプトが出ればデータベース本体は正常です。\l でデータベース一覧、\q で終了します。
データディレクトリ #
デフォルトのデータディレクトリは /var/lib/pgsql/data です。主なファイルと位置を整理すると次のとおりです。
postgresql.conf: サーバ全体の設定 (接続アドレス、メモリ、ログなど)pg_hba.conf: クライアント認証ルール (誰がどこからどの方式で接続するか)base/、global/: 実際のデータファイルpg_wal/: トランザクションログ (WAL)
運用環境ではデータディレクトリを別ディスクに移すケースが多いですが、そのとき SELinux コンテキストを一緒に揃えないとサービスが立ち上がりません。この部分は下の SELinux 節で扱います。
リモート接続設定 #
デフォルトインストール状態の PostgreSQL はローカルからのみ接続を受け付けます。外部から接続するには 2 つのファイルを直す必要があります。postgresql.conf で 受信アドレス を開き、pg_hba.conf で 認証ルール を追加する 2 段階です。
まず postgresql.conf で listen_addresses を修正します。デフォルト値は localhost で外部接続を塞ぎます。
# /var/lib/pgsql/data/postgresql.conf
listen_addresses = '*' # すべてのインタフェースで受信
port = 5432'*' はすべてのネットワークインタフェースで受け付けるという意味です。特定の IP だけ受けたい場合はそのアドレスを直接書きます。次に pg_hba.conf に認証ルールを追加します。このファイルは上から下へ読まれ、先に一致したルールが適用されます。
# /var/lib/pgsql/data/pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
host all all 192.168.1.0/24 scram-sha-256host は TCP/IP 接続を、scram-sha-256 は RHEL デフォルトの認証方式であるパスワードベース認証を意味します。過去の md5 より安全なので、新しく構成する環境では scram-sha-256 を使うのが適切です。ADDRESS には接続を許可するネットワーク帯域を書きます。2 つのファイルを直した後は、サービスに再読み込みさせてはじめて反映されます。
# 設定を再読み込み (ほとんどの設定は reload で十分)
sudo systemctl reload postgresqllisten_addresses のように一部の項目は reload では反映されず restart が必要です。変更した項目が reload で入るか確実でない場合は、sudo systemctl restart postgresql で再起動します。
firewalld で 5432 を開く #
PostgreSQL のデフォルトポートは 5432 です。RHEL は firewalld が有効化されていてこのポートが塞がっているので、恒久的に開放します。
# postgresql サービスを恒久許可 (5432/tcp)
sudo firewall-cmd --add-service=postgresql --permanent
# 反映
sudo firewall-cmd --reload
# 確認
sudo firewall-cmd --list-allfirewalld には postgresql という事前定義サービスがあるので、ポート番号を直接書く必要はありません。非標準ポートを使う場合は --add-port=5433/tcp のようにポートで直接開きます。--permanent で恒久ルールを作った後 --reload で反映する 2 段階の流れは、RHEL ファイアウォール作業の基本です。
SELinux が塞ぐ箇所 #
標準位置 (/var/lib/pgsql/data) で標準ポート (5432) で動作する限り、SELinux は静かです。しかし Web サーバのときとまったく同じく、非標準ディレクトリ や 非標準ポート に外れると即座に塞がれます。
非標準データディレクトリ #
データディレクトリを /data/pgsql のようなパスに移すと、そのディレクトリの SELinux コンテキストが PostgreSQL データタイプではないためサービスが立ち上がりません。postgresql_db_t タイプを恒久付与して適用します。
# /data/pgsql 以下を PostgreSQL データタイプとして恒久登録
sudo semanage fcontext -a -t postgresql_db_t "/data/pgsql(/.*)?"
# ディスクの実際のラベルに適用
sudo restorecon -Rv /data/pgsqlsemanage fcontext でポリシーにルールを追加し、restorecon で実際のファイルに適用する 2 段階が核心です。これとは別に systemd unit がデフォルトディレクトリを指しているので、データディレクトリ自体を移すときは unit の Environment=PGDATA= の値も一緒に合わせる必要があります。
非標準ポート #
PostgreSQL を 5433 のように別のポートで動かすと、設定が合っていても SELinux がそのポートをデータベースポートと認めず塞ぎます。
# 現在 postgresql_port_t に登録されたポートを確認
sudo semanage port -l | grep postgresql
# 5433 を PostgreSQL ポートとして恒久登録
sudo semanage port -a -t postgresql_port_t -p tcp 5433semanage がなければ policycoreutils-python-utils パッケージをインストールします。ポートを登録した後にサービスを再起動すると正常に立ち上がります。
ユーザと DB の作成 #
データベースを実際に使うには、アプリケーション用ユーザ (role) とデータベースを作る必要があります。postgres スーパーユーザで作業します。方法は 2 つで、シェルコマンドで作る方式と psql の中で SQL で作る方式です。
まずシェルからそのまま作る方式です。
# 対話的に role 作成 (パスワード入力プロンプト付き)
sudo -u postgres createuser --pwprompt appuser
# appuser を所有者とするデータベースを作成
sudo -u postgres createdb --owner=appuser appdb同じ作業を psql の中で SQL で処理することもできます。権限を細かく与えたいときはこちらが便利です。
-- sudo -u postgres psql で接続した後
CREATE ROLE appuser WITH LOGIN PASSWORD 'change_this_password';
CREATE DATABASE appdb OWNER appuser;
GRANT ALL PRIVILEGES ON DATABASE appdb TO appuser;LOGIN 属性があってはじめて接続可能なユーザになります。こうして作った appuser でリモートから接続するとき、先ほど pg_hba.conf に追加したルールと scram-sha-256 認証が合わさって、はじめて接続が成立します。
バックアップとリカバリ #
運用データベースでバックアップは選択ではなく基本です。PostgreSQL は論理バックアップツールとして pg_dump と pg_dumpall を提供します。pg_dump は単一のデータベースを、pg_dumpall は role を含むクラスタ全体を取得します。
# 単一データベースをバックアップ (カスタムフォーマット、圧縮・並列リカバリに有利)
sudo -u postgres pg_dump -Fc appdb -f /var/backups/appdb.dump
# クラスタ全体をバックアップ (role・権限を含む、平文 SQL)
sudo -u postgres pg_dumpall -f /var/backups/all.sql-Fc はカスタムフォーマットで、リカバリ時にテーブル単位の選択や並列処理が可能なため、単一データベースのバックアップに推奨されます。リカバリはフォーマットによってツールが異なります。カスタムフォーマットは pg_restore で、平文 SQL は psql で戻します。
# カスタムフォーマットのリカバリ (対象 DB は事前に作成)
sudo -u postgres createdb appdb_restore
sudo -u postgres pg_restore -d appdb_restore /var/backups/appdb.dump
# 平文 SQL のリカバリ
sudo -u postgres psql -f /var/backups/all.sqlこれらのバックアップは特定時点のスナップショットなので、障害時には最後のバックアップ以降の変更は失われます。損失を最小化するには WAL アーカイビングベースのポイントインタイムリカバリ (PITR) を別途構成する必要がありますが、そのテーマは別の記事で扱うほど深いので、ここでは論理バックアップまでにとどめます。バックアップファイルは cron や systemd timer で定期実行し、同じホストではなく別のストレージに移しておくことで初めて意味をなします。
詰まったときの診断 #
データベースに接続できないときは、次の順序で絞り込んでいくとほとんどの原因が見えてきます。
- サービスが立ち上がっているか。
systemctl status postgresql、失敗時はjournalctl -u postgresqlで起動ログを確認します。 - ローカルで開くか。
sudo -u postgres psqlで接続して、サーバ自体の問題とリモートアクセスの問題を切り分けます。 - ファイアウォールが開いているか。
firewall-cmd --list-allに postgresql または 5432 があるか。 - 認証が塞いでいるか。 リモートから
psql -h ホスト -U appuser -d appdbで接続してみて、拒否されたらpg_hba.confのルールと順序を確認します。 - SELinux が塞いでいるか。
sudo ausearch -m AVC -ts recentに postgresql 関連の denied があるか。
特に pg_hba.conf は上から下へ読まれ先に一致したルールが適用されるので、より広いルールが上にあると、下の意図したルールが隠れることがあります。認証エラーメッセージはどのルールで塞がれたかも PostgreSQL ログ (/var/lib/pgsql/data/log/) に残るので、接続拒否時はこのログを先に見るのが速いです。
運用ポイント #
今回の記事で押さえたこと:
- バージョンは module で。
dnf module list postgresqlで確認しdnf module enable postgresql:16の後にpostgresql-serverをインストール。 - 初期化が別段階。
postgresql-setup --initdbの後systemctl enable --now postgresql、データは/var/lib/pgsql/data。 - リモート接続は 2 つのファイル。
postgresql.confのlisten_addresses、pg_hba.confのscram-sha-256認証ルール、そして firewalld の 5432 開放。 - SELinux が塞ぐ 2 箇所。 非標準データディレクトリ (
semanage fcontext -t postgresql_db_t)、非標準ポート (semanage port -t postgresql_port_t)。 - バックアップ。 単一 DB は
pg_dump -Fc+pg_restore、クラスタ全体はpg_dumpall+psql、別のストレージで保管。 - 診断順序。 サービス → ローカル psql → ファイアウォール → 認証 (pg_hba) → SELinux。
次回: コンテナワークロード #
Web 層とデータ層を RHEL の上に直接立ち上げたので、今回は同じワークロードを コンテナ にまとめる方式に進みます。
#3 コンテナワークロード: Podman、systemd (quadlet) では RHEL のデフォルトコンテナエンジンである Podman でコンテナを実行し、それを systemd サービスとして登録する quadlet 方式まで、コンテナを RHEL 運用体系の中に引き込む 1 サイクルを整理します。