0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

開発用と本番用のDockerイメージを分ける――マルチステージビルドと非root実行

0
Last updated at Posted at 2026-09-09

npm run buildで生成したdistやpipで入れたパッケージは、ローカルPCに作られるものだと思っていました。実際にはビルド中のステージ内に作られます。この違いからCOPY --fromの役割を整理します。

FROMごとに新しいステージが始まる

マルチステージビルドでは、1つのDockerfileに複数のFROMを書きます。

FROM node:24 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html

/app/distbuilder内にあります。次のFROMで始まるproductionには存在しないため、COPY --from=builderでコピー元を明示します。

最終イメージへ渡るのはdistだけです。node_modulessrc、Node.js、npmは別途コピーしない限り入りません。distはそれらの複製ではなく、生成されたHTML、JavaScript、CSSなどの配信用成果物です。

Pythonでは仮想環境を成果物として渡せる

Pythonパッケージも同じ考え方です。次は、ビルド用・開発用・本番用を分けた例です。

# syntax=docker/dockerfile:1
FROM python:3.12-slim-bookworm AS base

ENV VIRTUAL_ENV=/opt/venv \
    PATH="/opt/venv/bin:$PATH"
WORKDIR /app

FROM base AS builder

RUN apt-get update \
    && apt-get install -y --no-install-recommends build-essential libpq-dev \
    && rm -rf /var/lib/apt/lists/*

RUN python -m venv "$VIRTUAL_ENV"
COPY requirements.txt .
RUN python -m pip install --no-cache-dir -r requirements.txt

FROM builder AS development

COPY requirements-dev.txt .
RUN python -m pip install --no-cache-dir -r requirements-dev.txt
COPY . .
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

FROM base AS production

RUN apt-get update \
    && apt-get install -y --no-install-recommends libpq5 \
    && rm -rf /var/lib/apt/lists/* \
    && groupadd --system --gid 10001 app \
    && useradd --system --uid 10001 --gid app \
       --no-create-home --no-log-init app

COPY --from=builder /opt/venv /opt/venv
COPY --chown=app:app . .

USER app
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "config.wsgi:application"]

builderでは/opt/venvへ依存パッケージを入れ、productionには仮想環境だけをコピーしています。

site-packages/usr/local/binを個別にコピーする方法もありますが、ライブラリ本体とコマンドをまとめた専用の仮想環境の方が追いやすくなります。

Pythonパッケージだけでは動かない場合がある

Pythonパッケージをコピーしても、OS側の依存関係は移動しません。例ではbuilderlibpq-devをコンパイル時に使い、本番側にはlibpq5だけを入れています。C拡張がある場合は、次も確認します。

  • ビルド側と本番側のPythonバージョンやCPUアーキテクチャに互換性がある
  • 必要な共有ライブラリが本番側に存在する
  • Debian系とAlpine系など、互換性のない環境を安易にまたがない

どのステージをイメージにするか

ターゲットを指定しなければ、最後のproductionが完成イメージになります。開発用は明示します。

docker build --target development -t myapp-dev .
docker build --target production -t myapp-prod .

2イメージが自動作成されるわけではありません。BuildKitは、ターゲットが依存しないステージを基本的に処理しません。

本番プロセスを非rootで動かす

useraddでユーザーを作り、COPY --chownで所有者を合わせ、USER appへ切り替えています。以降のCMDapp権限です。

非rootなら、脆弱性を突かれた場合のコンテナ内の権限を制限できます。ただし完全な隔離ではなく、マウントやLinux capabilitiesなども別途管理が必要です。

まとめ

  • FROMごとに新しいビルドステージとファイルシステムが始まる
  • ステージ間で必要な成果物だけを渡すのがCOPY --from
  • Pythonでは仮想環境をまとめてコピーできるが、OSの実行時依存関係も必要
  • --targetで開発用と本番用を個別にビルドする
  • 本番では所有権を整え、USERで非rootユーザーへ切り替える

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?