【AWS】Django環境をEC2+ELB+RDSで構築してみた

2021/9/20 Webサーバをプライベートサブネットに配置する構成に変更
2021/9/23 カスタムAMIを使用した起動テンプレートの作成について追記

#はじめに
Djangoの実行環境を構築する機会があったため、せっかくなので現在学習を進めているAWSに絡めて構築してみようと思います。
※AWSがメインの内容となるため、Django実行環境についてはさらっと記載します。
※Django実行環境については独学となるのでご容赦ください...

#構成図
以下のような構成を目指します。
![diagram01_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/f96dc1db-fa7e-6ab6-fb0c-1e4b3902d153.png)

EC2インスタンス OS：Amazon Linux 2
Webサーバ(リバースプロキシ)：Nginx
APサーバ(WSGIサーバ)：Gunicorn
データベース(RDS)：MySQL

* Nginx+GunicornをEC2インスタンスにインストール
* データベースとしてマルチAZ構成のRDS(MySQL)を使用
* EC2起動時にユーザーデータによって、Django環境構築を実行
* EC2インスタンスの起動テンプレートを作成し、Auto Scaling グループを作成
* ELBを作成し、EC2インスタンスへのトラフィックをロードバランシング
* CloudWatchでAuto Scaling グループのCPU使用率に基づいたアラームを設定し、CPU使用率によってAuto Scaling グループ内のEC2インスタンスの数が増減するように設定

※リソースはすべて東京リージョンに作成します。

#作業環境
macOS Big Sur バージョン 11.4

#ネットワーク/セキュリティグループ/踏み台サーバ 作成
まずはAWS内でのネットワーク環境とELB、EC2インスタンス、RDSそれぞれにどういった通信を許可するかを定義するためのセキュリティグループを作成していきます。
また、後の手順でWebサーバにSSH接続するための踏み台サーバを用意します。

#####ネットワークを作成
AWS上にネットワークを作成します。
作成するものは以下となります。

* VPC x 1
* インターネットゲートウェイ x 1
* NAT ゲートウェイ x 1
* ELB用パブリックサブネット x 2 (同VPC、別AZ)
* Webサーバ用プライベートサブネット x 2 (同VPC、別AZ)
* RDS用プライベートサブネット x 2 (同VPC、別AZ)

基本的な作成・設定手順は以前作成した以下の記事をご参照ください。

https://qiita.com/zeems/items/1d97d42a99936885ca4a

#####セキュリティグループを作成
ELB、踏み台サーバ、Webサーバ、RDSに適用するセキュリティグループを作成します。
ELB用セキュリティグループはインバウンドで以下を許可するように作成します。

* HTTP (Source:マイIP)

踏み台サーバ用セキュリティグループはインバウンドで以下を許可するように作成します。

* SSH (Source:マイIP)

Webサーバ用セキュリティグループはインバウンドで以下を許可するように作成します。

* HTTP (Source:ELB用セキュリティグループ)
* SSH (Source:踏み台サーバ用セキュリティグループ)

RDS用セキュリティグループはインバウンドで以下を許可するように作成します。

* MYSQL/Aurora (Source:Webサーバ用セキュリティグループ)

#####踏み台サーバを作成
踏み台サーバ用に1台のEC2インスタンスを起動します。
今回はELB用に作成したパブリックサブネットに配置し、踏み台サーバ用に作成したセキュリティグループを適用します。

#####構成図
ネットワーク作成後の構成図は以下の通りです。
![diagram03_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/fefa7ed7-9781-827b-b573-22c5d6942c11.png)

#RDS 作成
#####RDS(Relational Database Service)とは

* AWSがPaaSサービスとして提供するリレーショナルデータベース
* MySQL、MariaDB、PostgreSQL、Oracle、SQL Serverなどの様々なデータベースエンジンに対応
* 他のAZ(アベイラビリティーゾーン)にレプリカを配置するマルチAZ構成を取ることができる

#####サブネットグループを作成
RDSをVPC・サブネットに配置するためには事前にサブネットグループの作成が必要となります。

AWS Management Consoleにログインし、RDSのダッシュボードを開きます。
画面左ツリーの[サブネットグループ]をクリックし、サブネットグループ一覧を表示します。
![スクリーンショット 2021-07-10 15.07.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/bb8385d2-0719-0a2c-61d8-dbcac2fcfaa8.png)

画面右上の[DB サブネットグループを作成]をクリックします。
![スクリーンショット 2021-07-10 15.34.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/b563ccd2-506c-8128-4fa2-fbdb3d9e93fe.png)

`名前`にサブネットグループの名前、`VPC`にサブネットグループに使用するサブネットが存在するVPCを選択します。
![スクリーンショット 2021-07-10 15.34.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/b63feca4-370f-50d0-f878-bcb44ca21abc.png)

`アベイラビリティーゾーン`で追加するサブネットを含むAZを選択後、`サブネット`で追加するサブネットを選択し、[作成]をクリックします。
![スクリーンショット 2021-09-20 12.07.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/a41c88ee-b33a-1ff9-bff1-c676f8d305a6.png)

:::note alert
２つ以上のアベイラビリティーゾーンでサブネットグループを構成しないと以下のエラーが発生し、作成できません。
:::

![スクリーンショット 2021-07-10 15.35.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/65e2a891-627f-92b6-36f8-e3dca77956e3.png)

#####データベースを作成

サブネットグループが作成できたら、画面左ツリーの[データベース]をクリックし、データベース一覧を表示します。
一覧画面右上の[データベースの作成]をクリックします。

データベース作成ウィザードが開きます。
今回は以下の設定でデータベースを作成します。
設定が入力・選択できたら、[データベースの作成]をクリックします。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| データベース作成方法    | 標準作成                   |
| エンジンのタイプ        | MySQL                     |
| テンプレート           | 無料利用枠                  |
| DBインスタンス識別子    | django-database           |
| マスターユーザー名      | admin                     |
| マスターパスワード       | 任意のパスワード              |
| DBインスタンスクラス     | db.t2.micro               |
| VPC                 | 作成したVPC                 |
| サブネットグループ       | 作成したサブネットグループ       |
| パブリックアクセス        | なし                       |
| VPC セキュリティグループ  | 作成したRDS用セキュリティグループ |
| アベイラビリティーゾーン    | ap-northeast-1a           |
| データベースポート       | 3306                      |
| 最初のデータベース名     | mysite                    |
※記載のない項目はデフォルトとなります。

RDSが作成できたらEC2インスタンス側のDjango実行環境を構築していきます。
今回は起動テンプレート作成時にユーザーデータを使用することでDjango環境構築を行います。
まずはそのための事前準備を行っていきます。

#Djangoプロジェクト 作成
基本的に以下のDjangoチュートリアルに沿って作成を進めます。

https://docs.djangoproject.com/ja/3.2/intro/tutorial01/

今回のAWS構成に関連する部分、チュートリアル通りに作業が進まなかった部分等を以下で補足していきます。

#####データベース設定を変更
settings.py内のデータベース設定を変更する際に作成したRDSの設定情報を入力します。

```vim:settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mysite',
        'USER': 'admin',
        'PASSWORD': '<adminのパスワード>',
        'HOST': '<RDSのエンドポイント>',
        'PORT': '3306',
    }
}
```

#####開発用サーバー起動
開発用サーバーを起動する際に以下のエラーが発生しました。

```bash:ec2
$ python3 manage.py runserver

以下のエラーが発生
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?
```

どうやらmysqlclietモジュールをインストールする必要がある様子。
pip3でインストールを実行してみます。

```bash:ec2
$ pip3 install mysqlclient

以下のエラーが発生
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
```

以下を参考にしたところ、mysqlclientモジュールをインストールすることができました。

https://kamatimaru.hatenablog.com/entry/2020/05/28/014653

改めて開発用サーバーを起動します。
警告は出ていますが、開発用サーバーを起動することができました。

```bash:ec2
$ python3 manage.py runserver

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
July 18, 2021 - 08:22:04
Django version 3.2.5, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
```

#####ブラウザからアプリケーションにアクセス
外部からアクセスできるように開発用サーバーを起動する際にオプションを指定します。

```bash:ec2
$ python3 manage.py runserver 0:8000
```

EC2インスタンスのパブリックIPアドレスの8000番ポートに対してアクセスしましたが、デフォルト設定のままだと以下のエラーが発生します。

```bash:ec2
Invalid HTTP_HOST header: '<EC2インスタンスのパブリックIPアドレス>:8000'. You may need to add '<EC2インスタンスのパブリックIPアドレス>' to ALLOWED_HOSTS.
```

settings.pyの「ALLOWED_HOSTS」の値を変更することでアクセスできるようになります。

```vim:settings.py
ALLOWED_HOSTS = ["*"]
```

#####GitHubにDjangoプロジェクトをアップロード
チュートリアルに従って作成したDjangoプロジェクトをGitHubにアップロードしておきます。
後ほど、ユーザーデータの処理の中でDjangoプロジェクトをGitHubからダウンロードします。

#Djangoパラメータファイル 作成
RDSへの接続情報などをDjangoプロジェクト内のsettings.pyに記載していましたが、別ファイル「settings_secret.py」を作成しそちらに情報を転記しました。
settings_secret.py は .gitignore を使用してGit管理対象外とします。

```vim:settings_secret.py
SECRET_KEY = '<プロジェクト作成時に生成されるキー>'

DB_NAME = 'mysite'
DB_USER = 'admin'
DB_PASS = '<adminのパスワード>'
DB_HOST = '<RDSのエンドポイント>'
DB_PORT = '3306'
```

```vim:settings.py
from .settings_secret import *

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': DB_NAME,
        'USER': DB_USER,
        'PASSWORD': DB_PASS,
        'HOST': DB_HOST,
        'PORT': DB_PORT,
    }
}
```

#settings.py 設定変更
settings.py 内の「DEBUG」の値を変更し、「STATIC_ROOT」を追加します。

```vim:settings.py
DEBUG = False

STATIC_ROOT = '/usr/share/nginx/html/static'
```

#Gunicornサービスユニットファイル 作成
Gunicornをsystemdに登録し、サービスとして扱うためにサービスユニットファイルを作成します。

#####Gunicornサービスユニットファイルを作成
以下を参考にGunicornをsystemdに登録するためのサービスユニットファイルを作成します。

https://kamatimaru.hatenablog.com/entry/2020/05/28/015828

```vim:gunicorn.service
[Unit]
Description=gunicorn daemon
After=network.target

[Service]
Type=notify
PIDFile=/run/gunicorn/pid
User=ec2-user
Group=ec2-user
RuntimeDirectory=gunicorn
WorkingDirectory=/home/ec2-user/mysite
ExecStart=/usr/local/bin/gunicorn --workers 3 --bind=localhost:8000 mysite.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
Restart=always

[Install]
WantedBy=multi-user.target
```

#Nginx設定ファイル 作成
NginxとGunicornを連携させるためにNginxの設定を編集します。
デフォルトでは /etc/nginx/conf.d に設定ファイル(.conf)を配置すると、設定として読み込まれます。
※/etc/nginx/nginx.conf のhttpディレクティブ内の「include /etc/nginx/conf.d/*.conf」によって読み込まれます。

以下を参考に設定ファイルを作成します。

https://blog.narito.ninja/detail/21/

```vim:mysite.conf
server {
  listen 80;
  server_name ec2-localip;　→　ユーザーデータ処理時に実際のローカルIPアドレスに置換

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

  location / {
    proxy_pass http://localhost:8000;
    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;
  }
}
```

#Git認証情報ファイル 作成
ユーザーデータ処理の中でGitHubリポジトリからDjangoプロジェクトをダウンロードするにあたって、処理中に対話型でユーザー・パスワードを求められないようにします。
実行ユーザーのホームディレクトリに「.netrc」というファイルを作成することで、ファイルに記述した認証情報を使用することができます。

```vim:.netrc
machine github.com
login <GitHubのユーザー名>
password <ユーザーのパスワード or Personal Access Token>
```

※Personal Access Tokenの作成は以下を参照しました。

https://docs.github.com/ja/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token

#S3バケット 作成・ファイル配置
S3バケットを作成し、ここまで作成した以下のファイルを配置します。

* Djangoパラメータ用ファイル(settings_secret.py)
* Gunicornサービスファイル(gunicorn.service)
* Nginx設定ファイル(mysite.conf)
* Git認証情報ファイル(.netrc)

#IAMロール 作成
EC2インスタンスからS3へのアクセスを許可するためのIAMロールを作成します。
以下の設定でIAMロールを作成します。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| 信頼されたエンティティ    | EC2                       |
| ポリシー               | AmazonS3FullAccess        |
| ロール名              | 任意の名前                  |

これでDjango環境構築の事前準備は完了です。

#起動テンプレート 作成
Auto Scaling グループで使用する起動テンプレートを作成していきます。
EC2画面で[テンプレートの起動]から起動テンプレート一覧を開きます。
![スクリーンショット 2021-09-04 14.09.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/0a13f1f6-7940-d8b7-e914-ac5f8b76c092.png)

画面右上の[起動テンプレートを作成]をクリックします。
![スクリーンショット 2021-09-04 14.10.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/ff75e305-cabb-329a-d4c4-f1804230f019.png)

起動テンプレートの作成画面となります。
今回は以下の設定で起動テンプレートを作成します。
設定が入力・選択できたら、[起動テンプレートを作成]をクリックします。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| 起動テンプレート名    | django-launch-template                   |
| テンプレートバージョンの説明        | 任意                    |
| Auto Scaling のガイダンス           | 有効                  |
| AMI    | Amazon Linux 2 (x86)           |
| インスタンスタイプ      | t2.micro                     |
| キーペア       | 任意のキーペア              |
| ネットワーキングプラットフォーム       | Virtual Private Cloud (VPC) |
| ストレージ | 変更なし |
| ネットワークインターフェイス | １つ追加 ※詳細は下に記載 |
| IAM インスタンスプロフィール | S3アクセス用IAMロール |
| ユーザーデータ | ※詳細は下に記載|

【ネットワークインターフェイス】

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| デバイスインデックス    | 0                   |
| ネットワークインターフェイス        | New interface                     |
| セキュリティグループ          | Webサーバ用セキュリティグループ      |
| パブリック IP の自動割り当て    | 無効化           |

【ユーザーデータ】
ユーザーデータにより必要パッケージのインストールやファイル・ディレクトリの権限設定、GitHub・S3から必要なファイルのダウンロードなどの処理を行っていきます。
※GitHubリポジトリのURL、S3バケットの名前は置き換えて使用してください。

```shell:userdata
#!/bin/bash
#必要なパッケージ・モジュールをインストール
yum -y update
yum -y install python3 git python3-devel mysql mysql-devel gcc
pip3 install django
pip3 install mysqlclient
pip3 install gunicorn
amazon-linux-extras install nginx1

#Nginxサービスの自動起動設定・開始
systemctl enable nginx
systemctl start nginx

#Staicファイル用ディレクトリの作成
mkdir /usr/share/nginx/html/static

#git clone用認証情報ファイルのダウンロード
aws s3 cp s3://<S3のバケット名>/.netrc /root/.netrc

#DjangoプロジェクトをGitHubから取得
git clone <GitHubリポジトリのURL> /home/ec2-user/mysite

#Djangoプロジェクトの所有者をec2-userに変更
chown -R ec2-user:ec2-user /home/ec2-user/mysite

#setting.py内で使用するパスワード等を記述した外部ファイルをダウンロード
aws s3 cp s3://<S3のバケット名>/settings_secret.py /home/ec2-user/mysite/mysite/settings_secret.py

#上記ファイルの所有者をec2-userに変更
chown ec2-user:ec2-user /home/ec2-user/mysite/mysite/settings_secret.py

#Gunicornのサービスユニットファイルをダウンロード
aws s3 cp s3://<S3のバケット名>/gunicorn.service /etc/systemd/system/gunicorn.service

#Gunicornサービスユニットファイルの権限を変更
chmod 644 /etc/systemd/system/gunicorn.service

#Gunicornサービスを自動起動設定・開始
systemctl enable gunicorn.service
systemctl start gunicorn.service

#Nginx設定ファイルをダウンロード・ローカルIPアドレスを置換
aws s3 cp s3://<S3のバケット名>/mysite.conf /etc/nginx/conf.d/mysite.conf
localip=`curl http://169.254.169.254/latest/meta-data/local-ipv4`
sed -i -e "s/ec2-localip/$localip/g" /etc/nginx/conf.d/mysite.conf

#Nginxサービスを再起動
systemctl restart nginx

#StaticファイルをSTATIC_ROOTに記述したパスに配置
cd /home/ec2-user/mysite
python3 manage.py collectstatic

#Staticファイル用ディレクトリの所有者をec2-userに変更
chown -R ec2-user:ec2-user /usr/share/nginx/html/static
```

:::note info
ユーザーデータによる必要パッケージのインストールやファイル・ディレクトリの権限設定、GitHub・S3から必要なファイルのダウンロードなどの処理を事前に反映させたカスタムAMIから起動テンプレートを作成することも可能です。
:::

カスタムAMIの作成については別記事として記載しました。

https://qiita.com/zeems/items/7de9ffdacbd605ec9044

また、カスタムAMIを使用する場合は起動テンプレートを以下の設定で作成します。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| 起動テンプレート名    | django-launch-template                   |
| テンプレートバージョンの説明        | 任意                    |
| Auto Scaling のガイダンス           | 有効                  |
| AMI    | 作成したカスタムAMI           |
| インスタンスタイプ      | t2.micro                     |
| キーペア       | 任意のキーペア              |
| ネットワーキングプラットフォーム       | Virtual Private Cloud (VPC) |
| ストレージ | 変更なし |
| ネットワークインターフェイス | １つ追加 ※詳細は下に記載 |
| ユーザーデータ | ※詳細は下に記載|

※S3からのファイルダウンロードが不要となるため、IAMロールは適用しません。

【ネットワークインターフェイス】

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| デバイスインデックス    | 0                   |
| ネットワークインターフェイス        | New interface                     |
| セキュリティグループ          | Webサーバ用セキュリティグループ      |
| パブリック IP の自動割り当て    | 無効化           |

【ユーザーデータ】
Nginx設定ファイル内の`server_name`の値についてはWebサーバごとにローカルIPアドレスに置換する必要があるため、ユーザーデータで処理します。

```shell:userdata
#!/bin/bash
#Nginx設定ファイルのローカルIPアドレスを置換
localip=`curl http://169.254.169.254/latest/meta-data/local-ipv4`
sed -i -e "s/ec2-localip/$localip/g" /etc/nginx/conf.d/mysite.conf

#Nginxサービスを再起動
systemctl restart nginx
```

#Auto Scaling グループ 作成
次に Auto Scaling グループを作成します。
EC2管理画面で[Auto Scaling グループ]からAuto Scaling グループ画面を開きます。
![スクリーンショット 2021-09-04 14.12.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/e06c8adf-8de4-a824-562c-937819bb0001.png)

[Auto Scaling グループの作成]をクリックします。
![スクリーンショット 2021-09-04 14.12.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/6a6e1f11-ae2c-c468-912f-957873298506.png)

`Auto Scaling グループ名`で名前を入力後、`起動テンプレート`で作成した起動テンプレートを選択し、[次へ]をクリックします。
![スクリーンショット 2021-09-04 14.13.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/293a5a28-2c49-a25c-d227-bbc1600f9f66.png)

`VPC`で事前に作成したVPCを選択後、`サブネット`で２つのWebサーバ用プライベートサブネットを選択し、[次へ]をクリックします。
![スクリーンショット 2021-09-20 12.27.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/29759d7c-eed9-f4be-6c60-8f75447ccaf5.png)

`ロードバランシング`で[新しいロードバランサーにアタッチ]を選択後、以下の設定を行い、[次へ]をクリックします。
![スクリーンショット 2021-09-04 14.15.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/6c61baf6-bd3e-2971-0277-b1e908b9adac.png)

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| ロードバランサーのタイプ    | Application Load Balancer     |
| ロードバランサーの名前  | django-LB              |
| ロードバランサーのスキーム  | Internet-facing         |
| VPC  | 作成したVPC         |
| アベイラビリティーゾーンとサブネット  | ELB用パブリックサブネット    |
| プロトコル  | HTTP         |
| ポート  | 80         |
| デフォルトルーティング (転送先)  | ターゲットグループを作成する    |
| 新しいターゲットグループ名  | django-TG         |
| ヘルスチェックのタイプ  | EC2, ELB         |
| ヘルスチェックの猶予期間  | 300 秒         |

今回はEC2インスタンスは最小２台、最大４台となるようにスケーリングするため、`グループサイズ`では以下のように設定します。
設定が完了したら[次へ]をクリックします。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| 希望する容量    | 2                   |
| 最小キャパシティ  | 2                     |
| 最大キャパシティ  | 4                     |

通知は今回はなしで進めますので、そのまま[次へ]をクリックします。

任意でタグを設定し、[次へ]をクリックします。

設定内容を確認し、[Auto Scaling グループを作成]をクリックします。

#ターゲットグループ/ロードバランサー 設定変更
このままだとターゲットグループのヘルスチェックに失敗する状態となります。
原因は以下の２つです。

* ヘルスチェックのパスからのレスポンスコードが「200」でない
* ロードバランサーからEC2インスタンスへのHTTPが許可されていない

それぞれの問題をクリアするために対応を実施していきます。

#####ヘルスチェック設定を変更
Auto Scaling グループ作成ウィザード内でターゲットグループを作成したことにより、ヘルスチェック設定がデフォルト設定となっています。
今回の構成に合わせて設定を変更していきます。

EC2管理画面で[ターゲットグループ]からターゲットグループ一覧を開きます。
![スクリーンショット 2021-09-04 14.17.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/8c6520ed-cc82-4347-8109-a9fa736a3b10.png)

先ほど作成されたターゲットグループを選択し、画面下部で[Health checks]タブを選択後、[Edit]をクリックします。
![スクリーンショット 2021-09-04 14.17.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/b72d585c-c563-a0d6-ab45-39cc4c483b07.png)

以下のように設定を変更し、[Save changes]をクリックします。
※この画面では表記が英語になっています。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| Health check protocol    | HTTP           |
| Health check path      | /polls/          |
| Port       | 80              |
| Healty threshold       | 2              |
| Unhealth threshold       | 2              |
| Timeout       | 5 seconds              |
| Interval       | 10 seconds              |
| Success code       | 200              |

#####ロードバランサーのセキュティグループを変更
Auto Scaling グループ作成ウィザード内でロードバランサーを作成したことにより、VPCデフォルトのセキュリティグループが適用されています。
こちらも今回の構成に合わせて設定を変更していきます。

EC2管理画面で[ロードバランサー]からロードバランサー一覧を開きます。
![スクリーンショット 2021-09-04 14.18.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/73984355-d348-9bc5-7501-8ace321d1c78.png)

先ほど作成されたロードバランサーを選択し、[アクション]-[セキュリティグループの編集]をクリックします。
![スクリーンショット 2021-09-04 14.19.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/97760c82-2941-9fd8-6c80-4b9d43f7e34a.png)

VPCデフォルトのセキュリティグループの選択を外し、ELB用セキュリティグループを選択後、[保存]をクリックします。
![スクリーンショット 2021-09-04 14.19.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/463f745d-8c2f-4dcf-7047-76fb66656ccc.png)

#####ヘルスチェック/アクセスを確認
ターゲットグループのヘルスチェック状態を確認します。
ターゲットグループ一覧でターゲットグループを選択し、画面下部の[Targets]タブをクリックします。
登録済みのターゲットの一覧が表示されるので、すべてのEC2インスタンスで`Health status`が「healty」になっていることを確認します。
![スクリーンショット 2021-09-04 14.20.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/f3165206-e80c-3d32-74e9-de784bfa00ad.png)

ヘルスチェックが成功しているのを確認できたら、ブラウザから Django アプリケーションへのアクセスを確認します。
ブラウザで「http://`ロードバランサーのDNS名`/polls」にアクセスし、Djangoアプリケーションが表示されることを確認します。

#####構成図
Auto Scaling グループ作成後の構成図は以下の通りです。
スケーリングポリシーを設定していないので、EC2インスタンスは希望する容量で指定した２台となっています。
![diagram04_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/e6e52a94-3f62-a323-9ddd-081a80b0211b.png)

#RDS マルチAZ構成
RDSをマルチAZ構成とします。

RDS管理画面で[データベース]からデータベース一覧を開きます。
マルチAZ構成とするデータベースを選択し、画面上部の[変更]をクリックします。
![スクリーンショット 2021-09-04 14.25.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/bcb832f9-24bc-e0a4-8c10-d661d565096e.png)

変更画面が開くので、`マルチ AZ 配置`で「スタンバイインスタンスを作成する (本稼働環境向けに推奨)」を選択し、[続行]をクリックします。
![スクリーンショット 2021-09-04 14.26.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/648f52f6-cb0e-5090-0f8a-07e24bfbc77a.png)

`変更を適用するタイミング`で「すぐに適用」を選択し、[DB インスタンスを変更]をクリックします。
![スクリーンショット 2021-09-04 14.26.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/69f718ae-78a7-e898-94ec-0ca6b2cd3a31.png)

`ステータス`が「利用可能」、`マルチ AZ`が「あり」に変更されることを確認します。
![スクリーンショット 2021-09-04 14.37.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/017bf435-1765-cd35-31cb-a6f554a2efee.png)

#####構成図
RDSマルチAZ構成後の構成図は以下の通りです。
![diagram05_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/f69de80f-b5fc-e189-313c-4b741ee8686e.png)

# CloudWatch 監視設定
EC2インスタンスのCPU使用率に応じて、Auto Scaling グループ内のEC2インスタンス数を増減するための監視設定を行っていきます。

#####アラームを作成
まずは Auto Scaling グループのCPU使用率が高い場合のアラームを作成します。
CloudWatch管理画面で[アラーム]からアラーム一覧を開きます。
![スクリーンショット 2021-09-04 14.38.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/99a930f7-485b-c568-9a04-f7fdd1851f49.png)

[アラームの作成]をクリックします。
![スクリーンショット 2021-09-04 14.38.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/dd26671b-d310-7e45-0af3-5f9aadfab540.png)

[メトリクスの選択]をクリックし、[EC2]-[Auto Scaling グループ別]を選択します。
![スクリーンショット 2021-09-04 14.39.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/4449126f-cbf3-3cd4-e6b5-b26dcb5a3864.png)
![スクリーンショット 2021-09-04 14.39.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/b902b7bc-8dd0-fcbd-5e9c-1d061d9b1d3f.png)
![スクリーンショット 2021-09-04 14.39.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/c66bd278-b2bd-8f08-7220-3812937db9cd.png)

先ほど作成した Auto Scaling グループのメトリクス[CPUUtilization]を選択し、[メトリクスの選択]をクリックします。
![スクリーンショット 2021-09-04 14.39.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/a247ef57-1e3b-ab51-cefc-aeae4e3e01c6.png)

`条件`を設定し、[次へ]をクリックします。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| しきい値の種類    | 静的                   |
| CPUUtilization が次の時...    | より大きい             |
| ...よりも           | 70                  |
| アラームを実行するデータポイント    | 1 / 1           |
| 欠落データの処理      | 欠落データを不正 (しきい値を超えている) として処理  |

通知は今回はなしで進めますので、`通知`内の[削除]をクリック後、[次へ]をクリックします。
![スクリーンショット 2021-09-04 14.40.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/20f14c36-19da-3b7b-d4e4-a9d9c7e9bece.png)

`アラーム名`で名前を入力後、[次へ]をクリックします。
![スクリーンショット 2021-09-04 14.41.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/5bd87a8c-20d6-7a71-5fe9-4d321b965448.png)

設定内容を確認し、[アラームの作成]をクリックします。

次に Auto Scaling グループのCPU使用率が低い場合のアラームを作成します。
以下の条件設定を使用して、同様の手順でアラームを作成します。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| しきい値の種類    | 静的                   |
| CPUUtilization が次の時...    | より低い             |
| ...よりも           | 30                  |
| アラームを実行するデータポイント    | 1 / 1           |
| 欠落データの処理      | 欠落データを見つかりませんとして処理  |

#####スケーリングポリシーを設定
Auto Scaling グループ にスケーリングポリシーを設定していきます。
Auto Scaling グループ一覧画面で作成した Auto Scaling グループを選択し、画面下部の[自動スケーリング]タブを選択します。
[Create dynamic scaling policy]をクリックします。
![スクリーンショット 2021-09-04 14.43.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/e1c45ca2-3b2b-9e4c-33ab-1d899a3dc822.png)

まずはCPU高負荷時のスケーリングポリシーを作成します。
以下の設定を行い、[作成]をクリックします。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| ポリシータイプ    | シンプルなスケーリング                   |
| スケーリングポリシー名    | CPU_add             |
| CloudWatch アラーム     | 作成したCPU高負荷時のアラーム       |
| アクションを実行    | 追加： 1 キャパシティユニット           |
| 別のスケーリングアクティビティを許可するまでの秒数      | 30 秒  |

次にCPU低負荷時のスケーリングポリシーを作成します。
以下の設定で作成します。

| 項目                 | 値                        |
|:-------------------:|:-------------------------:|
| ポリシータイプ    | シンプルなスケーリング                   |
| スケーリングポリシー名    | CPU_remove             |
| CloudWatch アラーム     | 作成したCPU低負荷時のアラーム       |
| アクションを実行    | 削除： 1 キャパシティユニット           |
| 別のスケーリングアクティビティを許可するまでの秒数      | 30 秒  |

#####構成図
スケーリングポリシー設定後の構成図は以下の通りです。
![diagram06_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/d9f4bcfc-fe7a-aa91-42d1-f0a0735dc945.png)

#####負荷テスト
WebサーバにCPU負荷をかけて、自動スケーリングをテストします。
踏み台サーバ経由でWebサーバにSSH接続します。
SSH接続については以下を参照してください。

https://qiita.com/zeems/items/1d97d42a99936885ca4a#ssh%E6%8E%A5%E7%B6%9A

以下のコマンドを使用してCPU負荷をかけていきます。
※並列で負荷をかけるために複数回コマンドを実行しています。

```bash:ec2
$ yes >> /dev/null &
[1] 8457
$ yes >> /dev/null &
[2] 8459
$ yes >> /dev/null &
[3] 8460
$ yes >> /dev/null &
[4] 8461
```

top コマンドで確認すると、CPU使用率が100%近くになっていることが確認できます。

```bash:ec2
top - 08:24:23 up 17 min,  1 user,  load average: 2.68, 0.81, 0.31
Tasks:  94 total,   6 running,  52 sleeping,   0 stopped,   0 zombie
%Cpu(s): 99.7 us,  0.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1006892 total,   258452 free,   227764 used,   520676 buff/cache
KiB Swap:        0 total,        0 free,        0 used.   625988 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                   
 8457 ec2-user  20   0  114636    792    728 R 24.9  0.1   0:19.60 yes                                       
 8459 ec2-user  20   0  114636    756    692 R 24.9  0.1   0:17.22 yes                                       
 8461 ec2-user  20   0  114636    780    716 R 24.9  0.1   0:16.21 yes                                       
 8460 ec2-user  20   0  114636    764    700 R 24.6  0.1   0:16.70 yes
```

同様の手順でもう一台のWebサーバにも負荷をかけ、アラームが発生するまで待機します。

CloudWatch管理画面で[アラーム]からアラーム一覧を開きます。
![スクリーンショット 2021-09-04 14.54.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/f0efbddb-2937-e4ef-79c5-0d8dd78ac0a2.png)

CPU高負荷時のアラームの`状態`が「アラーム状態」になっていることを確認したら、EC2インスタンス画面を開き、EC2インスタンスが自動的に増えていることを確認します。
![スクリーンショット 2021-09-04 14.57.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/f134c21e-2b6d-1738-7edf-81ddd4a74241.png)

Auto Scaling グループのアクティビティ履歴からも自動スケールアウトが実行されたログを確認することができます。
![スクリーンショット 2021-09-04 14.58.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/8a2a7f68-6679-0765-7514-4b1bd6d76b76.png)

自動スケールアウトの動作が確認できたら、WebサーバのCPU負荷を下げ、自動スケールインの動作を確認します。
負荷をかけているWebサーバにSSH接続し、負荷をかけているコマンドのプロセスを停止します。

```bash:ec2
$ kill 8457
$ kill 8459
$ kill 8460
$ kill 8461
※プロセス番号が連続している場合は kill {８４５９..8461} のようにまとめて指定することが可能です。
```

CloudWatch管理画面で[アラーム]からアラーム一覧を開きます。
![スクリーンショット 2021-09-04 15.06.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/bcc35f6c-66e0-c51b-1b41-03a9a9ec5a93.png)

CPU低負荷時のアラームの`状態`が「アラーム状態」になっていることを確認したら、Auto Scaling グループのアクティビティ履歴を確認します。
![スクリーンショット 2021-09-04 15.07.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/59f91157-2eeb-9785-d57e-4b32c152455d.png)

スケールインの場合には即時EC2インスタンスが削除されると利用しているユーザーが困るため、`Draining`と呼ばれる期間が設けられています。

Auto Scaling グループのアクティビティ履歴からもステータスが`Draining`であるログを確認することができます。
`Draining`の期間はターゲットグループによって設定されています。
※デフォルトは５分

`Draining`の期間が経過すると、EC2インスタンスが削除されたことがアクティビティ履歴で確認できます。
![スクリーンショット 2021-09-04 15.16.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/dcfc4dbc-df0b-691d-a517-0d96100bf24e.png)

また、EC2インスタンス画面でもEC2インスタンスが削除されていることを確認します。
![スクリーンショット 2021-09-04 15.17.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1410257/8b99a215-6124-6340-c755-0e68d9ed7d10.png)

#あとがき
この記事は CloudTech の課題として作成しました。
動画やハンズオン等で学習を進めることができるので、AWS初学者にはおすすめです。

https://kws-cloud-tech.com/