はじめに
今回作成するもの
事前準備
・パブリックにアクセス可能なサーバ(本記事内の作業では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のイメージを取得してテストページを表示してみる。
version: '3.7'
services:
nginx:
container_name: nginx
image: nginx:latest
ports:
- 80:80
以下コマンドを実行しコンテナを立ち上げる。
docker-compose up -d
EC2インスタンスのIPアドレスをブラウザのURLバーに入れて表示してみる。
上のようなnginxのテストページが見えていればOK。
下記コマンドで一旦終了させる。
docker-compose down
3層アーキテクチャを構築する
nginxコンテナの準備(Webサーバ)
ディレクトリ構造
containers/
├ django/
│ └ ...
├ nginx/
└ conf/
└ nginx.conf
└ uwsgi_params
├ docker-compose.yml
volumeの割り当てと依存関係を追加する。
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番ポートへ通信を流す設定を行う。
upstream django {
server django:8000;
}
server {
listen 80;
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
}
wsgiの通信で必要とされる 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]
socket = :8000
module = djangoapp.wsgi
wsgi-file = /app/app/wsgi.py
logto = /wsgi/wsgi.log
py-autoreload = 1
djangoコンテナのイメージの元となるDockerfileを作成する。
FROM python:3
WORKDIR /app
COPY requirements.txt /app
RUN pip install -r requirements.txt
COPY . /app
djangoコンテナで必要とするPythonのモジュールのリストを作成する。
django
psycopg2
uwsgi
djangoコンテナ内で実行されるシェルスクリプト(startup.sh, setuser.sh)を作成する。
userを作成し、uwsgi,ini を実行する。
source /startup/setuser.sh # setuser.sh を実行する
uwsgi --ini /wsgi/uwsgi.ini # uwsgi.ini の設定をもとに uwsgi を実行する
#!/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 の修正を行う。
...
ALLOWED_HOSTS = ['*']
...
今回は外部の全ての通信を許可する'*'
を設定した。
docker-compose up -d
でコンテナを立ち上げ、EC2のパブリックIPアドレスにアクセスする。
上のような django のテストページが見えていればOK。
サンプルアプリケーションの作成
テストページが見れたので、ここからは自身で作成したページを表示する。
まずはホスト側でアプリケーションを作る。
python manage.py startapp sampleapp
sampleapp ディレクトリ直下に urls.py を作成する。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
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 の編集)
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)
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 コンテナに入り sqlmigrate
と migrate
を実行する
これによってデータベースへデータを入力するための準備を完了する。
[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コンテナの設定を追加する。
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コンテナ内でデータベースの中身を確認したもの。
おまけ
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を反映させる
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static') # add
プロジェクト直下のstaticディレクトリに静的ファイルをコピーする
python manage.py collectstatic
server {
listen 80;
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
location /static/ {
root /app/;
}
}