PostgreSQL環境構築リアルタイムアタックで世界一を目指す!

この記事は [NTTコムウェア Advent Calendar 2024](https://qiita.com/advent-calendar/2024/nttcomware) 2日目の記事です。

## はじめに

NTTコムウェアの黒田といいます、よろしくお願いします。

ふだんはデータベースエンジニアとして、おもにオープンソースのデータベースである
[PostgreSQL](https://www.postgresql.org/ "PostgreSQL") に関する技術調査・技術サポートに取り組んでおり、コミュニティ活動にも参加しています。

昨年は [「おもに小麦粉からなる生地にあんこをいれて金属製焼き型で焼成した和菓子の名前をPostgreSQLで扱えるようにする拡張機能pg_mochochoをつくってみた」](https://qiita.com/comware_kkuroda/items/a4ac4fba5b1ba505625b) 
という記事を書きましたので、こちらもぜひご覧ください。

今回は、あらためて調べていくと奥が深いなと感じている、PostgreSQLの環境構築について書いていきます。

## PostgreSQL環境構築を最速で実施したい

開発中のアプリケーションの動作検証など、手元にPostgreSQL環境がすぐほしくなること、あるかと思います。ちょっとしたお試しの場合はとにかくすぐに環境を構築したいですよね。

そういった場合にそなえて、最速でPostgreSQL環境を構築する方法をさぐるべく、PostgreSQL環境構築リアルタイムアタックに挑戦します。

## リアルタイムアタックとは

リアルタイムアタック(RTA)とは、おもにコンピュータゲームを対象として、記録開始から目標達成までにかかった時間をリアルタイム、つまり実際にかかった時間で計測し競い合うものです。

最近は各種動画配信サイトでも多くのリアルタイムアタック動画・配信をみることができ、とても盛り上がっていますね。

これにならって、PostgreSQL環境構築を開始してから、利用できるようになるまでにかかった時間を計測していきます。

## レギュレーション

リポジトリ・RPMのURLや各種手順については、バージョン変更やリポジトリサイトの修正にともない変更される可能性があります。

PostgreSQLにはWindows版もありますし、コンテナを用いて環境構築するといった方法もあります。今回は、minimal installで構築したKVM仮想ゲストのRocky Linux 9.5環境をベースとします。PostgreSQLに必要なパッケージを導入する時間も含めます。

OSパッケージに同梱されているPostgreSQLもあり、たとえばRocky 9系ではPostgreSQL 15,16が同梱されています。ただ、同梱版では最新バージョンが利用できません。今回は最新バージョン17を利用することを想定して、PostgreSQLコミュニティの公開しているリポジトリ・ソースコードを利用する前提とします。

タイマーストップは、「作成したユーザでpsqlコマンドによりPostgreSQLに接続できること」が確認できた時点とします。

PostgreSQL上に作成するユーザのパスワード設定も必要となるため、用意したコマンドはひとつずつ実行していくこととします。

PostgreSQLには、systemdによる停止・起動のための設定スクリプトも用意されていますが、なるべくシンプルな手順となるよう、PostgreSQL付属のコマンドを利用して環境構築・起動・停止を実施する手順とします。

PostgreSQLの設定変更も最小限とします。アプリケーションサーバなど別環境から接続することを想定して、クライアント認証ファイルおよびlisten_addressesの設定変更は実施します。

かかる時間についてはネットワーク速度やマシン性能にも大きく依存するため、ふんわりとした「最速」を目指していることはご了承ください。

## 想定チャート

3つのチャートを準備したのでそれぞれ見ていきましょう。

### 1. PostgreSQLコミュニティのリポジトリを利用するチャート

PostgreSQLコミュニティ公式サイトの [Linux downloads (Red Hat family)](https://www.postgresql.org/download/linux/redhat/) の手順にしたがい、リポジトリを利用するチャートです。

```shell
## 指定がない場合はsudoが利用可能な一般ユーザでコマンドを実行していきます

## RHEL9系向けのPostgreSQLリポジトリをインストール
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm

## OS同梱版PostgreSQLモジュールとの競合を防ぐため無効化
sudo dnf -qy module disable postgresql

## postgresql17-server をインストール
sudo dnf install -y postgresql17-server

## データベースクラスタ用ディレクトリ(PGDATA)を作成して所有者を変更
sudo mkdir -p /pgdata/data
sudo chown postgres:postgres -R /pgdata

## これ以降のコマンドは postgres ユーザで実行します。rootユーザを経由してpostgresユーザに変更
su
su - postgres

## データベースクラスタの初期化
/usr/pgsql-17/bin/initdb -D /pgdata/data

## 特定IPアドレスから、このあと作成する「testdb」に対して
## 「testuser」での接続を許可する設定をpg_hba.confに追加
## <clientipaddress>は接続元となるサーバのIPアドレスを指定
## <serveripaddress>はPostgreSQLサーバのIPアドレスを指定、こちらは接続確認用
## 認証方式「scram-sha-256」は、SCRAM-SHA-256暗号化パスワード認証での接続を許可する設定
echo "host testdb testuser <clientipaddress>/32 scram-sha-256" >> /pgdata/data/pg_hba.conf
echo "host testdb testuser <serveripaddress>/32 scram-sha-256" >> /pgdata/data/pg_hba.conf

## listen_addresses = 'localhost,<serveripaddress>' を指定することで、
## サーバの持つ特定のIPアドレスにPostgreSQLをLISTENさせます
echo "listen_addresses = 'localhost,<serveripaddress>'" >> /pgdata/data/postgresql.conf

## PostgreSQLを起動、-Dオプションでデータベースクラスタを指定
/usr/pgsql-17/bin/pg_ctl -D /pgdata/data start

## PostgreSQLに「testuser」というユーザを作成
## 同時にパスワードも設定するオプション--pwpromptを指定
/usr/pgsql-17/bin/createuser testuser --pwprompt

## PostgreSQLに「testdb」というデータベースを作成、所有者はさきほど作成した「testuser」
/usr/pgsql-17/bin/createdb testdb --owner=testuser

## IPアドレス経由で接続できることを確認
/usr/pgsql-17/bin/psql -h <serveripaddress> -U testuser testdb
```

構築したデータをすべて削除したうえで、インストールしたパッケージもすべて削除する手順は以下のとおりです。

```shell
## postgres ユーザでPostgreSQLを停止
sudo -u postgres /usr/pgsql-17/bin/pg_ctl -D /pgdata/data stop

## データベースクラスタディレクトリをすべて削除
sudo rm -f -r /pgdata/data

## 「postgresql17-server」パッケージの削除と、OS同梱版モジュール無効化設定のリセット
sudo dnf remove -y postgresql17-server
sudo dnf -qy module reset postgresql

## PostgreSQLコミュニティリポジトリの削除
sudo dnf remove -y pgdg-redhat-repo.noarch
```

### 2. 必要なRPMだけ直接取得するチャート

チャート1では、PostgreSQLコミュニティリポジトリをインストールしたあと、リポジトリを利用してPostgreSQLをインストールしています。

チャート2ではこのリポジトリインストールを省略して、直接RPMを入手することでより早い環境構築をねらっていきます。

直接RPMを入手したいユーザ向けに、PostgreSQLコミュニティは [RPM Chart](https://yum.postgresql.org/rpmchart/) というページも用意してくれています。こちらで必要なパッケージを確認して直接入手することが可能です。

オフライン環境など、構築したい環境がインターネットに直接出られない環境でも利用可能なのも良い点ですが、RPMパッケージの正当性やインストールするファイルのバージョン確認は自身で実施する必要があります。

```shell
## 指定がない場合はsudoが利用可能な一般ユーザでコマンドを実行していきます

## minimal installしたRocky 9.5では、
## RPMでPostgreSQLをインストールするためにlibicuというパッケージが追加で必要です
sudo dnf install -y libicu

## リポジトリ利用時は自動的にimportされる、
## RPMパッケージの正当性を確認するためのGPGファイルを手動でimportします
sudo rpm --import https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL

## PostgreSQL 17.2 のインストールに必要となるRPMファイルを直接指定してインストールします
## postgresql17がクライアントプログラムパッケージ、postgresql17-libsがライブラリパッケージ、
## postgresql17-serverがサーバパッケージです
sudo rpm -ivh \
https://download.postgresql.org/pub/repos/yum/17/redhat/rhel-9-x86_64/postgresql17-17.2-1PGDG.rhel9.x86_64.rpm \
https://download.postgresql.org/pub/repos/yum/17/redhat/rhel-9-x86_64/postgresql17-libs-17.2-1PGDG.rhel9.x86_64.rpm \
https://download.postgresql.org/pub/repos/yum/17/redhat/rhel-9-x86_64/postgresql17-server-17.2-1PGDG.rhel9.x86_64.rpm

## これ以降のデータベースクラスタ作成～接続確認はチャート1と同様です
```

構築したデータをすべて削除したうえで、インストールしたパッケージもすべて削除する手順は以下のとおりです。

```shell
## postgres ユーザでPostgreSQLを停止
sudo -u postgres /usr/pgsql-17/bin/pg_ctl -D /pgdata/data stop

## データベースクラスタディレクトリをすべて削除
sudo rm -f -r /pgdata/data

## インストールしたRPMの削除
sudo rpm -e \
postgresql17-libs-17.2-1PGDG.rhel9.x86_64 \
postgresql17-17.2-1PGDG.rhel9.x86_64 \
postgresql17-server-17.2-1PGDG.rhel9.x86_64

## 追加したlibicuパッケージの削除
sudo dnf remove -y libicu
```

### 3. ソースコードからビルドするチャート

さすがにソースコードを入手して1からビルドしたほうが早いというのはなさそうですが、この場合にどれくらいかかるかも試してみます。

```shell
## 指定がない場合はsudoが利用可能な一般ユーザでコマンドを実行していきます

## PostgreSQLをソースコードからビルドする場合は最低でも以下パッケージが必要です
## Rocky 9.5系のminimal install環境における例のため、
## 他環境では異なるパッケージが必要となる場合もあります
sudo dnf install -y tar make gcc bison flex libicu libicu-devel perl readline-devel zlib-devel

## PostgreSQL 17.2のソースコードを任意のディレクトリにダウンロードして展開します
cd ~
mkdir postgres
cd postgres
curl https://ftp.postgresql.org/pub/source/v17.2/postgresql-17.2.tar.gz -o postgresql-17.2.tar.gz
tar zxf postgresql-17.2.tar.gz
cd postgresql-17.2

## システムに合わせた事前設定を実行します。
## --prefixで、PostgreSQLコマンドやライブラリのインストール先ディレクトリを指定します
## 今回はこれ以外のオプションは指定していませんが、
## 実際にはデバッグ用設定などオプション指定が必要なケースが多いです
## また、オプションに応じて必要なOSパッケージも変わってきます
./configure --prefix=/usr/pgsql-17-source

## ビルドします。-jオプションでは多重度を指定でき、ここでは4多重で実行しています
make -j 4

## これ以降のコマンドは root ユーザで実行します
su

## ビルドした各種ファイルを、configure実行時に指定した
## 「/usr/pgsql-17-source」配下にインストールします
make install

## パッケージを利用した場合、インストール時自動的に「postgres」ユーザが作成されますが、
## ソースコードからビルドした場合は作成されないため手動で追加が必要です
adduser postgres

## これ以降のチャートは基本的に1,2と同じですが、
## インストール先に「/usr/pgsql-17-source」を指定しているためコマンドパスが異なる点に注意が必要です

## データベースクラスタ用ディレクトリ(PGDATA)を作成して所有者を変更
mkdir -p /pgdata/data
chown postgres:postgres -R /pgdata

## これ以降のコマンドは postgres ユーザで実行します。postgresユーザに変更
su - postgres

## データベースクラスタの初期化
/usr/pgsql-17-source/bin/initdb -D /pgdata/data

## 特定IPアドレスから、このあと作成する「testdb」に対して
## 「testuser」での接続を許可する設定をpg_hba.confに追加
## <clientipaddress>は接続元となるサーバのIPアドレスを指定
## <serveripaddress>はPostgreSQLサーバのIPアドレスを指定、こちらは接続確認用
## 認証方式「scram-sha-256」は、SCRAM-SHA-256暗号化パスワード認証での接続を許可する設定
echo "host testdb testuser <clientipaddress>/32 scram-sha-256" >> /pgdata/data/pg_hba.conf
echo "host testdb testuser <serveripaddress>/32 scram-sha-256" >> /pgdata/data/pg_hba.conf

## listen_addresses = 'localhost,<serveripaddress>' を指定することで、
## サーバの持つ特定のIPアドレスにPostgreSQLをLISTENさせます
echo "listen_addresses = 'localhost,<serveripaddress>'" >> /pgdata/data/postgresql.conf

## PostgreSQLを起動、-Dオプションでデータベースクラスタを指定
## ソースコードからビルドした場合、デフォルトではPostgreSQLはログファイルを出力しないため
## -lオプションを指定して、logfileにログを出力するよう指定するのが良いでしょう
/usr/pgsql-17-source/bin/pg_ctl -D /pgdata/data -l logfile start

## PostgreSQLに「testuser」というユーザを作成
## 同時にパスワードも設定するオプション--pwpromptを指定
/usr/pgsql-17-source/bin/createuser testuser --pwprompt

## PostgreSQLに「testdb」というデータベースを作成、所有者はさきほど作成した「testuser」
/usr/pgsql-17-source/bin/createdb testdb --owner=testuser

## IPアドレス経由で接続できることを確認
/usr/pgsql-17-source/bin/psql -h <serveripaddress> -U testuser testdb
```

構築したデータをすべて削除したうえで、ソースコードやビルドしたPostgreSQLファイルもすべて削除する手順は以下のとおりです。

```shell
## postgres ユーザでPostgreSQLを停止
sudo -u postgres /usr/pgsql-17-source/bin/pg_ctl -D /pgdata/data stop

## データベースクラスタおよびインストール先ディレクトリのPostgreSQLファイルをすべて削除
sudo rm -f -r /pgdata/data
sudo rm -f -r /usr/pgsql-17-source

## 入手したソースコードおよびビルドしたPostgreSQLファイルをすべて削除
sudo rm -f -r /home/<Username>/postgres/*

## PostgreSQLビルド用に追加したパッケージをすべて削除
sudo dnf remove -y tar make gcc bison flex libicu libicu-devel perl readline-devel zlib-devel
```

## ポイント解説

### PostgreSQLコミュニティリポジトリ一覧

チャート1で、PostgreSQLコミュニティのリポジトリをインストールすると、以下のとおり現在利用可能なメジャーバージョンのリポジトリがすべて追加されます。

```shell
$ sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm

$ sudo dnf repolist
repo id               repo name
appstream             Rocky Linux 9 - AppStream
baseos                Rocky Linux 9 - BaseOS
extras                Rocky Linux 9 - Extras
pgdg-common           PostgreSQL common RPMs for RHEL / Rocky / AlmaLinux 9 - x86_64
pgdg12                PostgreSQL 12 for RHEL / Rocky / AlmaLinux 9 - x86_64
pgdg13                PostgreSQL 13 for RHEL / Rocky / AlmaLinux 9 - x86_64
pgdg14                PostgreSQL 14 for RHEL / Rocky / AlmaLinux 9 - x86_64
pgdg15                PostgreSQL 15 for RHEL / Rocky / AlmaLinux 9 - x86_64
pgdg16                PostgreSQL 16 for RHEL / Rocky / AlmaLinux 9 - x86_64
pgdg17                PostgreSQL 17 for RHEL / Rocky / AlmaLinux 9 - x86_64
```

どのメジャーバージョンを使いたい場合でも利用できて便利ではありますが、ネットワークが遅い環境で `dnf list` を実行したりすると、すべてのリポジトリにアクセスが発生してしまいすこし面倒です。

`dnf config-manager --disable <repository_id>` でリポジトリを無効化することはできるので、使用するバージョンのみ有効化しておくのも良いですね。

### パッケージインストール時のPostgreSQLインストール先について

チャート1およびチャート2のように、パッケージを用いてインストールした場合、PostgreSQLの各種コマンドやライブラリは以下ディレクトリ配下にインストールされます。

```shell
## /usr/pgsql-<majorversion> にインストールされます。PostgreSQL 17の場合は以下
/usr/pgsql-17
```

メジャーバージョンが異なればインストール先も異なるため、同じサーバ上に複数のメジャーバージョンをインストールすることができます。

### データベースクラスタとは

同一クライアント接続先(インスタンス)で管理されるデータベースのデータの格納場所(ディレクトリ)です。

このディレクトリ配下に、各種データファイルが格納され、PostgreSQLはひとつのデータベースクラスタ内に複数のデータベースを持ちます。

今回の手順例では `/data/pgdata` がデータベースクラスタです。基本的にはこのディレクトリ配下にすべてのデータが格納されます。

シンボリックリンクを利用して、テーブルスペースや更新ログファイルを別ディレクトリに出力することができ、その場合はそちらのディレクトリにもデータが格納されることになります。

なお、パッケージからインストールした際は、postgresユーザの `.bash_profile` が自動的に更新され、データベースクラスタディレクトリを示す `PGDATA` が `/var/lib/pgsql/<majorversion>/data` に設定されます。こちらをそのまま利用してもかまいません。

### createuserコマンド、createdbコマンドについて

* [createuser](https://www.postgresql.org/docs/17/app-createuser.html)
* [createdb](https://www.postgresql.org/docs/17/app-createdb.html)

これらのコマンドは、データベース接続後に実行するSQLコマンドである `CREATE ROLE` および `CREATE DATABASE` のラッパーコマンドです。これらのSQLコマンドを用いてユーザやデータベースを作成するのと動作自体は同じものです。

`createuser` や `createdb` は、OSで実行することができ、対話的にユーザの設定を決めることができるなどの機能を備えています。SQLで接続してからSQLコマンドを実行するより手順もシンプルなので今回はこれらのコマンドを利用しています。

### pg_hba.confおよびlisten_addressesについて

* [pg_hba.conf](https://www.postgresql.org/docs/17/auth-pg-hba-conf.html)

`pg_hba.conf` は接続可能なクライアント認証を設定するファイルです。 `listen_addresses` パラメータは、PostgreSQLがLISTENするIPアドレスを指定するパラメータです。

いずれも、想定外の接続先からの接続を許容するような設定にはしないように注意しましょう。

## 結果

各チャートの作業毎の経過時間を以下の表に示します。

|                                          | チャート1(リポジトリ) | チャート2(RPM) | チャート3(ソースコードビルド) |
| ---------------------------------------- | --------------------- | -------------- | ----------------------------- |
| ビルド・インストール用パッケージ入手     | -                     | 00:00:07       | 00:00:55                      |
| ソースコード入手                         | -                     | -              | 00:01:33                      |
| ソースコードビルド                       | -                     | -              | 00:03:12                      |
| リポジトリインストール・モジュール無効化 | 00:00:47              | -              | -                             |
| PostgreSQL本体インストール               | 00:00:57              | 00:00:22       | 00:03:18                      |
| データベースクラスタ作成・初期化         | 00:01:10              | 00:00:35       | 00:03:31                      |
| 設定変更・起動・ユーザ作成・DB作成       | 00:01:30              | 00:00:55       | 00:03:51                      |
| 接続確認                                 | 00:01:35              | **00:01:00**   | 00:03:56                      |

RPMを直接入手するチャート2が最速でした。もう少しで1分切りも見えてきそうですね！

## まとめ

今回は、PostgreSQLの環境構築手順を整理しつつ、最速でPostgreSQL環境を構築できる方法をさぐってみました。手順自体はシンプルですが、PostgreSQL本体がどこにインストールされるのか、データベースのデータ自体はどこにあるのか、をしっかり押さえておくと、検証のために繰り返し環境構築・削除を行いたい場合に役立ちます。

最速はRPMを直接入手するチャート2でしたが、基本的には用意されているリポジトリを利用いただくチャート1の手順がわかりやすいですね。自動的に必要なパッケージもインストールしてくれます。

リポジトリ設定に手を入れたくない場合や、オフライン環境で環境を構築したい場合は、RPMを直接入手してインストールするチャート2で環境構築することができます。オフライン環境の場合は別環境でRPMを入手して転送する必要があります。

ソースコードからのビルドは、PostgreSQL本体やPostgreSQL拡張機能のデバッグ・開発をする場合に必要となります。単純にPostgreSQLを利用できれば良い、という場合はこの方法を取る必要はありません。

どんどんPostgreSQL環境を構築して、どんどん活用していきましょう。

記載されている会社名、製品名、サービス名は、各社の商標または登録商標です。