DockerfileのCMDとENTRYPOINT、Composeのcommandを見ていると、結局何が実行されるのか分からなくなりました。起動コマンドが決まる段階と、Djangoが初期化される段階を分けて追います。
CMDは既定値、ENTRYPOINTは実行本体
まずはexec形式で考えると整理しやすくなります。
CMD ["python", "app.py"]
docker run myappならpython app.pyです。docker run myapp bashと指定すると、CMD全体がbashへ置き換わります。
一方、次の組み合わせではENTRYPOINTが実行本体、CMDが既定の引数になります。
ENTRYPOINT ["python"]
CMD ["app.py"]
実行結果は次のとおりです。
| 実行方法 | 実際のコマンド |
|---|---|
docker run myapp |
python app.py |
docker run myapp test.py |
python test.py |
ENTRYPOINT ["python", "app.py"]だけなら、docker run myapp --debugはpython app.py --debugです。WebアプリではCMDだけでも十分なことが多く、実行本体を固定するCLIでは両者の組み合わせが使えます。
Composeはイメージの既定値を上書きする
ComposeのcommandはCMDに相当する既定値を、entrypointはENTRYPOINTを上書きします。
commandはイメージのSHELL設定内で自動実行されません。&&などを使うならシェルを明示します。
services:
web:
build: .
command:
- /bin/sh
- -c
- |
python manage.py migrate &&
exec python manage.py runserver 0.0.0.0:8000
/bin/sh -cが文字列を解釈し、migrateが成功した場合だけrunserverへ進みます。execはシェルをサーバープロセスへ置き換え、停止シグナルを受け取りやすくします。
manage.pyはDjango管理コマンドの入口
manage.pyはDjango管理コマンドの共通の入口です。
python manage.py shell
python manage.py migrate
python manage.py runserver
Djangoプロジェクト作成時のmanage.pyには、概ね次の処理があります。
import os
import sys
def main():
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()
DJANGO_SETTINGS_MODULEは使用するsettingsを伝えます。Composeのenv_fileなどですでに値があれば、setdefault()は上書きしません。execute_from_command_line()は渡された管理コマンドを実行し、Djangoは設定とアプリを初期化します。
通常のPythonシェルとの違い
単にpythonを起動してもDjangoは初期化されません。python manage.py shellなら初期化済みの対話シェルが起動し、Django 5.2ではインストール済みアプリのモデルも既定で自動インポートされます。
migrateとrunserverは別プロセス
先ほどのCompose設定では、Djangoが1回だけ初期化されるわけではありません。
python manage.py migrate
→ Python起動 → Django初期化 → migrate実行 → 終了
python manage.py runserver
→ 別のPython起動 → Django初期化 → 開発サーバー起動
1つ目のsettingsやメモリ上の状態を2つ目が引き継ぐわけではありません。共通するのは環境変数、ファイル、データベースなどです。
この構成はローカル開発向けです。runserverは本番用ではなく、複数コンテナが各自migrationを実行する設計にも注意が必要です。
まとめ
-
CMDは上書き可能な既定コマンド・引数として使われる -
ENTRYPOINTとCMDを組み合わせると、実行本体と既定引数を分けられる - Composeの
commandでシェル機能を使う場合は、/bin/sh -cを明示する -
manage.pyはsettingsを指定し、Django管理コマンドを実行する入口 -
migrateとrunserverは別々のPythonプロセスとしてDjangoを初期化する