Skip to content

Commit 34da9d1

Browse files
committed
bindマウントを短い書き方にそろえ、置き場が無くても起動できるようにする
NASのコンテナマネージャーで、置き場のディレクトリを作っていないとマウントに 失敗して起動できなかった。長い書き方(type: bind)は create_host_path の既定が かつて false で、短い書き方("ホスト:コンテナ")でだけ暗黙に true だったため。 新しいDockerは既定が true なので手元では再現せず、NAS側でだけ出ていた。 短い書き方は1文字列なので、マウント先が分かれているとアンカーを共有できない (YAMLは文字列を連結できない)。そこでdbも他と同じ /data にマウントし、PGDATAを /data/db/18/docker へ移した。空く /var/lib/postgresql (postgresイメージのVOLUME宣言) には tmpfs を当てて匿名ボリュームの量産を止める。 - ホスト側のディレクトリ構成は変わらない(data/db/18/docker のまま)。 既存環境の再移行は不要 - 3つのcomposeとも同じ形にそろえた 動作確認: 既存クラスタで Skipping initialization → 起動、親ディレクトリが無い 状態からの起動(Dockerが親を作り、initが3つを作って所有者を揃える)、dbコンテナの マウントが bind /data の1つだけで匿名ボリュームが増えないこと。 NAS側でも、この書き方で落ちずに起動することを確認済み。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent b01a73b commit 34da9d1

4 files changed

Lines changed: 34 additions & 27 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ LAN内の別端末から開発サーバを開くときは`ALLOWED_DEV_ORIGINS`(`
2222

2323
3つのcomposeファイル(`docker-compose.yml`=本番用 / `docker-compose.dev.yml`=開発用 / `docker-compose.standalone.example.yml`)はどれもプロジェクト名を`travel-log`に揃えてある。同じホスト上で本番用と開発用を**同時に**は動かせない(ポート7040も`data`も共有しているため、名前を分けても同時起動はできない)。切り替えるときは先に`docker compose -f <今動いている方> down`すること。
2424

25-
`docker-compose.standalone.example.yml`は、`.env`もリポジトリのクローンも置けない環境(NASのコンテナマネージャー等、管理画面にYAMLを貼り付けて起動するタイプ)向けの単体定義の雛形。`docker-compose.yml`との違いは「`${...}`を使わず値を直書きする」「bindマウントを絶対パスで書く」の2点だけで、サービス構成・起動順は同じ。**`docker-compose.yml`側のサービス・環境変数を変えたら、standalone側にも同じ変更を反映すること**(値の直書きぶん古くなりやすい)。**リポジトリに置くのは`.example`の付いた雛形だけ**で、実値を入れてコピーした`docker-compose.standalone.yml``.gitignore`してある(`.env.example``.env`の関係と同じ。この形式は`SESSION_SECRET`等を直書きするので、雛形を直接編集すると秘密がコミット対象に入る)。
25+
`docker-compose.standalone.example.yml`は、`.env`もリポジトリのクローンも置けない環境(NASのコンテナマネージャー等、管理画面にYAMLを貼り付けて起動するタイプ)向けの単体定義の雛形。`docker-compose.yml`との違いは「`${...}`を使わず値を直書きする」「bindマウントを絶対パスで書く」の2点だけで、サービス構成・起動順は同じ。**`docker-compose.yml`側のサービス・環境変数を変えたら、standalone側にも同じ変更を反映すること**(値の直書きぶん古くなりやすい)。**bindマウントは短い書き方(`"ホスト:コンテナ"`の1文字列)で書く。** 長い書き方(`type: bind`)は**`create_host_path`の既定がかつてfalseだった**(短い書き方でだけ暗黙にtrue)ため、古いDockerでは置き場がホストに無いとマウントできずに落ちる —— 新しい版は既定がtrueなので**手元では再現せず、NASでだけ出る**(実際にNASで落ち、短い書き方に直したら起動した)。この形を保つために、**3つのサービスとも置き場を同じ`/data`にマウントする**(1本のアンカーを共有できる。YAMLは文字列を連結できないので、ターゲットが分かれると短い書き方では書けない)。dbだけは`PGDATA`を`/data/db/18/docker`に移してそこへ寄せてあり、代わりに空く`/var/lib/postgresql`(postgresイメージの`VOLUME`宣言)には`tmpfs`を当てて匿名ボリュームの量産を止めている。
26+
27+
**リポジトリに置くのは`.example`の付いた雛形だけ**で、実値を入れてコピーした`docker-compose.standalone.yml``.gitignore`してある(`.env.example``.env`の関係と同じ。この形式は`SESSION_SECRET`等を直書きするので、雛形を直接編集すると秘密がコミット対象に入る)。
2628

2729
このプロジェクトにアプリコードのテストスイート/テストコマンドは存在しない(唯一のテストは`scripts/bootstrap-sql_test.sh`で、Supabase向けの一括SQLがアプリの起動時の適用と同じスキーマを作るかを突き合わせるもの。`db/migrations/README.md`参照)。リンターも未導入(Next.js 16で`next lint`が廃止された際、代替のESLint導入は見送った — eslint-config-nextの依存チェーンに未修正のbrace-expansion脆弱性(GHSA-mh99-v99m-4gvg)が含まれ、導入するとDependabotの高深刻度アラートが解消不能な形で付くため。エコシステム側の修正後に導入を検討する)。型チェックは`next build`が行う。
2830

docker-compose.dev.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ services:
99
POSTGRES_DB: travel_log
1010
# データの置き場をdata/1つにまとめ、その下をdb/photos/exportsに切っている
1111
# (本番と同じ形。docker-compose.ymlのdbサービスのコメント参照)
12-
PGDATA: /var/lib/postgresql/db/18/docker
12+
PGDATA: /data/db/18/docker
1313
volumes:
1414
# db/initはマウントしない。スキーマ本体(01_schema.sql)はinitサービスが流す
1515
# (git管理下のdb/initをコンテナに触らせるとホスト側のパーミッションが変わり、
1616
# スキーマを更新したときにgit pullが失敗するようになるため)
17-
# マウント先が /var/lib/postgresql/data ではなく1段上なのは本番用と同じ理由
18-
# (docker-compose.ymlのdbサービスのコメント参照)
19-
- ./data:/var/lib/postgresql
17+
# マウント先とPGDATAの決め方は本番用と同じ(docker-compose.ymlのコメント参照)
18+
- ./data:/data
19+
tmpfs:
20+
- /var/lib/postgresql
2021
healthcheck:
2122
test: ["CMD-SHELL", "pg_isready -U travel_log"]
2223
interval: 5s

docker-compose.standalone.example.yml

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ name: travel-log
3434
# <置き場>/db … Postgresの実データ(実体は db/18/docker)
3535
# <置き場>/photos … 訪問記録の写真。DBには相対パスだけが入るのでDBと一緒にバックアップする
3636
# <置き場>/exports … エクスポートのZIP。作り直せるのでバックアップは要らない
37-
# 親ディレクトリだけは**あらかじめ作っておくこと**(bindマウント先を自動作成しない環境がある)。
38-
x-data-dir: &data-dir "/path/to/travel-log/data"
37+
# 無ければDockerが作る。それも効かない環境では、この親だけを先に作っておくこと
38+
# —— 下の3つは init が作るので、用意するのは常にこの1つで足りる。
39+
# 書式は「ホスト側の絶対パス:コンテナ側」。3つのサービスが同じ場所を同じ /data に
40+
# マウントするので、1本のアンカーを共有できる
41+
x-data-volume: &data-volume "/path/to/travel-log/data:/data"
3942

4043
# ---- ここだけ編集: 実行ユーザー ----
4144
# 上のディレクトリをホスト側で読み書きするユーザーの `id -u`:`id -g`。
@@ -71,9 +74,7 @@ services:
7174
image: ghcr.io/rtcode337/travel-log:latest
7275
user: root
7376
volumes:
74-
- type: bind
75-
source: *data-dir
76-
target: /data
77+
- *data-volume
7778
entrypoint:
7879
- sh
7980
- -c
@@ -94,16 +95,18 @@ services:
9495
POSTGRES_DB: travel_log
9596
# 置き場を1つにまとめ、その下をdb/photos/exportsに切っている。既定
9697
# (/var/lib/postgresql/18/docker)のままだとマウント直下に18/が来て
97-
# photos・exportsと同じ段に並ぶので、db/の下へ寄せる
98-
PGDATA: /var/lib/postgresql/db/18/docker
98+
# photos・exportsと同じ段に並ぶので、db/の下へ寄せる。他のサービスと
99+
# 同じ /data にマウントするため、既定の位置からも移している
100+
PGDATA: /data/db/18/docker
99101
volumes:
100-
# db/init はマウントしない。スキーマ本体(01_schema.sql)は init サービスが流す。
101-
# postgres:18 はVOLUME宣言が /var/lib/postgresql なので、そこをマウントしないと
102-
# 匿名ボリュームが毎回作られる。置き場をそのままここへ当て、実データは上の
103-
# PGDATAで <置き場>/db/18/docker に入れる
104-
- type: bind
105-
source: *data-dir
106-
target: /var/lib/postgresql
102+
# db/init はマウントしない。スキーマ本体(01_schema.sql)はappが起動時に流す
103+
- *data-volume
104+
# postgres:18 は VOLUME /var/lib/postgresql を宣言しており、そこに何も
105+
# マウントしないと起動のたびに空の匿名ボリュームが作られて溜まっていく。
106+
# 実データは上のPGDATA(=/data配下)に置くのでここは使わない —— 使い捨ての
107+
# tmpfsを当てて抑止する
108+
tmpfs:
109+
- /var/lib/postgresql
107110
healthcheck:
108111
test: ["CMD-SHELL", "pg_isready -U travel_log"]
109112
interval: 5s
@@ -134,9 +137,7 @@ services:
134137
volumes:
135138
# 置き場ごとマウントして、中のどれを使うかは上のPHOTOS_DIR/EXPORTS_DIRで決める。
136139
# db/も見えるが、postgresがPGDATAを0700で作るのでappのユーザーからは読めない
137-
- type: bind
138-
source: *data-dir
139-
target: /data
140+
- *data-volume
140141
# 写真とエクスポートをホストのユーザー所有で書く。所有者合わせは init が済ませる
141142
user: *run-as
142143
ports:

docker-compose.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,19 @@ services:
3030
# データの置き場をdata/1つにまとめ、その下をdb/photos/exportsに切っている。
3131
# 既定(/var/lib/postgresql/18/docker)のままだとマウント直下に18/が来て
3232
# photos・exportsと同じ段に並ぶので、db/の下へ寄せる
33-
PGDATA: /var/lib/postgresql/db/18/docker
33+
# 他のサービスと同じ /data にマウントするため、既定の位置からも移している
34+
PGDATA: /data/db/18/docker
3435
volumes:
3536
# db/initはマウントしない。スキーマ本体(01_schema.sql)はinitサービスが流す
3637
# (git管理下のdb/initをコンテナに触らせるとホスト側のパーミッションが変わり、
3738
# スキーマを更新したときにgit pullが失敗するようになるため)
38-
# postgres:18のイメージはVOLUME宣言が /var/lib/postgresql なので、そこを
39-
# マウントしないと匿名ボリュームが毎回作られる。データの置き場(data/)を
40-
# そのままここへ当て、実データは上のPGDATAで data/db/18/docker に入れる。
4139
# 所有者はpostgresのエントリポイント(rootで起動して自分で揃える)が調整する
42-
- ./data:/var/lib/postgresql
40+
- ./data:/data
41+
# postgres:18は VOLUME /var/lib/postgresql を宣言しており、そこに何もマウント
42+
# しないと起動のたびに空の匿名ボリュームが作られて溜まっていく。実データは上の
43+
# PGDATA(=/data配下)に置くのでここは使わない —— 使い捨てのtmpfsを当てて抑止する
44+
tmpfs:
45+
- /var/lib/postgresql
4346
healthcheck:
4447
test: ["CMD-SHELL", "pg_isready -U travel_log"]
4548
interval: 5s

0 commit comments

Comments
 (0)