Djangoで作成したWebアプリを公開するには、HerokuやAWSを使用するなど様々な方法があります。
ただし、ポートフォリオを作成する場合や本番環境などの場合はHerokuではなくAWSの方がよいでしょう。
そこで今回は、WebアプリをAWSのEC2でデプロイする方法を紹介します。
まずはEC2上に直接開発サーバーを起動し、EC2のIPアドレスに対してアクセスするやり方でデプロイします。
以下の記事では、現役エンジニア目線でAWSやDjangoが学べるスクールを4つ厳選したのでぜひ参考にしてください。
はじめに
はじめに、本記事で構成する環境の全体像と、記事内のコマンド例の記法についてまとめておきます。
デプロイ環境の全体像
今回デプロイする環境の全体像は以下図の通りです(図の見方がわからなければ無視していただいてOKです)。
言葉で説明すると、外部からインターネット経由でEC2の8000番ポートにアクセスすると、Djangoで作ったアプリケーションにアクセスします。
また、DjangoのデータベースとしてはRDSを用います。
コマンド例の記法
本記事ではいくつかコマンド例が登場しますが、コマンド例の先頭の文字(プロンプト)は、以下のように使い分けています。
「このコマンドはどこの環境で実行するんだ?」と迷った時は参考にしてください。
プロンプト | 実行場所 |
---|---|
$ | ローカル環境のターミナル(Windowsならコマンドプロンプト) |
(venv)$ | 仮想環境をアクティベートした状態のローカル環境のターミナル |
[ec2-user]$ | EC2にSSHでログインした状態 |
(venv)[ec2-user]$ | 仮想環境をアクティベートした状態のEC2 |
MySQL [(none)]> | EC2でMySQLにログインした状態 |
また、プロンプト記号がない部分は、コマンドの実行結果です。
例えば、以下はEC2にSSHで接続した状態でpython3 --version
というコマンドを実行し、その結果ターミナルにPython 3.7.15
と表示されたことを表します。
[ec2-user]$ sudo yum install python3 -y
[ec2-user]$ python3 --version
Python 3.7.15
ローカルでの環境構築
まずは、ローカル(自分のPCなど)でDjangoプロジェクトを作成します。
その後、AWS環境にDjangoのプロジェクトをコピーして、デプロイします。
仮想環境の構築
今回はvenvを使った仮想環境で環境構築をしていきます。
仮想環境とは、簡単にいうとpipなどでインストールしたPythonのパッケージを、他のローカル環境とは分離して管理できる機能です。
プロジェクトごとにパッケージのバージョンなどを分離して管理できるので、Djangoとの相性がいいです。
今回はdjangoawsというプロジェクト名にしようと思うので、まずはdjangoawsというディレクトリを作成します。
$ mkdir djangoaws
次に、作成したディレクトリに移動して、仮想環境を作成しましょう。
仮想環境の作成は、python -m venv <仮想環境名>
で行います。
紛らわしいですが、仮想環境名はvenvとします。
$ cd ./djangoaws
$ python -m venv venv # 仮想環境の作成
仮想環境ができたら、venvディレクトリが作成されます。
仮想環境を有効化(アクティベート)するには、. <仮想環境ディレクトリ>/bin/activate
コマンドを実行します(Windows環境では.\<仮想環境名>\Scripts\activate
です)
仮想環境のアクティベートが完了すると、プロンプト(コマンドの先頭の記号)の前に(<仮想環境名>
)というのがつくようになります。
$ . venv/bin/activate
(venv)$
試しにこの状態でpip listコマンドを実行すると、確かにローカルにインストールしていた他のpythonパッケージが表示されないことが確認できます。
(venv)$ pip list
Package Version
---------- -------
pip 22.0.4
setuptools 58.1.0
pythonパッケージのインストール
では、この状態でdjangoをインストールしてみましょう。
インストールができたかを確認したいのですが、次はpip list
ではなくpip freeze
コマンドを使用します。pip freeze
の場合、パッケージ名==バージョン
の形式で出力されるので、後で解説するrequirements.txtにそのまま使用できるのがいい点です。
(venv)$ pip install django
(venv)$ pip freeze
asgiref==3.6.0
Django==3.2.16
sqlparse==0.4.3
djangoが正しくインストールできていたので、続いてMySQLのクライアントであるmysqlclientとgunicornをインストールします。
インストール後は、pip freeze
の出力をrequirements.txtに書き込みます。
このrequirements.txtを使用することで、他の環境でも同じパッケージを同じベージョンで一気にインストールできるので、後でEC2で同様のパッケージをインストールするのに便利です。
(venv)$ pip install mysqlclient gunicorn
(venv)$ pip freeze > requirements.txt
requirements.txtの内容は以下のような感じになるかと思います(バージョンはインストールしたタイミングで変わる可能性があります。私の環境に揃えたい場合は、バージョン指定してインストールしてください)
asgiref==3.6.0
Django==3.2.16
gunicorn==20.1.0
mysqlclient==2.1.1
sqlparse==0.4.3
Djangoプロジェクトの作成
djangoのインストールができたので、プロジェクトを作成します。django-admin startproject djangoaws .
コマンドを実行してください(最後のドットを忘れないでください)ls
コマンドの結果も記載しておくので、正しいディレクトリ構成になっているのか確認してみましょう。
(venv)$ django-admin startproject djangoaws . # djangoプロジェクトの作成
(venv)$ python manage.py startapp app # djangoアプリの作成
(venv)$ ls -1 # ディレクトリ構成の確認
app
db.sqlite3
djangoaws
manage.py
requirements.txt
venv
Djangoアプリの作成
続いてDjangoアプリの内容を作成していきます。
今回はデプロイが目的なので、ただ画面に”Hello World”と表示するだけのアプリとします。
settings.pyの修正
INSTALLED_APPS = [
・・・
'app.apps.AppConfig', # 追加
]
urls.pyの修正
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('app.urls'))
]
from django.urls import path
from .views import hello
urlpatterns = [
path('', hello, name='hello')
]
views.py
from django.http import HttpResponse
def hello(request):
return HttpResponse('Hello World')
サーバー起動
python manage.py runserver
コマンドで環境サーバーを起動して、http://127.0.0.1:8000/にアクセスしてブラウザ表示を確認しましょう。
「Hello World」と表示されれば成功です。
(venv)$ python manage.py runserver
AWS側のリソース作成(EC2, RDS)
EC2インスタンスの作成とパッケージのインストール
以下、プロンプトが[ec2-user]$
となっているコマンド例は、EC2上で実行するものと思ってください。
EC2インスタンスの作成
まずはデプロイ用のEC2インスタンスを作成します。
以下の記事ではAWS CLIを使用してEC2インスタンスを作成する方法を紹介しているのでご参照ください。
パッケージのインストール
EC2インスタンスを作成したら以下コマンドでパッケージ類のアップデートを行なっておきましょう。
[ec2-user]$ sudo yum update
続いてpython3のインストールです。python3 --version
でpython3が使えるかどうか確認します。
[ec2-user]$ sudo yum install python3 -y
[ec2-user]$ python3 --version
Python 3.7.15
最後に、その他必要なパッケージ(gcc, python-devel, mysql-devel)をインストールします。
[ec2-user]$ sudo yum install gcc python-devel mysql-devel -y
RDSインスタンスの作成とデータベースの作成
RDSインスタンスの作成
続いてdjangoのデータベースとして使用するRDSインスタンスを作成しましょう。
今回はDBエンジンとしてMySQLを使用します。
以下の記事ではAWS CLIを使用してRDSインスタンスを作成し、EC2インスタンスからアクセスする方法を紹介しているのでご参照ください。
データベースの作成
EC2からRDSにログインし、djangoアプリケーションで使用するデータベースを作成します。
今回はdjangodbというデータベース名にしました。
[ec2-user]$ mysql -h {RDSインスタンスのエンドポイント} -u {ユーザー名} -p # MySQLにログイン
Enter password:
(中略)
MySQL [(none)]> create database djangodb; # djangodbというデータベースを作成
ちゃんと作成できているかshow databases
クエリで確認しましょう。
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| djangodb |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
確認が完了したら、Crtl+Cかexit
コマンドを入力してRDSからログアウトしてください。
MySQL [(none)]> exit;
Bye
[ec2-user]$
Djangoアプリケーションのデプロイ
ファイルのコピー
ローカルのDjangoプロジェクトをEC2にコピーします。
githubなどを介してコピーする記事もよく見かけますが、今回はrsync
コマンドを使用してgithub経由よりも簡単にコピーするコマンドを紹介します(xx.xx.xx.xxはEC2のパブリックIPアドレス)。
$ rsync -rv -e 'ssh -i ~/.ssh/MyKeyPair.pem' djangoaws \
--exclude 'venv' \
--exclude 'db.sqlite3' \
ec2-user@xx.xx.xx.xx:
scp
コマンドでもいいですが、不要なファイルを除いたコピーができるrsync
を採用しました--exclude
パラメータを指定することで、特定のファイルやフォルダを除いてコピーすることが可能です。
今回は仮想環境のvenvとsqliteのファイルが不要なので除いています。
EC2側できちんとコピーできているか確認しましょう。
[ec2-user]$ ls ./djangoaws/
app djangoaws manage.py requirements.txt
仮想環境の設定とpythonパッケージのインストール
ローカル環境と同様にvenvで仮想環境を作ります。
[ec2-user]$ python3 -m venv venv
仮想環境をアクティベートしてpipをアップグレードし、requirements.txtに記載のパッケージをインストールします。
[ec2-user]$ . ./venv/bin/activate
(venv)[ec2-user]$
(venv)[ec2-user]$ python3 -m pip install --upgrade pip
(venv)[ec2-user]$ pip install -r requirements.txt
Djangoアプリケーションのデプロイ
EC2でのデプロイを有効化する
EC2でDjangoをデプロイするには、settings.pyのALLOWED_HOSTSにEC2のパブリックIPアドレスを追加する必要があります。
以下のように、IPアドレスをリストとして定義しましょう。
ALLOWED_HOSTS = ['xx.xx.xx.xx'] # xx.xx.xx.xxはEC2のパブリックIPアドレス
RDSをデータベースに設定する
DjangoのデータベースをRDSのMySQLにするために、settings.pyのDATABSEの値を以下のように書き換えてください。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # sqliteから変更
'NAME': 'djangodb', # プロジェクト用に作成したデータベース名
'USER': 'user', # RDSで作成したユーザー名
'PASSWORD': 'xxxxxx', # RDSで作成したユーザーのパスワード
'HOST': 'RDSのDBエンドポイント',
'PORT': '3306',
}
}
djangoのデータベースの設定がきちんとできているか確認するためにpython3 manage.py dbshell
でデータベースに接続してみます。
(venv)[ec2-user]$ python3 manage.py dbshell
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 369
Server version: 8.0.28 Source distribution
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [djangodb]>
うまく接続できたら一度exit
コマンドでデータベースから抜けて、migrateしておきましょう。
MySQL [djangodb]> exit;
Bye
(venv)[ec2-user]$ python3 manage.py migrate
セキュリティグループを追加する
EC2のセキュリティグループに、8000ポートのアクセスの許可設定を追加します。
これで、Djangoのサーバーを8000ポートで起動したときに、ブラウザでアクセスできるようになります。
以下ではCLIを使ってセキュリティグループの編集をしていますが、コンソール画面から行ってもOKです。
以下のコマンドでsg-xxxxxxはEC2に関連づけられているセキュリティグループのIDを表します。
$ aws ec2 authorize-security-group-ingress \
--group-id sg-xxxxxx \
--protocol tcp --port 8000 --cidr 0.0.0.0/0
サーバーを起動してブラウザでアクセス
以下のコマンドでDjangoの開発サーバーを立ち上げましょう。
(venv)[ec2-user]$ python3 manage.py runserver 0.0.0.0:8000
お好きなブラウザで「xx.xx.xx.xx:8000」(xx.xx.xx.xxはEC2のパブリックIP)にアクセスしてHello Worldが表示されれば成功です!
まとめ
今回はDjangoのバックエンドデータベースをRSDにし、EC2にデプロイする方法を紹介しました。
より実用的な構成としてはnginxとgunicornを使った構成ですね。
そのような構成に関しては、こちらのQiitaの記事が参考になるかと思います。
>>【丁寧解説】秒速でもDjango 3アプリをAWS EC2で公開【Nginx, gunicorn, postgresqlデプロイ】
コメント
コメント一覧 (2件)
良質な記事ありがとうございます。
無事にHello worldが見れました。今までこの分野を敬遠してたのでめちゃくちゃ嬉しいです。
やはりHerokuであっさりデプロイするのとは感動が違います。この感動をたくさんの人と共感したいので普及活動させてください。
確かにマネジメントコンソールで作った方が早い可能性はありますがそれはエラーなく進んだ場合だと思います。
エンジュニアさんのように本質を理解するとうまく動かなかった時もトラブルシューティングができるので、ぜひ他の方にもおすすめしたいと思います。
あとは毎度申し訳ないのですが気になったところ(つまづいたところ)を・・・
#1 がついていたりいなかったりするのは統一したほうがよい
このレベルの作業を行うのであればおそらくターミナルを少しは触ったことあると思うので心配無用と思いますが、私も昔、zsh bash の区別もつかず、% ?? $ ?? となった記憶があります。初学者を相手にするのであればぜひ統一していただければ彼らも嬉しいと思います。
#2 MySQL で; が大事な点
久々にMySQL触るとこれ忘れてました・・・。(あと、RDSのリンク先でMySQL がMyQSLとなっていたところがありました。気になるようであれば修正お願いします。)
#3 セキュリティグループを追加する
こちら、EC2インスタンスに設定したセキュリティグループを追加するのか、DBのセキュリティグループを追加するのか迷いましたが、まあ普通に考えればインスタンスの時のか、と思って前者にしました。(2つにセキュリティグループ分けるのが一般的でしょうか?)
#4 コピーコードのところ
特にRDSのところ1行目で出力したものを2行目に入力する、といったことも多いのですが一つのコードブロックに収まっていることがあったので、分けたほうが親切か、と思いました。
(まあこの辺は、ユーザーがその程度の労力を惜しまずにできなければサーバー立てたりは難しいと思いますが、少しでもドロップアウトを少なくするためには・・・と思ってのコメントです)
長々とすみません。少しでも参考にしていただければ幸いです。
近々、家族で子供向けのプログラミングコンテンツを趣味で作ろうと思っていたので、サーバーが立てられるようになって嬉しいです。そちらでもいずれ紹介させてください^^
嬉しいコメントありがとうございます!榎本純也様のお役に立ててなによりです。
また、細かいご指摘もありがとうございます!
このようなコンテンツは、読者のフィードバックがあってこそのものだと思いますので、大変助かります。
順次対応していきますので、引き続きよろしくお願い致します。
お子様向けのプログラミングコンテンツ応援しております^^