4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DjangoをWSGI + EC2 + NGINXでデプロイしてみた

Last updated at Posted at 2023-03-21

概要

Djangoで作ったアプリをEC2上にデプロイしましたが、色々とハマったので備忘録として記事にまとめました。
とりあえずEC2を立てて独自ドメインで動くところまで書きます。

環境

  • Macbook pro (M1 2020)
  • Python 3.10.8
  • Django 3.2
  • EC2 (Ubuntu 22.04)
  • Nginx 1.18

手順

  1. Djangoアプリを作る
  2. EC2を立てる
  3. ブラウザからDjangoアプリにアクセスできることを確認する
  4. Elastic IPでグローバルIPアドレスを固定する
  5. Nginxを設定する
  6. Route53で独自ドメインを取得
  7. SSL証明取得
  8. wsgiをデーモン化

1. Djangoアプリをつくる

ここでは簡易なDjangoプロジェクトのみ作成します。
Djangoの詳細な開発方法については書籍や他記事を参照してください。

A. 仮想環境を構築する
B. Djangoのプロジェクト・アプリを生成する
C. .envに機密情報を集約する
D. settingsファイルを分割する
E. インストールしたライブラリをrequirements.txtに出力する
F. Githubにpushする

A. 仮想環境を構築する

Pythonのデフォルトで使えるvenvで環境を作ります。

$ python -m venv django_env

仮想環境に入ります。

$ source django_env/bin/activate

必要なライブラリをインストールします。
ここでは2つだけですが、他に必要なライブラリがあれば適宜インストールしてください。

$ pip install django==3.2
$ pip install uwsgi==2.0.21

B. Djangoのプロジェクト・アプリを生成する

Djangoプロジェクトを作り、その下にアプリを作ります。

$ django-admin startproject sample_project .
$ django-admin startapp sample_app

末尾の . を忘れた場合はプロジェクトのディレクトリに移動すればOKです。

$ django-admin startproject sample_project
$ cd sample_project
$ django-admin startapp sample_app

C. .envに機密情報を集約する

ライブラリをインストールします。

$ pip install django-environ

.envファイルを manage.pyと同じで階層に作成してください。
ここにシークレットキーなどを格納します。
DBのパスワードなどもあればここに書いておきます。
環境によって変える変数もここに書いてもOKです (環境変数でもOK) 。
manage.pyと同じ階層に作成してください。

.env
SECRET_KEY=django-insecure-xxxxxxxxxxxxxxxxxxxxxxx
DEBUG=True
ALLOWED_HOSTS=*

settings.pyの該当部分を置き換えます。

sample_project/settings.py
import os
import environ
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# env系の設定
env = environ.Env()
env.read_env(os.path.join(BASE_DIR, '.env'))
SECRET_KEY = env('SECRET_KEY')
DEBUG = env('DEBUG') == 'True'
ALLOWED_HOSTS = [env('ALLOWED_HOSTS')]

...

Githubにアップロードされないように .gitignoreに載せておきます。

.gitignore
.env

D. settingsファイルを分割する

settingsディレクトリ作成し、以下のような構成にします。
settings.pycommon.py に名前を変更します。

├── settings
│   ├── common.py
│   ├── development.py
│   └── production.py

common.py に共通の設定を書いておき、 ログ設定など環境で変わる設定を development.pyproduction.py などに書きます。
環境ごとに読み込むファイルを変えればそれぞれの設定が反映されます。

development.py
from .common import *
...
production.py
from .common import *
...

settings ディレクトリで階層を深くしたので、 BASE_DIR の階層を修正します。
parent を1つ増やします。

common.py
BASE_DIR = Path(__file__).resolve().parent.parent.parent

settings.py を呼び出していたところを修正します。

manage.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample_project.settings.development')
sample_project/wsgi.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample_project.settings.production')

E. インストールしたライブラリをrequirements.txtに出力する

EC2上で同じライブラリをインストールできるように出力しておきます。

$ pip freeze > requirements.txt

F. Githubにpushする

ここまでの変更内容をGithubにpushしておきます。
manage.pyと同じ階層で実行します。
push先のリポジトリは事前にGithub上で作成しておきます。

$ git init
$ git remote add origin git@github.com:xxxxxxxxxxxxxxxxx.git
$ git add .
$ git commit -m 'create django project'
$ git push origin master

2. EC2を立てる

A. ubuntu22.04でEC2を立てる
B. EC2にsshで接続する
C. EC2でpyenvでPythonのバージョンを開発環境と合わせる
D. EC2上でGithubからpullする
E. EC2上に仮想環境を構築する

A. ubuntu22.04でEC2を立てる

AWSコンソールでEC2ページにアクセスします。
右上の「インスタンスを起動」からインスタンスを作成します。
ec2画面.png

サーバーをに名前をつけます。
何でもよければ雑に アプリ名-server あたりにします。
image.png

OSを選択します。
今回はUbuntu22.04でを選択します。
image.png

インスタンスタイプを選択します。
t2.microは1CPU・メモリ1GBなので、デプロイするアプリの規模が大きい場合は高性能なインスタンスを選択します。
image.png

ssh接続などでEC2にアクセスするときに必要です。必ず生成しましょう。
また、再発行ができないので大切に保管しましょう。
image.png

EC2へのアクセス制御を行います。
セキュリティグループを作成する場合、sshトラフィックの許可はプルダウンから「自分のIP」を選択しておきます。
スクリーンショット 2023-03-22 1.59.04.png

右下の「インスタンスを起動」ボタンを押下するとEC2インスタンスが立ち上がります。
image.png

B. EC2にsshで接続する

EC2のコンソールから先程作成したインスタンスを選択してください。
インスタンス概要ページの「接続」を選択すると、SSHクライアントの接続情報が表示されます。
.pem は先程生成したキーペアです。保存した先のPathも含めてください。

$ ssh -i "/to/your/pem_path/xxxxx.pem" ubuntu@ec2-IPアドレス.ap-northeast-1.compute.amazonaws.com

C. EC2でpyenvでPythonのバージョンを開発環境と合わせる

local環境のPythonバージョンを確認します。

$ python -V
Python 3.10.8

EC2にssh接続し、必要なライブラリをインストールします。

$ sudo apt update; sudo apt install build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev curl \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

pyenvをpullします。

$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv

pathを通します。

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
$ echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(pyenv init -)"' >> ~/.bashrc

変更内容を反映します。

$ source .bashrc

pyenvがインストールできていることを確認します。

$ pyenv --version

localのPythonと同じバージョンがインストール可能かチェックします。

$ pyenv install -list

localのPythonと同じバージョンをインストールします。

pyenv install 3.10.8

指定したバージョンがインストールされていることを確認します。

$ pyenv versions

Pythonのバージョンを変更します。

$ pyenv global 3.10.8

指定したバージョンに変更されていることを確認します。

$ python -V
Python 3.10.8

D. EC2上でGithubからpullする

sshのディレクトリに移動します。

$ cd ~/.ssh

鍵を作ります。

$ ssh-keygen -t rsa

id_rsa.pubの中身をコピーし、↓で New SSH key として登録します。

接続確認をします。 You've successfully authenticated と出力されればOKです。

$ ssh -T git@github.com

Djangoアプリをpullします。

$ cd ~
$ git clone git@github.com:xxxxxxxxxxxxx.git

E. EC2上に仮想環境を構築する

localで作ったときと同様に構築します。

$ python -m venv django_env
$ source django_env/bin/activate

ライブラリをインストールします。

$ pip install -r requirements.txt

3. ブラウザからDjangoアプリにアクセスできることを確認する

A. とりあえずmanage.pyで動かす
B. wsgiで動かす

A. とりあえずmanage.pyで動かす

EC2インスタンスに割り当てているセキュリティグループで8000番ポートを解放します。
インバウンドルールで以下のように設定します。
image.png

manage.pyのあるディレクトリ移動して次を実行します。

$ python manage.py runserver 0.0.0.0:8000

ブラウザからEC2インスタンスのグローバルIPアドレスで接続できるか確認します。

http://グローバルIP:8000

B. wsgiで動かす

manage.pyを停止し、同じ階層で実行します。

$ uwsgi --http :8000 --module sample_project.wsgi

同じくブラウザからグローバルIPアドレスで接続できるか確認します。

http://グローバルIP:8000

4. Elastic IPでグローバルIPアドレスを固定する

A. Elastic IPを割り当てる (= 取得する)
B. Elastic IPをEC2に関連付ける
C. EC2の .env にElastic IPで固定したグローバルIPアドレスを追加

A. Elastic IPを割り当てる

EC2のコンソールで左のメニューから Elastic IP を選択します。
スクリーンショット 2023-03-21 23.18.42.png

右上オレンジの Elastic IP アドレスを割り当てる を押下します。
image.png

設定は特に何も触れず右下の 割り当て を押下するとElastic IPが割り当てられます。
image.png

B. Elastic IPをEC2に関連付ける

先程割り当てたElastic IPを選択します。
スクリーンショット 2023-03-21 23.20.41.png

右上オレンジの Elastic IP アドレスの関連付け を押下します。
スクリーンショット 2023-03-21 23.23.41.png

インスタンスの検索ウインドウから先程つくったEC2を選択します。
プライベート IP アドレス の検索ウインドウで選択したインスタンスのIPアドレスが選択できるようになるので、それを選択します。
右下の 関連付ける を押下するとEC2インスタンスにElastic IPがグローバルIPとして関連付けられます。
ElasticIP関連付け.png

関連付けが完了するとEC2のホスト名が変わるので、ssh接続する際は改めてEC2コンソールから接続情報を確認してください。

C. EC2の .env にElastic IPで固定したグローバルIPアドレスを追加

.env
ALLOWED_HOSTS=固定したグローバルIPアドレス

5. Nginxを設定する

A. nginx.confを編集する
B. uwsgiファイルをコピーする
C. 静的ファイルをNginxに配置する
D. nginxを動かす

A. nginx.confを編集する

設定を書くファイルを作成します。

$ sudo touch /etc/nginx/sites-available/sample_project

中身は以下のようにします。

sample_project
upstream django {
    server unix:///home/ubuntu/sample_project/sample_project.sock;
}

server {
    listen 80;
    server_name グローバルIPアドレス;
    root /usr/share/nginx/html;

    location /static {
        alias /usr/share/nginx/html/static;
    }

    location /media {
        alias /usr/share/nginx/html/media;
    }

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarder-Proto $scheme;
        uwsgi_pass django;
        include /home/ubuntu/sample_project/uwsgi_params;
    }
}

シンボリックリンクを張って nginx.conf に読み込ませます。

$ sudo ln -s /etc/nginx/sites-available/sample_project /etc/nginx/sites-enabled/sample_project

B. uwsgiファイルをコピーする

uwsgi_params ファイルをNginxからプロジェクトのmanage.pyと同じ階層のディレクトリにコピーします。

$ sudo cp /etc/nginx/uwsgi_params /home/ubuntu/sample_project/

C. 静的ファイルをNginxに配置する

Djangoプロジェクトの静的ファイル (cssとjavascriptなど) をNginxに配置します。

配置するディレクトリを作成します。

$ sudo mkdir /usr/share/nginx/html/static
$ sudo mkdir /usr/share/nginx/html/media

ディレクトリの所有者を書き換えます。

$ sudo chown ubuntu:ubuntu /usr/share/nginx/html/static
$ sudo chown ubuntu:ubuntu /usr/share/nginx/html/media

settingsの設定を書き換えます。

development.py
from .common import *

STATIC_ROOT = BASE_DIR / "static"
...
production.py
from .common import *

STATIC_ROOT = '/usr/share/nginx/html/static'
MEDIA_ROOT = '/usr/share/nginx/html/media'
...

manage.pyの階層に行き、以下コマンドを実行します。

$ python manage.py collectstatic

静的ファイルが配置されたか確認します。

$ ls -lrt /usr/share/nginx/html/static

D. Nginxを動かす

Nginxを有効化・起動します。

$ sudo systemctl enable nginx
$ sudo systemctl start nginx

EC2インスタンスのセキュリティグループからインバウンドでhttpリクエストを有効にします。
8000番ポートを有効にしたインバウンドルールは削除しておきます。
image.png

manage.pyのあるディレクトリで以下を実行し、ブラウザからグローバルIPでアクセスできることを確認します。

$ uwsgi --socket /home/ubuntu/sample_project/sample_project.sock --module sample_project.wsgi --chmod-socket=666

6. Route53で独自ドメインを取得

A. ドメイン名を決めて購入
B. 固定したグローバルIPを紐付ける
C. ドメイン名をNginxとDjangoに反映する

A. ドメイン名を決めて購入

AWSコンソールでRoute53にアクセスします。
ドメインの登録に取得したいドメイン名を入力し、チェックを押下します。
image.png

次ページで購入するドメイン名を確定し、以降のページで購入手続きを完了してください。
個人利用の場合、whoisで調べても個人情報の連絡先等はすべてAWSの連絡先になっていました。
筆者は購入完了後20分程度でドメインが有効化されました。

B. 固定したグローバルIPを紐付ける

Route53のダッシュボードからホストゾーンを選択します。

スクリーンショット 2023-03-22 0.11.05.png

有効になったドメイン名を選択し、レコードを作成を押下します。
スクリーンショット 2023-03-22 0.13.08.png

以下を入力し、レコードを作成を押下します。

  • レコード名: サブドメインを使用する場合は入力
  • 値: EC2インスタンスに割り当てた固定グローバルIPアドレス
    スクリーンショット 2023-03-22 1.55.23.png

これでドメイン名が固定したグローバルIPに紐付けられました。

C. ドメイン名をNginxとDjangoに反映する

Nginxにドメイン名を追加

/etc/nginx/sites-available/sample_project
server_name グローバルIPアドレス 取得したドメイン名;
common.py
...
ALLOWED_HOSTS = ['取得したドメイン名', env('ALLOWED_HOSTS')]
...

これでドメイン名でアクセスできるようになります。

http://取得したドメイン名

7. SSL証明取得

A. Let’s Encryptをインストール
B. Nginxで443へのアクセスを受け付ける

A. Let’s Encryptをインストール

nginxを停止し、Let’s Encryptをインストールします。

$ sudo systemctl stop nginx
$ sudo apt-get install letsencrypt

SSL化に必要な証明書を取得します。

$ sudo certbot certonly --standalone -d 取得したドメイン名

証明書の実行権限を書き換えます。

$ sudo chmod 700 /etc/letsencrypt/live/

B. Nginxで443へのアクセスを受け付ける

sample_project
upstream django {
    server unix:///home/ubuntu/sample_project/sample_project.sock;
}

server {
    listen 80;
    server_name ドメイン名;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name ドメイン名;
    root /usr/share/nginx/html;

    ssl_certificate /etc/letsencrypt/live/ドメイン名/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ドメイン名/privkey.pem;

    location /static {
        alias /usr/share/nginx/html/static;
    }

    location /media {
        alias /usr/share/nginx/html/media;
    }

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarder-Proto $scheme;
        uwsgi_pass django;
        include /home/ubuntu/sample_project/uwsgi_params;
    }
}

EC2インスタンスのセキュリティグループでインバウンドルールにhttpsリクエストを追加します。
image.png

Nginxを再起動します。

$ sudo systemctl start nginx

これでhttpsでアクセスできるようになります。

https://ドメイン名

8. wsgiをデーモン化

A. wsgi.iniファイルを作成
B. バックグラウンドで動かす

A. wsgi.iniファイルを作成

いままでDjangoを実行していた↓のコマンドをファイルに転記します。

$ uwsgi --socket /home/ubuntu/sample_project/sample_project.sock --module sample_project.wsgi --chmod-socket=666
uwsgi.ini
[uwsgi]
socket = /home/ubuntu/sample_project/sample_project.sock
module = sample_project.wsgi
chmod-socket = 666

動作確認をします。

$ uwsgi --ini uwsgi.ini

B. バックグラウンドで動かす

バックグラウンドで動けばssh接続を切ってもアプリが動き続けます。

$ uwsgi --ini uwsgi.ini &
4
2
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
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?