Guide
Zennでも同じ記事を投稿しています。以下のリンクから参照できます。
概要
AWS EC2+Nginx+gunicorn+Django+PostgreSQLでシンプルなwebアプリを作成したログ.
以下の内容は,すでにAWS EC2へSSH接続していることを前提としている.
環境構築
今回の記事で使用するパッケージなどのインストール方法をまとめて記載する.
$ sudo yum install git
$ sudo yum install openssl-devel
$ sudo yum install python-psycopg2
$ sudo amazon-linux-extras install nginx1
$ sudo amazon-linux-extras install postgresql14
Pythonのversionを管理するために,pyenv
をインストールする.
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
$ echo '# pyenv' >> ~/.bashrc
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(pyenv init --path)"' >> ~/.bashrc
$ source .bashrc
$ pyenv -v
pyenv 2.3.6-15-g13d85686
プロジェクトディレクトリを作成する.
Pythonバージョンを3.9.13
とし,venv
で仮想環境を作成する.
$ mkdir ~/django_app
$ cd ~/django_app/
$ pyenv install 3.9.13
$ python -V
Python 3.9.13
# 仮想環境`venv`を作成する
$ python -m venv venv
# 仮想環境に入る
source activate venv/bin/activate
# pipを最新バージョンに更新する
pip install --upgrade pip
pipで必要なpythonモジュールをインストールする.
# Djano関連
$ pip install django
$ pip install django-environ
$ pip install dj-database-url
$ pip install djangorestframework
$ pip install mojimoji
# PostgreSQL関連
$ pip install -U setuptools
$ pip install psycopg2
# Gunicorn
$ pip install gunicorn
Webサーバ構築
WebサーバとしてNginxを利用する.
# バージョン確認
$ sudo nginx -v
nginx version: nginx/1.22.0
Nginxの設定
Nginxの初期設定ファイルのバックアップを取る
$ sudo cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup
Nginxの通信データをhttp://127.0.0.1:8000
へ送るように設定する.
これにより,サーバの80
番ポートに来たアクセスをDjango側へ転送できる.
$ sudo vim /etc/nginx/nginx.conf
以下のように記述.
~
http{
~
server{
~
location / {
# `http://xx.xx.xx.xx/`へ来たアクセスをDjango側へ転送する.
proxy_set_header Host $http_host; # host name
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # source address
proxy_set_header X_Forwarded-Proto $scheme; # URL scheme
# localhostの8000番ポートへ転送する.
proxy_pass http://127.0.0.1:8000;
}
# ここまで
~
}
~
}
~
Nginxを起動する.
$ sudo systemctl start nginx
インスタンス起動時にNginxも自動で起動させる.
$ sudo systemctl enable nginx
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.
サービスステータス表示する.
$ systemctl status nginx
● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
Active: active (running) since {DayOfWeek} {Year}-{Month}-{Day} {Hour}:{Minute}:{Seconds} UTC; 28s ago
Main PID: 26497 (nginx)
CGroup: /system.slice/nginx.service
├─26497 nginx: master process /usr/sbin/nginx
├─26498 nginx: worker process
└─26499 nginx: worker process
PostgreSQLの設定
Djangoアプリケーションで利用するためのDBを構築する.
初期化
まず,データベースサーバを初期化する.
$ sudo /sbin/service postgresql initdb
Hint: the preferred way to do this is now "postgresql-setup initdb"
Initializing database ... OK
サービス起動設定
今回の記事では,サーバ内でDBサーバを構築する方法を取った.
データベースサーバを起動する.
$ sudo /sbin/service postgresql start
$ sudo systemctl status postgresql
● postgresql.service - PostgreSQL database server
Loaded: loaded (/usr/lib/systemd/system/postgresql.service; disabled; vendor preset: disabled)
Active: active (running) since ** **-**-** **:**:** UTC; **min **s ago
Process: 9585 ExecStart=/usr/bin/pg_ctl start -D ${PGDATA} -s -o -p ${PGPORT} -w -t 300 (code=exited, status=0/SUCCESS)
Process: 9580 ExecStartPre=/usr/bin/postgresql-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
Main PID: 9589 (postgres)
CGroup: /system.slice/postgresql.service
├─9589 /usr/bin/postgres -D /var/lib/pgsql/data -p 5432
├─9590 postgres: logger process
├─9592 postgres: checkpointer process
├─9593 postgres: writer process
├─9594 postgres: wal writer process
├─9595 postgres: autovacuum launcher process
└─9596 postgres: stats collector process
自動起動設定をする.
$ sudo systemctl enable postgresql
Created symlink from /etc/systemd/system/multi-user.target.wants/postgresql.service to /usr/lib/systemd/system/postgresql.service.
DB作成
Djangoアプリが利用するDBを作成する.
$ sudo -u postgres psql
psql (9.2.24)
Type "help" for help.
postgres=#
以下のように入力し,DBを作成した.
-- 新規にDBを作成する.
postgres=# CREATE DATABASE djangodb;
CREATE DATABASE
-- ユーザ名とパスワードを設定する.
postgres=# CREATE USER django WITH PASSWORD '****';
CREATE ROLE
-- 文字コードを`UTF-8`にする.
postgres=# ALTER ROLE django SET client_encoding TO 'utf8';
ALTER ROLE
-- トランザクション分離レベルを設定する.
postgres=# ALTER ROLE django SET default_transaction_isolation TO 'read committed';
ALTER ROLE
-- タイムゾーンを東京/日本とする.
postgres=# ALTER ROLE django SET timezone TO 'UTC+9';
ALTER ROLE
-- `django`ユーザにDBの全ての権限を付与する.
postgres=# GRANT ALL PRIVILEGES ON DATABASE djangodb TO django;
GRANT
-- psqlを修了する.
postgres-# \q
postgresqlの設定ファイルのバックアップを取り,認証方法を変更する.
$ sudo cp /var/lib/pgsql/data/pg_hba.conf /var/lib/pgsql/data/pg_hba.conf.backup
$ sudo vim /var/lib/pgsql/data/pg_hba.conf
変更箇所は以下の通り.認証方法をident
からmd5
へ変更する.
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all peer
# IPv4 local connections:
host all all 127.0.0.1/32 md5 # <-- here!
# IPv6 local connections:
host all all ::1/128 md5 # <-- here!
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication postgres peer
host replication postgres 127.0.0.1/32 ident
host replication postgres ::1/128 ident
設定ファイルの変更を適用するため,serviceを再起動する.
$ sudo systemctl restart postgresql
簡易的なWebアプリ作成
Djangoプロジェクトの作成
Djanoでプロジェクトを作成する.
$ cd ~/django_app/
$ django-admin startproject website .
~/django_app/website/settings.py
を修正する.
# DBへのパス
DATABASE_URL=postgres://django:{password}@localhost:/djangodb
# アクセスを許可するホスト
ALLOWED_HOSTS={Your IP},localhost,127.0.0.1
# URLの末尾に`/`がない時,`/`を付けたURLへリダイレクトしない
APPEND_SLASH=False
# タイムゾーンを日本に設定
TIME_ZONE = 'Asia/Tokyo'
# 日本語
LANGUAGE_CODE = 'ja'
# 静的ファイルが集約されるパス
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
gunicornを起動
- gunicornを起動する.
-
127.0.0.1:8000
をバインドし,Nginxからのデータを受け取る. - Djangoプロジェクトのwsgi設定ファイル
~/django_app/website/wsgi.py
を指定する. - バックグラウンドで実行する.
-
# Djangoプロジェクトのrootに移動する
$ cd ~/django_app/
# 仮想環境に入る(既に入っていたら必要なし)
$ source venv/bin/activate
$ gunicorn --bind 127.0.0.1:8000 website.wsgi -D
簡単なテスト
現在,EC2のIPアドレスへHTTPリクエストを送信すると,ポート80
でリクエストを受信したNginxがローカルホストのポート8000
へデータを転送し,gunicornが転送されてきたデータをdjango_app
のwsgiに紐づけて,Djangoアプリがリクエストを処理する,という仕組みになっている.
試しに,別のホストからcurl
でEC2サーバにHTTP GET リクエストを送信してみる.
$ curl http://{Your IP}/
レスポンスとして,Djangoのロケットのページを見ることができる.
簡単なAPIの実装
今回は,DBへのデータの登録と呼び出し,削除機能を実装する.
まず,API実装のためのrest_framework
をプロジェクトへ追加する.~/django_app/website/settings.py
を編集する.
INSTALLED_APPS = [
...
'rest_framework', #new
]
DB操作を実現するためのアプリを作成する.
- アプリ名:
crud_app
$ python manage.py startapp crud_app
新規にアプリを作成したため,~/django_app/website/settings.py
へ追記する.
INSTALLED_APPS = [
...
'rest_framework',
'crud_app', #new
]
今回は,以下に示す簡易的なテーブルを作成する.
テストテーブル
-----------------
名前 (char)
数量 (int)
作成日時 (date)
~/django_app/crud_app/models.py
を以下のように記載し,DBモデルを作成する.
from django.db import models
class DjangoModel(models.Model):
name = models.CharField("Name", max_length=240) # 名前
amount = models.IntegerField("Amount", default=1) # 数量
created = models.DateField(auto_now_add=True)
def __str__(self):
return self.name
プロジェクトのrootで,マイグレーションを適用し,dbカラムを作成する.
$ python manage.py makemigrations
$ python manage.py migrate
~/django_app/crud_app/views.py
を編集し,リクエストに応じてDBを操作する.
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
from django.http.response import JsonResponse
import json
from .models import DjangoModel
@csrf_exempt
def control_data(request):
"""データの新規作成,確認,削除を行うインターフェース"""
# 確認
if request.method == 'GET':
# `name`で昇順にソートし,全件取得
models = DjangoModel.objects.order_by('name')
# レスポンス作成
all_models_dict = {}
for model in models:
all_models_dict[model.name] = model.amount
return JsonResponse(all_models_dict)
# 新規作成
if request.method == 'POST':
# HTTPリクエストbodyのデータをjson形式で取得する
data = json.loads(request.body)
# `amount`がinputになければ,デフォルト値を代入する
if not 'amount' in data:
data['amount'] = 1
# 新規作成
model = DjangoModel(name=data['name'], amount=data['amount'])
model.save()
# レスポンス作成
response = HttpResponse('created!')
return response
# 全て削除
elif request.method == 'DELETE':
# レコード削除
DjangoModel.objects.all().delete()
# レスポンス作成
response = HttpResponse('deleted!')
return response
# 未実装のHTTP methodへの処理
else:
return HttpResponse('Not Implemented.')
特定のURLにアクセスしたとき,呼び出す処理を記載する.
~/django_app/crud_app/urls.py
を作成する.
from django.urls import include, path
from .views import control_data
urlpatterns = [
path('', control_data, name='control-data'),
]
rootのurls.py
からdjango_app
アプリのurls.py
へ接続するようにする.
~/django_app/website/urls.py
を編集する.
from django.urls import path, include
urlpatterns = [
path('/', include('django_app.urls')),
]
以上により,簡易なAPIの実装を終えた.
Djangoアプリの機能がいくつか変更・追記されたので,gunicornを再度起動し,変更を適用する.
$ pkill gunicorn
$ gunicorn --bind 127.0.0.1:8000 website.wsgi -D
APIのテスト
実装したAPIが正常に動作確認するために,curl
でテストできる.
# コマンド例
## 新規登録
$ curl -d '{"name": "computer", "amount": 3}' -H 'Content-Type: application/json' http://{Your IP}/
created!
## 確認
$ curl http://{Your IP}/
{"computer":3}
## 削除
$ curl -X DELETE http://{Your IP}/
deleted!
## 確認
$ curl http://{Your IP}/
{}
改善点など
この記事の設定では,nginxとDjangoはHTTPで通信するが,UNIXソケットを使用するようにすると高速な通信が期待できる.おそらく,多くの同時アクセスを想定する場合は,UNIXソケットを使用した方が良いという予想.
他にも改善できる設定は存在すると思うので,勉強中.
参考
- Request and response objects, Documentation, django (https://docs.djangoproject.com/en/4.1/ref/request-response/#httpresponse-objects)
- リクエストとレスポンスのオブジェクト, Django 4.0.6 ドキュメント (https://man.plustar.jp/django/ref/request-response.html)