コンテンツにスキップ

8.4.5 コンテナ化とデプロイ

  • なぜ LLM アプリが特にコンテナ化に向いているのかを理解する
  • 最小限の Dockerfile の重要な構成を読めるようになる
  • イメージ、コンテナ、ポート、環境変数といった基本概念を理解する
  • 小さな Docker Compose の起動方法を読めるようになる
  • コンテナ化はデプロイの終点ではなく、出発点だと理解する

Docker は、まず名詞を分けると怖さが減ります。

用語初学者向けの意味なぜ重要か
imageパッケージ化された実行テンプレート。レシピ + 食材セットのようなもの先に image を作り、そこから container を起動する
containerimage から作られた実行中のインスタンス実際にリクエストを処理するプロセス
Dockerfileimage を作るための手順書ベース image、依存関係、ファイル、起動コマンドを記録する
portサービスがリクエストを受け取る入口-p 8000:8000 はホスト側ポートとコンテナ側ポートをつなぐ
environment variableコード外から注入する設定API key、モデル名、実行モードをコードに直接書かないため
Compose関連する複数コンテナをまとめて起動する道具ベクトル DB、Redis、Postgres などが必要なときに便利

中心となる考え方は Docker コマンドの丸暗記ではなく、実行環境を再現可能にすることです。


なぜコンテナ化が必要なのか?

Section titled “なぜコンテナ化が必要なのか?”

ローカルスクリプトの最大の落とし穴は何か?

Section titled “ローカルスクリプトの最大の落とし穴は何か?”

ローカルでプロジェクトが動くときは、たいてい多くの暗黙の条件に支えられています。

  • Python のバージョン
  • パッケージのバージョン
  • システム依存関係
  • 環境変数
  • 起動コマンド

これらの条件は、担当者が変わる、マシンが変わる、サーバーが変わるだけで、すぐに問題を起こします。

コンテナ化は何を解決するのか?

Section titled “コンテナ化は何を解決するのか?”

コンテナ化の本質は次のとおりです。

アプリと、それが依存する実行環境をまとめてパッケージ化すること。

こうすることで、次の内容をより安定して再現できます。

  • 何をインストールしたか
  • どのバージョンを使ったか
  • どのコマンドで起動するか

これは LLM アプリにとても重要です。なぜなら、LLM アプリは次のようなものに依存することが多いからです。

  • Web フレームワーク
  • モデルサービス
  • ベクトルデータベース
  • システムツール

イメージとコンテナとは何か?

Section titled “イメージとコンテナとは何か?”
  • イメージ(image):レシピ + 食材セット
  • コンテナ(container):そのレシピで実際に作った一皿

つまり、

  • イメージは静的なテンプレート
  • コンテナは実行中のインスタンス

デプロイでは通常、次の順番で進めます。

  1. まずイメージを build する
  2. それからコンテナを起動する

この順番を理解していないと、後で Docker コマンドを見たときにずっと混乱します。

Docker イメージ、コンテナと Compose デプロイ図


最小限の Dockerfile はどんな形なのか?

Section titled “最小限の Dockerfile はどんな形なのか?”
FROM python:3.14-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]
  • FROM

    • ベースイメージを選ぶ
  • WORKDIR

    • 作業ディレクトリを指定する
  • COPY requirements.txt .

    • 依存ファイルをコピーする
  • RUN pip install ...

    • 依存関係をインストールする
  • COPY . .

    • そのあとプロジェクトコードをコピーする
  • EXPOSE 8000

    • サービスが外部に対して待ち受けるポートを示す
  • CMD

    • コンテナ起動時にデフォルトで実行するコマンド

これが Dockerfile の最も基本的な骨組みです。


まずは本当に動く小さなアプリを用意する

Section titled “まずは本当に動く小さなアプリを用意する”

後の Docker デプロイ例を具体的にするために、まずはとてもシンプルな app.py を書きます。

app.py
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/health":
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({"status": "ok"}).encode())
return
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({"message": "hello from llm app"}).encode())
server = HTTPServer(("0.0.0.0", 8000), Handler)
print("8000 で起動中")
server.serve_forever()

まずローカルで実行します。

Terminal window
python app.py

別のターミナルでサービスを確認します。

Terminal window
curl http://localhost:8000/
curl http://localhost:8000/health

想定出力:

{"message": "hello from llm app"}
{"status": "ok"}

それは、コンテナ化は Dockerfile だけを眺める話ではなく、
実際に動くアプリを中心に理解する必要があるからです。


この最小サービスはサードパーティのパッケージを使わないので、空ファイルでも構いません。
ただし、実際のプロジェクトに近づけるために、ここでは構成だけ残しておきます。

requirements.txt
FROM python:3.14-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8000
CMD ["python", "app.py"]
Terminal window
docker build -t mini-llm-app .
docker run -p 8000:8000 mini-llm-app

そのあと、次の URL にアクセスします。

  • http://localhost:8000/
  • http://localhost:8000/health

すると、返り値を確認できます。

コマンドラインでも確認できます。

Terminal window
curl http://localhost:8000/
curl http://localhost:8000/health

想定出力:

{"message": "hello from llm app"}
{"status": "ok"}

これで、最小限のコンテナ化の流れが完成です。


LLM アプリでは、次のような設定がよく登場します。

  • API Key
  • モデル名
  • ベクトルデータベースのアドレス
  • 実行モード

これらはコードに直接書き込まず、環境変数で渡すほうが適しています。

import os
model_name = os.getenv("MODEL_NAME", "demo-model")
port = int(os.getenv("PORT", "8000"))
print("MODEL_NAME =", model_name)
print("PORT =", port)

追加の環境変数を渡さない場合の想定出力:

MODEL_NAME = demo-model
PORT = 8000
Terminal window
docker run -p 8000:8000 -e MODEL_NAME=qwen-demo mini-llm-app

このステップはとても重要です。実際のデプロイでは、ほぼ必ず設定の注入が必要になるからです。

サービスから設定を返したい場合は、app.pyMODEL_NAME を読み、ルートエンドポイントから返すこともできます。重要なのは、コードは安定させ、設定はイメージの外から変えるという考え方です。


なぜ Compose がよく使われるのか?

Section titled “なぜ Compose がよく使われるのか?”

実際のプロジェクトは 1 つのサービスだけではないから

Section titled “実際のプロジェクトは 1 つのサービスだけではないから”

LLM アプリは、次のようなサービスと組み合わせることがよくあります。

  • Web サービス
  • ベクトルデータベース
  • Redis
  • Postgres

それぞれを手動で docker run すると、すぐに管理が複雑になります。

version: "3.9"
services:
app:
build: .
ports:
- "8000:8000"
environment:
MODEL_NAME: demo-model

起動方法:

Terminal window
docker compose up --build

これが、Compose がローカル開発や小規模デプロイでとても便利な理由です。


コンテナ化はデプロイ完了を意味しない

Section titled “コンテナ化はデプロイ完了を意味しない”

これはとてもよくある誤解です。

コンテナ化が解決するのは「パッケージ化と実行環境」

Section titled “コンテナ化が解決するのは「パッケージ化と実行環境」”

でも、本番運用ではさらに次のことを考える必要があります。

  • ログ
  • ヘルスチェック
  • リソース制限
  • 自動再起動
  • 段階的リリース
  • リバースプロキシ

ヘルスチェックの重要な考え方

Section titled “ヘルスチェックの重要な考え方”

前の例のような

  • /health

というエンドポイントは、とても価値があります。
なぜなら、デプロイシステムは次のことを知る必要があるからです。

このコンテナは今生きているか、リクエストを受けられるか。


すべてを巨大なイメージに詰め込む

Section titled “すべてを巨大なイメージに詰め込む”

イメージがどんどん重くなります。

サービスが壊れても気づけません。

環境が変わるとすぐに問題になります。

コンテナ化したら自動でスケールすると思う

Section titled “コンテナ化したら自動でスケールすると思う”

そうではありません。
コンテナ化は最初の一歩で、その後にオーケストレーション、監視、運用が続きます。

ローカル Docker のディスク使用量を見ない

Section titled “ローカル Docker のディスク使用量を見ない”

build 中に no space left on device が出たら、まず Docker の使用量を確認します。

Terminal window
docker system df
docker builder prune

不要だと分かっているものだけを削除してください。チームのマシンや CI 環境では、いきなり image や volume を消すより、まず build cache を掃除する方が安全です。


このページを終えたら、この証拠カードを残します。

サービス契約
エンドポイント、入力スキーマ、出力スキーマ、エラースキーマ
実行シグナル
レイテンシ、スループット、ログ、ヘルスチェック、またはコンテナ状態
可観測性
request id、trace id、構造化ログ、または metric
失敗確認
タイムアウト、リトライの連鎖、ログ不足、デプロイ不一致
運用アクション
バックオフ、キュー、アラート、段階展開、またはロールバック

この節で最も大事なのは Docker コマンドを暗記することではなく、次を理解することです。

コンテナ化の本質的な価値は、「アプリ + 依存関係 + 起動方法」をまとめて標準化し、デプロイを個人のPCの経験から再現可能な流れへ変えること。

ここをしっかり固めることで、その先のサービス構成や本番運用の土台ができます。


  1. この節の app.py と Dockerfile を使って、ローカルで本当に最小イメージを build してみましょう。
  2. サービスに APP_MODE=dev のような環境変数を追加してみましょう。
  3. 考えてみましょう:なぜ /health エンドポイントがデプロイシステムにとって重要なのでしょうか?
  4. 自分の言葉で説明してみましょう:なぜコンテナ化はデプロイの終点ではなく、出発点なのでしょうか?
参考実装と解説
  1. build 結果は、安定して起動し、期待する port と health endpoint を公開する image であるべきです。
  2. APP_MODE は環境変数から読み、コード変更なしで config や log に反映します。
  3. /health は、デプロイシステムが traffic を流すか、container を再起動するか、rollback するかを判断する材料になります。
  4. container は runtime をまとめますが、deployment には secrets、scaling、logs、monitoring、storage、networking、security、release process がまだ必要です。