LoginSignup
1

More than 1 year has passed since last update.

EC2でwebアプリを作成したメモ

Last updated at Posted at 2022-12-12

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ソケットを使用した方が良いという予想.
他にも改善できる設定は存在すると思うので,勉強中.

参考

  1. Request and response objects, Documentation, django (https://docs.djangoproject.com/en/4.1/ref/request-response/#httpresponse-objects)
  2. リクエストとレスポンスのオブジェクト, Django 4.0.6 ドキュメント (https://man.plustar.jp/django/ref/request-response.html)

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
1