PythonアプリのDocker構成入門:Dockerfile、Compose、requirements.txtの役割

pondering female secretary picking folder in workplace
Photo by Andrea Piacquadio on Pexels.com

PythonアプリケーションをDockerで動かすとき、最初につまずきやすいのは、どのファイルに何を書くかという役割分担です。

requirements.txt、Dockerfile、docker-compose.ymlの関係を理解すれば、構成を上から順に読み解けます。

Dockerイメージはアプリケーションの実行環境を組み立てるひな型であり、コンテナはそのイメージから起動する実行単位です。

Pythonパッケージだけを分離したい場合は、venvとDockerの違いを先に確認すると、使い分けが明確になります。

3つの設定ファイルの関係

三つのファイルは同じ設定を重ねて書くものではなく、それぞれ異なる範囲を担当します。

PythonアプリをDockerで動かすための主要ファイル
ファイル 担当すること 読むとわかること
requirements.txt Pythonパッケージの依存関係を記録する アプリケーションが必要とするライブラリ
Dockerfile Dockerイメージの構築手順を定義する ベース環境、ファイル配置、インストール処理、起動方法
docker-compose.yml 複数のサービスをまとめて定義する アプリケーション、Redisなどの接続関係と起動設定

一つのコンテナだけで足りる構成では、まずDockerfileだけで試し、複数サービスが必要になった段階でComposeを加えると理解しやすくなります。

基本のディレクトリ構成

小規模なFlaskアプリケーションを例にすると、プロジェクトの入口は次のように整理できます。

my-python-app/
├── app/
│   ├── __init__.py
│   └── main.py
├── tests/
│   ├── __init__.py
│   └── test_main.py
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
└── README.md

この構成は出発点であり、アプリケーションの規模や採用するフレームワークに合わせて分割方法を変えます。

ディレクトリとファイルの役割
項目 役割
app/ アプリケーションのソースコードを置く
tests/ 動作を確認するテストコードを置く
requirements.txt 必要なPythonパッケージを記録する
Dockerfile イメージを構築する手順を書く
docker-compose.yml 複数サービスの構成をまとめる
README.md 準備、起動、停止、テストなどの手順を共有する

requirements.txtでPythonの依存関係を管理する

requirements.txtには、アプリケーションが利用するPythonパッケージを一行ずつ記述します。

FlaskアプリケーションがHTTP通信にRequestsも使う場合、最小例は次のとおりです。

Flask
requests

バージョンを固定する場合は、パッケージ名の後ろに==とプロジェクトで検証したバージョンを書きます。

特定の古いバージョンを例として固定すると、そのままコピーした読者が古い環境を作るおそれがあるため、採用バージョンは公式情報とアプリケーションの互換性を確認して決めます。

ローカル環境では、次のコマンドで一覧に記載したパッケージをインストールできます。

python -m pip install -r requirements.txt

requirements.txtが担当するのはPythonパッケージであり、Python本体やOS側の環境は次のDockerfileで定義します。

Dockerfileでイメージの構築手順を定義する

Dockerfileは、ベースとなるイメージ、作業場所、依存関係のインストール、ソースコードの配置、起動コマンドを上から順に記述するファイルです。

次の例は、app/main.pyを起動できることを前提にした最小構成です。

FROM python:3-slim

WORKDIR /app

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

COPY app ./app

CMD ["python", "app/main.py"]
Dockerfileの各命令が行うこと
命令 役割
FROM 構築の土台にするイメージを指定する
WORKDIR 後続の処理で使う作業ディレクトリを設定する
COPY 必要なファイルをイメージ内へコピーする
RUN イメージの構築中にコマンドを実行する
CMD コンテナ起動時の既定コマンドを指定する

python:3-slimは構成を説明するための例なので、実際のプロジェクトでは動作確認を済ませたベースイメージのタグを選びます。

Flask以外の選択肢も含めて構成を考える場合は、Pythonフレームワークの比較も参考になります。

docker-compose.ymlで複数のサービスをまとめる

Composeは、WebアプリケーションとRedisのように、複数のコンテナを一つの構成として起動したい場合に使います。

元の例を現在のCompose仕様に合わせて簡潔にすると、次のように書けます。

services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/app
    depends_on:
      - redis

  redis:
    image: redis:alpine
Compose例の主な設定
設定 意味
build: . 現在のディレクトリにあるDockerfileからWeb用イメージを構築する
ports ホスト側のポート5000をコンテナ側のポート5000へ割り当てる
volumes 現在のディレクトリをコンテナ内の/appへ割り当てる
depends_on webより先にredisを起動する依存関係を示す

depends_onの短い書き方は起動順を示しますが、Redisが接続を受け付けられる状態まで待つ設定ではありません。

準備完了を確認してからWebアプリケーションを動かす必要がある場合は、ヘルスチェックと条件付きの依存関係を別に設計します。

このComposeファイルはコンテナ同士の構成を定義するだけなので、Flask側にもRedisへ接続する処理と設定が必要です。

Webアプリケーションがコンテナ内のポート5000で待ち受ける構成であれば、次のコマンドで構築と起動をまとめて実行できます。

docker compose up --build

確認後は、次のコマンドでComposeが管理するコンテナを停止して削除します。

docker compose down

構築を進める順序

  1. app/にアプリケーションを書き、ローカルで起動できることを確認します。
  2. 必要なPythonパッケージをrequirements.txtに記録します。
  3. Dockerfileに依存関係のインストールと起動方法を書きます。
  4. Redisなど別のサービスが必要な場合にdocker-compose.ymlを追加します。
  5. 構築、起動、接続、停止の手順をREADME.mdへ残します。

この順序なら、問題が起きたときにアプリケーション、イメージ構築、複数サービス連携のどこで失敗したかを切り分けやすくなります。

初心者が迷いやすい点

症状から確認場所を絞るための一覧
症状 最初に確認する場所 確認内容
Pythonパッケージを読み込めない requirements.txtとRUN パッケージ名とインストール処理が一致しているか
コンテナがすぐ停止する CMDとアプリケーション 起動するファイルのパスと実行時エラー
ブラウザから接続できない portsとアプリケーション コンテナ内の待受ポートと割り当てが一致しているか
Redisへの接続に失敗する 接続設定と準備完了の判定 サービス名、接続先、起動後に利用可能になるまでの待機

設定ファイルを一度に増やすより、単体で動くアプリケーションから順に確認したほうが、原因を追いやすくなります。

まとめ

requirements.txtはPythonの依存関係、Dockerfileはイメージの構築、docker-compose.ymlは複数サービスの連携を担当します。

まず小さな構成で各ファイルの境界を理解し、データベースやキャッシュなどが必要になった時点でComposeへサービスを追加すると、構成の意図を保ちやすくなります。

株式会社greedenは、PythonやDockerを用いるWebシステムについて、要件整理から設計、実装、テスト、リリース後の保守まで支援しています。構成ファイルの役割を整理し、運用しやすい開発環境を整えたい場合はご相談ください。

投稿者 greeden

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)