BETA

Challenge - DockerでDjangoの開発環境を構築!(Docker-compose/Django/postgreSQL/gunicorn/nginx)

投稿日:2019-07-11
最終更新:2019-09-26

はじめに

エンジニアに転職しましたのこのこです!

今回はチームでの開発に非常に便利なDockerとDocker-composeを利用してDjango/postgreSQL/gunicorn/nginxの開発環境を構築していきましょう!!

全体の流れはほぼ以下のサイトを参考にさせてもらっていますが、設定ファイル等のほぼ全行にコメントを挟むなど理解しやすいような記事にしたつもりです!
Dockerizing Django with Postgres, Gunicorn, and Nginx

結構長い記事になってしまったので淡々と進めます!

この記事でやること

  • DockerとDocker-composeのインストール
  • pipenvのインストールと仮想環境構築
  • Dockerコンテナの構築
     - Django
     - postgres
     - gunicorn
     - nginx
     - 静的ファイルの処理

DockerとDocker-composeのインストール

皆さんはご存知かと思いますが、Dockerは簡単に言えば(簡単にしか言えない)お手持ちのPCのなかに仮想的に別のOSを入れて動かせる上にその環境を丸ごとコピーして他の人に渡せるツールのことです!

詳しくは以下のページなどを参考ください!
Docker入門(第一回)~Dockerとは何か、何が良いのか~

なにはともあれDockerをインストールしましょう!

docker

https://docs.docker.com/docker-for-mac/install/からインストール

docker-compose

$ curl -L https://github.com/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose  

$ chmod +x /usr/local/bin/docker-compose  

バージョンの確認

以下のコマンドでバージョンが表示されればインストール完了です!

$ docker --version  
Docker version 18.09.2  

$ docker-compose --version  
docker-compose version 1.23.2  

Django用のディレクトリを作成

次にDjangoプロジェクトを作成するためのディレクトリを作成しましょう

<!-- プロジェクト用のディレクトリを作成(appディレクトリがdjangoプロジェクトのルートディレクトリ) -->  
$ mkdir docker-demo-with-django && cd docker-demo-with-django  
$ mkdir app && cd app  

pipenvでホスト側の環境構築

pipenvは最近開発されたvenvやvenv-virtuarenvなどと比較されるpythonの仮想環境構築ツールです

pipとvirtuarenvが合体した様な機能を持っており、pipfileおよびPipfile.lockの2種類のファイルで仮想環境およびパッケージのバージョンを管理できる優れものです

DockerでDjangoの環境を構築する際には上記2ファイルをコピーしてpipenvを実行するだけで同様のpython開発環境を作成できますのでこれを使用していきましょう

docker-demo-with-django/app/にPipfileを用意してpackagesの欄にDjango = "==2.2.3"を入れるのを忘れない様にしましょう

docker-demo-with-django/app/Pipfile

[[source]]  
name = "pypi"  
url = "https://pypi.org/simple"  
verify_ssl = true  

[dev-packages]  

[packages]  
Django = "==2.2.3"  

[requires]  
python_version = "3.7"  

pipfileを作成したら同ディレクトリで以下のコマンドを入力しますy

<!-- pipenv本体のインストール -->  
:app$ pip install pipenv  

<!-- Pipfileから仮想環境構築 -->  
:app$ pipenv install  

<!-- 仮想環境に入る -->  
:app$ pipenv shell  

<!-- Djangoプロジェクトのスタート -->  
(app) :app$ django-admin.py startproject django_demo .  

<!-- モデルの内容をデータベースに適用 -->  
(app) :app$ python manage.py migrate  

<!-- 開発用サーバーの起動 -->  
(app) :app$ python manage.py runserver  

http://localhost:8000/にアクセスしてみてください。Djangoのwelcome screenが表示されるはずです

現在のディレクトリ構成

:docker-demo-with-django$ tree  
.  
└── app  
    ├── Pipfile  
    ├── Pipfile.lock  
    ├── db.sqlite3  
    ├── django_demo  
    │ ├── __init__.py  
    │ ├── settings.py  
    │ ├── urls.py  
    │ └── wsgi.py  
    └── manage.py  

Dockerコンテナの構築

Django

appディレクトリに以下の様なDockerfileを追加します
今回は最低限の環境を作るのが目的のため、公式からインストールするDockerイメージはすべて軽量なalpine linuxを利用します
これによってubuntu等のイメージと比較して1/10程度の容量で環境を構築できます

docker-demo-with-django/app/Dockerfile

# 公式からpython3.7 on alpine linuxイメージをpull  
FROM python:3.7-alpine  

# 作業ディレクトリを設定  
WORKDIR /usr/src/app  

# 環境変数を設定  
# Pythonがpyc filesとdiscへ書き込むことを防ぐ  
ENV PYTHONDONTWRITEBYTECODE 1  
# Pythonが標準入出力をバッファリングすることを防ぐ  
ENV PYTHONUNBUFFERED 1  

# Pipenvをインストール  
RUN pip install --upgrade pip \  
&& pip install pipenv  

# ホストのpipfileをコンテナの作業ディレクトリにコピー  
COPY ./Pipfile /usr/src/app/Pipfile  

# pipfileからパッケージをインストールしてDjango環境を構築  
RUN pipenv install --skip-lock --system --dev  

# ホストのカレントディレクトリ(現在はappディレクトリ)を作業ディレクトリにコピー  
COPY . /usr/src/app/  

次にプロジェクトのルート(docker-demo-with-django)にdocker-compose.ymlを追加

version: '3.7'  

services:  
    # サービス名は自由に設定  
    django:  
        # appディレクトリの中から`Dockerfile`を探す  
        build: ./app  
        # サービス起動後に入力されるコマンドを設定  
        command: python manage.py runserver 0.0.0.0:8000  
        # データを永続化させる場合の設定。`host:container`でパスを記載  
        volumes:  
            - ./app/:/usr/src/app/  
        # 開放するポートを指定。`host:container`でポートを記載  
        ports:  
            - 8000:8000  
        # 環境変数を指定  
        environment:  
            # 1ならデバックモード  
            - DEBUG=1  
    # setting.pyに記載されているSECRET_KEYを記入  
            - SECRET_KEY=hoge  

djangoプロジェクトのsetting.pyを修正します。
修正項目はSECRET_KEY,DEBUG,ALLOWED_HOSTSの3項目

# 環境変数からSECRET_KEYを取得する設定  
SECRET_KEY = os.environ.get('SECRET_KEY')  

# 環境変数からDEBUGを取得。デフォルトはTrue(本番環境モード)  
DEBUG = int(os.environ.get('DEBUG', default=0))  

# 許可するホストを記載  
ALLOWED_HOSTS = ['localhost', '127.0.0.1']  

修正が終わったらdocker-compose up -d --buildコマンドでビルドと起動を同時に行います

-dオプションはバックグランドで起動することを意味します

http://localhost:8000/に接続してwelcome screenが表示されれば成功です

Postgres

psotgresを追加するにはdocker-compose.ymlに新しいサービスを追加します
同時に、djangoサービスにデータベースの設定をする必要があります

version: '3.7'  

services:  
    # サービス名は自由に設定  
    django:  
        # appディレクトリの中から`Dockerfile`を探す  
        build: ./app  
        # サービス起動後に入力されるコマンドを設定  
        command: python manage.py runserver 0.0.0.0:8000  
        # データを永続化させる場合の設定。`host:container`でパスを記載  
        volumes:  
            - ./app/:/usr/src/app/  
        # 開放するポートを指定。`host:container`でポートを記載  
        ports:  
            - 8000:8000  
        # 環境変数を指定  
        environment:  
            # 1ならデバックモード  
            - DEBUG=1  
            - SECRET_KEY=hoge  
            - DATABASE_ENGINE=django.db.backends.postgresql  
            - DATABASE_DB=django_db  
            - DATABASE_USER=django_db_user  
            - DATABASE_PASSWORD=password1234  
            - DATABASE_HOST=postgres  
            - DATABASE_PORT=5432  
        # 接続するサービスを指定  
        depends_on:  
            - postgres  

    postgres:  
        # 公式からイメージをpull  
        image: postgres:11.4-alpine  
        # データベースの永続化  
        # ホストのディレクトリにマウントしない様に、先頭に`./`をつけない  
        volumes:  
            - postgres_data:/var/lib/postgresql/data  
        # su権限を持つ、指定されたユーザーと同じ名前のデータベースを作成  
        # valueはdjangoサービスで指定したものと同じ様に記載する  
        environment:  
            - POSTGRES_USER=django_db_user  
            - POSTGRES_PASSWORD=password1234  
            - POSTGRES_DB=django_db  

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる  
volumes:  
    postgres_data:  

次にsetting.pyのDATABASESの項目を書き換えます

DATABASES = {  
    'default': {  
        'ENGINE': os.environ.get('DATABASE_ENGINE', 'django.db.backends.sqlite3'),  
        'NAME': os.environ.get('DATABASE_DB', os.path.join(BASE_DIR, 'db.sqlite3')),  
        'USER': os.environ.get('DATABASE_USER', 'user'),  
        'PASSWORD': os.environ.get('DATABASE_PASSWORD', 'password'),  
        'HOST': os.environ.get('DATABASE_HOST', 'localhost'),  
        'PORT': os.environ.get('DATABASE_PORT', '5432'),  
    }  
}  

djangoからpostgresに接続するためにはドライバーを利用する必要があります
今回はもっともメジャーなドライバーであるpsycopg2を利用するためにdocker-demo-with-django/app/Dockerfileを修正しましょう
Dockerfileは以下の様になります
alpine linuxのパッケージマネージャーであるapkを利用しで依存関係をインストールした後にpipからpsycopg2をインストールしましょう

# 公式からpython3.7 on alpine linuxイメージをpull  
FROM python:3.7-alpine  

# 作業ディレクトリを設定  
WORKDIR /usr/src/app  

# 環境変数を設定  
# Pythonがpyc filesとdiscへ書き込むことを防ぐ  
ENV PYTHONDONTWRITEBYTECODE 1  
# Pythonが標準入出力をバッファリングすることを防ぐ  
ENV PYTHONUNBUFFERED 1  

# psycopg2のインストール  
RUN apk update \  
    && apk add --virtual build-deps gcc python3-dev musl-dev \  
    && apk add postgresql-dev \  
    && pip install psycopg2 \  
    && apk del build-deps  

# Pipenvをインストール  
RUN pip install --upgrade pip \  
&& pip install pipenv  

# ホストのpipfileをコンテナの作業ディレクトリにコピー  
COPY ./Pipfile /usr/src/app/Pipfile  

# pipfileからパッケージをインストールしてDjango環境を構築  
RUN pipenv install --skip-lock --system --dev  

# ホストのカレントディレクトリ(現在はappディレクトリ)を作業ディレクトリにコピー  
COPY . /usr/src/app/  

先ほど起動していたコンテナ群をdocker-compose down -vでいったん停止させて、再度docker-compose up -d --buildを入力してコンテナを再起動させましょう
-vオプションはボリュームの削除を表します

<!-- コンテナの停止 -->  
$ docker-compose down -v  

<!-- コンテナの起動 -->  
$ docker-compose up -d --build  

<!-- マイグレーション -->  
<!-- $ docker-compose exec <service_name> python manage.py migrate --noinput -->  
$ docker-compose exec django python manage.py migrate --noinput  

何度か同じ様なことをやっていると稀にDjangoとpostgresが接続されず、postgresコンテナが停止します
その際にはlogを確認してみましょう
おそらくpostgres_1 | initdb: directory "/var/lib/postgresql/data" exists but is not empty
の様な記載が確認できると思いますのでホスト側のdocker-demo-with-django/postgres_dataを削除してあげましょう

docker-compose psコマンドで以下の様に両方のコンテナが起動中(StateがUp)なことを確認します

$ docker-compose ps  
               Name                             Command               State           Ports           
----------------------------------------------------------------------------------------------------  
docker-demo-with-django_django_1     python manage.py runserver ...   Up      0.0.0.0:8000->8000/tcp  
docker-demo-with-django_postgres_1   docker-entrypoint.sh postgres    Up      5432/tcp   

次にデータベースに指定したデータベースが作成されていることも確認してください

$docker-compose exec postgres psql --username=django_db_user --dbname=django_db  
psql (11.4)  
Type "help" for help.  

django_db=# \l  
                                          List of databases  
   Name    |     Owner      | Encoding |  Collate   |   Ctype    |         Access privileges           
-----------+----------------+----------+------------+------------+-----------------------------------  
 django_db | django_db_user | UTF8     | en_US.utf8 | en_US.utf8 |   
 postgres  | django_db_user | UTF8     | en_US.utf8 | en_US.utf8 |   
 template0 | django_db_user | UTF8     | en_US.utf8 | en_US.utf8 | =c/django_db_user                +  
           |                |          |            |            | django_db_user=CTc/django_db_user  
 template1 | django_db_user | UTF8     | en_US.utf8 | en_US.utf8 | =c/django_db_user                +  
           |                |          |            |            | django_db_user=CTc/django_db_user  
(4 rows)  

django_db=# \dt  
                      List of relations  
 Schema |            Name            | Type  |     Owner        
--------+----------------------------+-------+----------------  
 public | auth_group                 | table | django_db_user  
 public | auth_group_permissions     | table | django_db_user  
 public | auth_permission            | table | django_db_user  
 public | auth_user                  | table | django_db_user  
 public | auth_user_groups           | table | django_db_user  
 public | auth_user_user_permissions | table | django_db_user  
 public | django_admin_log           | table | django_db_user  
 public | django_content_type        | table | django_db_user  
 public | django_migrations          | table | django_db_user  
 public | django_session             | table | django_db_user  
(10 rows)  

django_db=# \q  

確認できたら、postgresへの接続を確認した後にマイグレーションを自動で行うためにentrypoint.shをappディレクトリに追加します

#!/bin/sh  

if [ "$DATABASE" = "postgres" ]  
then  
    echo "Waiting for postgres..."  

    while ! nc -z $DATABASE_HOST $DATABASE_PORT; do  
      sleep 0.1  
    done  

    echo "PostgreSQL started"  
fi  

python manage.py flush --no-input  
python manage.py migrate  

exec "[email protected]"  

追加後にchmod +x app/entrypoint.shコマンドで実行権限を付与します
最後にentrypoint.shを実行するためにDockerfileを修正し、環境変数$DATABASEを追加します

# 公式からpython3.7 on alpine linuxイメージをpull  
FROM python:3.7-alpine  

# 作業ディレクトリを設定  
WORKDIR /usr/src/app  

# 環境変数を設定  
# Pythonがpyc filesとdiscへ書き込むことを防ぐ  
ENV PYTHONDONTWRITEBYTECODE 1  
# Pythonが標準入出力をバッファリングすることを防ぐ  
ENV PYTHONUNBUFFERED 1  

# psycopg2のインストール  
RUN apk update \  
    && apk add --virtual build-deps gcc python3-dev musl-dev \  
    && apk add postgresql-dev \  
    && pip install psycopg2 \  
    && apk del build-deps  

# Pipenvをインストール  
RUN pip install --upgrade pip \  
&& pip install pipenv  

# ホストのpipfileをコンテナの作業ディレクトリにコピー  
COPY ./Pipfile /usr/src/app/Pipfile  

# pipfileからパッケージをインストールしてDjango環境を構築  
RUN pipenv install --skip-lock --system --dev  

# entrypoint.shをコピー  
COPY ./entrypoint.sh /usr/src/app/entrypoint.sh  

# ホストのカレントディレクトリ(現在はappディレクトリ)を作業ディレクトリにコピー  
COPY . /usr/src/app/  

# entrypoint.shを実行  
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]  

少し待って起動完了後にhttp://localhost:8000/に接続できれば完了です

ひとまずこれで個人開発環境は整いました
しかし本番環境の場合、環境変数を非公開にしなければいけません。また、python manage.py runserverでは機能が不足していたり、セキュリティ面で問題があるため別の方法でサーバーを起動させる必要もあります。
そのためにアプリケーションとWebサーバーのインターフェイスであるgunicorn(WSGIサーバー)のインストールやenv_fileの設定、静的ファイルを処理するためにgunicornのリバースプロキシとして動作するnginxの設定などを行わなければなりません
次はそれらを行なっていきます

Gunicornのインストールと本番用設定ファイルの作成

pipfileにgunicornを追記しましょう

[[source]]  

url = "https://pypi.python.org/simple"  
verify_ssl = true  
name = "pypi"  


[packages]  

django = "==2.2"  
gunicorn= "==19.9.0"  


[dev-packages]  



[requires]  

python_version = "3.7"  

さらにdocker-compose.ymlと同じディレクトリにdocker-compose.prod.ymlを追記し、本番環境用の設定を記載します

version: '3.7'  

services:  
    # サービス名は自由に設定  
    django:  
        # appディレクトリの中から`Dockerfile`を探す  
        build: ./app  
        # サービス起動後に入力されるコマンドを設定  
        command: gunicorn django_demo.wsgi:application --bind 0.0.0.0:8000  
        # データを永続化させる場合の設定。`host:container`でパスを記載  
        volumes:  
            - ./app/:/usr/src/app/  
        # 開放するポートを指定。`host:container`でポートを記載  
        ports:  
            - 8000:8000  
        # 環境変数を指定  
        env_file: .env  
        # 接続するサービスを指定  
        depends_on:  
            - postgres  

    postgres:  
        # 公式からイメージをpull  
        image: postgres:11.4-alpine  
        # データベースの永続化  
        volumes:  
            - postgres_data:/var/lib/postgresql/data  
        env_file: .env.db  

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる  
volumes:  
  postgres_data:  

開発環境の設定と比較するとenvironment:env_file:に変わっています。これにより本番環境用の設定を直接ymlに書かなくてよくなります
また、djangoサービスのcommand:gunicornの起動に関するコマンドを指定して、runserverではなくgunicornを起動させています

env_filedocker-compose.prod.ymlと同様のディレクトリに置いて、以下の様に記載しましょう
この際に、.envDEBAG=0にするのを忘れない様にしましょう(DEBAG=0でデバッグモードをオフ)

/docker-demo-with-django/.env

DEBUG=0  
SECRET_KEY=hoge  
DATABASE_ENGINE=django.db.backends.postgresql  
DATABASE_DB=django_db  
DATABASE_USER=django_db_user  
DATABASE_PASSWORD=password1234  
DATABASE_HOST=postgres  
DATABASE_PORT=5432  
DATABASE=postgres  

/docker-demo-with-django/.env.db

POSTGRES_USER=django_db_user  
POSTGRES_PASSWORD=password1234  
POSTGRES_DB=django_db  

加えて、現段階ではコンテナが起動するたびにmigrateが実行されてしまいますのでentrypoint.shも本番環境用にentrypoint.prod.shを作成しましょう

/docker-demo-with-django/app/entrypoint.prod.sh

#!/bin/sh  

if [ "$DATABASE" = "postgres" ]  
then  
    echo "Waiting for postgres..."  

    while ! nc -z $DATABASE_HOST $DATABASE_PORT; do  
      sleep 0.1  
    done  

    echo "PostgreSQL started"  
fi  

exec "[email protected]"  

Dockerfileも本番用のものを作成します

/docker-demo-with-django/app/Dockerfile.prod

# 公式からpython3.7 on alpine linuxイメージをpull  
FROM python:3.7-alpine  

# 作業ディレクトリを設定  
WORKDIR /usr/src/app  

# 環境変数を設定  
# Pythonがpyc filesとdiscへ書き込むことを防ぐ  
ENV PYTHONDONTWRITEBYTECODE 1  
# Pythonが標準入出力をバッファリングすることを防ぐ  
ENV PYTHONUNBUFFERED 1  

# psycopg2のインストール  
RUN apk update \  
    && apk add --virtual build-deps gcc python3-dev musl-dev \  
    && apk add postgresql-dev \  
    && pip install psycopg2 \  
    && apk del build-deps  

# Pipenvをインストール  
RUN pip install --upgrade pip \  
&& pip install pipenv  

# ホストのpipfileをコンテナの作業ディレクトリにコピー  
COPY ./Pipfile /usr/src/app/Pipfile  

# pipfileからパッケージをインストールしてDjango環境を構築  
RUN pipenv install --skip-lock --system --dev  

# entrypoint.shをコピー  
COPY ./entrypoint.prod.sh /usr/src/app/entrypoint.prod.sh  

# ホストのカレントディレクトリ(現在はappディレクトリ)を作業ディレクトリにコピー  
COPY . /usr/src/app/  

# entrypoint.shを実行  
ENTRYPOINT ["/usr/src/app/entrypoint.prod.sh"]  

当然、docker-compose.prod.ymlも本番用のファイルを読み込む様に書き換えます

version: '3.7'  

services:  
    # サービス名は自由に設定  
    django:  
        build:  
            # 読み込むファイル名が`Dockerfile`ではない場合contextに相対パス、dockerfileにファイル名を記載  
            context: ./app  
            dockerfile: Dockerfile.prod  
        # サービス起動後に入力されるコマンドを設定  
        command: gunicorn django_demo.wsgi:application --bind 0.0.0.0:8000  
        # データを永続化させる場合の設定。`host:container`でパスを記載  
        volumes:  
            - ./app/:/usr/src/app/  
        # 開放するポートを指定。`host:container`でポートを記載  
        ports:  
            - 8000:8000  
        # 環境変数を指定  
        env_file: .env  
        # 接続するサービスを指定  
        depends_on:  
            - postgres  

    postgres:  
        # 公式からイメージをpull  
        image: postgres:11.4-alpine  
        # データベースの永続化  
        volumes:  
            - postgres_data:/var/lib/postgresql/data  
        env_file: .env.db  

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる  
volumes:  
    postgres_data:  

設定が終わったら再度コンテナを起動させてみましょう

$ docker-compose down -v  

<!-- -fオプションでdocker-compose.prod.ymlを指定 -->  
$ docker-compose -f docker-compose.prod.yml up -d --build  

<!-- entrypoint.prod.shではmigrateをしていないので手動で実行 -->  
$ docker-compose -f docker-compose.prod.yml exec django python manage.py migrate --noinput  

起動したらhttp://localhost:8000/adminにアクセスしてみましょう
接続されてdjangoの管理用のログイン画面が表示されると思いますが、静的ファイル(CSS等)が表示されていないはずです
これはデバッグモードをオフにしたため静的ファイルが読み込まれなくなっています
また、設定通りGunicornが起動しましたがGunicorn静的ファイルの配信に対応しておらずアプリケーション(今回であればdjango)を提供するだけですので、上記2つの設定を修正しなければ静的ファイルの配信はできません
具体的にはpython manage.py collectstaticで静的ファイルを1箇所に集めてそれを読み込む様にすることと、nginxなどのwebサーバーをgunicornのリバースプロキシとして利用することです

まずはnginxをサービスに追加しましょう

nginxを追加

プロジェクトのルート(/docker-demo-with-django/)にnginxディレクトリを作成し、その中にDockerfilenginx.confを追加します

/docker-demo-with-django/nginx/Dockerfile

FROM nginx:1.15.12-alpine  

# デフォルトのconfを消して、別の設定を追加  
RUN rm /etc/nginx/conf.d/default.conf  
COPY nginx.conf /etc/nginx/conf.d  

/docker-demo-with-django/nginx/nginx.conf

upstream config {  
    # コンテナのサービス名を指定すると名前解決してくれる  
    server django:8000;  
}  

server {  
    # 80ポートで待ち受け  
    listen 80;  

    location / {  
        proxy_pass http://config;  
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
        proxy_set_header Host $host;  
        proxy_redirect off;  
    }  

}  

それからdocker-compose.prod.ymlにnginxを追記します

version: '3.7'  

services:  
    # サービス名は自由に設定  
    django:  
        build:  
            # 読み込むファイル名が`Dockerfile`ではない場合contextに相対パス、dockerfileにファイル名を記載  
            context: ./app  
            dockerfile: Dockerfile.prod  
        # サービス起動後に入力されるコマンドを設定  
        command: gunicorn django_demo.wsgi:application --bind 0.0.0.0:8000  
        # データを永続化させる場合の設定。`host:container`でパスを記載  
        volumes:  
            - ./app/:/usr/src/app/  
        # 指定されたポートは接続されたサービスからアクセス可能  
        expose:  
            - 8000  
        # 環境変数を指定  
        env_file: .env  
        # 接続するサービスを指定  
        depends_on:  
            - postgres  

    postgres:  
        # 公式からイメージをpull  
        image: postgres:11.4-alpine  
        # データベースの永続化  
        volumes:  
            - postgres_data:/var/lib/postgresql/data  
        env_file: .env.db  

    nginx:  
        build: ./nginx  
        ports:  
            - 1337:80  
        depends_on:  
            - django  

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる  
volumes:  
    postgres_data:  

djangoサービスにホストOSから直接リクエストすることはなくなるのでports:expose:に変えましょう
これで指定されたポートはホストOSには公開されませんが、リンクされたサービスからは接続できる様になります

先ほどと同様にサービスを再起動します

$ docker-compose -f docker-compose.prod.yml down -v  
$ docker-compose -f docker-compose.prod.yml up -d --build  
$ docker-compose -f docker-compose.prod.yml exec djnago python manage.py migrate --noinput  

http://localhost:1337/admin/に接続してみましょう。管理画面が表示されるはずです

これでnginxとの接続が完了しました。現段階でのディレクトリ構成は以下の通りです

$tree  
.  
├── app  
│ ├── Dockerfile  
│ ├── Dockerfile.prod  
│ ├── Pipfile  
│ ├── Pipfile.lock  
│ ├── django_demo  
│ │ ├── __init__.py  
│ │ ├── settings.py  
│ │ ├── urls.py  
│ │ └── wsgi.py  
│ ├── entrypoint.prod.sh  
│ ├── entrypoint.sh  
│ └── manage.py  
├── docker-compose.prod.yml  
├── docker-compose.yml  
└── nginx  
    ├── Dockerfile  
    └── nginx.conf  

静的ファイルの処理

次に静的ファイルを処理する設定です。djangoプロジェクトのsetting.pyを末尾を修正し、entrypoint.shに追記します

/docker-demo-with-django/app/django_demo/settings.py

STATIC_URL = '/staticfiles/'  
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')  

/docker-demo-with-django/app/entrypoint.sh

#!/bin/sh  

if [ "$DATABASE" = "postgres" ]  
then  
    echo "Waiting for postgres..."  

    while ! nc -z $DATABASE_HOST $DATABASE_PORT; do  
      sleep 0.1  
    done  

    echo "PostgreSQL started"  
fi  

python manage.py flush --no-input  
python manage.py migrate  
python manage.py collectstatic --no-input --clear  

exec "[email protected]"  

これでpython manage.py collectstaticによって静的ファイルがSTATIC_ROOTで指定したパスに集められ、さらにそこにstaticfilesディレクトリに存在する静的ファイルを配信する様になります

次に、docker-compose.prod.ymlのdjangoとnginxのボリュームに同じものを設定し、nginxコンテナにdjangoプロジェクトを接続したのちに静的ファイルのリクエストをstaticfilesディレクトリにルーティングする様に設定しましょう

/docker-demo-with-django/docker-compose.prod.yml

version: '3.7'  

services:  
    # サービス名は自由に設定  
    django:  
        build:  
            # 読み込むファイル名が`Dockerfile`ではない場合contextに相対パス、dockerfileにファイル名を記載  
            context: ./app  
            dockerfile: Dockerfile.prod  
        # サービス起動後に入力されるコマンドを設定  
        command: gunicorn django_demo.wsgi:application --bind 0.0.0.0:8000  
        # データを永続化させる場合の設定。`host:container`でパスを記載  
        volumes:  
            - static_volume:/usr/src/app/staticfiles  
        # 指定されたポートは接続されたサービスからアクセス可能  
        expose:  
            - 8000  
        # 環境変数を指定  
        env_file: .env  
        # 接続するサービスを指定  
        depends_on:  
            - postgres  

    postgres:  
        # 公式からイメージをpull  
        image: postgres:11.4-alpine  
        # データベースの永続化  
        volumes:  
            - postgres_data:/var/lib/postgresql/data  
        env_file: .env.db  

    nginx:  
        build: ./nginx  
        volumes:  
            - static_volume:/usr/src/app/staticfiles  
        ports:  
            - 1337:80  
        depends_on:  
            - django  

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる  
volumes:  
    postgres_data:  
    static_volume:  

/docker-demo-with-django/nginx/nginx.conf

upstream config {  
    # コンテナのサービス名を指定すると名前解決してくれる  
    server django:8000;  
}  

server {  
    # 80ポートで待ち受け  
    listen 80;  

    location / {  
        proxy_pass http://config;  
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
        proxy_set_header Host $host;  
        proxy_redirect off;  
    }  

    # 静的ファイルの要求をstaticfilesにルーティング  
    location /staticfiles/ {  
        alias /usr/src/app/staticfiles/;  
    }  

}  

これですべての設定が完了しました!
改めてコンテナを起動してみましょう!

$ docker-compose -f docker-compose.prod.yml down -v  
$ docker-compose -f docker-compose.prod.yml up -d --build  
$ docker-compose -f docker-compose.prod.yml exec django python manage.py migrate --noinput  
$ docker-compose -f docker-compose.prod.yml exec django python manage.py collectstatic --no-input --clear  

起動を確認したらhttp://localhost:1337/adminに接続してみてください。管理画面にCSSが設定されているはずです!あとはお好きな様にDjangoプロジェクトを改修してください!

お疲れ様でした!!

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

Python (Django)・JavaScript(Vue.js)・GISについて書いていきます。 たまにちゃんとした記事も書きますが、基本的には自分用のメモを残していきます

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!