【後編】【Django+Nginx+Gunicorn】アプリケーションをEC2へデプロイしよう
Django をサーバーへデプロイして、ブラウザからアクセスできるようにします。
後編では、ソースをアップロードして、サーバーで動作するようになるまでをやってみます。
システムの概要や Django・サーバーの下準備は前編でご紹介しています。
Django のソースをアップロードする
zip ファイルにした Django のソースを EC2 へアップロードします
サーバーへファイルをアップロードするscp コマンド
scp -i <キーペアのパス> <アップロードするファイルのパス> ec2-user@<パブリックIP or DNS>:~/
筆者はTeratermを使用して、サーバーへ ec2-user で ssh 接続して、 zip ファイルを Teraterm のウインドウへドラッグ&ドロップします
すると、以下のウインドウが現れ、「OK」を押してアップロードします
ホームディレクトリとは??サーバーにログインしたときの最初のディレクトリです。
おおよそ、「**/home/<ユーザー名>/**」 になります
→ 今回は、/home/ec2-user になります。
zip ファイルを解凍し、フォルダを移動させる
※ここからは root ユーザーに切り替えます。 また、Django のプロジェクト名は django_practice とします。
(実行手順)
-
root ユーザーに切り替える
sudo su
-
zip ファイルを解凍する
# zipファイルがあることを確認する ll # zipファイルを解凍する unzip <Djangoプロジェクトのzipファイル> # zipファイルと解凍されたフォルダが存在することを確認する ll
-
解凍したフォルダを /var/www/ へ移動する
# フォルダを /var/www/ へコピーする cp -r <解凍したフォルダ> /var/www/ # /var/www/ へ移動する cd /var/www/ # フォルダを移動できているか確認する ll
(実行結果)
[root@ip-×××-××-××-×× ec2-user]# ll
total 376
-rw-r--r-- 1 ec2-user ec2-user 383985 Oct 30 20:19 django_practice.zip
[root@ip-×××-××-××-×× ec2-user]# unzip django_practice.zip
Archive: django_practice.zip
creating: django_practice/accounts/
extracting: django_practice/accounts/__init__.py
extracting: django_practice/accounts/admin.py
inflating: django_practice/accounts/apps.py
creating: django_practice/accounts/migrations/
extracting: django_practice/accounts/migrations/__init__.py
inflating: django_practice/accounts/models.py
creating: django_practice/accounts/templates/
creating: django_practice/accounts/templates/accounts/
inflating: django_practice/accounts/templates/accounts/login.html
inflating: django_practice/accounts/templates/accounts/signup.html
----------略------------------------------------------------------------
[root@ip-×××-××-××-×× ec2-user]# ll
total 376
drwxr-xr-x 8 root root 220 Oct 31 00:44 django_practice
-rw-r--r-- 1 ec2-user ec2-user 383985 Oct 30 20:19 django_practice.zip
[root@ip-×××-××-××-×× ec2-user]# cp -r django_practice /var/www/
[root@ip-×××-××-××-×× ec2-user]# cd /var/www/
[root@ip-×××-××-××-×× www]# ll
total 0
drwxr-xr-x 8 root root 220 Oct 31 00:45 django_practice
Python コマンドのエイリアスを設定する
ここで、Python のバージョンを確認します。
[root@ip-×××-××-××-×× ~]# python -V
Python 3.9.12
[root@ip-×××-××-××-×× ~]# pip --version
pip 22.0.4 from /usr/local/lib/python3.9/site-packages/pip (python 3.9)
後のことを考えるとpython のバージョンは 3 系のほうがいいので、 2 系のバージョンの方はエイリアスを設定しましょう
# python->python3となるよう設定する
echo 'alias python=python3' >> ~/.bashrc
# pip->pip3となるよう設定する
echo 'alias pip=pip3' >> ~/.bashrc
# .bashrcを読み込んで、エイリアスを読み込む
source ~/.bashrc
# pythonのバージョン確認
python -V
# pipのバージョン確認
pip --version
Python のライブラリをインストールする
前編で作成したrequirements.txt を使用して、ライブラリをインストールします
# Djnagoプロジェクトのルートディレクトリへ移動
cd django_practice
# ライブラリをインストール
pip install -r requirements.txt
# インストール後、確認
pip list
PostgreSQL の設定をする
ライブラリをインストールする
Djnago で PostgreSQL を使用するためのライブラリをインストールしましょう
pip install psycopg2
Django 用の PostgreSQL ユーザー&DB を作成する
Django の下準備として、settings/production.py に記載したユーザーを作成します
私の場合は
# PostgreSQLを使用するようにする
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": "djangodatabase", # データベース名
"USER": "djangouser", # ユーザー名
"PASSWORD": "djangopassword", # パスワード
"HOST": "localhost",
"PORT": "5432",
}
}
に合うように設定していきます
# PostgreSQLにログイン
[root@ip-×××-××-××-×× django_practice]# psql -U postgres -d postgres
Password for user postgres:
psql (14.3)
Type "help" for help.
# ユーザー作成
postgres=# CREATE USER djangouser WITH PASSWORD 'djangopassword';
CREATE ROLE
postgres=# ALTER ROLE djangouser SET CLIENT_ENCODING TO 'utf8';
ALTER ROLE
postgres=# ALTER ROLE djangouser SET DEFAULT_TRANSACTION_ISOLATION TO 'read committed';
ALTER ROLE
postgres=# ALTER ROLE djangouser SET TIMEZONE TO 'Asia/Tokyo';
ALTER ROLE
# DBの作成
postgres=# CREATE DATABASE djangodatabase;
CREATE DATABASE
# djangouser と djangodatabase を紐づける
postgres=# GRANT ALL PRIVILEGES ON DATABASE djangodatabase TO djangouser;
GRANT
# ログアウト
postgres=# \q
# 作成したユーザーとDBにアクセスしてみる
[root@ip-×××-××-××-×× django_practice]# psql -U djangouser -d djangodatabase
Password for user djangouser:
psql (14.3)
Type "help" for help.
djangodatabase=>
# ログアウトする
djangodatabase=> \q
Django の DB をマイグレーションする
python manage.py migrate --settings=django_practice.settings.production
--settings=django_practice.settings.production とは??
manage.py にてデフォルトで settings/dev.py を読み込むようにしているので、 意図的に settings/production.py を読み込むように指定しています
スーパーユーザーを作成する
[root@ip-×××-××-××-×× django_practice]# python manage.py createsuperuser --settings=django_practice.settings.production
ユーザー名 (leave blank to use 'root'): admin
メールアドレス: admin@admin.com
Password:
Password (again):
このパスワードは ユーザー名 と似すぎています。
このパスワードは短すぎます。最低 8 文字以上必要です。
このパスワードは一般的すぎます。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
ユーザー名とパスワードを一緒にしたので、怒られました...
もちろん、セキュリティ的にはだめですが、今回はこれでいきます
static ファイルを作成する
WEB サーバー(Nginx)に静的ファイルを管理させるために、静的ファイルを 1 か所に集めます
(実行コマンド&結果)
[root@ip-×××-××-××-×× django_practice]# python manage.py collectstatic --settings=django_practice.settings.production
206 static files copied to '/usr/share/nginx/html/static'.
STATIC_ROOT に指定したディレクトリに静的ファイルがコピーされたので、見てみましょう
ll -R /usr/share/nginx/html/static
Gunicorn をインストールする
ライブラリをインストールする
[root@ip-×××-××-××-×× django_practice]# pip install gunicorn
Collecting gunicorn
Downloading gunicorn-20.1.0-py3-none-any.whl (79 kB)
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq 79.5/79.5 KB 9.8 MB/s eta 0:00:00
Requirement already satisfied: setuptools>=3.0 in /usr/local/lib/python3.9/site-packages (from gunicorn) (58.1.0)
Installing collected packages: gunicorn
Successfully installed gunicorn-20.1.0
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
Gunicorn を使用して Django を起動する
[root@ip-×××-××-××-×× django_practice]# gunicorn djangosnippets.wsgi --bind=0.0.0.0:8000 --env DJANGO_SETTINGS_MODULE=django_practice.settings.production
[2022-11-01 00:44:39 +0900] [29221] [INFO] Starting gunicorn 20.1.0
[2022-11-01 00:44:39 +0900] [29221] [INFO] Listening at: http://0.0.0.0:8000 (29221)
[2022-11-01 00:44:39 +0900] [29221] [INFO] Using worker: sync
[2022-11-01 00:44:39 +0900] [29222] [INFO] Booting worker with pid: 29222
ブラウザからアクセスしてみる
http://<パブリック IP アドレス or DNS>:8000/admin
にアクセスします
css は後で効くようにするので、ご心配なく
一応、スーパーユーザーでログインしてみます。
ログインできました
ここで、いったん gunicorn を Ctrl + cで停止しておきます
Django をサービスに登録する
ここでは3つのファイルを作成し、Django をサービス化します
- socket ファイルNginx との接続設定を行うための設定ファイル
- gunicorn.conf.pygunicorn の設定ファイル
- service ファイルDjango との接続設定を行うための設定ファイル
socket ファイル
**/etc/systemd/system/**に <Django プロジェクト名>.socketを作成します
vi コマンドで socket ファイルを作ります
vi /etc/systemd/system/<Djangoプロジェクト名>.socket
i を押して、INSERT モードにし、下記を記入します
[Unit]
# socketファイルの説明
Description=gunicorn socket
[Socket]
# serviceファイルのポート番号を指定(今回はsockファイルを指定)
ListenStream=/var/run/<Djangoプロジェクト名>.sock
[Install]
# 必ず「sockets.target」を指定
WantedBy=sockets.target
Esc キーで INSERT モードを抜け、「:wq!」で保存します。
cat コマンドでファイルの中身を確認します
cat /etc/systemd/system/<Djangoプロジェクト名>.socket
gunicorn.conf.py
# gunicorn.conf.py を入れるフォルダを作成
mkdir /etc/gunicorn
# 作成したフォルダへ移動
cd /etc/gunicorn
# gunicorn.conf.pyを作成
vi gunicorn.conf.py
# 先ほどと同じ要領でファイルを作成します
-----gunicorn.conf.py のなかみ--------------------
#
# Gunicorn config file
#
wsgi_app = '<Djangoプロジェクト名>.wsgi:application'
# Server Mechanics
#========================================
# current directory
chdir = '/var/www/<Djangoプロジェクト名>'
# daemon mode
daemon = False
# enviroment variables
raw_env = [
'DJANGO_SETTINGS_MODULE=<Djangoプロジェクト名>.settings.production',
]
# Server Socket
#========================================
bind = '0.0.0.0:8000'
# Worker Processes
#========================================
workers = 2
# Logging
#========================================
# access log
accesslog = '/var/log/gunicorn/access.log'
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
# gunicorn log
errorlog = '-'
loglevel = 'info'
-----------------------------------------------------
# 中身を確認
cat gunicorn.conf.py
# ログ用のフォルダ作成
mkdir /var/log/gunicorn
conf ファイル内のパラメータに関しては下記をご参考ください
service ファイル
まず、which コマンドでgunicorn コマンドのパスを調べます
[root@ip×××-××-××-×× gunicorn]# which gunicorn
/usr/local/bin/gunicorn
筆者の場合は、/usr/local/bin/gunicornでした
これをもとに service ファイルを作成します
# serviceファイルの作成
vi /etc/systemd/system/<Djangoプロジェクト名>.service
# なかみの確認
cat /etc/systemd/system/<Djangoプロジェクト名>.service
(service ファイルのなかみ)
[Unit]
# serviceファイルの説明
Description=gunicorn daemon
# serviceファイルの起動時に、socketファイルの起動を要求
Requires=<Djangoプロジェクト名>.socket
# 必ず「network.target」を指定
After=network.target
[Service]
# Djangoプロジェクトを指定(manage.pyがあるディレクトリ)
WorkingDirectory=/var/www/<Djangoプロジェクト名>
# 通信開始時に起動するコマンドを設定
# 先ほど調べたgunicornコマンドのパスを使用
ExecStart=/usr/local/bin/gunicorn --config /etc/gunicorn/gunicorn.conf.py <Djangoプロジェクト名>.wsgi:application
Restart=always
[Install]
# 必ず「multi-user.target]を指定
WantedBy=multi-user.target
サービス化できているか確認する
# 先ほど作成したファイルを systemd に反映します
systemctl daemon-reload
# サービスの起動&ステータス確認
systemctl start <Djangoプロジェクト名.>service
systemctl status <Djangoプロジェクト名>.service
# 自動起動設定
sudo systemctl enable <Djangoプロジェクト名>.service
sudo systemctl is-enabled <Djangoプロジェクト名>.service
(実行結果)
[root@ip-×××-××-××-×× gunicorn]# systemctl daemon-reload
[root@ip-×××-××-××-×× gunicorn]# systemctl start django_practice.service
[root@ip-×××-××-××-×× gunicorn]# systemctl status django_practice.service
● django_practice.service - gunicorn daemon
Loaded: loaded (/etc/systemd/system/django_practice.service; disabled; vendor preset: disabled)
Active: active (running) since Wed 2022-11-02 01:17:04 JST; 5s ago
Main PID: 7398 (gunicorn)
CGroup: /system.slice/django_practice.service
tq7398 /usr/local/bin/python3.9 /usr/local/bin/gunicorn --config /etc/gunicorn/gunicorn.conf.py django_practice.wsgi:application
tq7399 /usr/local/bin/python3.9 /usr/local/bin/gunicorn --config /etc/gunicorn/gunicorn.conf.py django_practice.wsgi:application
mq7400 /usr/local/bin/python3.9 /usr/local/bin/gunicorn --config /etc/gunicorn/gunicorn.conf.py django_practice.wsgi:application
Nov 02 01:17:04 ip-×××-××-××-××.ap-northeast-1.compute.internal systemd[1]: Started gunicorn daemon.
Nov 02 01:17:04 ip-×××-××-××-××.ap-northeast-1.compute.internal gunicorn[7398]: [2022-11-02 01:17:04 +0900] [7398] [INFO] Starting gunicorn 20.1.0
Nov 02 01:17:04 ip-×××-××-××-××.ap-northeast-1.compute.internal gunicorn[7398]: [2022-11-02 01:17:04 +0900] [7398] [INFO] Listening at: unix:/var/r...7398)
Nov 02 01:17:04 ip-×××-××-××-××.ap-northeast-1.compute.internal gunicorn[7398]: [2022-11-02 01:17:04 +0900] [7398] [INFO] Using worker: sync
Nov 02 01:17:04 ip-×××-××-××-××.ap-northeast-1.compute.internal gunicorn[7398]: [2022-11-02 01:17:04 +0900] [7399] [INFO] Booting worker with pid: 7399
Nov 02 01:17:05 ip-×××-××-××-××.ap-northeast-1.compute.internal gunicorn[7398]: [2022-11-02 01:17:05 +0900] [7400] [INFO] Booting worker with pid: 7400
Hint: Some lines were ellipsized, use -l to show in full.
[root@ip-×××-××-××-×× gunicorn]# systemctl enable django_practice.service
Created symlink from /etc/systemd/system/multi-user.target.wants/django_practice.service to /etc/systemd/system/django_practice.service.
[root@ip-×××-××-××-×× gunicorn]# systemctl is-enabled django_practice.service
enabled
Nginx が Django にアクセスするように設定
conf ファイルの作成
cd /etc/nginx
mkdir sites-available
cd sites-available/
vi <Djnagoプロジェクト名>
conf ファイルのなかみ
server {
# 設定しているウェブサイトへのリクエストを通すポート
listen 80;
# ウェブサイトのIPもしくはドメイン名
# EIPを設定しいない場合は, localhost と設定
server_name <IP or ドメイン>;
# ファビコンが見つからないというエラーを回避するおまじない
location = /favicon.ico {access_log off; log_not_found off;}
### 以下、リクエストURLとNGINX上のパスとの対応を定義
# Django静的ファイルへのパスを設定
location /static {
alias /usr/share/nginx/html/static;
}
location /media {
alias /usr/share/nginx/html/media;
}
# リクエストをウェブページに流すためにunixソケットへのパスを設定
location / {
# djangapp.sockにリクエスト結果を流している
proxy_pass http://unix:/var/run/<Djangoプロジェクト名>.sock;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
シンボリックリンクを作成する
先ほど作成したファイルのシンボリックリンクを /etc/nginx/sites-enabled/ に貼ります
これになんの意味があるかというと、
/etc/nginx/sites-available/:Nginx の conf ファイルを置いておく(読み込まない)
/etc/nginx/sites-enabled/:ここにある conf ファイルを読み込む
なので、conf ファイルは /etc/nginx/sites-available/ に作成しておき、読み込ませたい conf ファイルをシンボリックリンクで制御します
# /etc/nginx/sites-enabled を作成
mkdir /etc/nginx/sites-enabled
cd /etc/nginx/sites-enabled
# confファイルのシンボリックリンクを作成
ln -s /etc/nginx/sites-available/<Djangoプロジェクト名> <Djangoプロジェクト名>
ll
# ↓のように表示されればOK!
# lrwxrwxrwx 1 root root 41 Nov 3 00:58 <Djangoプロジェクト名> -> /etc/nginx/sites-available/<Djangoプロジェクト名>
/etc/nginx/sites-enabled/内のファイルを読み込ませる
# バックアップを作成する
cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.`date +%Y%m%d`
# ファイルを編集する
vi /etc/nginx/nginx.conf
include /etc/nginx/conf.d/*.conf; の下に
include /etc/nginx/sites-enabled/*;
と追記
これでNginx が/etc/nginx/sites-enabled/内のファイルを読み込むようになります
conf ファイルのテストをする
[root@ip-×××-××-××-×× nginx]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
テストに成功したのでNginx と Django のアプリを再起動します
systemctl restart nginx
systemctl status nginx
systemctl restart <Djangoプロジェクト名>
systemctl status <Djangoプロジェクト名>
ブラウザからアクセスしてみる
http://<パブリック IP アドレス or DNS>/admin
にアクセスしてみます
管理画面が開き、ついでに css も効いています
これで、デプロイ完了です
大変でしたが、Django 以外の勉強になったので、よかったです!!
ソースの更新方法ややり残したことをおまけ編にまとめました
時間があれば、ご一読ください
参考文献
Discussion