Docker Composeを使った、Nginx+uWSGI+Djangoのデプロイ
概要
ElasticBeanstalkのDocker マルチコンテナでのデプロイのために、ひとまずDocker composeで、Nginx+uWSGI+Djangoのデプロイをしてみる。
参考サイト
https://dockify.io/a-django-uwsgi-postgres-step-by-step-howto/dockify.io
やってみたこと
- djangoプロジェクトを作る
- uwsgiの設定
- docker-compose.ymlを作成する。
- nginxを設定する
djangoプロジェクトを作る
djangoプロジェクトを作成しておく
/code/myproject/ | -- myapp/ | -- manage.py | -- myapp/ | -- __init__.py | -- settings.py | -- urls.py | -- wsgi.py | -- requirements.txt
requirements.txtの中身
Django==1.10.3 uwsgi psycopg2
settings.pyのstaticfileの設定
# staticfile関連の設定 ... # staticfile関連の設定 STATIC_URL = '/static/' MEDIA_URL = '/media/' STATIC_ROOT = '/static' MEDIA_ROOT = '/media' STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), )
uWSGIの設定
Dockerfileを作成する。
code/myproject/Dockerfile
FROM python:2.7 RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi COPY /myapp/requirements.txt /requirements.txt RUN pip install -r /requirements.txt COPY /myapp /app RUN chown -R uwsgi /app COPY uwsgi.sh /uwsgi.sh RUN chmod +x /uwsgi.sh
ポイント
- python:2.7のイメージからビルドする。
- groupadd -r はよくわかんないけど、-rは、再帰的という意味があるからそういう意味なのかも。
- groupadd でuwsgiグループを作って、useraddで、-g uwsgiグループに、uwsgiユーザを作る
- 再帰的なのは、自分をそのuserにするということなのかな。ここでは、uwsgiグループのuwsgiユーザーになる。
- myappを全部 コンテナのappにコピーする。
- chown -R uwsgi /appで、appのオーナーをuwsgiにする。
- uwsgi.sh を、uwsgi.shとしてコンテナにコピーする
- chmod +x で、実行権限を付与する。(userのオプションを省略しているので、全てのuserにという意味かな)
uwsgi.shを作成する。
#!/bin/sh chown uwsgi /static chown uwsgi /media su -m uwsgi -c "python /app/manage.py collectstatic --noinput" su -m uwsgi -c "/usr/local/bin/uwsgi --socket :5000 --wsgi-file /app/myapp/wsgi.py --master --processes 4 --threads 2 --chdir /app"
ポイント
- static, media以降のオーナーをuwsgiにしている。
- su とsu -の違い
- su:rootに変身するが、switch userしたディレクトリのまま。
- su - :rootに変身して、rootのホームディレクトリに勝手に移動する。`
- su -m - > -m, -p 環境変数"HOME","USER","LOGNAME","SHELL"を変更しない
- -c command ユーザーの切り替え後にcommandを実行する
- su -m uwsgi -c "python /app/manage.py collectstatic --noinput"は、uwsgiに、切り替えて、collectstaticを実行する。
- socket:5000 :socket :5000で、nginxとやりとりをする。
- prosesses 4 : ワーカープロセスの数
- threads 2 : 並列処理の数
- chdir: Python の import パスに必要なディレクトリのパスです。 – 例では myapp パッケージを含むディレクトリです。
うーん、linuxコマンドはやっぱり難しいなぁ。
docker-compose.ymlを作成する。
code/myproject/docker-compose.yml を作成する。
uwsgi: build: . links: - postgres command: ./uwsgi.sh volumes: - /static - /data/media:/media
ポイント
- build: . -> さっき作った同じディレクトリにある、Dockerfileからbuildする。(ここはdockerrun.aws.jsonだとimageである必要あり。)
- links: コンテナとリンクさせる。
- command: コンテナ内で動かすコマンド
- volumes: /static ボリュームをマウントする->そのコンテナ内の指定した場所(ボリューム)を、利用可能な状態にする(マウントする)こと。
- なので、volumes: /staticは、staticを利用可能な状態にしている。
- ホスト側で、マウントするパスを指定する場合は、ホストのディレクトリパス: コンテナのディレクトリパスを指定する。(ホストの /data/mediaを、コンテナの/mediaにマウント可能な状態にする。
Nginxを設定する。
nginx用のDockerfileを作成する。
FROM nginx:latest ADD nginx.conf /etc/nginx/nginx.conf
nginx.confファイルを取得する。
$ docker run -it nginx:latest /bin/bash /# cat /etc/nginx/nginx.conf ※もしくは、docker cp を使う
nginx.confファイルを修正する。(upstream)
include /etc/nginx/conf.d/*.conf;
を削除して、置き換える。
- include /etc/nginx/conf.d/*.conf; + upstream uwsgi { server uwsgi:5000; }
ポイント
- upstreamは、nginxがrequestを送る先を指定する。ここでは、uwsgiでsocket:5000を指定する。
nginx.confファイルを修正する~バーチャルサーバの設定を追加する。
server { listen 80; charset utf-8; location / { uwsgi_pass uwsgi; include uwsgi_params; } location /static { alias /static; } location /media { alias /media; } }
ポイント:
- listen 80で、webclientからhttp通信を受け取るポートを指定する。(開放する?)
- location / で、/以降のアドレスは、全て
uwsgi_pass uwsgi;
,nclude uwsgi_params;
が反映される。つまり、to our uwsgi upstream server -> uwsgi upstream serverにrequestを流すという意味。 - location /staticで、static以降のアドレスは、nginxの/staticで扱う。(mediaも同様)
nginx.confの変更をまとめる。
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; upstream uwsgi { server uwsgi:5000; } server { listen 80; charset utf-8; location / { uwsgi_pass uwsgi; include uwsgi_params; } location /static { alias /static; } location /media { alias /media; } }
こんな感じのはず。
nginxのcontainerのbuildについてdocker-compose.ymlに追記する。
nginx: build: ./nginx links: - uwsgi volumes_from: - uwsgi ports: - "0.0.0.0:80:80"
ポイント
- build: nginx内のDockerfileからbuildするよう指示(これはdockerrun.aws.jsonでは、imageで指定する。)
- links : uwsgiとリンクさせる
- volumes_from: uwsgi -> uwsgiコンテナの Data Volume を全部マウントする。(全て利用可能な状態にする)
- ports: ホスト:コンテナで設定できる。そのため、ホスト0.0.0.0:80->コンテナ:80に繋いでいる状態
postgre_sqlの設定の箇所を修正する。
- 参考サイトにpostgreの設定がなかったので、sqliteに戻す。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
どのみち、AWSでは、RDSを使うはずなので、postgreの設定はその時にすればいいか(^ ^)
いざdocker-compose build , up
$ docker-compose build
とりあえずこれはうまくいった!
$ docker-compose up ... ERROR: for uwsgi Cannot start service uwsgi: Mounts denied: The path /data/media is not shared from OS X and is not known to Docker. You can configure shared paths from Docker -> Preferences... -> File Sharing. See https://docs.docker.com/docker-for-mac/osxfs/#namespaces for more info. .
Error発生!/data/mediaのフォルダへのマウントがうまくできないとのこと。これはフォルダの権限の問題だと思うんだけど、よくわからないから、/data/mediaの箇所は削除して再度トライ
$ docker-compose up Recreating myproject_uwsgi_1 Creating myproject_nginx_1 Attaching to myproject_uwsgi_1, myproject_nginx_1
お、なんかうまくいったぽい。
localhost:80 にアクセスしてみる。(80はいらないかも)
オーーー、無事表示された!
nginx, uwsgi, djangoだったら、そんなに難しくないぽいな。 コンテナの設定は、nginx, uwsgi+djangoという感じなんだね。
よっしゃ、次は、dockerrun.aws.jsonでやってみよう。 docker-compose.ymlで、buildしたところを、imageに変えればあとは大体一緒のはず。 imageはdocker hubから試してみて、できそうならECRを使ってみよう。