今更ながらDockerの勉強を始めました。
とりあえずプログラマのためのDocker教科書 第2版を読み終えたので力試しのトライです。
今回はnginx + (Gunicorn + Django) + PostgreSQLな構成で作成します。
本記事で使用しているコードはここにあるので気になった方は見てみてください。
##開発環境
- macOS High Sierra 10.13.6
- Docker for Mac
- Engine : 18.06.1-ce
- Compose : 1.22.0
- Python : 3.7
- Django : 2.1
##0.プロジェクトの雛形用意
今回のディレクトリの構成このようになっています。
docker-django
├── docker-compose.yml
├── django
│ ├── Dockerfile
│ ├── Dockerfile.base
│ ├── requirements.txt
│ └── requirements.base.txt
└── nginx
├── mime.types
└── nginx.conf
コンテナ毎の設定ファイルをそれぞれのコンテナ名のディレクトリ下に配置しています。
例えばnginx用のコンテナに使用する設定ファイルはnginx/のフォルダに格納されています。
##1.Django+Gunicorn用のベースイメージを作成
はじめにDjangoプロジェクトをゴリゴリ開発するためのベースイメージを作成します。
このイメージの用途はdjangoのプロジェクト作成コマンド(django-admin startproject
)を実行するときのコンテナに使ったり、後に作成するデプロイ用イメージのベースイメージとして使用します。
ベースイメージのDockerfileはこのようになります。
FROM python:3.7
ENV APP_PATH /opt/apps
COPY requirements.base.txt $APP_PATH/
RUN pip install --no-cache-dir -r $APP_PATH/requirements.base.txt
WORKDIR $APP_PATH
また、requirements.base.txtの内容はこのようになっています。
Django==2.1.11
gunicorn==19.9.0
psycopg2==2.7.5
psycopg2-binary==2.7.5
これをサクッとビルドしましょう。
$ docker build -t django2.1 -f ./django/Dockerfile.base ./django
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
django2.1 latest c391e4c70e98 6 minutes ago 960MB
python 3.7 825141134528 4 weeks ago 923MB
python:3.7
のイメージをもとにdjango2.1:latest
というイメージが作成されたのがわかります。
##2.Djangoプロジェクトのローカルへの作成
Djangoを使用できるイメージを作成したところで、
次はこのイメージを使ったコンテナからプロジェクトファイルを作成します。
ここでキモとなるのがコンテナ作成時にdocker-django/django/
をmountオプションでバインドします。
こうすることで何が嬉しいかというとコンテナがあたかもvirtualenvのように扱えるのです!
早速次のコマンドを実行します。
$ cd django
$ docker run --rm \
--mount type=bind,src=$(pwd),dst=/opt/apps \
django2.1 \
django-admin startproject my_docker_project .
少しコマンドが長くなってしまいましたので改行をいれてあります。
一行ずつみていきましょう。
まずはDjangoプロジェクトを配置したい、./django/
へカレントディレクトリを移動します。
次の行からコンテナの起動オプションです。
1行目の--rm
はコマンドの実行が終えたらコンテナを削除するオプションです。
2行目の--mount
はコンテナ起動時にマウントするホストディレクトリなどを設定します。今回はホストへの読み書きを許可するのでtypeはbind,srcはカレントディレクトリ,dstはコンテナ側のカレントディレクトリを指定します。
3行目にはコンテナのもととなるイメージを指定しています。
4行目がコンテナ内で実行されるコマンドです。今回はmy_docker_project
という名前のプロジェクトを作成しました。
実行後、djangoフォルダの中身をみてみると、
$ tree
.
├── Dockerfile
├── Dockerfile.base
├── manage.py
├── my_docker_project
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── requirements.base.txt
└── requirements.txt
Djangoのプロジェクトファイルがローカルに作成されています。素晴らしい!!
このようにDjangoのコマンドを使ってあれこれするときは、ホストをマウントしたコンテナで実行していきます。
ここまでできたらDjango関連は一旦おやすみです。
##3.docker-compose.ymlの作成と各コンテナ間の接続設定
Django+Gunicornな環境構築が落ち着いたところで、視点をコンテナの全体構成に向けます。
目標は、nginx + (Gunicorn + Django) + PostgreSQLな構成でした。
docker-compose.ymlは以下のようになります。
version: '3.7'
services:
django:
restart: always
build: ./django
expose:
- "3031"
depends_on:
- postgres
command: bash -c "python manage.py migrate && gunicorn my_docker_project.wsgi -b 0.0.0.0:3031"
volumes:
- "staticdata:/opt/static/"
nginx:
restart: always
image: nginx
depends_on:
- django
ports:
- "80:80"
volumes:
- "./nginx/:/etc/nginx/"
- "staticdata:/opt/apps/static/"
postgres:
image: postgres
ports:
- "5432:5432"
volumes:
- "dbdata:/var/lib/postgresql/data"
environment:
POSTGRES_PASSWORD: hogemojahogemoja
volumes:
dbdata:
staticdata:
ここでは、
「DBのデータは永続化したい。」
「静的ファイルはnginxコンテナから配信したい。」
がキモになります。
#####「DBのデータは永続化したい。」 <=> 「コンテナ外部のボリュームを使いたい」
ので、docker-compose.ymlのトップレベルに dbdata
というボリュームを宣言し、
それをコンテナにマウントしています。
#####「静的ファイルはnginxコンテナから配信したい。」 <=> 「コンテナ間でリソースを共有したい。」
ので、同じくトップレベルにstaticdata
というボリュームを宣言し、
それをdjangoとnginx両方のコンテナにマウントしています。
###3-1.nginxコンテナの設定
今回の構成ではnginxで受け取ったリクエストを適宜Django(Gunicorn)に振り分けたり、静的ファイルを配信したりします。
さっそくconfファイルに書いていきましょう。
http {
#~中略~
include /etc/nginx/mime.types;
#~中略~
upstream django_server {
#docker-compose.ymlに記入したサーバ名、ポートを指定します。 #
server django:3031 fail_timeout=0;
}
server {
#~中略~
location /static/ {
#先ほどしていた静的ファイルのボリュームのマウント先を指定します。#
alias /opt/apps/static/;
}
location / {
try_files $uri @proxy_to_django;
}
location @proxy_to_django {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_pass http://django_server;
}
#~中略~
}
}
nginxコンテナの設定は以上です。
今回は最小の構成で、mime.typesはデフォルトのまま使用しています。
###3-2.djangoコンテナの設定とビルド用のDockerfile作成
docker-compose.ymlやnginx.confファイルの情報をもとにsettings.pyを編集していきます。
#〜中略〜
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'postgres', #作成時のデフォルト
'USER': 'postgres', #作成時のデフォルト
'PASSWORD' : 'hogemojahogemoja', #作成時にdocker-compose.ymlで設定
'HOST' : 'postgres', #コンテナのサーバ名
'PORT' : 5432,
}
}
#〜中略〜
STATIC_URL = '/static/'
STATIC_ROOT = '/opt/static' #ボリュームマウント先のパス
#〜中略〜
続いてDockerfileを編集していきます。
FROM django2.1:latest
ENV APP_PATH /opt/apps
COPY . $APP_PATH/
RUN pip install --no-cache-dir -r $APP_PATH/requirements.txt
WORKDIR $APP_PATH
RUN python manage.py collectstatic --noinput
こちらのイメージは先ほどのベースイメージとは異なり、デプロイ用のイメージです。
プロジェクトで使用しているpythonライブラリをインストールしたり、配信用に静的ファイルをまとめたりしてます。
##4.Docker Composeでupする。
お疲れさまでした。
あとはupするだけです。
$ docker-compose up --build
各コンテナが立ち上がったあとlocalhostにアクセスすると…
素晴らしい!!
5.終わりに
Dockerを触り始めて日が浅いですが
こんな風に各コンテナが協調して動いてるのが作れると脳汁出まくりでホント楽しいです。
今後もっと業務や趣味にDocker取り入れて行きたいです!!