Skip to content

Commit 7d7af56

Browse files
committed
db-init(prepare)を廃止し、マイグレーションのサービス名を init に変える
db/data の作成と所有者調整は postgres のエントリポイントが自分でやるので、 専用のワンショットは要らなかった。「ホスト側にディレクトリが無くても起動できる」 が狙いだったが、それが必要な standalone 環境ほど bind マウント先の自動作成に 頼れず、結局あらかじめ作っておく運用になっていた。standalone の雛形には 「データ置き場は先に作っておく」を明記した。 起動順は db → init → app に簡素化。GHCR のイメージ名(travel-log-db-init)は 据え置き(変えると workflow と GHCR 側の整理が要るため。名残であることは db/Dockerfile のコメントに書いた)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 2c78708 commit 7d7af56

12 files changed

Lines changed: 65 additions & 125 deletions

.github/dependabot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ updates:
2323
- dependency-name: "@types/node"
2424
update-types: ["version-update:semver-major"]
2525

26-
# Dockerfileのベースイメージ(アプリ本体=node、db-init=postgres)
26+
# Dockerfileのベースイメージ(アプリ本体=node、DB用イメージ=postgres)
2727
- package-ecosystem: "docker"
2828
directories:
2929
- "/"

.github/workflows/docker-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# mainへのpushで本番用Dockerイメージをビルドし、GHCRへ公開する。
22
# 本番環境の更新はこれらをpullするだけでよい(README「本番運用」参照)。
33
# ghcr.io/rtcode337/travel-log … アプリ本体(Dockerfileのprodステージ、appサービス)
4-
# ghcr.io/rtcode337/travel-log-db-init … DBの準備とマイグレーション適用
5-
# (db/Dockerfile、db-init/db-migrateサービス)
4+
# ghcr.io/rtcode337/travel-log-db-init … スキーマ・マイグレーション適用
5+
# (db/Dockerfile、initサービス。イメージ名は旧構成の名残)
66
#
77
# アプリ本体は amd64/arm64 を「それぞれのアーキのネイティブランナー」で並列ビルドし、
88
# ダイジェストで push したものを最後の merge ジョブでマニフェストにまとめる。

CLAUDE.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
55
## コマンド
66

77
```bash
8-
docker compose -f docker-compose.dev.yml up --build # 開発用: アプリ(localhost:7040, next dev+ホットリロード)+Postgres。スキーマ作成・未適用マイグレーションはdb-migrateサービスが自動で行う
9-
docker compose pull && docker compose up -d # 本番用: GHCRのビルド済みイメージ(mainへのpushでGitHub Actionsが自動ビルド)で起動。未適用のマイグレーションはdb-migrateサービスが自動で当てる。SESSION_SECRET環境変数が必須(.env可)
8+
docker compose -f docker-compose.dev.yml up --build # 開発用: アプリ(localhost:7040, next dev+ホットリロード)+Postgres。スキーマ作成・未適用マイグレーションはinitサービスが自動で行う
9+
docker compose pull && docker compose up -d # 本番用: GHCRのビルド済みイメージ(mainへのpushでGitHub Actionsが自動ビルド)で起動。未適用のマイグレーションはinitサービスが自動で当てる。SESSION_SECRET環境変数が必須(.env可)
1010
npm run dev # Next.js開発サーバー(ローカルPostgresを直接使う場合のみ)
1111
npm run build # 本番ビルド(型チェック込み)
1212
```
@@ -35,20 +35,21 @@ DB定義は`db/init/01_schema.sql`の1ファイルにすべてまとまってい
3535

3636
### DBの初期化・マイグレーションの流れ
3737

38-
composeは`db-init``db``db-migrate``app`の順に起動する`db-init``db-migrate`は同じイメージ(`db/Dockerfile``db/entrypoint.sh`のサブコマンド違い)で、どちらも1回走って終了するワンショット
38+
composeは`db``init``app`の順に起動する。
3939

4040
| サービス | 役割 | タイミング |
4141
|---|---|---|
42-
| `db-init` (`prepare`) | `db/data`の作成と所有者/パーミッション調整 | dbの起動**** |
43-
| `db` | Postgres本体(空のDBができるだけ。スキーマは作らない) ||
44-
| `db-migrate` (`migrate`) | スキーマ本体(`/init/01_schema.sql`)と`/migrations`の未適用SQLを適用し`schema_migrations`に記録 | dbのhealthcheck通過**** |
45-
| `app` | Next.js。`db-migrate`が正常終了するまで起動しない | 最後 |
42+
| `db` | Postgres本体(空のDBができるだけ。スキーマは作らない)。`db/data`が無ければDockerが作り、所有者はpostgresのエントリポイントが自分で揃える ||
43+
| `init` | スキーマ本体(`/init/01_schema.sql`)と`/migrations`の未適用SQLを適用し`schema_migrations`に記録するワンショット(`db/Dockerfile``db/entrypoint.sh`) | dbのhealthcheck通過**** |
44+
| `app` | Next.js。`init`が正常終了するまで起動しない | 最後 |
4645

47-
スキーマ本体もマイグレーションSQLも`db-init`イメージに焼き込まれるため、本番ホストのリポジトリの新旧に関わらず、pullしたイメージの中身がそのまま適用される。マイグレーションが失敗すると`db-migrate`が非ゼロ終了し、`app`も起動しないため、古いスキーマのままアプリが動くことはない。
46+
かつては`db/data`の作成とchownを行う`db-init`サービス(prepareサブコマンド)がdbの起動前にあったが、postgresのエントリポイントが同じことを自分でやるため廃止した。「ホスト側にディレクトリが無くても起動できる」が狙いだったものの、それが必要なstandalone環境ほどbindマウント先の自動作成に頼れず、結局あらかじめ作っておく運用になっていた。GHCRのイメージ名(`travel-log-db-init`)はこの名残で、`init`サービスが使い続けている。
47+
48+
スキーマ本体もマイグレーションSQLも`travel-log-db-init`イメージに焼き込まれるため、本番ホストのリポジトリの新旧に関わらず、pullしたイメージの中身がそのまま適用される。マイグレーションが失敗すると`init`が非ゼロ終了し、`app`も起動しないため、古いスキーマのままアプリが動くことはない。
4849

4950
`01_schema.sql``schema_migrations`上では`000_init_schema`という名前の「一番先頭のマイグレーション」として扱う。空のDBには実行し、既にテーブルがあるDB(旧方式でinitdbが作ったもの)には実行せず適用済みとして記録するだけにするので、既存の本番DBをそのまま引き継げる。
5051

51-
**`db/init`をdbコンテナにマウントしないのは意図的**。かつては`docker-entrypoint-initdb.d``:ro`マウントし、`db-init``chmod -R a+rX`をかけていたが、git管理下のファイルのパーミッションをrootで書き換えるため、`01_schema.sql`を更新するとホスト側の`git pull`が失敗するようになっていた。スキーマ本体もイメージ側から流す方式にして解消した(`prepare`が触るのはgit管理外の`db/data`だけ)。
52+
**`db/init`をdbコンテナにマウントしないのは意図的**。かつては`docker-entrypoint-initdb.d``:ro`マウントし、起動前処理が`chmod -R a+rX`をかけていたが、git管理下のファイルのパーミッションをrootで書き換えるため、`01_schema.sql`を更新するとホスト側の`git pull`が失敗するようになっていた。スキーマ本体もイメージ側から流す方式にして解消した(コンテナがホスト側で触るのはgit管理外の`db/data`だけ)。
5253

5354
開発環境では、スキーマを変えたら`db/data/`を捨てて作り直すのが手軽(移行スクリプトの検証は下記の使い捨てDBで行う)。
5455

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ docker compose -f docker-compose.dev.yml up --build
4747
```
4848

4949
Node や Postgres をローカルにインストールする必要はない。初回起動時、
50-
`db-migrate` コンテナが `01_schema.sql` を自動実行してテーブルと既定のスポット種別
50+
`init` コンテナが `01_schema.sql` を自動実行してテーブルと既定のスポット種別
5151
(`tourist`=観光地、データは空)を作成する。以降スキーマに変更が入った場合も、
5252
起動するたびに未適用のマイグレーションが自動で当たる。
5353

@@ -136,15 +136,15 @@ docker compose pull && docker compose up -d
136136
docker compose pull && docker compose up -d
137137
```
138138
- 公開されるイメージは2つ。`ghcr.io/rtcode337/travel-log`(アプリ本体)と
139-
`ghcr.io/rtcode337/travel-log-db-init`(DBの準備とマイグレーション適用)
139+
`ghcr.io/rtcode337/travel-log-db-init`(スキーマ・マイグレーション適用。`init`サービスが使う)
140140
- **PostgreSQL 16 時代の`db/data`を持つ既存環境は、更新前に1回だけデータ移行が必要**
141141
([docs/postgres-18-upgrade.md](docs/postgres-18-upgrade.md))。移行せずに起動すると
142142
dbコンテナが起動に失敗する(データは壊れない)
143-
- **DBスキーマの更新は自動**`docker compose up`すると`db-migrate`サービスが未適用の
143+
- **DBスキーマの更新は自動**`docker compose up`すると`init`サービスが未適用の
144144
マイグレーションを順に当ててから`app`を起動する(失敗した場合は`app`も起動しないので
145145
古いスキーマのまま動くことはない)。適用状況は
146146
`docker compose exec db psql -U travel_log -d travel_log -c "select * from schema_migrations"`
147-
ログは`docker compose logs db-migrate`で確認できる
147+
ログは`docker compose logs init`で確認できる
148148
- GitHub Actions(`GITHUB_TOKEN`)から公開したパッケージはリポジトリに自動リンクされ、
149149
可視性もリポジトリと同じ(=public)になるため、追加設定なしで匿名pullできる。
150150
リポジトリをprivateにした場合は本番ホストで`docker login ghcr.io`

db/Dockerfile

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
1-
# DBの準備・マイグレーション適用用のイメージ。
1+
# スキーマ・マイグレーション適用用のイメージ(composeの init サービス)
22
# postgres公式イメージをそのまま使う(psql/pg_isreadyが入っており、
33
# サーバ本体と同じバージョンのクライアントで確実に接続できるため)。
44
#
5-
# 2つの用途を1つのイメージのサブコマンドで賄う(docker-compose.yml参照):
6-
# prepare … db/data の作成と所有者調整(dbサービスの起動前)
7-
# migrate … スキーマ本体と未適用のマイグレーションSQLの自動適用(dbサービスの起動後)
8-
#
95
# mainへのpushでGitHub Actions(.github/workflows/docker-publish.yml)が
106
# ghcr.io/<repo>-db-init として公開する。本番はこのイメージをpullして使う
7+
# (かつては db/data の準備(prepare)も担っていた名残でこのイメージ名。
8+
# サービス名を変えてもイメージ名を変えると GHCR 側の整理が要るため据え置き)
119
FROM postgres:18-alpine
1210

1311
# スキーマ本体とマイグレーションSQLはイメージに焼き込む(本番ホストのリポジトリの
14-
# 新旧に関わらず、pullしたイメージの中身がそのまま適用される)。
15-
# /db 配下ではなく /init・/migrations に置く — prepareでは ./db をbindマウントするため、
16-
# /db 配下だとマウントで隠れてしまう
12+
# 新旧に関わらず、pullしたイメージの中身がそのまま適用される)
1713
COPY init /init
1814
COPY migrations /migrations
1915
COPY entrypoint.sh /usr/local/bin/db-init
2016

2117
RUN chmod +x /usr/local/bin/db-init
2218

2319
ENTRYPOINT ["/usr/local/bin/db-init"]
24-
CMD ["migrate"]

db/entrypoint.sh

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
#!/bin/sh
2-
# DBの準備・マイグレーション適用(db/Dockerfile のENTRYPOINT)。
3-
#
4-
# db-init prepare … db/data の作成と所有者調整。dbサービスの起動前に走る
5-
# db-init migrate … スキーマ本体(/init/01_schema.sql)と /migrations の未適用SQLを
6-
# 適用する。dbサービスの起動後に走る
7-
#
8-
# どちらも1回実行して終了するワンショット(compose側は restart: "no")。
2+
# スキーマ本体(/init/01_schema.sql)と /migrations の未適用SQLの適用
3+
# (db/Dockerfile のENTRYPOINT。composeの init サービス)。
4+
# dbサービスの起動後に1回実行して終了するワンショット(compose側は restart: "no")。
95
set -eu
106

117
MIGRATIONS_DIR="${MIGRATIONS_DIR:-/migrations}"
@@ -15,20 +11,6 @@ SCHEMA_VERSION=000_init_schema
1511
# マイグレーションの同時実行を防ぐためのadvisory lockのキー(任意の定数)
1612
LOCK_KEY=8241973
1713

18-
prepare() {
19-
# db/dataは.gitignore対象でgit管理下になく、cloneした直後は存在しない。dbサービスが
20-
# 直接./db/dataをbindマウントすると、環境によってはホスト側にディレクトリがない状態で
21-
# コンテナ起動そのものが失敗しうるため、ここで先に作ってpostgres(uid/gid 70)に
22-
# 所有者を揃えておく。
23-
# ここで触るのはgit管理外のdb/dataだけにすること — かつてはdb/initにもchmodを
24-
# かけていたが、git管理下のファイルのパーミッションをrootで書き換えるため、
25-
# db/init/01_schema.sqlに変更が入るとホスト側のgit pullが失敗するようになっていた
26-
# (スキーマ本体はdbサービスにマウントせず、migrate側が流す方式に変えて解消した)
27-
mkdir -p /db/data
28-
chown -R 70:70 /db/data
29-
echo "db-init: prepared /db/data"
30-
}
31-
3214
wait_for_db() {
3315
# compose側でdbのhealthcheck完了を待ってから起動する想定だが、
3416
# 単体で起動された場合にも耐えるよう自前でも待つ
@@ -101,11 +83,4 @@ migrate() {
10183
echo "db-init: migrations done (applied=$applied, skipped=$skipped)"
10284
}
10385

104-
case "${1:-migrate}" in
105-
prepare) prepare ;;
106-
migrate) migrate ;;
107-
*)
108-
echo "usage: db-init [prepare|migrate]" >&2
109-
exit 64
110-
;;
111-
esac
86+
migrate

db/migrations/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010

1111
## 適用は自動
1212

13-
`docker compose up`すると`db-migrate`サービス(`db/Dockerfile`のイメージ`migrate`サブコマンド)が
13+
`docker compose up`すると`init`サービス(`db/Dockerfile`のイメージ)が
1414
dbの起動を待って、スキーマ本体と未適用のスクリプトを連番順に当てる。手で流す必要はない。
1515

1616
- 適用済みのリビジョンは`schema_migrations`テーブルに記録され、2回目以降はスキップされる
17-
- `app`サービスは`db-migrate`**正常終了するまで起動しない**(古いスキーマのままアプリが
17+
- `app`サービスは`init`**正常終了するまで起動しない**(古いスキーマのままアプリが
1818
動くのを防ぐ)。マイグレーションが失敗したらアプリも上がらないので、失敗に気づける
1919
- 1本のスクリプトとその適用記録は1トランザクションにまとまっている。途中で失敗すれば
2020
記録も残らないため、スクリプトを直して`docker compose up`し直せばよい
@@ -24,7 +24,7 @@ dbの起動を待って、スキーマ本体と未適用のスクリプトを連
2424
docker compose exec -T db psql -U travel_log -d travel_log -c "select version, applied_at from schema_migrations order by version"
2525

2626
# ログを見る
27-
docker compose logs db-migrate
27+
docker compose logs init
2828
```
2929

3030
## 書き方のルール
@@ -48,7 +48,7 @@ docker compose logs db-migrate
4848
# 1. 旧スキーマのダンプを復元したDBにマイグレーションを当てる
4949
docker compose -f docker-compose.dev.yml exec -T db psql -U travel_log -d postgres -c "create database t_old"
5050
docker compose -f docker-compose.dev.yml exec -T db psql -U travel_log -d t_old < <旧スキーマのダンプ>.sql
51-
docker compose -f docker-compose.dev.yml run --rm -e PGDATABASE=t_old db-migrate
51+
docker compose -f docker-compose.dev.yml run --rm -e PGDATABASE=t_old init
5252

5353
# 2. 最新スキーマで新規作成したDBを用意する
5454
docker compose -f docker-compose.dev.yml exec -T db psql -U travel_log -d postgres -c "create database t_fresh"

docker-compose.dev.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ services:
1111
# 16の頃に必要だったPGDATAの上書き(マウント直下だとドットファイルの存在で
1212
# initdbが初期化を拒否する対策)は、既定がサブディレクトリになったので不要
1313
volumes:
14-
# db/initはマウントしない。スキーマ本体(01_schema.sql)はdb-migrateが流す
14+
# db/initはマウントしない。スキーマ本体(01_schema.sql)はinitサービスが流す
1515
# (git管理下のdb/initをコンテナに触らせるとホスト側のパーミッションが変わり、
1616
# スキーマを更新したときにgit pullが失敗するようになるため)
1717
# マウント先が /var/lib/postgresql/data ではなく1段上なのは本番用と同じ理由
@@ -25,7 +25,7 @@ services:
2525

2626
# スキーマ本体と未適用のマイグレーションを順に自動適用するワンショット(本番と同じ仕組み)。
2727
# 開発では公開イメージをpullせず、db/Dockerfileをその場でビルドして使う
28-
db-migrate:
28+
init:
2929
build:
3030
context: ./db
3131
depends_on:
@@ -49,7 +49,7 @@ services:
4949
context: .
5050
target: dev
5151
depends_on:
52-
db-migrate:
52+
init:
5353
condition: service_completed_successfully
5454
environment:
5555
DATABASE_URL: postgres://travel_log:travel_log@db:5432/travel_log

0 commit comments

Comments
 (0)