ちょっとしたサービスを作りたいと思い、勉強しながらConoHa VPS上でDocker環境を構築中です。
前記事(docker-composeで「https-portal+Django+postgreSQL」を立ち上げる(環境変数は未考慮))で、「https-portal+Django+postgreSQL」を立ち上げることができたのですが、環境変数の考慮がされていないためセキュリティ情報や環境依存するパラメータがコード内に入ってしまっています。
今回は環境変数を考慮して、構成を見直したので、その備忘録をまとめたいと思います。
※「https-portal」は自動でhttpsの証明書の更新をしてくれる便利なコンテナです。「https-portal」内にnginxがあるので、プロキシの機能はそれを使うことにします。
環境は以下です。
- ConoHa VPS (メモリ 1GB/CPU 2Core/SSD 100GB)
- Ubuntu 18.04.4 LTS
- Docker: 19.03.8 (2020/3/22 執筆時点の最新)
- Docker Compose: 1.25.4 (2020/3/22 執筆時点の最新)
- https-portal: 1 (2020/3/22 執筆時点の最新)
- Python: 3.8 (2020/3/22 執筆時点の最新)
- Django: 3.0.4 (2020/3/22 執筆時点の最新)
- psycopg: 2.8.4 (2020/3/22 執筆時点の最新)
- uWSGI: 2.0.18 (2020/3/22 執筆時点の最新)
- postgres: latest (2020/3/22 執筆時点では、12.2が最新)
目次
事前準備
事前に以下の準備が必要です。
- Docker、Docker ComposeをインストールしたLinuxサーバ
もし、まだ準備されていないようなら、以下に導入方法をまとめたので、よろしければご覧になってください。
※仮想専用サーバのConoHa VPS上での構築を想定していますが、Linuxであれば同じ手順を踏襲することができると思います。
docker-composeで「https-portal+Django+postgreSQL」を立ち上げる
作業ディレクトリとしてホームディレクトリに「work」を作成し、以下のようなディレクトリ構成を作成していきます。
work
|-- db
| `-- dbdata
|-- django
| |-- Dockerfile
| `-- requirements.txt
|-- docker-compose.yml
|-- nginx
| |-- conf
| | `-- mysite_nginx.conf.temp
| |-- script
| | `-- 11-set-nginx-env
| |-- ssl_certs
| `-- uwsgi_params
`-- src
|-- manage.py
|-- mysite
| |-- __init__.py
| |-- asgi.py
| |-- settings.py
| |-- urls.py
| `-- wsgi.py
`-- static
ホスト側のサーバとコンテナ側の構成イメージは以下のような関係となります。
環境変数を定義する
環境変数化するのは、セキュリティ関係、環境依存するものということですが、今後変える可能性があるものも含めて環境変数として定義したいと思います。以下を環境変数化しました。
- ドメイン名(DOMAIN_NAME)
- https-portalコンテナの名称(HTTPS_PORTAL_CONTAINER_NAME)
- nginxが監視するポート番号(NGINX_WATCH_PORT)
- Djangoコンテナの名称(DJANGO_CONTAINER_NAME)
- Djangoコンテナのポート番号(DJANGO_CONTAINER_PORT)
- DBコンテナの名称(DB_CONTAINER_NAME)
- DBコンテナのポート番号(DB_CONTAINER_PORT)
- DBへログインするためのユーザ名(DB_USER_NAME)
- DBへログインするためのパスワード(DB_PASSWORD)
- DBの名称(DB_NAME)
以下のコマンドで環境変数を定義するファイルを作成します。なお、環境変数は「.env」という名称で「docker-compose.yml」と同じ階層に置いておくと、docker-copose
実行時に自動で環境変数を読み取ってくれます。
$ vi ~/work/.env
【編集内容】
DOMAIN_NAME=[ドメイン名]
HTTPS_PORTAL_CONTAINER_NAME=web
NGINX_WATCH_PORT=8080
DJANGO_CONTAINER_NAME=django
DJANGO_CONTAINER_PORT=8001
DB_CONTAINER_NAME=db
DB_CONTAINER_PORT=5432
DB_USER_NAME=[ユーザ名]
DB_PASSWORD=[パスワード]
DB_NAME=testDB
- [ドメイン名]、[ユーザ名]、[パスワード]はご自分の環境に合わせて設定してください。
- 他の環境変数も任意のものに変更しても大丈夫です。
「docker-compose.yml」を作成する
以下のコマンドで「docker-compose.yml」を作成します。
このファイルはDocker Composeで立ち上げるコンテナの定義をしています。
$ vi ~/work/docker-compose.yml
【編集内容】
version: '3.5'
services:
web:
image: steveltn/https-portal:1
container_name: ${HTTPS_PORTAL_CONTAINER_NAME}
restart: always
volumes:
- ./nginx/ssl_certs:/var/lib/https-portal
- ./nginx/conf/mysite_nginx.conf.temp:/etc/nginx/conf.d/mysite_nginx.conf.temp:ro
- ./nginx/script/11-set-nginx-env:/etc/cont-init.d/11-set-nginx-env:ro
- ./nginx/uwsgi_params:/etc/nginx/uwsgi_params:ro
- ./src/static:/static
ports:
- '80:80'
- '443:443'
env_file:
- .env
environment:
DOMAINS: ${DOMAIN_NAME} -> http://${HTTPS_PORTAL_CONTAINER_NAME}:${NGINX_WATCH_PORT}/
STAGE: 'production'
depends_on:
- django
django:
build: ./django
container_name: ${DJANGO_CONTAINER_NAME}
volumes:
- ./src:/code
expose:
- ${DJANGO_CONTAINER_PORT}
env_file:
- .env
command: uwsgi --socket :${DJANGO_CONTAINER_PORT} --module mysite.wsgi
depends_on:
- db
db:
image: postgres:latest
container_name: ${DB_CONTAINER_NAME}
volumes:
- ./db/dbdata:/var/lib/postgresql/data
expose:
- ${DB_CONTAINER_PORT}
env_file:
- .env
environment:
- POSTGRES_USER=${DB_USER_NAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_NAME}
image: steveltn/https-portal:1
、image: postgres:latest
でhttps-portalとPostgreSQLの最新のイメージをDocker Hubから自動でダウンロードしてくれます。build: ./django
は./djangoにあるDockerfileからDockerイメージをビルドしてくれます。- https-portalによって、
${DOMAIN_NAME}
で定義したドメイン名に対して、httpsで接続できるようになります。 web:
のenvironment:
のSTAGE
は証明書の種類を指定できます。STAGE: 'staging'
でhttps-portalは指定したドメインに対してオレオレ証明書を作成してくれます。STAGE: 'production'
にすれば、https-portalは指定したドメインに対してLet's EncryptでDV証明書を作成してくれます。volumes:
でホスト側からコンテナ側へボリュームをマウントすることができます。(ホスト側とコンテナ側で共有ディレクトリを作るイメージ) 後で作成するnginxコンフィグなどをマウントしています。django:
の- ./src:/code
とweb:
の- ./src/static:/static
でdjango:
の/codeとweb:
の/staticをホスト側の./srcを介してつなげています。これにより、static関連のファイルをDjangoにアクセスせず、nginxから取得できるようにしています。ports:
でホスト側のポートとコンテナ側のポートをつなげることができます。https-portalは- "80:80"
と- "443:443"
とすることで、HTTPの80番ポートとHTTPSの443番ポートに対して、ホスト側とコンテナ側をつなげています。expose:
はコンテナ側でのみ公開するポートを指定できます。django:
とdb:
はホスト側からの接続はしないため、expose:
でコンテナ側のみ公開するポートを指定します。env_file:
は環境変数を定義したファイルを読み取ることができます。environment:
で環境変数を指定できます。db:
にはDB接続するためのユーザ名やパスワード、DB名を指定しています。depends_on:
は依存関係を定義することができます。docker-containerコマンドを実施したときに、依存関係のコンテナが出来上がるのを待って、自コンテナの立ち上げをすることができます。
「django/Dockerfile」を作成する
以下のコマンドで「django/Dockerfile」を作成します。
このファイルはdjangoコンテナのイメージを定義するファイルになります。
$ mkdir -p ~/work/django/
$ vi ~/work/django/Dockerfile
【編集内容】
FROM python:3.8
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/
FROM python:3.8
でpythonのバージョン3.8のDockerイメージをDocker Hubからダウンロードします。RUN pip install -r requirements.txt
で「requirements.txt」で定義されたライブラリをインストールする。※「requirements.txt」は下記で作成する。
「django/requirements.txt」を作成する
以下のコマンドで「django/requirements.txt」を作成します。
このファイルはdjangoコンテナのイメージにインストールするライブラリを定義するファイルになります。
$ vi ~/work/django/requirements.txt
【編集内容】
Django==3.0.4
psycopg2==2.8.4
uwsgi==2.0.18
「nginx/conf/mysite_nginx.conf.temp」を作成する
以下のコマンドで「nginx/conf/mysite_nginx.conf.temp」を作成します。
「docker-compose.yml」のhttps-portalコンテナのDOMAINS: '[ドメイン名] -> http://web:8080/'
によって、指定ドメインへのアクセスを受けたら、web:8080/
に転送してくれます。ここで、web
とはhttps-portalコンテナを示しますが、そのアクセスを受け付けるのはhttps-portalコンテナ内で動いているnginxです。そのnginxの設定を行うのが、「mysite_nginx.conf.temp」です。
.tempとしているのは、環境変数を直接${環境変数}
と記入してもnginxのコンフィグファイルは読み取ってくれないため、コンテナ起動時に環境変数を置換するようなスクリプトで対応するためです。
$ mkdir -p ~/work/nginx/conf/
$ vi ~/work/nginx/conf/mysite_nginx.conf.temp
【編集内容】
upstream django {
ip_hash;
server DJANGO_CONTAINER_NAME:DJANGO_CONTAINER_PORT;
}
server {
listen NGINX_WATCH_PORT;
server_name dockerhost;
charset utf-8;
client_max_body_size 75M;
location /static {
alias /static;
}
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
}
- 指定ドメインへのアクセスを受けたら、https-portalコンテナに
NGINX_WATCH_PORT
で転送されるので、nginxが監視するのはNGINX_WATCH_PORT
を設定します。 server_name
にはホスト側のIPアドレスを設定する必要があるのですが、dockerhost
という変数がホスト側のIPアドレスを格納しているので、それを使用します。location /static
でアクセスがあったら、/staticを返すようにしています。これで、staticファイルはDjangoまでアクセスしないようにしています。location /
でアクセスあったら、uwisgiを介してDJANGO_CONTAINER_NAME:DJANGO_CONTAINER_PORT
へ通信が引き継がれます。ここら辺の書き方は、Djangoにuwisgiを使ってアクセスするときの一般的な書き方です。(自分もあまり詳しくないです。。。)
「nginx/script/11-set-nginx-env」を作成する
上で「nginx/conf/mysite_nginx.conf.temp」を作成したときにも記載しましたが、環境変数を直接${環境変数}
と記入してもnginxのコンフィグファイルは読み取ってくれません。そのため、https-portalコンテナが起動するときに環境変数を置換するスクリプトを実行させます。
ここでは、そのスクリプトを作成します。
$ mkdir -p ~/work/nginx/script/
$ vi ~/work/nginx/script/11-set-nginx-env
【編集内容】
#!/usr/bin/with-contenv sh
cp /etc/nginx/conf.d/mysite_nginx.conf.temp /etc/nginx/conf.d/mysite_nginx.conf
sed -i -e "s/DJANGO_CONTAINER_NAME/$DJANGO_CONTAINER_NAME/g" /etc/nginx/conf.d/mysite_nginx.conf
sed -i -e "s/DJANGO_CONTAINER_PORT/$DJANGO_CONTAINER_PORT/g" /etc/nginx/conf.d/mysite_nginx.conf
sed -i -e "s/NGINX_WATCH_PORT/$NGINX_WATCH_PORT/g" /etc/nginx/conf.d/mysite_nginx.conf
#!/usr/bin/with-contenv sh
はhttps-portalが起動時スクリプトとして「s6-overlay」を利用しており、スクリプト内で環境変数を定義できるようにするおまじないです。sed
コマンドで、環境変数の置換をしています。
「11-set-nginx-env」ができたら、スクリプトに実行権限を与えます。
$ sudo chmod +x ~/work/nginx/script/11-set-nginx-env
「nginx/uwsgi_params」を作成する
以下のコマンドで「nginx/uwsgi_params」を作成します。
このファイルはuwsgiサーバで使用するパラメータを定義するファイルになります。
$ vi ~/work/nginx/uwsgi_params
【編集内容】
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
その他で使用するディレクトリを作成する
あとは、以下のコマンドで、DBマウント用の「~/work/db/dbdata」、Djangoのプロジェクトファイル格納用の「~/work/src」、httpsの証明書関連を格納する「~/work/nginx/ssl_certs」ディレクトリを作成します。
$ mkdir -p ~/work/db/dbdata
$ mkdir -p ~/work/src
$ mkdir -p ~/work/nginx/ssl_certs
- 「~/work/db/dbdata」配下のファイルはPosreSQLのコンテナ生成時に自動でできあがります。ただし、コンテナ側で実行する権限がroot権限のため、dbdata配下はroot権限となってしまうことに注意です。
- 「~/work/src」配下のファイルは、コマンドを入力することで Djangoのプロジェクトファイルを作成します。やり方は後述します。
- 「~/work/nginx/ssl_certs」配下のファイルはhttps-portalのコンテナ生成時に自動でできあがります。
Djangoのプロジェクトを作成する(/src配下のファイルを作成する)
/src配下にはDjangoのプロジェクトファイルが入るので、ここではその設定を行います。
Djangoのプロジェクトファイルを作成するには、Djangoの環境が構築されたサーバ上で「django-admin startproject
」というコマンドを実行する必要があります。
そのため、上で定義したdjangoコンテナを一度立ち上げて、djangoコンテナ上でDjangoプロジェクトを作成する「django-admin startproject
」というコマンドを実行する。
djangoコンテナ上でコマンドを打つのは、以下のようなコマンドをホスト側で実行すればOK
$ cd ~/work/
$ docker-compose run django django-admin startproject mysite .
docker-compose run django
で、djangoコンテナを立ち上げています。- その後、
django-admin startproject mysite .
がdjangoコンテナ 上で実行されるコマンドです。
【実行結果】
これを実行後、/srcディレクトリを確認すれば、Djangoのプロジェクトファイルが出来上がっているはずです。
`-- src
|-- manage.py
`-- mysite
|-- __init__.py
|-- asgi.py
|-- settings.py
|-- urls.py
`-- wsgi.py
Djangoのプロジェクトファイルを編集する
Djangoのデフォルトの設定では、外部からのアクセスが禁止されているので、外部からのアクセスを有効にします。また、DjangoからDBにアクセスできるように、DBの設定やstaticファイルをnginxからアクセスできるように、 staticファイルをsrc/staticに統合する設定を行います。
以下のコマンドで、「~/work/src/mysite/settings.py」を変更します。
$ sudo vi ~/work/src/mysite/settings.py
【変更点】
ALLOWED_HOSTS = ["*"]
# ~~~~
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER_NAME'],
'PASSWORD': os.environ['DB_PASSWORD'],
'HOST': os.environ['DB_CONTAINER_NAME'],
'PORT': os.environ['DB_CONTAINER_PORT']
}
}
# ~~~~
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
また、以下のコマンドで、staticファイルを/src/staticに集約させます。
$ docker-compose run django ./manage.py collectstatic
コンテナを起動させる
これで、設定は終了です。
最後に、Docker Composeを実行して、コンテナを起動させます。
$ cd ~/work
$ docker-compose up
【実行結果】
以下のように表示されればOK
Starting db ... done
Starting django ... done
Starting nginx ... done
Djangoにアクセスしてみる
ブラウザから、「https://サーバのIPアドレス」にアクセスしてみてください。
ドメインを設定しているなら、「https://ドメイン名」でもアクセスできるはずです。
以下のように、ロケットが表示されればうまくDjangoが起動しています。また、URLの横に鍵マークがあることが確認できれば、httpsでアクセスも成功しています。
関連情報
ConoHa上でDockerを導入したり、Webアプリを立ち上げたりした内容を以下の記事でまとめました。こちらもよろしければご覧になってください。
広告
Dockerをやるなら以下の書籍がオススメです。
Dockerとは何か?から複数のコンテナを管理することができるDocker Compose、Kubernetesまでまとめられています。
ConoHa VPS
は初期費用不要で月に数百円で利用できる仮想サーバのサービスです。以下のような方には非常にオススメのサービスとなっています!
- 勉強がてらLinuxの環境をちょっと触ってみたい
⇒管理者権限が実行可能なLinuxサーバ環境が構築可能です! - スモールスタートでサービスを提供して、うまくいったら規模をスケールアップしたい
⇒後からメモリサイズやCPU数などのスケールアップ/スケールダウン可能です! - AWSやGCPなどのクラウドサービスは高いので、もっと安くサーバ構築したい
⇒初期費用なし、月数百円(1時間単位も可)で利用可能です!
以上!
コメント