コンテンツにスキップ
最終更新日: 2020年7月24日

DjangoアプリをDockerで運用する

Djangoとは、Pythonで利用できるWebアプリケーションフレームワークです。
この記事では、Djangoを使って開発したアプリケーションをDocker環境へデプロイした際の手順を記載します。

準備

ベースイメージの選定

オリジナルのアプリケーションをDocker上で動作させるため、Dockerイメージを自前でビルドします。 今回はPythonでDjangoアプリケーションを動かすので、python:3.8-alpineを選択しました。1

コンテナ設計

図 1. コンテナ構成図[^2]

上記図のような感じにしたいと思います。

  • アプリサーバ
    • Djangoアプリは、Webアプリと定期バッチアプリがあります。
      これらは同じイメージを使用するが、コンテナは別々に2つ起動します。
    • nginxとWebアプリの接続には、uWSGIを使用します。
  • httpサーバ
    • 外部に公開するネットワークポートはnginxのHTTP(80)/HTTPS(443)のみとします。
    • nginxでHTTP over SSL/TLS(HTTPS)に対応させます。
      証明書はLet's Encryptで取得します。
    • 画像などの静的コンテンツは共有Volumeに置き、Webアプリを介さずにnginxから直接配信します。
    • コンテナイメージは steveltn/https-portal を使用します。
  • データベースサーバ

    • Djangoアプリケーションではデータを保存するためにRDBを使用するため、今回はPostgreSQLを選択しています。
    • コンテナイメージは PostgreSQLのDocker公式イメージ を使用します。
  • 各コンテナ間の通信には、Docker内部ネットワークを使用します。

前提

以降の作業では、下記を前提として書かれています。

  • Djangoアプリケーションを開発し、開発環境で動作している。
  • Dockerがインストールされた環境が存在している。

Djangoアプリケーションの設定

Djangoのデプロイ時チェック

Djangoには管理コマンドに、デプロイ時のチェックをする機能が備わっています。
実行すると、デプロイする場合の設定エラーや警告が表示されます。

$ python manage.py check --deploy

System check identified some issues:

WARNINGS:
?: (security.W004) You have not set a value for the SECURE_HSTS_SECONDS setting.
If your entire site is served only over SSL, you may want to consider setting a
value and enabling HTTP Strict Transport Security. Be sure to read the
documentation first; enabling HSTS carelessly can cause serious, irreversible
problems.
?: (security.W006) Your SECURE_CONTENT_TYPE_NOSNIFF setting is not set to True,
so your pages will not be served with an 'x-content-type-options: nosniff' header.
You should consider enabling this header to prevent the browser from identifying
content types incorrectly.
?: (security.W007) Your SECURE_BROWSER_XSS_FILTER setting is not set to True,
so your pages will not be served with an 'x-xss-protection: 1; mode=block' header.
You should consider enabling this header to activate the browser\'s XSS filtering
and help prevent XSS attacks.
?: (security.W008) Your SECURE_SSL_REDIRECT setting is not set to True.
Unless your site should be available over both SSL and non-SSL connections,
you may want to either set this setting True or configure a load balancer or
reverse-proxy server to redirect all connections to HTTPS.
?: (security.W012) SESSION_COOKIE_SECURE is not set to True. Using a secure-only
session cookie makes it more difficult for network traffic sniffers to hijack user
sessions.
?: (security.W016) You have 'django.middleware.csrf.CsrfViewMiddleware' in your
MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. Using a secure-only
CSRF cookie makes it more difficult for network traffic sniffers to steal the CSRF
token.
?: (security.W017) You have 'django.middleware.csrf.CsrfViewMiddleware' in your
MIDDLEWARE, but you have not set CSRF_COOKIE_HTTPONLY to True. Using an HttpOnly
CSRF cookie makes it more difficult for cross-site scripting attacks to steal the
CSRF token.
?: (security.W019) You have 'django.middleware.clickjacking.XFrameOptionsMiddleware'
in your MIDDLEWARE, but X_FRAME_OPTIONS is not set to 'DENY'. The default is
'SAMEORIGIN', but unless there is a good reason for your site to serve other parts
of itself in a frame, you should change it to 'DENY'.

なんだか、たくさん警告が表示されたので、ドキュメント3 を参考にしながらsettings.pyを修正しました。

settings.pyファイルの修正

Djangoアプリケーションの設定はdjango-admin startprojectで作成したディレクトリ内にsettings.pyというファイルに記載されています。
ここに設定されている値の初期値は開発向けに設定されているため、本番環境用に設定値に変更していきます。

ただし、Dockerコンテナで動作させるため、コンテナ内の環境変数から設定値を取得できるようにし、コンテナ実行時に設定値を注入できるようにしています。 また、ログはコンテナ側で管理するため、ファイル出力ではなく標準出力に表示するように設定しています。

設定にあたっては、Django公式ドキュメントを参考にしました。4 5 6 7 8 9

settings.py を表示

Dockerファイルの作成

次は、アプリケーションのDockerイメージをビルドするため、Dockerfileと
コンテナ起動時に呼び出すentrypoint.shを作ります。

  • Dockerfile
  • entrypoint.sh

ポイントを下記に挙げます。

  • apkコマンドに--no-cache オプションを付けることで、ローカルキャッシュを使用せず、ダウンロードしたパッケージファイルも実行後に削除されます。
  • Dockerイメージのビルドでは、各行の実行結果がレイヤとして保存されるため、ビルドのみに必要な gcc, linux-headers, musl-dev, postgresql-dev といったパッケージ群は、同一行内で追加・ビルド・削除することで、 アプリ実行に不要なパッケージをインストールした状態でレイヤを保存させないようにしています。
  • (2020/07/24追加) マルチステージビルドを使用して、ビルド時のみに必要なパッケージのインストールをアプリ実行ステージから分離し、実行ステージのイメージサイズを小さくします。
  • ライブラリパッケージのインストールを先に、アプリケーションのコピーを後に書くことで、アプリのみの更新の場合には以前のレイヤキャッシュを使用できるようにしています。
  • uWSGIの起動の前に以下のコマンドを実行しています。
    • python manage.py migrate:
      DBへテーブル作成・カラムの変更などを反映するコマンドです。 DBインスタンスが起動しているタイミングで実行する必要があります。
    • python manage.py collectstatic:
      画像などの静的コンテンツファイルを1箇所に集めるコマンドです。 nginxと共有するボリュームへコピーしたいため、コンテナ起動時に実行しています。
  • コンテナ起動時に ~/.profile を読み込み、環境変数 SECRET_KEY を動的に生成するようにしています。 しかし、これはベストプラクティスではなく、本来は .envファイルや docker secret などを用いるのが良いのではないかと思います。

uWSGI設定ファイルの作成

DjangoのドキュメントにはuWSGI上で動かす方法10 も載っているので、これを見ながら設定します。

Nginxの設定

httpサーバのNginxのイメージは、前述の通り steveltn/https-portal を使用します。
このイメージは、自動でLet's Encryptの証明書取得・更新してくれるため、証明書の期限切れを気にする必要がない上、cronなどの追加設定が不要なものです。
Nginxの設定ファイルはeRubyファイルになっており、/var/lib/nginx-conf/[ドメイン名].ssl.conf.erbというファイルを配置すると、対応したドメイン名でのアクセスに適用されます。

設定ファイルはuWSGIのドキュメント11 を見つつ、作成しました。

nginx_uwsgi.ssl.conf.erb を表示

docker-compose.ymlファイル

最後に、前述のコンテナを起動するためのdocker-composeファイルです。

docker-compose.yml を表示

最後に

この他にやるべきこと

  • (言及すべくもないが、)gitなどでソース管理をする。
  • DBの定期バックアップ
  • ユーザーアップロードコンテンツがあるアプリの場合は、それも定期バックアップが必要。
  • きちんと製品として運用するのであれば、k8sなどに載せる。

感想

アプリケーションを作り始めるところからデプロイまで、Djangoの公式ドキュメントが充実しており、いたせり尽くせりといった感じでした。

ドキュメントの日本語翻訳もしっかりされていること、作り始める時の手軽さ、Pythonのエコシステムが活かせることもあり、初心者にもオススメできるものだと思います。