10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Web3層構造 on Docker ~nginx, django, postgreSQL の連携~

Last updated at Posted at 2021-06-29

はじめに

今回作成するもの

Group_3.png

事前準備

・パブリックにアクセス可能なサーバ(本記事内の作業ではEC2インスタンスを使用。OSはAmazon Linux 2を選択。)
・Python3系のインストール(本記事内の作業では 3.8.9 のバージョンを使用。記事の後半にpyenv経由のインストールの手順あり。)

docker-compose の動作確認 (nginx を起動するまで)

dockerのインストール

EC2インスタンスにsshでログインし、下記コマンドを実行してdockerをインストールする。
また、docker の起動 (start) 及びインスタンス起動時に自動で起動 (enable) できるようにする。

sudo yum install -y docker
sudo systemctl start docker
sudo systemctl enable docker
sudo docker info # 確認

ec2-user を docker グループに追加する

これにより sudo なしで docker コマンドが打てるようになる。
また、docker 公式ドキュメントにあるように

If testing on a virtual machine, it may be necessary to restart the virtual machine for changes to take effect.

グループ追加後にインスタンスを再起動する。

sudo usermod -aG docker $USER
cat /etc/group | grep docker # 確認
sudo reboot # 再起動

docker-composeのインストール

docker 公式ドキュメントを確認し、version を決定する(今回は1.29.2)。

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose version # 確認

docker-compose からコンテナの起動を確認する

docker-composeでnginxのイメージを取得してテストページを表示してみる。

docker-compose.yml
version: '3.7'
services:
    nginx:
        container_name: nginx
        image: nginx:latest
        ports:
            - 80:80

以下コマンドを実行しコンテナを立ち上げる。

docker-compose up -d

EC2インスタンスのIPアドレスをブラウザのURLバーに入れて表示してみる。
Screen Shot 2021-06-20 at 12.21.32.png
上のようなnginxのテストページが見えていればOK。
下記コマンドで一旦終了させる。

docker-compose down

3層アーキテクチャを構築する

nginxコンテナの準備(Webサーバ)

ディレクトリ構造

containers/
 ├ django/
 │ └ ...
 ├ nginx/
   └ conf/
    └ nginx.conf
   └ uwsgi_params
 ├ docker-compose.yml

volumeの割り当てと依存関係を追加する。

docker-compose.yml
version: '3.7'
services:
    nginx:
        container_name: nginx
        image: nginx:latest
        volumes: # add
            - ./nginx/conf:/etc/nginx/conf.d # add
            - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params # add
        ports:
            - 80:80
        depends_on: # add
            - django # add

nginx.conf を作成する。
今回は upstream ディレクティブの記述にあるように django コンテナの8000番ポートへ通信を流す設定を行う。

nginx.conf
upstream django {
    server django:8000;
}

server {
    listen 80;
    location / {
        uwsgi_pass django;
        include /etc/nginx/uwsgi_params;
    }
}

wsgiの通信で必要とされる uwsgi_params の作成を行う(参考)。

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 REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_ADDR $server_addr;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;

djangoコンテナの準備(アプリケーションサーバ)

ディレクトリ構造

containers/
 ├ django/
 │ └ startup
 │   └ setuser.sh
 │   └ startup.sh
 │ └ uwsgi
 │   └ uwsgi.ini
 │ └ Dockerfile
 │ └ requirements.txt
 ├ nginx/
 │ └ ...
 ├ docker-compose.yml

djangoコンテナ側でnginxコンテナからの通信を受けられるように、uwsgi.ini の作成を行う。

uwsgi.ini
[uwsgi]
socket = :8000
module = djangoapp.wsgi
wsgi-file = /app/app/wsgi.py
logto = /wsgi/wsgi.log
py-autoreload = 1

djangoコンテナのイメージの元となるDockerfileを作成する。

Dockerfile
FROM python:3
WORKDIR /app
COPY requirements.txt /app
RUN pip install -r requirements.txt
COPY . /app

djangoコンテナで必要とするPythonのモジュールのリストを作成する。

requirements.txt
django
psycopg2
uwsgi

djangoコンテナ内で実行されるシェルスクリプト(startup.sh, setuser.sh)を作成する。
userを作成し、uwsgi,ini を実行する。

startup.sh
source /startup/setuser.sh # setuser.sh を実行する
uwsgi --ini /wsgi/uwsgi.ini # uwsgi.ini の設定をもとに uwsgi を実行する
setuser.sh
#!/bin/bash -e
SHELL_NAME='setuser.sh'
echo "[$SHELL_NAME] START"

# setup group
if getent group "$GROUP_ID" > /dev/null 2>&1; then
    echo "[$SHELL_NAME] GROUP_ID '$GROUP_ID' already exists."
else
    echo "[$SHELL_NAME] GROUP_ID '$GROUP_ID' does NOT exist. So execute [groupadd -g \$GROUP_ID \$GROUP_NAME]."
    groupadd -g $GROUP_ID $GROUP_NAME
fi

# setup user
if getent passwd "$USER_ID" > /dev/null 2>&1; then
    echo "[$SHELL_NAME] USER_ID '$USER_ID' already exists."
else
    echo "[$SHELL_NAME] USER_ID '$USER_ID' does NOT exist. So execute [useradd -m -s /bin/bash -u \$USER_ID -g \$GROUP_ID \$USER_NAME]."
    useradd -m -s /bin/bash -u $USER_ID -g $GROUP_ID $USER_NAME
fi

echo "[$SHELL_NAME] FINISH"

startupディレクトリ配下の全てのファイルに実行権限を付与する。

chmod +x -R ~/dev/3l_on_docker/django/startup/

ホスト側でdjangoプロジェクトを作成する。
そのためまずはホスト側でdjangoのインストールを行う。

pip install django

django ディレクトリ内でdjango projectの作成を行う。
今回はプロジェクト名をdjangoappとする。

django-admin startproject djangoapp

プロジェクトを作成するとdjangoディレクトリ内にdjangoappディレクトリが作成される。
デフォルトでは外部からの通信は全て拒否されているため、setting.py の修正を行う。

setting.py
...

ALLOWED_HOSTS = ['*']

...

今回は外部の全ての通信を許可する'*'を設定した。

docker-compose up -dでコンテナを立ち上げ、EC2のパブリックIPアドレスにアクセスする。

Screen Shot 2021-06-24 at 9.54.07.png

上のような django のテストページが見えていればOK。

サンプルアプリケーションの作成

テストページが見れたので、ここからは自身で作成したページを表示する。
まずはホスト側でアプリケーションを作る。

python manage.py startapp sampleapp

sampleapp ディレクトリ直下に urls.py を作成する。

urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
views.py
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

http://[IP アドレス]/[app 名]にアクセスし、"Hello, world. You're at the polls index."が確認できればOK。

データベースのレイアウト(models.py の編集)

models.py
class User(models.Model):
    user_name = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class FigurePaths(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    figure_url = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    like_point = models.IntegerField(default=0)
setting.py
INSTALLED_APPS = [
    'instalikeapp.apps.InstalikeappConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
python manage.py makemigrations sampleapp

migration ディレクトリ内に0001_initial.pyが作られる。

docker exec -it bashで django コンテナに入り sqlmigratemigrate を実行する
これによってデータベースへデータを入力するための準備を完了する。

[ec2-user@ip-10-0-1-89 djangoapp]$ docker exec -it django bash
root@87e83722b47a:/app# python manage.py sqlmigrate sampleapp 0001
root@87e83722b47a:/app# python manage.py migrate

postgreSQLコンテナの準備(DBサーバ)

postgresqlコンテナの設定を追加する。

docker-compose.yml
version: '3.7'
services:
    nginx:
        container_name: nginx
        image: nginx:latest
        volumes:
            - ./nginx/conf:/etc/nginx/conf.d
            - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
        ports:
            - 80:80
        depends_on:
            - django
    
    django:
        container_name: django
        build: ./django
        command: bash -c /startup/startup.sh
        volumes:
            - ./django/uwsgi:/wsgi
            - ./django/djangoapp:/app
            - ./django/startup:/startup
        ports:
            - 8000:8000
        environment:
            - USER_ID=1000
            - GROUP_ID=1000
            - USER_NAME=ec2-user
            - GROUP_NAME=ec2-user
        depends_on: # add
            - postgresql # add

    postgresql: # add
        image: postgres:latest # add
        container_name: postgresql # add
        environment: # add
            - POSTGRES_DB=instalikeapp_db # add
            - POSTGRES_USER=user # add
            - POSTGRES_PASSWORD=password # add
        volumes: # add
            - ./pgdata:/var/lib/postgresql/data # add
        ports: # add
            - 5432:5432 # add

コンテナを立ち上げ直す。

docker-compose restart

djangoコンテナ内のpython の インタプリタからデータベースにデータを入れてみる

詳細はdjangoのチュートリアルを参考にすると良い。
djangoコンテナに入り、pythonのインタプリタからデータベースへの入力を行う。

docker exec -it django bash
root@87e83722b47a:/app# python manage.py shell
>>> from instalikeapp.models import User, FigurePaths

下記コマンドでpostgresqlにログインする。

psql -d myapp -U USERNAME -h localhost

djangoコンテナからデータベースへデータの入力を行い、postgresqlコンテナでデータベースの中身を確認する。
以下の図の左はdjangoコンテナ内でデータベースへの入力操作を行なったもの、 右側はpostgresqlコンテナ内でデータベースの中身を確認したもの。
Screen Shot 2021-06-24 at 13.17.23.png

おまけ

Python 3系のインストール(pyenv経由)

sudo yum install git
git clone https://github.com/yyuu/pyenv.git ~/.pyenv
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init --path)"' >> ~/.bashrc
echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\nfi' >> ~/.bashrc
source ~/.bashrc
sudo yum install gcc zlib-devel bzip2 bzip2-devel readline readline-devel sqlite sqlite-devel openssl openssl-devel -y
pyenv install 3.8.9
pyenv global 3.8.9

DjangoサイトにCSSを反映させる

setting.py
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static') # add

プロジェクト直下のstaticディレクトリに静的ファイルをコピーする

python manage.py collectstatic
nginx.conf
server {
    listen 80;
    
    location / {
        uwsgi_pass django;
        include /etc/nginx/uwsgi_params;
    }

    location /static/ {
        root    /app/;
    }
}
10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?