Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

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を使ってみよう。