nginx + uwsgi + django on dockerでサイトを公開する

nginx + uwsgi + django on dockerでサイトを構築し,localhost上で公開するまでの解説です.

webサーバーの動き方に関して

djangoにはpython3 manage.py runserverで起動できる便利な開発サーバー機能が備わっています. しかし,本番環境ではこの機能は使わずに,apacheやnginxなどのwebサーバーを使用することが推奨されています. 今回は,nginxを使用してdjangoアプリケーションデプロイすることにします. また,環境の移植性を考慮して,dockerを使用したデプロイを行います.

nginx + uwsgi + djangoの具体的な処理の流れはこちらで解説されていますので,ぜひ参照してください. 非常に分かりやすく書かれており,これを読むだけである程度の設定はできてしまいます.

Setting up Django and your web server with uWSGI and nginx

簡単に図解すると,下の図のようにリクエストが流れていくことになります.

webクライアント -> nginx(80,443ポート) -> uwsgi(8000ポート) -> djangoバックエンド
  1. nginxがwebサーバーとして機能しwebクライアントからリクエストを受け取る.
  2. uwsgiがアプリケーションサーバーとしてnginxからのリクエストを受け取る.
  3. uwsgiがdjangoを動かし,結果のレスポンスを返す

この構成を実現するため,以下ではnginx, uwsgi, djangoの各項目の設定事項を見ていきます.

docker-compose.ymlの中身

まず,全体の見通しをよくするために,docker-compose.ymlの中身を紹介しておきます.

version: "3"

services:
  nginx:
    image: nginx:latest
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d
      - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
      - ./django-project/myapp/static:/static
      - ./django-project/myapp/media:/media
    ports:
      - 8000:80
    environment:
      - LANG=C.UTF-8
      - LANGUAGE=en_US
    depends_on:
      - django-project
      - mysql

  django-project:
    build: ./django-project
    restart: always
    command: bash /init.sh
    tty: true
    volumes:
      - ./django-project/app:/myapp
      - ./django-project/init.sh:/init.sh
    environment:
      - DEBUG=False
      - LOCALHOST=localhost
      - DEPLOYHOST=#デプロイ先のドメイン名
    depends_on:
      - mysql
      - redis-server

  mysql:
    # mysqlに関する記述
    ....

各ファイルのフォルダ構成

project/
|__django-project/
|    |__myapp/
|    |    |__myapp/
|    |    |__static/
|    |    |__media/
|    |    |__manage.py
|    |__Dockerfile
|__nginx
|    |__conf/nginx.conf
|    |__uwsgi_params
|__mysql
|__docker-compose.yml

nginxの設定内容

まずnginxの設定を行います. ホストOS側で./nginx/conf/nginx.confを作成し,に以下の項目を書き足していきます.

upstream django {
    ip_hash;
    server django-project:8000;
}

server {
    listen 80;
    charset utf-8;

    client_max_body_size 75M;

    location /static {
        alias /static;
    }

    location /media {
        alias /media;
    }

    location / {
        uwsgi_pass django;
        include /etc/nginx/uwsgi_params;
    }
}

server_tokens off;

上記のポイントは,以下です.

  1. upstreamでuwsgiサーバーを示すホスト名:ポート名を指定
  2. listen 80で80番ポートをリッスン
  3. location /static, location /mediaで静的ファイルの場所を指定.docker-compose.yml内でマウントした/static, /media内のファイルを探します.
  4. location /でuwsgiをリクエストを流す先として指定します.uwsgi_paramsここからコピペで問題ありません.

uwsgiの設定内容

uwsgiはdjangoと同じdockerイメージの中に入れます../django-project/Dockerfile内に以下の内容を書き加えます.

FROM python:3.8-slim-buster

# djangoなど必要なライブラリをインストール
COPY ./requirements.txt /requirements.txt

# RUN source /venv/bin/activate
RUN python3.8 -m pip install --upgrade pip
RUN python3.8 -m pip install -r requirements.txt
RUN python3.8 -m pip install uwsgi # uwsgiを入れること

これでdjangoをuwsgiで動かす準備はできました. 次にuwsgiでリクエストを待ち受けるために,コンテナ起動時に実行するシェルスクリプトを書いていきます.

#!/bin/bash

cd /myapp;
bash -c "python3 manage.py makemigrations";
bash -c "python3 manage.py migrate";
bash -c "python3 manage.py collectstatic --noinput"
bash -c "uwsgi --socket :8000 --module myapp.wsgi --py-autoreload 1s";

uwsgi(8000番ポート)で待ち受けることを最後の行で書き加えています.

djangoの設定内容

最後に,djangoの設定を行なっていきます. 変更が必要なのは,/myapp/myapp/settings.pyのみです.

  1. デバッグモードのON/OFFをdocker-compose.ymlから指定できるようにする. docker-compose.ymlenvironmentDEBUG=True/Falseと指定することでデバッグモードが変えられるように,環境変数の読み込み部分を追記します. また,djangoはDEBUG=Falseの際には外部からの通信を受け付けないので,ALLOWED_HOSTSにも環境変数の読み込みを追加します. DEPLOYHOSTの部分にデプロイするサーバーのホスト名を書きます.
# settings.py
# SECURITY WARNING: don't run with debug turned on in production!
is_debug_mode = os.environ.get("DEBUG")
DEBUG = (is_debug_mode == "True")
ALLOWED_HOSTS = [os.environ.get("LOCALHOST"), os.environ.get("DEPLOYHOST")]
  1. nginxが静的ファイルをサーブできるように,staticファイルのルートを指定する. STATIC_ROOTMEDIA_ROOTを指定することで,DEBUG=Falseの際に静的ファイルはnginxが直接サーブするようにできます.
# settings.py
STATIC_URL = '/static/'
STATICFILE_DIRS = [
    os.path.join(BASE_DIR, 'userctrl/static/'),
    os.path.join(BASE_DIR, 'contest_manage/static/'),
    os.path.join(BASE_DIR, 'ace-builds')
]
STATIC_ROOT = os.path.join(BASE_DIR, "static")

MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"

戻る

About

IMAXおじさんが(主に)技術系記事を備忘録として残していくブログです.

Category

  1. Tech
  2. Daily
  3. Job
  4. Other