【AWS EC2でデプロイ】EC2+RDS(MySQL)+Apache+Python+Djangoの環境構築〜デプロイまで

#はじめに
インフラ構築初心者が、チーム開発でアプリケーションをデプロイするところまでの備忘録として掲載します。
かなり苦労したのですが、自分のアウトプットを含め同じようにインフラ構築でつまづいている方のへの参考になれば幸いです。

前提として、ローカル環境でDjangoのアプリケーションが動作し、GitHubなどのホスティングサービスのリポジトリに上がっている状態とします。
また、ある程度のLinux知識は持っている状態として説明していますので、コマンド等でわからないところがあれば調べていただけますと助かります。

#動作環境
```
Amazon EC2 Linux2
Amazon RDS(MySQL 8.0.25)
Apache 2.4.46
Python 3.7.2
Django 3.2.3
mod-wsgi 4.8.0
MySQL 8.0.25
mysqlclient 2.0.3
```

#AWS EC2インスタンス作成
AWS EC2のインスタンスを作成していきます。
今回は全て無料枠での設定になります。
Webサーバーでアプリケーションをデプロイするところが目的なので、
作成方法については、以下の記事を参考に作成して下さい。

【VPC作成】

https://dev.classmethod.jp/articles/creation_vpc_ec2_for_beginner_1/

【EC2インスタンス作成】

https://dev.classmethod.jp/articles/creation_vpc_ec2_for_beginner_2/

【パブリックDNSの設定】

https://qiita.com/KKDDD/items/89af575523eab915b002



##Elastic IPの設定
ElasticIPとは、端的に言えば　”EC2インスタンスの変動しない固定されたパブリックIPアドレス”　です。
パブリックIPアドレスとは、公開用のIPアドレスで、通常IPアドレスは自動割り当てされますが、定期的に更新されてしまい、ネットワークから切断して再接続すると変わってしまいます。そこでAWSは、特定のリージョンのAWSアカウントに関連づけた静的IPv4アドレスをパブリックIPアドレスとして無償で提供してくれます。これを設定することにより、設定したIPアドレスでEC2インスタンスのサーバーに恒久的にアクセスすることが可能となります。

###設定方法
1. EC2コンソールを開く
1. 左にあるメニューバーの「ネットワーク&セキュリティ」から【Elastic IP】をクリック
1. 画面右上の【Elastic IPアドレスの割り当て】をクリック
1. 画面右下の【割り当て】をクリック
1. 割り当てたIPアドレスが選択されている状態で、画面右上の「アクション」から【Elastic IPアドレスの関連付け】をクリック
1. 設定を以下の通りにし、画面右下の【関連付ける】をクリック
    * リソースタイプ　　　　　=　インスタンス（デフォルト）
    * インスタンス　　　　　　=　作成したインスタンス
    * プライベートIPアドレス　=　作成したインスタンスのプライベートIPアドレス

よりわかりやすい記事があるので[こちら](https://qiita.com/Jerid/items/d5dd3a29ed9a0e374493)を参照してみてください。

##sshでインスタンスへ接続
1. ターミナルを開き、キーを保存したディレクトリへ移動
1. キーのパーミッションを変更する【`$ chmod 400 ****.pem`】
1. 以下のコードを使って、sshで接続する。　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　
（ちなみにコードはEC2のコンソールで、作成したインスタンスにチェックを入れた状態で、右上の方にある「接続」をクリックすると、sshを使った接続コードがあるのでそれをコピーしてターミナルへ貼り付ける方が簡単です）

```
$ ls #カレントディレクトリにキーがあることを確認
***.pem

$ ssh -i "***.pem" ***@ec2-13-231-109-224.ap-northeast-1.compute.amazonaws.com #例です
Are you sure you want to continue connecting (yes/no)?　　# yesと入力

Last login: Mon Jun 28 11:28:51 2021 from dynamic-134-180-221-3.gol.ne.jp

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[***@-**-**-*-** ~]$ #このような状態になれば接続成功です。
```

##セキュリティグループの設定
セキュリティグループでは、ファイアーウォールの役目があり、任意でEC２へのアクセスを許可する設定ができます。デフォルトでは、sshの接続が0.0.0.0となっており世界中の人がアクセス可能になる状態なので、お勧めできません。大した情報はないから大丈夫といってもガバガバのセキュリティで公開するのはエンジニアとしてはいかがなものかと思いますので、勉強がてら設定を行うことをお勧めします。

###設定方法
1. EC2コンソールを開く
1. 左にあるメニューバーの「ネットワーク&セキュリティ」から【セキュリティグループ】をクリック
1. デフォルトで作成されているセキュリティグループをクリック
1. 「インバウンド」タブを選択し、【インバウンドルールを編集】をクリック
1. 「タイプ」のプルダウン形式になっているところから必要なタイプを選択する。以下例として記載

| タイプ　　　| ポート範囲　| ソース | アドレス |
|:------|--------:|:-----|:-------|
|HTTP　　　　　　|(自動表記)　|カスタム |0.0.0.0|
|SSH 　　　　　　|(自動表記)　|マイIP  |(自動表記)|
|MYSQL　　　　|(自動表記)　|カスタム |0.0.0.0|
|カスタムTCP|8000　　|カスタム |0.0.0.0|　

6.　画面右下の【ルールの保存】をクリック

その他アプリケーションや開発形態によって制限が必要になってくると思いますので、それは適宜設定をしてください。


#パッケージのインストール・設定
ここでは必要なパッケージのインストール及び設定について説明します。
インストールを行うのは以下のものです。

・Apache
・mod_wsgi
・Python
・Django
・MySQL
・mysqlclient

##Apache,mod_wsgiのインストール
Apacheのインストール及び設定の手順を記載します。
ターミナルでEC2にログインしている状態を前提とします。

```html
$ sudo yum update -y # yumを最新にする
$ sudo yum -y install httpd httpd-devel #　Apacheのインストール

$ sudo service httpd start  #　インストールしたApacheを起動

$ sudo systemctl enable httpd.service  #　起動時にApacheが自動起動するよう設定

$ sudo systemctl status httpd.service #　Apacheがちゃんと起動しているか確認

# 以下のように　active　(running)と表示されていれば正常に起動しています。
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: active (running) since 月 2021-06-28 11:29:51 UTC; 3 days ago
     Docs: man:httpd.service(8)
（以下省略）
```

##Pythonのインストール
続いて、Pythonをインストールします。

```
# Python3に必要なモジュールをインストール
$ sudo yum install -y httpd-devel python3-devel gcc gcc-c++ git

#python3インストール
$ sudo yum install -y python3 #おそらくデフォルトでインストール済みかもしれません
```
ここで注意点ですが、Pythonで何かしら実行する場合は、”python3"として実行してください。
”python”だけだと、デフォルトのPython2系で実行されてしまいます。

Pythonのバージョンを確認しましよう。(アップデート状況によって若干バージョンが異なる可能性があります）

```
$ python3 -V 
```
##Djangoのインストール

```
$ sudo pip3 install django mod_wsgi

#　インストール確認 => djangoとmod_wsgiが表示されればインストール成功
$ pip3 list
```

##MySQL,mysqlclientのインストール

###インストール手順
```
# My SQL yumリポジトリからMySQL8.0のパッケージをダウンロード
$ sudo yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm

#MySQLインストール
$ sudo yum install --enablerepo=mysql80-community mysql-community-server -y

# MySQL確認
mysql --version

# MySQLの自動起動設定
$ sudo systemctl start mysqld.service
$ sudo systemctl enable mysqld.service
$ systemctl status mysqld.service

# mysqlclientのインストール
$ sudo yum install -y mysql-devel
$ sudo pip3 install mysqlclient
```
※mysqlclient = DjnagoからMySQLを操作するためのドライバ

DBに接続できるか確認しましょう。

```
$ mysql -u [ユーザーID] -p -h [RDSのエンドポイント]

# Passwordが要求されるので、入力
Enter password: 

#以下のように表示されたら接続成功です。
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 17938
Server version: 8.0.20 Source distribution

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 
```
###その他必要なモジュールについて
アプリケーションによってはこのほかにも必要になるモジュールがあると思います。ローカルでインストールしているものは必ずEC2でもインストールしておいて下さい。エラーの原因となります。

デプロイする前に、プロジェクトへrequirements.txtというファイルを作成して、そこにアプリケーションで使用する、モジュール等を記載しておけば以下のコマンドを実行すると楽にインストールできます。（事前にGithub等にマージした状態にして下さい）
なお、この作業を行う場合はあらかじめDjangoアプリをクローンしておく必要があります。（次節で説明）


```requirements.text
# （例）
django-bootstrap4==3.0.1
django-mdeditor==0.1.18
Markdown==3.3.4
```
requirements.textがあるフォルダで以下のコマンドを実行

```
$ sudo pip3 install -r requirements.txt
```


#Djnagoアプリの配置（GitHubを使用）
GitHubからDjangoアプリをクローンします。
公開鍵を登録しないといけないので、できていない場合は[こちら](https://qiita.com/Hide-Zaemon/items/400b21183197481ecef4)を参照して登録してください


```
# Apacheがプロジェクトにアクセスするためにディレクトリの権限を変更
sudo chmod 755 /home/ec2-user/

#　home/ec2-userに移動して、Djangoプロジェクトをgit cloneする
$ git clone [リモートリポジトリのアドレス]
```


###Djangoアプリの設定
こちらではアプリ公開用に設定が必要となるのでそちらの説明をします。
まずはsettings.pyについてです。

####git対象管理外にする設定ファイルの作成(local.settings.py)
settings.pyにはデフォルトで以下の設定になっています。

```settings.py
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'pej^q2ql$$#bzg#crh6k_9(p=%p)&6x(kwh@nos&=!$ej&60fh' #example
```

`DEBUG = True`とは、httpエラーの際に詳しいエラー内容をブラウザ上で表示してくれる設定です。
しかし、この状態だと本番環境ではサーバーの情報が第三者にダダ漏れになってしまうので、セキュリティ上よくありません。
なので、本番環境の設定では、ここを`DEBUG = False`とすることを推奨されています。

`SECRET_KEY`は、暗号化やハッシュ化で用いられる秘密鍵なので、プロジェクト外に公開してはならない情報です。

これらは、本番環境では公開しませんので、ローカル用のsettings.pyを作成し、.gitignoreに記載して管理対象外にする必要があります。
プロジェクト内に`local_settings.py`を作成し以下のように記述します。

```local_settings.py
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False # Falseに変更

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'pej^q2ql$$#bzg#crh6k_9(p=%p)&6x(kwh@nos&=!$ej&60fh' #example
```

記述できたら`settings.py`の内容を以下のように変更します。

```settings.py
from .local_settings import *   # 上部に追加

#以下は削除
"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'pej^q2ql$$#bzg#crh6k_9(p=%p)&6x(kwh@nos&=!$ej&60fh' #example  
"
```

####機密情報を管理するjsonファイルの作成
今回、DBをRDSで接続しているので、settings.pyにはDB情報にアカウント、パスワード等の情報が記載されます。
ただ、そのまま記載すると上記の理由と同じく、第三者にダダ漏れ状態となるので、これも公開しない設定をする必要があります。
そこで、jsonファイルを使った方法でDBの機密情報を管理外に設定します。

※もしかしたら、`local_settings.py`に記載するだけで管理外とできるかもしれませんが、今回別々で設定しているため
目的が同じでも別々の方法をとっています。もし余力があれば片方だけで機能するか試してみてもいいかもしれません。

作成方法は[こちら](https://hiroki-sawano.hatenablog.com/entry/django-secrets#%E3%82%B5%E3%83%BC%E3%83%90%E8%B5%B7%E5%8B%95)を参照してください。

###runserverでの動作確認
ここまでできたら、一度runserverを起動してアプリケーションが動くか確認しましよう。
EC2ではパブリックIPv4　DNSのポート8000でrunserverにアクセスできます。

```
$ python3 manage.py runserver ec2-***.compute-1.amazonaws.com:8000 
```

#公開用設定
無事アプリケーションが動いたら、公開用の設定を行っていきます。
まずはApacheとmod_wsgiの連携を行います。

最初にmod_wsgiのファイルがどこにあるのか確認します。

```
$ sudo find / -name mod_*.so
/usr/local/lib64/python3.7/site-packages/mod_wsgi/server/mod_wsgi-py37.cpython-37m-x86_64-linux-gnu.so
```
たくさん何かが出てくると思いますが、上記の一番長いやつが本体です。これは後で使用するので、メモ等をしておいて下さい。

続いて、Python３の場所も確認しておきます。こちらも使用しますので、メモ等しておいて下さい。

```
$ which python3
```


次はApacheに設定を追加していきます。
mod_wsgiについて記載する設定ファイルを新たに作ります。

```
sudo vi /etc/httpd/conf.d/project.conf

# ファイル名は何でも構いません。
```
作成した設定ファイルに以下を記述します。

```myporject.conf
# 上記で検索したファイル名を記載します。
LoadModule wsgi_module /usr/local/lib64/python3.7/site-packages/mod_wsgi/server/mod_wsgi-py37.cpython-37m-x86_64-linux-gnu.so

#EC2インスタンスのパブリックIPを記載します。
ServerName <パブリックIP>

　　　　　　　　　　　　　　　　　　　　　　　　　　　　　#　↓ 左右に半角スペース
WSGIScriptAlias / [wsgi.pyの絶対パス]
WSGIPythonPath [プロジェクトの絶対パス]:[Python3のパス] 

Alias /static/ [staticフォルダの絶対パス]/ # "/"を最後つけること
<Directory [staricフォルダの絶対パス]> # 最後の”/"は必要なし
Require all granted
</Directory>

<Directory [wsgi.pyが格納されているフォルダの絶対パス]>
<Files wsgi.py>
Require all granted
</Files>
</Directory>

※[]は外して下さい
```

最後にApacheをリスタートさせます。

```
sudo service httpd restart
```

以上でようやくデプロイ完了です。
EC2のパブリックIPにアクセスしてDjangoアプリが表示されれば大成功です。

※参考記事※

https://hirahira.blog/ec2deploy-django/

https://qiita.com/saki-engineering/items/5ea8e66b498fc7d4a399

# 終わりに
いかがでしたでしょうか。
プロジェクトによったり、設計によったりしてデプロイまでの内容は変わりますのでこの通りしてもうまくいかないかもしれませんが、デプロイまでの参考として、お役に立てれば幸いです。
また、今後もしデプロイしたアプリケーションを運用してくのであれば、セキュリティや耐久性の対策はまだまだ不十分です。
知識がつき次第アップロードしようと思いますが、この記事ではその辺りはカバーしておりませんのでご了承をお願いいたします。

また、初学者で至らない部分があるかと思います。
修正、アドバイス等あればご指摘いただけると幸いです。