docker-composeでWebアプリケーションをデプロイする

djangoで作成したWebアプリケーションをLinuxマシンにデプロイしてみたのでやったことを書き記します。

システム構成

このようなシステム構成でdockerコンテナを構成します。

システム構成

WebServerとAPServerのdockerコンテナを作り、それらのコンテナをdocker-composeでまとめてビルド・実行します。WebServerとしてはNGiNXを使い、静的コンテンツはVolumeから配信します。動的コンテンツはAPServerにリクエストを送信し、APServerがレスポンスを生成します。

APServerの負荷軽減のため、静的コンテンツと動的コンテンツの配信責務を分担する構成としています。静的コンテンツはコンテナをビルドする際に共有しているVolumeに配置します。

各ソフトウェアについて軽く説明します。

NGiNX

Webサーバ。リバースプロキシ、ロードバランサ、HTTPキャッシュなどの機能を持つ。性能面に焦点をあてて開発がされています。

NGiNX → Gunicorn(http)

リクエストに応じて、動的コンテンツはアプリケーションサーバへルーティングする。

Gunicorn

グリーンユニコーン。PythonのWSGI HTTPサーバ。Python のAPサーバとしては、他にもuWSGIやNGiNX Unit等があるらしい。

WSGI

Web Server Gateway Interfaceの略。Pythonにおいて、WebサーバとWebアプリケーションを接続するための標準化されたインタフェース定義。ういすきー(whisky?)と発音するらしい。

Django

ジャンゴ。Pythonで実装されたWebアプリケーションフレームワーク。MVCライクなMTVモデルのフレームワークを提供する。Flaskがマイクロフレームワークに対して、Djangoはフルスタックフレームワークとなっている。

Python

ぱいそん。プログラミング言語。ここではPythonで書かれたアプリケーションを指す。

docker-compose構成

このようなファイル構成で実現します。

docker-compose
    ├─<django-project> #このディレクトリ以下の構成は省略
    ├─nginx
    │   ├─default.conf
    │   └─Dockerfile
    ├─.env
    ├─docker-compose.yml
    ├─Dockerfile
    ├─entrypoint.sh
    └─requirements.txt

上から順に中身を書いていきます。

upstream django {
    server gunicorn_django:8000;
}

server {
    listen 80;

    location / {
        proxy_pass http://django;
    }

    location /static/ {
        alias /static/;
    }
}
FROM nginx:1.19.0-alpine

COPY ./default.conf /etc/nginx/conf.d/default.conf
SECRET_KEY=<settings.pyのSECRET_KEY>
DEBUG=False
HTTP_PROXY=<user>:<pass>@<proxyserver>:<port>
NO_PROXY=*.*.*.* #プロキシ通したくないドメインやIPを設定する

企業内等でプロキシサーバの設定が必要な場合は環境変数として設定しておく。プロキシ設定しておかないとコンテナビルド時のpip install等ができなくなります。企業内でなければ設定しなくてもいいかと思います。

version: '3.8'

services:
  gunicorn_django:
    environment:
      - HTTP_PROXY=$HTTP_PROXY
      - http_proxy=$HTTP_PROXY
      - HTTPS_PROXY=$HTTP_PROXY
      - https_proxy=$HTTP_PROXY
      - FTP_PROXY=$HTTP_PROXY
      - ftp_proxy=$HTTP_PROXY
      - NO_PROXY=$NO_PROXY
      - no_proxy=$NO_PROXY
    volumes:
      - static:/static
    env_file:
      - .env
    build:
      context: .
      args:
        - HTTP_PROXY=$HTTP_PROXY
        - http_proxy=$HTTP_PROXY
        - HTTPS_PROXY=$HTTP_PROXY
        - https_proxy=$HTTP_PROXY
        - FTP_PROXY=$HTTP_PROXY
        - ftp_proxy=$HTTP_PROXY
        - NO_PROXY=$NO_PROXY
        - no_proxy=$NO_PROXY
    ports:
      - "8000:8000"
  nginx:
    build:
      context: ./nginx
      args:
        - HTTP_PROXY=$HTTP_PROXY
        - http_proxy=$HTTP_PROXY
        - HTTPS_PROXY=$HTTP_PROXY
        - https_proxy=$HTTP_PROXY
        - FTP_PROXY=$HTTP_PROXY
        - ftp_proxy=$HTTP_PROXY
        - NO_PROXY=$NO_PROXY
        - no_proxy=$NO_PROXY
    volumes:
      - static:/static
    ports:
      - "80:80"
    depends_on:
      - gunicorn_django

volumes:
  static:
FROM python:3.9-alpine

RUN pip install --upgrade pip

COPY ./requirements.txt .
RUN pip install -r requirements.txt

COPY ./<django-project> /app
WORKDIR /app

COPY ./entrypoint.sh /
ENTRYPOINT ["sh", "/entrypoint.sh"]
#!/bin/sh
python manage.py migrate --no-input
python manage.py collectstatic --no-input

gunicorn <appname>.wsgi:application --bind 0.0.0.0:8000

<appname>には<django-project>内のアプリケーション名を記述します。migrateとcollectstaticのオプションに–no-inputをつけておかないとアプリケーションをアップデートして再ビルドした時にユーザ入力待ちになってしまいタイムアウトでビルドが失敗します。

Django==3.1.5
gunicorn==20.0.4

requirements.txtには他にもpip installする必要があるパッケージを記述します。

いざ、デプロイ

ここまで設定ができたらコンテナをビルドして実行します。バックグラウンドで実行する場合は-dのデタッチオプションをつけてください。

$ docker-compose up --build

アプリケーションを更新するときはCtrl+Cで止めるか、docker-compose downして止めます。コンテナがdownしているのを確認して、アプリケーション更新(git pull等)して再度上記のビルドコマンドを叩けば更新できます。