Docker と Podman の両方で動く compose 環境を構築するためのパターンと Tips。
ベースの compose.yml は Docker 互換で書き、Podman 固有の設定(userns_mode 等)は compose.override.podman.yml に分離する。
project/
├── compose.yml # Docker 互換(ベース)
└── compose.override.podman.yml # Podman 固有の設定
Docker 環境:
docker compose upPodman 環境:
podman-compose -f compose.yml -f compose.override.podman.yml up毎回 -f を指定したくない場合は override ファイルをコピーする:
cp compose.override.podman.yml compose.override.yml
podman-compose upcompose.override.yml は Docker Compose / podman-compose が自動的に読み込むファイル名のため、コピーするだけで Podman 設定が適用される。
compose.override.ymlは.gitignoreに追加しておく。各環境で使う override ファイルが異なるため、リポジトリには含めない。
compose.yml (ベース):
services:
app:
build: .
volumes:
- .:/app:rw
- node_modules:/app/node_modules:rw
init: true
volumes:
node_modules: {}compose.override.podman.yml (Podman 固有):
services:
app:
userns_mode: "keep-id"
volumes:
- .:/app:rw
- node_modules:/app/node_modules:rw,Uポイント:
- ベースの
compose.ymlは Docker/Podman 共通で使える最小構成にする userns_mode: "keep-id"で UID マッピングを設定- named volume に
:Uフラグを追加(volume の所有者をコンテナ内ユーザーに自動修正)
Docker と Podman の両方でパーミッション問題なく動く Dockerfile の書き方:
-
依存ディレクトリに
chmod -R a+rwXを適用する。named volume は初回作成時にイメージ内のパーミッションを引き継ぐため、ここで設定した権限が volume にも反映されるRUN npm ci && \ chmod -R a+rwX node_modules -
任意 UID 用のホームディレクトリを作成する。コンテナが任意の UID で実行される場合、ホームがないとツールが正常に動作しないことがある
ENV HOME=/home/app RUN mkdir -p /home/app && chmod 777 /home/app
-
Podman 環境では
userns_modeを使用する。ホスト UID をコンテナ内にそのままマッピングし、userディレクティブは不要# compose.override.podman.yml services: app: userns_mode: "keep-id"
.venvやnode_modulesをバインドマウントすると UID マッピングの違いでパーミッション問題が発生しやすい- named volume はコンテナ内部で管理されるため、ホストの UID マッピングの影響を受けない
services:
app:
volumes:
- .:/app:rw # source code (bind mount)
- node_modules:/app/node_modules:rw # deps (named volume)
volumes:
node_modules: {}Podman の named volume に :U フラグを指定すると、volume 内のファイル所有者がコンテナ内ユーザーに自動修正される:
# compose.override.podman.yml
services:
app:
volumes:
- .:/app:rw
- node_modules:/app/node_modules:rw,U
:Uは Podman 固有のフラグのため、Docker 用のcompose.ymlには記載せず Podman 用の override ファイルにのみ記載する。
-
init: trueを常に指定する: PID 1 が init でない場合、SIGTERMでコンテナが停止しないことがある。init: trueで tini が PID 1 として動作し、シグナルを適切に転送するservices: app: init: true
-
環境変数ファイルの管理:
env.exampleをテンプレートとしてリポジトリに含め、利用時に.envにコピーする。.envは Docker Compose / podman-compose が自動で読み込むため起動時のオプション指定は不要。秘密情報を含むため.gitignoreに追加するcp env.example .env # .env を編集して値を設定 -
特権ポートの制限: rootless では 1024 未満のポートにバインドできない。ホスト側のポートを 1024 以上に変更してマッピングする
services: web: ports: - "127.0.0.1:8080:80"
-
バインドアドレスの明示: ポート指定でホストアドレスを省略すると
0.0.0.0(全インターフェース)にバインドされる。NIG スパコンのようなマルチユーザー環境ではセキュリティ上127.0.0.1を明示するports: # NG: 全インターフェースに公開される - "8080:80" # OK: localhost のみ - "127.0.0.1:8080:80"