Flyway + PostgreSQL 18 を Dev Container で再現する最小構成
1. はじめに
DB スキーマ変更は、チーム開発で最も「環境差分」が出やすい領域です。個々のローカルで手作業をすると、適用順や適用漏れが起きやすく、検証環境と同じ状態を再現するのも困難になります。
本記事では、Flyway + PostgreSQL 18 を Dev Container で最小構成化したサンプルをベースに、構成ファイルの役割と運用ポイントを整理します。
2. 全体像
構成はシンプルに 2 サービスのみです。
-
db: PostgreSQL 18 -
flyway: Flyway CLI コンテナ (マイグレーション実行)
VS Code の Dev Containers が flyway に接続します。学習用途のため、flyway migrate は手動実行します。
3. ディレクトリ構成
Flyway/
.devcontainer/devcontainer.json
compose.yaml
flyway.conf
sql/
V1__create_schemas.sql
V2__create_tables.sql
V3__seed_data.sql
V4__create_views.sql
4. devcontainer.json のポイント
devcontainer.json は「どのコンテナに VS Code で接続するか」を定義します。
{
"name": "flyway-postgres18-dev",
"dockerComposeFile": ["../compose.yaml"],
"service": "flyway",
"workspaceFolder": "/workspace",
"remoteUser": "root"
}
ポイント:
-
service: "flyway"で Flyway コンテナへ接続。 -
flyway migrateは手動実行。意図的にpostCreateCommandは設定しません。 -
workspaceFolderはcompose.yamlの volume マウントと一致させることが重要。
5. compose.yaml のポイント
services:
flyway:
image: redgate/flyway:12.0.0
entrypoint: ["/bin/sh", "-c", "sleep infinity"]
environment:
FLYWAY_CONFIG_FILES: /workspace/flyway.conf
volumes:
- .:/workspace:cached
working_dir: /workspace
depends_on:
db:
condition: service_healthy
db:
image: postgres:18
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- "15432:5432"
volumes:
- db-data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
interval: 5s
timeout: 5s
retries: 12
volumes:
db-data:
解説:
-
entrypoint: ["/bin/sh", "-c", "sleep infinity"]で Flyway コンテナを常駐化。VS Code が接続し続けるための最小手段です。 -
FLYWAY_CONFIG_FILESをenvironmentで固定し、flyway infoなど短縮コマンドを使えるようにします。 -
depends_on.condition: service_healthyにより、DB が起動完了するまで Flyway 側の起動を待ちます。 -
ports: "15432:5432"はホストからの接続用。必要なときだけ利用すれば OK。 -
db-dataボリュームで DB データを永続化。コンテナ再作成でもデータが残ります。
healthcheck の意味
healthcheck は「DB が本当に利用可能か」を Docker が判定するための設定です。pg_isready で PostgreSQL の準備完了を確認しています。
-
test: コンテナ内でpg_isready -U postgres -d postgresを実行し、成功なら healthy とみなします。 -
interval: 5s: 5 秒ごとにチェックします。 -
timeout: 5s: 1 回のチェックが 5 秒以内に終わらなければ失敗扱いです。 -
retries: 12: 連続 12 回失敗すると unhealthy 判定になります。
これにより depends_on.condition: service_healthy が機能し、DB が起動完了してから Flyway を開始できます。
6. flyway.conf のポイント
flyway.url=jdbc:postgresql://db:5432/postgres
flyway.user=postgres
flyway.password=postgres
flyway.locations=filesystem:/workspace/sql
flyway.connectRetries=60
ポイント:
-
flyway.locationsは SQL マイグレーションの配置先。 -
connectRetries=60により、DB 起動直後の接続失敗に耐性を持たせています。 - ここを環境変数に切り替えれば、CI や本番向けにも転用しやすくなります。
7. SQL マイグレーション構成
サンプルでは、スキーマ・テーブル・初期データ・ビューまでを段階的に作成します。
-
V1__create_schemas.sql:study_core/study_sales/study_auditを作成 -
V2__create_tables.sql: ユーザ・商品・注文・監査イベントなどのテーブル -
V3__seed_data.sql: 学習用のサンプルデータ投入 -
V4__create_views.sql: 注文サマリビューstudy_sales.order_summary
Flyway の命名規則は V<番号>__<説明>.sql です。番号の順序が適用順序になるため、ここはチーム内で明確にルール化しておくと安全です。
7.1 マイグレーション SQL(サンプル)
-- V1__create_schemas.sql
CREATE SCHEMA IF NOT EXISTS study_core;
CREATE SCHEMA IF NOT EXISTS study_sales;
CREATE SCHEMA IF NOT EXISTS study_audit;
-- V2__create_tables.sql
CREATE TABLE study_core.users (
user_id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE study_core.products (
product_id BIGSERIAL PRIMARY KEY,
sku TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
price_cents INTEGER NOT NULL CHECK (price_cents >= 0),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE study_sales.orders (
order_id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES study_core.users(user_id),
order_status TEXT NOT NULL,
ordered_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE study_sales.order_items (
order_id BIGINT NOT NULL REFERENCES study_sales.orders(order_id),
product_id BIGINT NOT NULL REFERENCES study_core.products(product_id),
quantity INTEGER NOT NULL CHECK (quantity > 0),
unit_price_cents INTEGER NOT NULL CHECK (unit_price_cents >= 0),
PRIMARY KEY (order_id, product_id)
);
CREATE TABLE study_audit.events (
event_id BIGSERIAL PRIMARY KEY,
event_type TEXT NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- V3__seed_data.sql
INSERT INTO study_core.users (email, display_name) VALUES
('alice@example.com', 'Alice'),
('bob@example.com', 'Bob');
INSERT INTO study_core.products (sku, name, price_cents) VALUES
('SKU-001', 'Notebook', 1200),
('SKU-002', 'Pen', 300),
('SKU-003', 'Sticker', 150);
INSERT INTO study_sales.orders (user_id, order_status) VALUES
(1, 'PAID'),
(2, 'PENDING');
INSERT INTO study_sales.order_items (order_id, product_id, quantity, unit_price_cents) VALUES
(1, 1, 1, 1200),
(1, 2, 2, 300),
(2, 3, 5, 150);
INSERT INTO study_audit.events (event_type, payload) VALUES
('order_created', '{"order_id": 1, "user_id": 1}'),
('order_created', '{"order_id": 2, "user_id": 2}');
-- V4__create_views.sql
CREATE VIEW study_sales.order_summary AS
SELECT
o.order_id,
u.display_name,
o.order_status,
o.ordered_at,
SUM(oi.quantity * oi.unit_price_cents) AS total_cents
FROM study_sales.orders AS o
JOIN study_core.users AS u ON u.user_id = o.user_id
JOIN study_sales.order_items AS oi ON oi.order_id = o.order_id
GROUP BY
o.order_id,
u.display_name,
o.order_status,
o.ordered_at;
8. 起動手順 (VS Code)
-
Infra/Database/Flywayを VS Code で開く -
Dev Containers: Reopen in Containerを実行 -
flyway migrateを手動で実行してスキーマを構築
状態確認(短縮版 / 環境変数設定済み):
flyway info
flyway validate
環境変数を設定していない場合(明示指定):
flyway -configFiles=/workspace/flyway.conf info
flyway -configFiles=/workspace/flyway.conf validate
8.1 -configFiles を省略したい場合
Dev Container 内で flyway info のように短縮したい場合は、設定ファイルのパスを環境変数で固定します。最も確実なのは compose.yaml の flyway.environment に FLYWAY_CONFIG_FILES を設定する方法です。
services:
flyway:
environment:
FLYWAY_CONFIG_FILES: /workspace/flyway.conf
本サンプルは compose.yaml に設定済みのため、コンテナ内では flyway info / flyway validate で動作します。
環境変数を使わない場合は、毎回 -configFiles=/workspace/flyway.conf を付けて実行してください。
一時的に試すだけなら、コンテナ内で bash から次のように設定できます。
export FLYWAY_CONFIG_FILES=/workspace/flyway.conf
flyway info
flyway validate
8.2 Dev Containers: Rebuild Container の使いどころ
Rebuild は、設定変更を確実に反映させるための再作成です。以下のときに使います。
-
compose.yaml/devcontainer.jsonを変更した -
.devcontainerの有無や参照先を切り替えた - 拡張機能や features が反映されない
- コンテナが不安定・起動しない
補足:
- Rebuild だけでは DB ボリュームは削除されません。初期化したい場合は
docker compose ... down -vを実行します。
8.3 手動で flyway migrate を実行する
学習用途として、常に手動実行します。
コンテナ内で実行:
flyway migrate
環境変数を設定していない場合(明示指定):
flyway -configFiles=/workspace/flyway.conf migrate
ホストから実行:
docker compose -f Infra/Database/Flyway/compose.yaml exec flyway flyway migrate
環境変数を設定していない場合(明示指定):
docker compose -f Infra/Database/Flyway/compose.yaml exec flyway flyway -configFiles=/workspace/flyway.conf migrate
9. 動作確認
DBクライアントツールで接続する場合の情報(compose.yaml の該当項目も併記):
- ホスト: localhost(
db.ports: 15432:5432の左側) - ポート: 15432(
db.ports) - データベース: postgres(
db.environment.POSTGRES_DB) - ユーザー: postgres(
db.environment.POSTGRES_USER) - パスワード: postgres(
db.environment.POSTGRES_PASSWORD)
SQL 例:
SELECT * FROM study_sales.order_summary;
Flyway の動作確認用 SQL(履歴テーブルの確認):
SELECT
installed_rank,
version,
description,
type,
script,
installed_on,
success
FROM public.flyway_schema_history
ORDER BY installed_rank;
Flyway の履歴は public.flyway_schema_history に記録されます。検証時に便利なので覚えておくと良いです。
9.1 Dev Container 起動トラブルシュート
調査手順
- Dev Containers のログを確認:
Dev Containers: Show Log - Docker 側の状態確認:
docker ps -a --filter name=flyway
docker logs flyway-db-1
docker logs flyway-flyway-1
よくある原因と対応
-
PostgreSQL 18 のデータ形式変更で db が起動しない
症状:/var/lib/postgresql/dataの警告 /database files are incompatible
対応: 学習用途ならボリューム削除で初期化docker compose -f Infra/Database/Flyway/compose.yaml down -vもしくは DB のボリュームを
/var/lib/postgresqlにマウントする構成に変更します(本サンプルは対応済み)。 -
flyway コンテナが即終了する
症状:flyway-flyway-1が Exited /Invalid flag: sleep
対応: Flyway 公式イメージは entrypoint がflyway固定のため、command: ["sleep", "infinity"]は不可。
entrypoint: ["/bin/sh", "-c", "sleep infinity"]で常駐させます(本サンプルは対応済み)。 -
postCreateCommandが実行されない
症状:flyway migrateが自動実行されずスキーマが作成されない
対応: 本サンプルは手動実行前提。手動でflyway migrateを実行します。
10. 運用で意識したいポイント
- 既存の migration ファイルは編集しない: 適用済みファイルの改変は checksum 不一致の原因になるため、新規ファイルで差分を追加します。
- validate を CI に組み込む: 想定外の改変を早期に検知できます。
-
開発環境と本番で接続情報を分離:
flyway.confを環境変数化して安全性を高めるのが定番です。
11. validate の検証エラーと復旧手順
validate は 履歴テーブルと SQL ファイルの整合性チェックです。まずは状況を整理します。
-
flyway info(環境変数未設定ならflyway -configFiles=/workspace/flyway.conf info)で状態確認 -
public.flyway_schema_historyで該当バージョンの履歴を見る
代表的なエラーと復旧の考え方:
-
Checksum mismatch: 適用済みファイルを編集したのが原因です。VCS から元に戻すか、新しい
Vx__で差分を追加します。学習用の開発環境だけならflyway repairで checksum を合わせる手もあります。 -
Applied migration not resolved: 適用済みだがファイルが見つからない状態です。削除/改名したファイルを復元し、命名も元に戻します。やむを得ず消す場合は開発環境で
repairを検討します。 -
Failed migration: 途中失敗で履歴に失敗レコードが残ります。SQL 失敗箇所を修正し、部分適用された変更を手動でクリーンアップしてから
flyway repair→migrateが基本です。 -
Out of order: バージョン番号が逆転しています。番号を付け直すか、運用方針に合わせて
outOfOrder=trueを設定します。
補足: 学習用のローカル環境でのみ、DB ボリュームを削除して初期化するのが最短ルートになる場合があります。実運用環境では避けるべきです。
12. まとめ
このサンプルは、Flyway の「マイグレーション管理」と Dev Container の「環境再現性」を最小構成で組み合わせています。
13. 付録: 主要コマンド
# マイグレーション状況の確認
flyway info
# マイグレーションの適用
flyway migrate
# チェックサム検証
flyway validate
環境変数を設定していない場合(明示指定):
# マイグレーション状況の確認
flyway -configFiles=/workspace/flyway.conf info
# マイグレーションの適用
flyway -configFiles=/workspace/flyway.conf migrate
# チェックサム検証
flyway -configFiles=/workspace/flyway.conf validate
以上で、Flyway + PostgreSQL の Dev Container 最小構成は完了です。運用で困ったら、まず flyway_schema_history と validate を確認する癖を付けるとトラブルシュートが速くなります。
Discussion