メモ帳

python, juliaで機械学習をやっていく

DockerでDocker-composeを使った本番環境(Heroku)と開発環境の切り分け

Djangoのdeploy

クラウドにdeployすることが主流なので、ローカル開発環境と本番運用環境ではDBの設定などが異なることが多いと思われる。そこで、djangoで本番環境と開発環境を切り分けるには、settings.pyを2つ作って使い分ける。これを実現するためにはdocker-composeを使うと便利だったので、設定をまとめる。

主な手順は、

  • 共通設定ファイルをつくる。開発環境用設定ファイルと本番環境用設定ファイルを切り分ける
  • 環境に応じて読み込む設定ファイルを環境変数で切り替える。

である。 今回は、

  1. 共通設定ファイルをつくり、それを継承した開発環境用設定ファイル、本番環境用設定ファイルをつくる
  2. Django, DB, wsgi server, web serverをdocker-composeで構成。開発環境用設定ファイルを読み込ませる
  3. 本番環境用設定ファイルを読み込むDocker image(Django partのみ)を作成。本番環境にpushする

今回は、Herokuを本番環境とする。しかし、他の環境でも応用可能だと思われる。

requirements.txt

以下のライブラリが必要

dj-database-url
django-heroku
gunicorn

wsgi serverはgunicornじゃなくても多分動きます。

共通設定ファイルと本番環境用ファイルを分割

originalのsettings.pyをbase_settings.pyにrenameする。 heroku用にDB、ログの設定をbase_settingsから上書きしたsettingsファイルを作成する。loggerについては別記事でまとめます。

settings.py (本番環境用)

from main.base_settings import *

# 本番環境(heroku)
DEBUG = True
# loggerの設定
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler'
        },
    },
    'loggers': {
        '': {  # 'catch all' loggers by referencing it with the empty string
            'handlers': ['console'],
            'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
        },
    },
}
# DB設定
import dj_database_url
DATABASES = {
    'default': dj_database_url.config(conn_max_age=600, ssl_require=True)
}
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
ALLOWED_HOSTS = ['*']

# Activate Django-Heroku.
import django_heroku
django_heroku.settings(locals())

開発環境用ファイルを作成

開発用のsettings.pyをつくる

settings_development.py (開発環境用)

from main.base_settings import *

# for development
DEBUG = True
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler'
        },
    },
    'loggers': {
        '': {  # 'catch all' loggers by referencing it with the empty string
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },
}

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
ALLOWED_HOSTS = ['*']

Docker

開発環境

Djangoが呼び出すsettingsは環境変数DJANGO_SETTINGS_MODULEで決まっている。 docker-composeで環境変数に開発環境ようのsettingファイルであるsettings_development.pyを指定して、development用docker imageを作成する

(docker-compose-dev.yml)

...
    environment:
      - DJANGO_SETTINGS_MODULE=main.settings_development
    command: python manage.py runserver
...

commandは以上の設定にしておけば、pythonコードの編集の度に自動でapp serverが再起動し、更新が反映されるので開発用に適していると思われます。

本番環境用

DJANGO_SETTINGS_MODULEはdefaultでsettings.pyが設定されているので特に変わった設定は必要ない。

docker-compose.yml

version: '3.4'

services: 
  web:
    build: ./[dir]
    container_name: web
    volumes:
      - "./[dir]:/opt/backend"
    ports:
      - "8000:8000"

heroku用の設定

non-root userの作成 gunicorn, uwsgi or mod_wsgiいずれの場合でもport番号は$PORTで指定する。docker-compose.ymlはimageのbuildにしか使わない。heroku上ではDockerfile内のCMD以降のコマンドが呼ばれる。portをべた書きしてしまうとherokuでは動かないので注意が必要

in Dockerfile

...

RUN useradd -m myuser
USER myuser

CMD exec gunicorn -b 0.0.0.0:$PORT main.wsgi

docker imageの作成

$ docker-compose build

herokuへのdocker image deploy

$ heroku login
$ heroku container:login
$ docker tag [img name] registry.heroku.com/[app name]/web
$ docker push registry.heroku.com/[app name]/web
$ heroku container:release web -a [app name]

heroku上でDatabase migrate

Databaseを使う場合はmigrateを初めに行う。

$ heroku run python manage.py migrate
$ heroku run python manage.py createsuperuser

docker-composeで環境の切り替え

開発環境でapp起動

$ docker-compose -f docker-compose-dev.yml up

本番環境でimage build

$ docker-compose build

deploy時にsettings.pyに手を加えなくて良くなるので、deployの手間も減り、思わぬバグが混入する機会も減ると思います