はじめに#
モダンな開発では、ローカル環境にデータベースを直接インストールするのではなく、Dockerコンテナとして起動するのが一般的です。環境の再現性、チーム間での設定共有、クリーンアップの容易さなど、多くのメリットがあります。
本記事では、PostgreSQLとDockerを組み合わせた実践的な環境構築から本番運用まで解説します。Docker Composeでの開発環境構築、ボリュームによるデータ永続化、初期化スクリプトの活用、本番環境での考慮点、さらにKubernetes上でのPostgreSQL運用の概要まで、段階的に学んでいきます。
この記事を読むことで、以下のことができるようになります。
- Docker ComposeでPostgreSQL開発環境を構築できる
- ボリュームを使ってデータを永続化し、コンテナの再起動でもデータを保持できる
- 初期化スクリプトでテーブル作成やテストデータ投入を自動化できる
- 本番環境でDockerized PostgreSQLを運用する際の考慮点を理解できる
- Kubernetes上でのPostgreSQL運用の選択肢を把握できる
前提条件#
- Docker Desktop(Windows/Mac)またはDocker Engine(Linux)がインストールされていること
- Docker Composeが利用可能であること(Docker Desktop には同梱)
- PostgreSQLの基本的な知識(CREATE TABLE、INSERT、SELECTなど)があること
Docker Composeの基本操作に不安がある場合は、Docker Compose入門 - compose.yamlの書き方と基本操作を参照してください。
PostgreSQL公式Dockerイメージの基本#
公式イメージの概要#
PostgreSQLの公式Dockerイメージは、Docker Hubでpostgresとして提供されています。PostgreSQL Docker Communityによってメンテナンスされており、Debian系(bookworm、trixie)とAlpine Linux系の両方が用意されています。
flowchart TD
subgraph OfficialImages["PostgreSQL 公式イメージ"]
direction TB
Latest["postgres:latest\n(= postgres:18)"]
subgraph DebianBased["Debian系(本番推奨)"]
V18Bookworm["postgres:18-bookworm"]
V17Bookworm["postgres:17-bookworm"]
V16Bookworm["postgres:16-bookworm"]
end
subgraph AlpineBased["Alpine系(軽量)"]
V18Alpine["postgres:18-alpine"]
V17Alpine["postgres:17-alpine"]
V16Alpine["postgres:16-alpine"]
end
end
Latest --> V18Bookwormイメージの選択基準は以下の通りです。
| イメージ種別 |
特徴 |
推奨用途 |
| postgres:18(Debian系) |
フル機能、拡張機能のインストールが容易 |
本番環境、拡張機能を使用する場合 |
| postgres:18-alpine |
軽量(約80MB)、起動が速い |
開発環境、CI/CD |
| postgres:18-bookworm |
明示的にDebianバージョンを指定 |
再現性を重視する場合 |
必須の環境変数#
PostgreSQLコンテナを起動するには、最低限POSTGRES_PASSWORD環境変数が必要です。これは公式イメージの仕様で、セキュリティ上の理由から必須とされています。
1
2
|
# 最小限の起動コマンド
docker run --name my-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres:18
|
主要な環境変数の一覧です。
| 環境変数 |
必須 |
説明 |
デフォルト値 |
| POSTGRES_PASSWORD |
必須 |
スーパーユーザーのパスワード |
なし |
| POSTGRES_USER |
任意 |
スーパーユーザー名 |
postgres |
| POSTGRES_DB |
任意 |
初期作成されるデータベース名 |
POSTGRES_USERの値 |
| PGDATA |
任意 |
データディレクトリのパス |
/var/lib/postgresql/data |
| POSTGRES_INITDB_ARGS |
任意 |
initdbに渡す追加引数 |
なし |
| TZ |
任意 |
タイムゾーン |
UTC |
PGDATA設定の重要な変更点#
PostgreSQL 18以降の公式イメージでは、PGDATAのデフォルト値が変更されました。この変更により、メジャーバージョンアップグレード時にpg_upgradeの--linkオプションが使いやすくなっています。
| バージョン |
PGDATAのデフォルト |
ボリュームマウント先 |
| 17以前 |
/var/lib/postgresql/data |
/var/lib/postgresql/data |
| 18以降 |
/var/lib/postgresql/18/docker |
/var/lib/postgresql |
PostgreSQL 17以前を使用する場合は、必ず/var/lib/postgresql/dataにボリュームをマウントしてください。/var/lib/postgresqlにマウントするとデータが永続化されません。
Docker Composeでの開発環境構築#
基本的なcompose.yamlの構成#
開発環境用のPostgreSQL設定をDocker Composeで定義します。以下は、日本語対応とタイムゾーン設定を含めた実用的な構成例です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# compose.yaml
services:
db:
image: postgres:18
container_name: dev-postgres
environment:
POSTGRES_USER: devuser
POSTGRES_PASSWORD: devpassword
POSTGRES_DB: myapp_development
TZ: Asia/Tokyo
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=C"
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U devuser -d myapp_development"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
volumes:
postgres-data:
|
この設定のポイントを解説します。
- 明示的なイメージタグ:
postgres:18のように明示的にバージョンを指定することで、予期しないアップデートを防ぎます
- 日本語対応:
POSTGRES_INITDB_ARGSでUTF-8エンコーディングを指定しています
- ヘルスチェック:
pg_isreadyコマンドでPostgreSQLの起動完了を確認します
- ボリューム: 名前付きボリューム
postgres-dataでデータを永続化します
起動と接続確認#
Docker Composeで環境を起動し、接続を確認します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# バックグラウンドで起動
docker compose up -d
# ログを確認
docker compose logs -f db
# 起動完了を待つ(healthcheck利用)
docker compose up -d --wait
# psqlで接続
docker compose exec db psql -U devuser -d myapp_development
# または外部からの接続
psql -h localhost -p 5432 -U devuser -d myapp_development
|
コンテナ内でpsqlを実行した場合、以下のようなプロンプトが表示されれば成功です。
1
2
3
4
5
|
myapp_development=# SELECT version();
version
---------------------------------------------------------------------------------------------------------
PostgreSQL 18.1 on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-12) 14.2.0, 64-bit
(1 row)
|
複数データベースの作成#
開発環境では、アプリケーション用とテスト用など複数のデータベースが必要になることがあります。初期化スクリプトを使って複数のデータベースを作成できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# compose.yaml
services:
db:
image: postgres:18
container_name: dev-postgres
environment:
POSTGRES_USER: devuser
POSTGRES_PASSWORD: devpassword
POSTGRES_DB: myapp_development
TZ: Asia/Tokyo
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql
- ./docker/db/init:/docker-entrypoint-initdb.d:ro
volumes:
postgres-data:
|
初期化スクリプト(docker/db/init/01-create-databases.sh)を作成します。
1
2
3
4
5
6
7
8
9
10
|
#!/usr/bin/env bash
set -e
# テスト用データベースを作成
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE DATABASE myapp_test;
GRANT ALL PRIVILEGES ON DATABASE myapp_test TO $POSTGRES_USER;
EOSQL
echo "Additional database 'myapp_test' created successfully."
|
Windowsを使用している場合は、ファイルの改行コードをLFに設定してください。CRLFの場合、スクリプトが正しく実行されません。
データ永続化とボリューム設定#
ボリュームマウントの仕組み#
Dockerコンテナは一時的な存在であり、コンテナを削除すると内部のデータも失われます。PostgreSQLのデータを永続化するには、ボリュームをマウントする必要があります。
flowchart LR
subgraph Container["PostgreSQLコンテナ"]
PGDATA["/var/lib/postgresql\n(PGDATA)"]
end
subgraph DockerVolume["Docker ボリューム"]
Volume["postgres-data\n(Dockerが管理)"]
end
subgraph Host["ホストマシン"]
HostPath["/var/lib/docker/volumes/\npostgres-data/_data"]
end
PGDATA <--> Volume
Volume <--> HostPathボリュームには2つのタイプがあります。
| タイプ |
定義方法 |
用途 |
| 名前付きボリューム |
postgres-data:/var/lib/postgresql |
本番環境、長期間のデータ保持 |
| バインドマウント |
./data:/var/lib/postgresql |
開発環境、ホストからの直接アクセス |
一般的には、名前付きボリュームを推奨します。Dockerが管理するため、パフォーマンスが良く、ポータビリティにも優れています。
バックアップとリストア#
ボリューム内のデータをバックアップ・リストアする方法を紹介します。
1
2
3
4
5
6
7
8
9
10
11
|
# pg_dumpでバックアップ(SQL形式)
docker compose exec db pg_dump -U devuser -d myapp_development > backup.sql
# pg_dumpでバックアップ(カスタム形式 - 推奨)
docker compose exec db pg_dump -U devuser -Fc myapp_development > backup.dump
# リストア(SQL形式)
docker compose exec -T db psql -U devuser -d myapp_development < backup.sql
# リストア(カスタム形式)
docker compose exec db pg_restore -U devuser -d myapp_development --clean backup.dump
|
ボリューム自体をバックアップする場合は、以下のようにします。
1
2
3
4
5
6
7
8
9
10
11
|
# ボリュームのバックアップ(tarアーカイブ)
docker run --rm \
-v postgres-data:/source:ro \
-v $(pwd):/backup \
alpine tar cvf /backup/postgres-backup.tar -C /source .
# ボリュームのリストア
docker run --rm \
-v postgres-data:/target \
-v $(pwd):/backup \
alpine sh -c "cd /target && tar xvf /backup/postgres-backup.tar"
|
ボリュームの管理コマンド#
よく使うボリューム管理コマンドをまとめます。
1
2
3
4
5
6
7
8
9
10
11
|
# ボリューム一覧
docker volume ls
# ボリュームの詳細確認
docker volume inspect postgres-data
# 使用していないボリュームの削除
docker volume prune
# 特定のボリュームを削除(データが消えるので注意)
docker volume rm postgres-data
|
初期化スクリプトの活用#
docker-entrypoint-initdb.dの仕組み#
PostgreSQL公式イメージには、初期化スクリプトを実行する仕組みが組み込まれています。/docker-entrypoint-initdb.dディレクトリに配置されたファイルは、初回起動時(データディレクトリが空の場合)に自動実行されます。
sequenceDiagram
participant User as ユーザー
participant Docker as Docker
participant Entrypoint as entrypoint.sh
participant InitDB as initdb
participant Scripts as 初期化スクリプト
participant PG as PostgreSQL
User->>Docker: docker compose up
Docker->>Entrypoint: コンテナ起動
Entrypoint->>Entrypoint: PGDATAが空か確認
alt PGDATAが空
Entrypoint->>InitDB: initdb実行
InitDB-->>Entrypoint: 完了
Entrypoint->>PG: 一時的に起動(Unixソケットのみ)
Entrypoint->>Scripts: /docker-entrypoint-initdb.d/ 内のファイルを順次実行
Scripts->>PG: .sql, .sh を実行
Scripts-->>Entrypoint: 完了
Entrypoint->>PG: 再起動(ネットワーク接続可能)
else PGDATAに既存データあり
Entrypoint->>PG: 通常起動
end対応するファイル形式は以下の通りです。
| 拡張子 |
実行方法 |
備考 |
| .sql |
psqlで実行 |
POSTGRES_USERで実行される |
| .sql.gz |
gunzip後、psqlで実行 |
圧縮されたSQLファイル |
| .sh(実行可能) |
直接実行 |
chmod +x が必要 |
| .sh(実行不可) |
sourceで読み込み |
シェル変数の設定など |
ファイルはファイル名のアルファベット順(ロケールに依存)で実行されます。明示的に順序を制御したい場合は、プレフィックスに番号を付けます。
テーブル作成スクリプト#
プロジェクトのスキーマを初期化するSQLスクリプトの例です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
-- docker/db/init/01-schema.sql
-- ユーザーテーブル
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 記事テーブル
CREATE TABLE IF NOT EXISTS articles (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
content TEXT,
status VARCHAR(20) DEFAULT 'draft' CHECK (status IN ('draft', 'published', 'archived')),
published_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- インデックス
CREATE INDEX IF NOT EXISTS idx_articles_user_id ON articles(user_id);
CREATE INDEX IF NOT EXISTS idx_articles_status ON articles(status);
CREATE INDEX IF NOT EXISTS idx_articles_published_at ON articles(published_at);
-- 更新日時を自動更新するトリガー関数
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- トリガーの設定
CREATE TRIGGER users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER articles_updated_at
BEFORE UPDATE ON articles
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
テストデータ投入スクリプト#
開発用のテストデータを投入するスクリプト例です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
-- docker/db/init/02-seed.sql
-- 開発用ユーザー
INSERT INTO users (email, name, password_hash) VALUES
('admin@example.com', '管理者', '$2b$10$dummyhashvalue1'),
('user1@example.com', 'テストユーザー1', '$2b$10$dummyhashvalue2'),
('user2@example.com', 'テストユーザー2', '$2b$10$dummyhashvalue3')
ON CONFLICT (email) DO NOTHING;
-- サンプル記事
INSERT INTO articles (user_id, title, content, status, published_at) VALUES
(1, 'はじめての投稿', 'これは最初の記事です。', 'published', CURRENT_TIMESTAMP),
(1, '下書き記事', '公開前の記事内容です。', 'draft', NULL),
(2, 'ユーザー1の記事', '別のユーザーが投稿した記事です。', 'published', CURRENT_TIMESTAMP - INTERVAL '1 day')
ON CONFLICT DO NOTHING;
|
環境別の初期化制御#
本番環境ではシードデータを投入したくない場合があります。シェルスクリプトで環境変数を参照して制御できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#!/usr/bin/env bash
# docker/db/init/02-seed.sh
set -e
# 環境変数で制御(デフォルトはdevelopment)
APP_ENV=${APP_ENV:-development}
if [ "$APP_ENV" = "development" ] || [ "$APP_ENV" = "test" ]; then
echo "Seeding database for $APP_ENV environment..."
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
INSERT INTO users (email, name, password_hash) VALUES
('admin@example.com', '管理者', '\$2b\$10\$dummyhashvalue1')
ON CONFLICT (email) DO NOTHING;
EOSQL
echo "Seeding completed."
else
echo "Skipping seed data for $APP_ENV environment."
fi
|
compose.yamlで環境変数を設定します。
1
2
3
4
5
6
7
8
9
10
11
|
services:
db:
image: postgres:18
environment:
POSTGRES_USER: devuser
POSTGRES_PASSWORD: devpassword
POSTGRES_DB: myapp_development
APP_ENV: development # development, test, production
volumes:
- postgres-data:/var/lib/postgresql
- ./docker/db/init:/docker-entrypoint-initdb.d:ro
|
本番環境での考慮点#
セキュリティ設定#
本番環境でDockerized PostgreSQLを運用する際は、以下のセキュリティ対策が必要です。
パスワード管理#
環境変数にパスワードを直接書くのは危険です。Docker Secretsまたは環境変数ファイルを使用しましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# compose.yaml(Docker Secrets使用)
services:
db:
image: postgres:18
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
POSTGRES_DB: myapp_production
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt # このファイルはGit管理外に
|
もしくは.envファイルを使用します。
1
2
3
4
5
6
|
# compose.yaml
services:
db:
image: postgres:18
env_file:
- .env.production # Git管理外
|
1
2
3
4
|
# .env.production
POSTGRES_USER=appuser
POSTGRES_PASSWORD=very_secure_random_password_here
POSTGRES_DB=myapp_production
|
ネットワークの分離#
本番環境では、データベースを外部に公開しないようにします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
services:
app:
build: .
networks:
- frontend
- backend
db:
image: postgres:18
# portsを指定しない = 外部からアクセス不可
networks:
- backend # バックエンドネットワークのみ
networks:
frontend:
backend:
internal: true # 外部ルーティング不可
|
リソース制限#
コンテナに適切なリソース制限を設定し、他のサービスへの影響を防ぎます。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
services:
db:
image: postgres:18
deploy:
resources:
limits:
cpus: '2.0'
memory: 4G
reservations:
cpus: '0.5'
memory: 1G
# PostgreSQLの共有メモリ設定
shm_size: 256mb
|
shm_sizeは重要な設定です。PostgreSQLは共有メモリを多く使用するため、デフォルトの64MBでは不足する場合があります。shared_buffersの設定値に応じて調整してください。
PostgreSQL設定のチューニング#
本番環境では、デフォルト設定では性能が不足します。カスタム設定ファイルを使用するか、起動時オプションで設定を上書きします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
services:
db:
image: postgres:18
command:
- "postgres"
- "-c"
- "shared_buffers=1GB"
- "-c"
- "effective_cache_size=3GB"
- "-c"
- "maintenance_work_mem=256MB"
- "-c"
- "work_mem=16MB"
- "-c"
- "max_connections=100"
- "-c"
- "random_page_cost=1.1" # SSD使用時
- "-c"
- "log_statement=all" # 本番では'ddl'または'none'推奨
- "-c"
- "log_min_duration_statement=1000" # 1秒以上のクエリをログ
|
または、カスタム設定ファイルをマウントします。
1
2
3
4
5
6
7
|
services:
db:
image: postgres:18
volumes:
- postgres-data:/var/lib/postgresql
- ./config/postgresql.conf:/etc/postgresql/postgresql.conf:ro
command: ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"]
|
ログ管理#
本番環境ではログの適切な管理が重要です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
services:
db:
image: postgres:18
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
command:
- "postgres"
- "-c"
- "logging_collector=on"
- "-c"
- "log_directory=/var/log/postgresql"
- "-c"
- "log_filename=postgresql-%Y-%m-%d.log"
- "-c"
- "log_rotation_age=1d"
- "-c"
- "log_rotation_size=100MB"
volumes:
- postgres-logs:/var/log/postgresql
volumes:
postgres-logs:
|
コンテナでPostgreSQLを運用する際の注意点#
Dockerized PostgreSQLを本番環境で使用する際は、以下の点を考慮してください。
| 観点 |
考慮事項 |
対策 |
| 可用性 |
単一コンテナは単一障害点になる |
レプリケーション構成、マネージドサービスの検討 |
| バックアップ |
定期バックアップの仕組みが必要 |
cronジョブ、バックアップコンテナの追加 |
| 監視 |
コンテナとPostgreSQL両方の監視が必要 |
Prometheus + postgres_exporter |
| アップグレード |
メジャーバージョンアップは慎重に |
pg_upgradeの手順確認、十分なテスト |
| パフォーマンス |
オーバーヘッドは最小限だが存在する |
ベンチマークで確認 |
多くのケースでは、本番環境ではAWS RDS、Google Cloud SQL、Azure Database for PostgreSQLなどのマネージドサービスの利用を検討することを推奨します。
Kubernetes上でのPostgreSQL運用#
Kubernetesでのデータベース運用の選択肢#
Kubernetes上でPostgreSQLを運用する方法は複数あります。
flowchart TD
Start["Kubernetes上でPostgreSQLが必要"] --> Decision1{"マネージドDBは使える?"}
Decision1 -->|"はい"| Managed["マネージドサービス\n(RDS, Cloud SQL等)\n⭐推奨"]
Decision1 -->|"いいえ(オンプレ等)"| Decision2{"運用負荷を許容できる?"}
Decision2 -->|"できるだけ低く"| Operator["PostgreSQL Operator\n(CloudNativePG等)"]
Decision2 -->|"自前で管理する"| StatefulSet["StatefulSet + Helm"]
Managed --> Note1["接続はExternalName Serviceで抽象化"]
Operator --> Note2["CRDでPostgreSQLクラスタを宣言的に管理"]
StatefulSet --> Note3["PV/PVC、ConfigMap、Secretを自前で管理"]各選択肢の比較です。
| 選択肢 |
運用負荷 |
柔軟性 |
推奨シーン |
| マネージドサービス |
低 |
中 |
クラウド環境での本番運用 |
| PostgreSQL Operator |
中 |
高 |
オンプレ/マルチクラウド |
| StatefulSet(自前) |
高 |
最高 |
特殊要件がある場合 |
CloudNativePGの概要#
CloudNativePGは、CNCFサンドボックスプロジェクトとして2025年1月に採択された、Kubernetes上でPostgreSQLを運用するためのOperatorです。宣言的なリソース定義で、PostgreSQLクラスタのデプロイ、スケーリング、バックアップ、フェイルオーバーを自動化できます。
CloudNativePGの主な機能は以下の通りです。
- 宣言的なPostgreSQLクラスタ定義(Custom Resource Definition)
- 自動フェイルオーバーとレプリカ昇格
- 継続的バックアップ(S3、GCS、Azure Blob対応)
- ポイントインタイムリカバリ(PITR)
- ローリングアップデート
- TLS暗号化通信
基本的なクラスタ定義の例です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
# cluster.yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: myapp-db
spec:
instances: 3 # プライマリ1 + レプリカ2
postgresql:
parameters:
shared_buffers: "256MB"
effective_cache_size: "768MB"
storage:
size: 10Gi
storageClass: standard
bootstrap:
initdb:
database: myapp
owner: myapp
backup:
barmanObjectStore:
destinationPath: "s3://my-bucket/backups"
s3Credentials:
accessKeyId:
name: s3-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: s3-creds
key: SECRET_ACCESS_KEY
|
この定義をapplyすることで、3ノードのPostgreSQLクラスタが自動的に構築されます。
1
2
3
4
5
6
7
8
9
10
|
# CloudNativePGのインストール
kubectl apply --server-side -f \
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.25/releases/cnpg-1.25.0.yaml
# クラスタの作成
kubectl apply -f cluster.yaml
# クラスタの状態確認
kubectl get clusters
kubectl get pods -l cnpg.io/cluster=myapp-db
|
他のPostgreSQL Operatorとの比較#
CloudNativePG以外にも、いくつかのPostgreSQL Operatorが存在します。
| Operator |
開発元 |
特徴 |
ライセンス |
| CloudNativePG |
EDB |
CNCFプロジェクト、活発な開発 |
Apache 2.0 |
| Crunchy PGO |
Crunchy Data |
豊富な機能、商用サポートあり |
Apache 2.0 |
| Zalando Postgres Operator |
Zalando |
大規模運用実績 |
MIT |
| Percona PG Operator |
Percona |
Percona製品との統合 |
Apache 2.0 |
選択の際は、コミュニティの活発さ、ドキュメントの充実度、商用サポートの有無を考慮してください。
Kubernetesでの運用時の注意点#
Kubernetes上でステートフルなワークロード(PostgreSQL)を運用する際は、以下に注意してください。
- PersistentVolumeの選択: 適切なStorageClassを選択し、ディスクI/O性能を確保する
- Node Affinity: データベースPodが適切なノードにスケジュールされるよう設定する
- Pod Disruption Budget: メンテナンス時のダウンタイムを制御する
- ネットワークポリシー: データベースへのアクセスを必要なPodに限定する
- シークレット管理: External Secrets OperatorやVaultとの連携を検討する
開発ワークフローのベストプラクティス#
プロジェクト構成の例#
PostgreSQLを使用するプロジェクトの推奨ディレクトリ構成です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
project/
├── compose.yaml # 開発環境用
├── compose.production.yaml # 本番環境用(必要に応じて)
├── .env.example # 環境変数のテンプレート
├── .env # 実際の環境変数(Git管理外)
├── docker/
│ └── db/
│ ├── init/
│ │ ├── 01-schema.sql # スキーマ定義
│ │ ├── 02-seed.sql # シードデータ
│ │ └── 03-extensions.sql # 拡張機能
│ └── config/
│ └── postgresql.conf # カスタム設定(必要に応じて)
├── .gitignore
└── src/
└── ...
|
.gitignoreの設定例です。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 環境変数ファイル
.env
.env.production
.env.*.local
# シークレット
secrets/
# バックアップファイル
*.sql
*.dump
*.tar
backup/
|
便利なMakefileの作成#
よく使うコマンドをMakefileにまとめておくと便利です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
.PHONY: up down logs db-shell db-reset db-backup db-restore
# 開発環境の起動
up:
docker compose up -d --wait
# 開発環境の停止
down:
docker compose down
# ログの表示
logs:
docker compose logs -f db
# PostgreSQLに接続
db-shell:
docker compose exec db psql -U devuser -d myapp_development
# データベースのリセット(注意: 全データ削除)
db-reset:
docker compose down -v
docker compose up -d --wait
# バックアップの作成
db-backup:
@mkdir -p backup
docker compose exec db pg_dump -U devuser -Fc myapp_development > backup/backup-$$(date +%Y%m%d-%H%M%S).dump
@echo "Backup created: backup/backup-$$(date +%Y%m%d-%H%M%S).dump"
# 最新のバックアップからリストア
db-restore:
@LATEST=$$(ls -t backup/*.dump 2>/dev/null | head -1); \
if [ -z "$$LATEST" ]; then \
echo "No backup found in backup/"; \
exit 1; \
fi; \
echo "Restoring from $$LATEST..."; \
docker compose exec -T db pg_restore -U devuser -d myapp_development --clean < $$LATEST
|
まとめ#
本記事では、PostgreSQLとDockerを組み合わせた実践的な環境構築から本番運用までを解説しました。
開発環境では、Docker Composeを使うことで、チームメンバー全員が同じデータベース環境を簡単に構築できます。初期化スクリプトを活用すれば、スキーマ定義やテストデータの投入も自動化できます。
本番環境では、セキュリティ設定(パスワード管理、ネットワーク分離)、リソース制限、適切なPostgreSQL設定のチューニングが重要です。多くのケースでは、マネージドサービスの利用を検討することを推奨します。
Kubernetes環境では、CloudNativePGなどのOperatorを使うことで、PostgreSQLクラスタの運用を自動化できます。ただし、ステートフルワークロードの運用には固有の課題があるため、十分な検証が必要です。
Dockerを活用することで、PostgreSQLの環境構築と運用を大幅に効率化できます。本記事で紹介した設定やベストプラクティスを参考に、プロジェクトに最適な構成を構築してください。
参考リンク#