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?

マルチステージビルドで積み替えても、3MBしか減らなかった話

0
Last updated at Posted at 2026-07-11

2382.png

🍞sales_data_appのDockerfileを実際に書き換えて、Before/Afterを計測したら分かったこと

はじめに

現役のトラックドライバーとして7年以上現場を走りながら、Pythonを独学しているtosane932です。(学習時間は現在*125時間)

これまで「積載ミス」や「防御的プログラミング×停止距離」など、物流の現場経験をベースにした技術記事を書いてきました。今回は自作アプリ「sales_data_app」のDockerfileを実際に書き換えて、マルチステージビルドを導入する前と後で、イメージサイズが本当に変わるのかを計測してみた記録です。

先に結論を書いておくと、期待していたほど劇的には変わりませんでした。ただ、その「なぜ減らなかったのか」を調べる過程で得られた学びの方が、今回は収穫が大きかったです。

🐋今までのDockerfile

sales_data_appは Flask + PostgreSQL + Gemini API という構成のアプリです。今までのDockerfileはこんな感じでした。

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]

シンプルですが、ビルドに必要な道具(コンパイラなど)まで、本番で動かす環境にそのまま残ってしまう構成です。

これを物流に例えると、 「工場の重い工具箱や梱包資材のゴミまで、本番の高速道路を走るトラックに積みっぱなしにしている」 状態に近いと思っています。荷物を作るための道具は、荷造り場に置いてくればいいのに、トラックにそのまま載せて走っているイメージです。

📦マルチステージビルドという「積み替え」

そこで導入したのが、マルチステージビルドです。

# ステージ1: 荷造り場(builder)
FROM python:3.12-slim AS builder
WORKDIR /app

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

COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

# ステージ2: 本番トラック
FROM python:3.12-slim
WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends \
    libpq5 \
    && rm -rf /var/lib/apt/lists/*

COPY --from=builder /root/.local /root/.local
COPY . .

ENV PATH=/root/.local/bin:$PATH

EXPOSE 5000
CMD ["python", "app.py"]

考え方はシンプルです。

  • ステージ1(荷造り場):パッケージのビルドに必要な道具(gcclibpq-dev)を用意して、荷造り(pip install)を終わらせる
  • ステージ2(本番トラック):荷造り場から完成した荷物だけ/root/.local以下)を積み替えて、本番の環境には道具箱(gcc一式)を持ち込まない

こうすることで、本番用のイメージには「動かすために本当に必要なもの」だけが載る、という設計です。

📊実際に計測してみた

docker images コマンドで、Before/Afterのサイズを比較しました。

CONTENT SIZE
Before(シングルステージ) 212MB
After(マルチステージ) 209MB

削減できたのは、わずか 3MB でした。

正直、記事のタイトルに「劇的に軽量化した」と書きたい気持ちもありましたが、事実ベースで書くと決めているので、そのまま報告します。

❓なぜ劇的に減らなかったのか

原因を調べてみると、requirements.txt の中身に理由がありました。

sales_data_appが使っている psycopg2-binary は、その名の通りあらかじめコンパイル済みのバイナリ(wheel形式)として配布されているパッケージです。つまり、ビルド時にgccのようなコンパイラを用意しなくても、コンパイル済みのファイルをダウンロードしてくるだけで動いてしまいます。

今回のケースでは、そもそも「重い工具箱(gcc、libpq-dev)」を用意すること自体、あまり意味がなかった可能性が高いです。マルチステージビルドの効果がはっきり出るのは、psycopg2(binaryではない方)のようにその場でソースコードからコンパイルする必要があるパッケージを使っている場合だと考えられます。

マルチステージビルドは「魔法のように必ずサイズが減る技術」ではなく、「依存パッケージの性質によって効果が変わる技術」 という、実際にやってみないと分からなかった学びでした。

🔐副産物:API keyの扱い方を見直すきっかけになった

書き換えたイメージを起動したところ、Gemini APIキーが認識されず、エラーになりました。

[ERROR] GEMINI_API_KEY is missing from environment variables.

原因は単純で、ホスト側の .env ファイルは自動的にコンテナに渡らない、という基本的な仕様でした。

docker run -p 5000:5000 --env-file .env sales-after

これで解決したのですが、この一件で改めて意識したのが、「イメージ本体」と「秘密の鍵(APIキー)」は別物として扱うべきという考え方です。

Dockerイメージは、GitHubなどと同じように共有・配布されうるものです。もしAPIキーをイメージの中に焼き込んでしまうと、

  • イメージを誰かに渡した瞬間、APIキーも一緒に渡ってしまう
  • docker history などでレイヤーの中身を覗かれる可能性がある

といったリスクがあります。これは、GitHubに.envを含めないよう.gitignoreで除外するのと、根っこは全く同じ考え方です。「コードや入れ物」と「秘密の鍵」を分離するという原則を、Dockerでも改めて実践する形になりました。

まとめ

  • マルチステージビルドを導入したが、今回のケースではイメージサイズの削減は3MBにとどまった
  • 理由は、使用しているパッケージ(psycopg2-binary)がすでにコンパイル済みだったため
  • マルチステージビルドの効果は、依存パッケージの性質に左右される
  • 副産物として、APIキーとイメージを分離して扱う重要性を再確認できた

次回は、今回の作業と並行して導入を進めているpytestとCI/CDについて、実際につまずいたポイントを交えて書く予定です。


※(この記事は実際の計測結果に基づいて書いています。環境やパッケージ構成によって結果は変わる可能性があります。)

GitHubはこちらをご覧ください👇

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?