はじめに
AWSのEC2上でDjangoプロジェクトを展開しようとして丸一日悩んでしまったので、手順をまとめました。あくまでも自己責任でお願いします。
やったこと
EC2インスタンスを立てて何も考えずにDjangoを動かして、ネット上からアクセスできるようにする。
今回はSQLite3,nginx,gunicornを使います。
注意
- ec2-userをそのまま使っていたり、セキュリティ的に怪しい所があるため注意すること。今回はAWSでdjangoプロジェクトをとりあえず動かせるようにすることを目的とする。
- VPC、サブネット、インターネットゲートウェイの設定はすでに完了していて、EC2自体はネットから参照できる状態になっている所からスタートする
- ssh(22)、HTTP(80)とDjango用(8000)のポートが開放されている状態を前提とする
- DBはSQLite3を使用する。また、SQLite3のバージョンを最新にするため既存のソフトウェアでSQLite3を使用している際は注意すること。
- Djangoの管理者画面がスタイルシートなどを含めて読み込めたところで本記事のゴールとする
参考にした文献
- 【20分でデプロイ】AWS EC2にDjango+PostgreSQL+Nginx環境を構築してササッと公開
- AWSにDjangoアプリケーションをデプロイ(Nginx, gunicorn, postgresql)
- AWS Amazon Linux2 で Django 2.2以降の環境を構築する
- 【AWS】EC2でWebサーバの構築 – Nginx構築 -【Nginx】
- Nginxで躓いたところ及びメモ
必要なものを揃える
本記事ではvenvなどは使わず、そのままpython3を使っていくため注意すること。
# 後述処理でパスワードを求められるため設定する。また、rootを使えるようにする
$ sudo passwd ec2-user
$ sudo passwd root
$ sudo yum update
$ sudo yum install python3
# Amazon Linux 2で$sudo yum install nginx と入力するとコマンドを催促されるので、それを入力する。
$ sudo amazon-linux-extras install nginx1
$ sudo pip3 install django
$ sudo pip3 install gunicorn
Amazon Linux 2にもデフォルトでSQLite3がインストールされていたが、Djangoで求めるバージョン(3.8.3以降)にマッチしないため、SQLiteのアプデを実施する。
# 後述のSQLite3インストール時のmakeファイルのビルドに使用する。
$ sudo yum install gcc
# 最新版のSQLite3をインストールする。執筆当時は3.33.0が最新だった。(https://www.sqlite.org/download.html)
# 展開した後にmakeファイルをビルドしてmake installまで実施する
$ wget https://www.sqlite.org/2020/sqlite-autoconf-3330000.tar.gz
$ tar zxvf sqlite-autoconf-3330000.tar.gz
$ cd sqlite-autoconf-3330000
# makeファイルのビルド
$ ./configure --prefix=/usr/local
$ sudo make
$ sudo make install
次に、過去に入っていたSQLite3から現行のSQLite3を使用するように設定を通す。
# すでにインストール済みのsqlite3を確認する
$ sudo find / -name sqlite3
# もともとあったやつは多分これ。バージョンが古いことを一応確認しておく。
/usr/bin/sqlite3 --version
# 今回インストールしたのは多分これ。バージョンが古いことを一応確認しておく。
/usr/local/bin/sqlite3 --version
# 古い方のsqlite3のフォルダ名を変え、また新しい方のsqlite3へパスを設定しておく
sudo mv /usr/bin/sqlite3 /usr/bin/sqlite3_old
sudo ln -s /usr/local/bin/sqlite3 /usr/bin/sqlite3
# ライブラリへのパスを通しておく
su
sudo touch /etc/ld.so.conf.d/sqlite3.conf
sudo echo /usr/local/lib >> sqlite3.conf
# rootからログアウト
exit
# 過去のsqlite3へのリンクとディレクトリを削除
$ sudo rm /lib64/libsqlite3.so.0
$ sudo rm -rf /lib64/libsqlite3.so.[過去のバージョン]
# ライブラリのパスを更新する
sudo ldconfig
# 新しいライブラリへのパス(/usr/local/lib)が通っているか確認する
$ ldconfig -p | grep sqlite
# 一応バージョンも確認する
sqlite3 --version
最後のバージョン確認で現行のSQlite3のものが出力されれば、次に進む。
Djangoの設定
とりあえずプロジェクトを作る。プロジェクト名は公式ドキュメントでおなじみのmysiteにする。
# /home/ec2-userなどの任意のユーザのhomeディレクトリへ移動
$ cd /~
$ django-admin startproject mysite
今回はadmin画面が見えればOKなので、最低限の設定だけをする。
$ cd mysite/mysite
$ vi settings.py
setting.pyにおいて設定するのは
- 末尾にSTATIC_ROOTを追加(あわせてimport osも追記)
- ALLOWED_HOSTSに自分のパブリックドメインやパブリックIPアドレスを設定
ぐらい。一応全文を載せておく
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'hogepugefugahogepugefuga...'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# 自分のドメインやパブリックIPアドレスとか
ALLOWED_HOSTS = ['*.*.*.*',]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'mysite.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'mysite.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
終わったら、djangoプロジェクトの静的ファイルをまとめたり、
管理者ページに入るまでの設定をしておく。
$ cd ~/mysite
# 自身の情報をここで入れておく。パスは忘れないように
$ python3 manage.py makemigrations
$ python3 manage.py migrate
$ python3 manage.py createsuperuser
# STATICファイルをとりまとめておく
$ python3 manage.py collectstatic
もし、python3 manage.py collectstaticでcheck_sqlite_versionと書かれたエラーを吐いた場合はSQLite3の設定を見直すこと。過去のSQLite3を見に行っている可能性が高い。
Djangoそのものの設定がうまくできているか確認するために、gunicornから動かしてみる。
# 必ずmanage.pyなどが存在しているフォルダへ移動すること
$ cd ~/mysite
$ sudo gunicorn mysite.wsgi --bind=0.0.0.0:8000
ブラウザでパブリックIPあるいはドメインへアクセスした際に、例のロケットが表示されればOK。確認できたらgunicornをCtrl+Xなどで終了する。
gunicornの設定
以下の設定ファイルを作成(編集)する。
- /etc/systemd/system/gunicorn.service
- /etc/systemd/system/gunicorn.socket
なお、gunicorn.serviceのExecStartのパスはwhichコマンドで確認する。
which gunicorn
ユーザー名(グループ名)、WorkingDirectory、ExecStartのwsgiファイル名とListenStreamは各自の環境に揃えておくこと。(特に工夫がなければ、ユーザー名はnginxと一致させておく。)
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
Type=notify
# the specific user that our service will run as
# ここのユーザ名とグループ名はデーモンを実行するユーザを入れる。
# 特に何も考えていなければec2-userにする。
User=ec2-user
Group=ec2-user
# another option for an even more restricted service is
# DynamicUser=yes
# see http://0pointer.net/blog/dynamic-users-with-systemd.html
RuntimeDirectory=gunicorn
# Djangoプロジェクトのパスを入れる
WorkingDirectory=/home/ec2-user/mysite
# gunicornの実行ファイルのあるパスと、プロジェクト名.wsgi
ExecStart=/usr/local/bin/gunicorn mysite.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target
[Unit]
Description=gunicorn socket
[Socket]
# sockファイルを作る場所を入れる。基本はdjangoプロジェクトのパスを入れておく。
ListenStream=/home/ec2-user/mysite/mysite.sock
# Our service won't need permissions for the socket, since it
# inherits the file descriptor by socket activation
# only the nginx daemon will need access to the socket
# gunicorn.serviceと同じユーザを入れる
SocketUser=ec2-user
# Optionally restrict the socket permissions even more.
# SocketMode=600
[Install]
WantedBy=sockets.target
**ここで注意として、sockファイルとsocketファイルを混同させないようにすること。**あくまでも、sockファイルはnginxとgunicornが共有するファイルである。
終わったら、gunicornのデーモンの起動と自動起動の登録をする。
$ sudo systemctl start gunicorn.service
$ sudo systemctl enable gunicorn
# ちゃんとプロセスが動いていることを確認する。
$ sudo systemctl status gunicorn
プロセスが正常に起動していれば、次に進む。
nginxの設定
まず、nginxが正常に動いていて、ネットからつながることを確認する。つながらない場合は、EC2ダッシュボードのインスタンス→セキュリティからポート80が開放されているか、あるいはEC2のダッシュボード上で示されているパブリックIPアドレスやドメインが正しいか確認する。
systemctl start nginx
# コマンド実行後にEC2のパブリックIPアドレスにブラウザからアクセスして、
# nginxが動いているページが見えることを確認する。
# 確認できたら、以下のコマンドで一旦終了する。
systemctl stop nginx
確認できたら、nginxの設定をする。設定するファイルは次の通り。(mysite.confは新規作成する。また、拡張子が.confなら好きな名前でOK)
- /etc/nginx/nginx.conf
- /etc/nginx/conf.d/mysite.conf
# 全文は多すぎるので省略。userをnginxからec2-userなどのサービスを動かすユーザーへ変える。
# 思い入れがなければ、gunicorn起動時のユーザと一致させておく
...
user ec2-user;
...
# httpブロック内などに、この一行があるか確認する。書いてあればOK
include /etc/nginx/conf.d/*.conf;
...
server {
listen 80;
# ドメインあるいはIPアドレスを設定する
server_name *.*.*.*;
# djangoの静的ファイルの置き場所を指定する。
location /static {
alias /home/ec2-user/mysite/static;
}
# djangoのadmin用の静的ファイルの置き場所を指定する。
location /static/admin {
alias /home/ec2-user/mysite/static/admin;
}
# gunicorn起動時に作成されるsockファイルの場所を指定する。
location / {
proxy_pass http://unix:/home/ec2-user/mysite/mysite.sock;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
作成したら、デーモンをstart(あるいはrestart)させる。statusでプロセスが問題なく起動していれば次に進む。
sudo systemctl start nginx
# すでに起動していた場合はsudo systemctl restart nginx
# プロセスが適切に動いていることを確認する
sudo systemctl status nginx
ブラウザから管理者画面を確認してみる
以下コマンドでnginxとgunicornのプロセスがバックグラウンドで動いていることを確認する。動いていなければエラーから各設定を修正してstartしておく。
$ sudo systemctl status nginx
$ sudo systemctl status gunicorn
そのあと、自身のDjangoプロジェクトフォルダ(/home/ec2-user/mysite)内にmysite.sockが作成されていることを確認し、ブラウザで「自身のパブリックIPアドレスorドメイン/admin」で管理者画面を開いて見る。CSSを含めてAdminのログインページが表示されれば完了。
もしCSSが死んでいる場合はこのあたりを疑ってみる。
- Djangoの設定でSTATIC_ROOTを忘れている。
- mysite.confのLocationやAliasが間違っている。
また、もし502エラー(Bad Gateway)が出てくるようであれば、このあたりを間違えているかも。
- nginx.confやgunicorn.serviceのユーザーが同じではなかった(意図した権限を持ったユーザじゃなかった)
- 同ファイルにおいてsockファイルへのパスが異なっている。
今日はここまで。