前回の記事にて、個人アプリをDjangoを4.1にアップデートするついでに、DBをPostgreSQLからSQLiteへと移行しました。
https://thinkami.hatenablog.com/entry/2022/09/14/215942
DBをSQLiteに移行したことにより、DjangoアプリをDockerの1コンテナで起動できそうでした。
そこで、WSL2上のDockerに既存のDjangoアプリを載せてみたため、メモを残します。
目次
- 環境
- 準備
- ビルド
- Dockerコンテナを起動
- Dockerコンテナの中身を確認
- 動作確認
- Dockerコンテナの停止と削除
- その他やったこと
- ソースコード
環境
- WSL2 Ubuntu 22.04.1 LTS
- Docker上の環境
- 開発環境
- Windows11上
- JetBrains Gateway 2022.2.2 RC
- WSL2上
- PyCharm 2022.2.1
- Windows11上
- アプリのSQLiteの状態
ringo.db
にデータを保存済- マイグレーションも適用済
準備
Dockerfileの作成
今回、Dockerで動作させるDjangoアプリは、WSL2上にあるDjangoアプリのソースコードを COPY
して使うことにします。
あとは、Dockerの中で SQLite を使うので、 sqlite3
パッケージを追加します。
FROM python:3.10.7-slim # Djangoアプリの8000番ポートを公開するおしらせ EXPOSE 8000 # 必要なライブラリをインストール # 実行時に `debconf: delaying package configuration, since apt-utils is not installed` が出るが、無視してよさそう RUN apt-get update -qq \ && apt-get install -y --no-install-recommends sqlite3 \ && apt-get -y clean \ && rm -rf /var/lib/apt/lists/* # working directoryの設定 WORKDIR /app # リポジトリのファイルをコピー COPY . /app # パッケージインストール RUN pip install --no-cache-dir -r requirements.txt # 起動 CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
.dockerignore ファイルを用意
今回、WSL2上のリポジトリのファイル群をDocker内に COPY
しています。
ただ、venv環境のファイルや .env
ファイルなどはコピーしてほしくありません。
そこで、 .dockerignore
ファイルを用意し、Dockerにコピーしてほしくないファイルを指定しました。
- .dockerignore ファイル | Dockerfile リファレンス — Docker-docs-ja 20.10 ドキュメント
- .dockerignore アンチパターン - Qiita
- .dockerignoreが効かない?.gitignoreとは書き方が違うよ! - Qiita
以下が今回の .dockerignore
ファイルです。
# .env .env .env_example # git .git .gitignore .idea # Docker Dockerfile .dockerignore # DB dumps dump.json latest.dump # venv env env_3_10_7 env_3_10_7_1 # pyenv .python-version # pytest .pytest_cache conftest.py pytest.ini # docs *.md
Dockerfileを書く上でのメモ
Dockerfileでは apt ではなく apt-get を使う
apt
を指定すると、Dockerイメージのビルド時に以下の警告が出ます。
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
apt(8)の以下の記載にあるように、スクリプトでは apt-get
を使ったほうが良いようです。
apt(8) コマンドラインはエンドユーザ向けツールとして設計されています。動作はバージョン間で変更される可能性があります。後方互換性を損なうことのないようには努めますが、変更がインタラクティブな使用に有益と思われる場合には、その保証はありません。
apt(8) のすべての機能は、apt-get(8) や apt-cache(8) など専用の APT ツールで利用可能です。apt(8) は、単にいくつかのオプションのデフォルト値を変更します (apt.conf(5) の特にバイ ナリ範囲を参照)。可能な限り下位互換性を保つように、スクリプトでは (潜在的に有効になっているいくつかの追加オプションをつけて) コマンドを使うべきです。
https://manpages.ubuntu.com/manpages/jammy/ja/man8/apt.8.html
そのため、Dockerfile内でも apt-get
を使うようにします。
警告: debconf: delaying package configuration, since apt-utils is not installed
上記と同じく、Dockerイメージをビルドしている時に
debconf: delaying package configuration, since apt-utils is not installed
という警告が出ました。
調べてみたところ、以下の記事にある通り、無視しても良さそうでした。
Dockerビルド時のエラーメッセージ debconf: delaying package configuration, since apt-utils is not installed | かきノート
そこで、今回は個人アプリということもあり、無視することにしました。
EXPOSEでDjangoアプリを起動するポートを指定
Dockerの公式ドキュメントには
EXPOSE 命令だけは、実際にはポートを 公開しません。これは、どのポートを公開する意図なのかという、イメージの作者とコンテナ実行者の両者に対し、ある種のドキュメントとして機能します。コンテナの実行時に実際にポートを公開するには、 docker run で -p フラグを使い、公開用のポートと割り当てる( マップする)ポートを指定します。
とあります。
今回のDocker上のDjangoアプリは 8000
ポートで起動するため、 EXPOSE 8000
を指定しておきます。
apt-get install の --no-install-recommends について
今回 apt-get install
時に、以下の記事を参考にして --no-install-recommends
を追加しています。
デフォルトだと recommends しているだけの必須ではないパッケージも一緒に入って時間がかかるので --no-install-recommends をつけるのが常套手段
ビルド用ライブラリは追加してないので、 apt-get remove は不要
今回のDjangoアプリではビルド用ライブラリを追加していないため、 apt-get remove
は不要でした。
aptキャッシュはクリーンにする
Dockerの公式ドキュメントの「Dockerfile のベスト・プラクティス」には以下の記載がありました。
apt キャッシュをクリーンアップし /var/lib/apt/lists を削除するのは、イメージ容量を小さくするためです。そもそも apt キャッシュはレイヤー内に保存されません。RUN 命令は apt-get update から始めているので、 apt-get install の前に必ずパッケージのキャッシュが更新されます。
注釈 公式の Debian と Ubuntu のイメージは 自動的に apt-get clean を実行する ので、明示的にこのコマンドを実行する必要はありません。
そこで、今回は
&& apt-get -y clean \ && rm -rf /var/lib/apt/lists/*
を追加しています。
なお、今回のベースイメージが slim
なこともあり、念のため apt-get clean
を実行しています。
ちなみに、 apt-get clean
は以下の挙動のようです。
The same as above, except it removes all packages from the package cache. This may not be desirable if you have a slow Internet connection, since it will cause you to redownload any packages you need to install a program.
The package cache is in /var/cache/apt/archives .
...
pip installで no-cache-dir してキャッシュを使わないようにする
以下にあるように、 pip install する時に --no-cache-dir
を指定して、キャッシュを使用しないようにしました。
python - What is pip's --no-cache-dir
good for? - Stack Overflow
なお、上記のコメントにある通り、Python 3.6.10 以降のイメージであれば PIP_NO_CACHE_DIR
という設定も使えるようです。
python - How to suppress pip upgrade warning? - Stack Overflow
ビルド
いつも通りDockerイメージをビルドします。
$ docker build -t ringo-tabetter:0.1 . ... Successfully built 8b90c3a7c141 Successfully tagged ringo-tabetter:0.1
Dockerコンテナを起動
$ docker run -p 8888:8000 ringo-tabetter:0.1 Watching for file changes with StatReloader
Dockerコンテナの中身を確認
余計なファイルが COPY
されていないか、別ターミナルを開いて確認します。
.dockerignore
で指定したファイルは COPY
されていませんでした。
# コンテナIDの確認 $ docker ps CONTAINER ID IMAGE COMMAND ... 2f3d4fdb4e6b ringo-tabetter:0.1 "python manage.py ru…" ... # コンテナの中に入る $ docker exec -i -t 2f3d4fdb4e6b bash root@2f3d4fdb4e6b:/app# # ファイルを確認 root@2f3d4fdb4e6b:/app# ls -al total 516 drwxr-xr-x 1 root root 4096 Sep 19 11:19 . drwxr-xr-x 1 root root 4096 Sep 19 11:20 .. -rw-r--r-- 1 root root 1076 Sep 8 14:07 LICENSE drwxr-xr-x 2 root root 4096 Sep 14 12:30 __pycache__ -rw-r--r-- 1 root root 4374 Sep 11 13:07 apples.yaml drwxr-xr-x 7 root root 4096 Sep 11 02:31 apps drwxr-xr-x 1 root root 4096 Sep 18 09:37 dj_ringo_tabetter -rw-r--r-- 1 root root 266 Sep 18 09:31 manage.py -rw-rw-r-- 1 root root 541 Sep 18 09:01 requirements.txt -rw-r--r-- 1 root root 471040 Sep 13 11:49 ringo.db drwxr-xr-x 4 root root 4096 Sep 8 14:07 static drwxr-xr-x 3 root root 4096 Sep 8 14:07 templates
動作確認
今回は Docker のポート 8000
を、ホストの 8888
で公開しています。
そこで、 http://127.0.0.1:8888/hc/total
にアクセスすると、いつものアプリが表示されました。
これにより、WSL2上のDockerで動いていることが確認できました。
Dockerコンテナの停止と削除
必要に応じて、コンテナの停止と削除を行います。
# コンテナIDを確認 $ docker ps CONTAINER ID IMAGE COMMAND ... 2f3d4fdb4e6b ringo-tabetter:0.1 "python manage.py ru…" ... # コンテナを停止 $ docker stop 2f3d4fdb4e6b 2f3d4fdb4e6b # コンテナを削除 $ docker rm 2f3d4fdb4e6b 2f3d4fdb4e6b
その他やったこと
WSL2 + Ubuntu 22.04.1 な環境のDockerにDjangoアプリを載せる以外に、今回やったこともメモしておきます。
requirements.txt から不要なパッケージを削除
Heroku + PostgreSQLで動かしていた時のパッケージがあったため、削除しました。
pytzを削除し、zoneinfoを使うよう修正
Django4系から pytz
パッケージが非推奨となりました。
Django 4.0 主な変更点まとめ | ryu22eBlog
そのため、pytz
を使っていた部分を修正し、テストコードでも zoneinfo
を使うよう修正しました。
settings.pyを分割
Herokuで動かしていた時は settings.py は1つでした。
ただ、本来は各環境ごとに settings.py
を用意するのが良さそうです。
そこで、以下を参考に、 settings.py
を分割し、各環境のファイルを用意しました。
[Django] プロジェクト構成のベストプラクティスを探る - 2.設定ファイルを本番用と開発用に分割する - Qiita
ソースコード
Githubに上げました。
https://github.com/thinkAmi/dj_ringo_tabetter
今回のプルリクはこちらです。
https://github.com/thinkAmi/dj_ringo_tabetter/pull/17