AWSでウェブアプリケーション環境構築：③ロードバランサーを作成し、webサーバを冗長化する

# はじめに
これはシリーズ記事の中の1つです。

[AWSでウェブアプリケーション環境構築：⓪概要](https://qiita.com/minato-naka/items/ddb5f5301f9f590cdcbf)
[AWSでウェブアプリケーション環境構築：①EC2インスタンスにwebサーバを構築](https://qiita.com/minato-naka/items/c21e4d53db7a720f8fdd)
[AWSでウェブアプリケーション環境構築：②RDSでDBを作成し、Laravelサンプルアプリを動かす最小構成を構築](https://qiita.com/minato-naka/items/03f9dbd09eb3919a501f)
[AWSでウェブアプリケーション環境構築：③ロードバランサーを作成し、webサーバを冗長化する](https://qiita.com/minato-naka/items/396e2a25ec9a94840a67)
**↑↑↑現在の記事↑↑↑**
[AWSでウェブアプリケーション環境構築：④ElastiCacheのRedisを作成し、セッション共有可能にする](https://qiita.com/minato-naka/items/76cd5f660d987d34f7be)
[AWSでウェブアプリケーション環境構築：⑤webサーバをプライベートサブネットに配置し、メンテナンス用踏み台サーバを構築](https://qiita.com/minato-naka/items/406c2f247c0d29a93665)
[AWSでウェブアプリケーション環境構築：⑥Route53を利用して独自ドメインでアクセス可能にする](https://qiita.com/minato-naka/items/6f29694c1540f04dedb2)
[AWSでウェブアプリケーション環境構築：⑦ACMを利用してHTTPS通信可能にする](https://qiita.com/minato-naka/items/1a4f03e85857b8f61c4f)
[AWSでウェブアプリケーション環境構築：⑧サーバ負荷に応じてwebサーバがオートスケーリングする仕組みを構築](https://qiita.com/minato-naka/items/c0fab5d9fbb1cce68ac9)


# 前回まで
![2.png](https://qiita-image-store.s3.amazonaws.com/0/297693/c6a9fec5-47d4-dedb-df9c-a6f808b6f907.png)

前回はRDSでDBサーバを構築し、
webサーバにLaravelサンプルアプリをのせて実際に動作させるところまでやりました。

# 今回構築する環境
![3.png](https://qiita-image-store.s3.amazonaws.com/0/297693/43905eae-217e-93ba-6be7-64594be321c0.png)

ロードバランサーを作成し、webサーバを2台構成にします。

前回までの状態では
webサーバが1台しかないため、
その1台に障害が発生したらその時点でサービスが停止してしまいます。

webサーバを2台構成にすることで
どちらか1台に障害が発生しても、もう1台でサービスを継続することができるようになります。

# webサーバ2台目を構築
## publicサブネット2つ目を作成
まずは、webサーバ2台目を配置するためのpulicサブネットを作成します。
サーバ構成図の右上に配置されているpublicサブネットです。

2つ目のpublicサブネットを作成せず、
1つ目のpublicサブネットにEC2を2台とも配置することも可能ですが、
耐障害性が向上するため複数のアベイラビリティーゾーンに配置することが推奨されています。
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-increase-availability.html

マネジメントコンソールの[VPC]のページの
サイドメニューの[サブネット]をクリックし、
上部にある[サブネットの作成]ボタンをクリック。

下記のように設定します。
![screencapture-us-east-2-console-aws-amazon-vpc-home-2019-01-31-10_13_14.png](https://qiita-image-store.s3.amazonaws.com/0/297693/96640248-1eab-d562-96a8-0e5039bfe00a.png)

|項目|設定値|説明|
|:------------------------|:-------------------------|:-------------------------|
|名前タグ|laravel-sample-subnet-public-b|-|
|VPC|{以前作成した[laravel-sample-vpc]のVPC}|-|
|アベイラビリティーゾーン|us-east-2b|publicサブネット1つ目とは別のアベイラビリティーゾーンにします。|
|IPv4 CIDR ブロック|10.0.3.0/24|-|

## ルートテーブルをサブネットに関連付ける
サブネットの一覧で先ほど作成した[laravel-sample-subnet-public-b]をチェックし、
ページ上部の[アクション]→[ルートテーブルの変更]をクリック。

[ルートテーブルID]に以前作成した[laravel-sample-route-table-public]のルートテーブルを設定します。

これでwebサーバ2台目を配置するためのpublicサブネット2つ目が完成しました。


## webサーバ1台目のAMIを作成
次にwebサーバ2台目を作成しますが、
インストールしたApacheやPHPなどのミドルウェア、
Laravelサンプルアプリの設定など
webサーバ1台目と全く同じ状態で作成したいです。

そのため、1台目のwebサーバのAMIを作成し、
そのAMIから2台目のwebサーバを作成します。

EC2インスタンスの一覧で以前作成した[laravel-sample-ec2-web-a]をチェックし、
ページ上部の[アクション]→[イメージ]→[イメージの作成]をクリック。

下記のように設定します。
<img width="532" alt="screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-10_26_31.png" src="https://qiita-image-store.s3.amazonaws.com/0/297693/0a80c6bb-c561-5184-5608-7731b37b16a2.png">

[イメージ名]の項目だけ自分がわかるように[laravel-sample-ami-web]として、
それ以外はデフォルト状態のままで問題ないです。

これでwebサーバ1台目のAMI作成は完了です。

## webサーバ2台目を作成
マネジメントコンソールの[EC2]のページの
サイドメニューの[インスタンス]をクリックし、
上部にある[インスタンスの作成]ボタンをクリック。

### AMIの選択
サイドメニューの[マイAMI]をクリックし表示されたAMI一覧から
先ほど作成した[laravel-sample-ami-web]を選択します。
![screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-10_33_50.png](https://qiita-image-store.s3.amazonaws.com/0/297693/0fab7f56-faa0-a740-ff41-61a8810c1bfd.png)

### インスタンスタイプの選択
1台目と同じようにt2.microを選択します。

### インスタンスの詳細の設定
下記のように設定します。
![screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-10_36_48.png](https://qiita-image-store.s3.amazonaws.com/0/297693/73ff5b99-425d-d862-04c1-bef6def931d9.png)


下記の3項目だけ間違えないように設定し、
それ以外はデフォルト設定で問題ないです。

|項目|設定値|説明|
|:------------------------|:-------------------------|:-------------------------|
|ネットワーク|{以前作成した[laravel-sample-vpc]のVPC}|このインスタンスが作られるVPC。|
|サブネット|{先ほど作成した[laravel-sample-subnet-public-b]のサブネット}|このインスタンスが作られるサブネット。|
|自動割り当てパブリック IP|有効|パブリックIPを利用するかどうか。|

### ストレージの追加
1台目と同じようにデフォルトのまま次に進んで問題ないです。

### タグの追加
Nameタグ[laravel-sample-ec2-web-b]だけ追加します。
![screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-10_40_29.png](https://qiita-image-store.s3.amazonaws.com/0/297693/344b391a-0fba-de3b-4e3d-ef8dfa09c5dc.png)

### セキュリティグループの設定
以前作成し、1台目のwebサーバに適用しているセキュリティグループと同じものを
2台目にも適用します。

[セキュリティグループの割り当て]で[既存のセキュリティグループを選択する]を選択します。

表示されたセキュリティグループの一覧から
以前作成した[laravel-sample-sg-web]を選択します。
<img width="960" alt="screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-10_41_27.png" src="https://qiita-image-store.s3.amazonaws.com/0/297693/4f32ef42-78a6-2af5-faad-88d8baf89c35.png">

### 確認
設定内容を確認し、[起動]ボタンをクリックします。

キーペアの設定画面が表示されるので、
[既存のキーペアの選択]を選択し、
以前作成した[laravel-sample-key-pair]を設定します。

最後に[インスタンスの作成]ボタンをクリックしたら、
EC2インスタンスが作成されます。

インスタンスの一覧で、
作成したインスタンスの[インスタンスの状態]が[running]になっていれば
EC2インスタンスが正常に起動しています。

### 動作確認
webサーバ2台目のパブリックIPをブラウザのアドレスバーに入力してアクセスします。

1台目と同じようにLaravelサンプルアプリが表示され、
アカウント登録やログインなど正常に行えていれば
webサーバ2台目は完成です。

# ロードバランサーを構築
AWSの提供するロードバランサーのサービスは現在
・CLB
・ALB
・NLB
と3種類ありますが、
今回はALBを利用します。

ALBにはリスナー、ルール、ターゲットグループなどのコンポーネントがあり、
それらを設定することで利用可能になります。

各コンポーネントについての詳細な解説はせずに
どんどん作成していきますので、
概念の理解はこのようなサイトの解説を読むといいです。
https://dev.classmethod.jp/cloud/aws/alb-application-load-balancer/

## ターゲットグループの作成
まずはターゲットグループを作成します。

[EC2]のページの
サイドメニューの[ターゲットグループ]をクリックし、
上部にある[ターゲットグループの作成]ボタンをクリック。

下記のように設定します。
<img width="960" alt="screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-11_48_17.png" src="https://qiita-image-store.s3.amazonaws.com/0/297693/74bf137d-f2f6-d497-bca2-686042b38d73.png">

|項目|設定値|説明|
|:------------------------|:-------------------------|:-------------------------|
|ターゲットグループ名|laravel-sample-target-group|このターゲットグループの名前。自分が管理するための任意の名前を付ける。|
|ターゲットの種類|インスタンス|ロードバランサーが受け取ったリクエストを何にルーティングするか。<br>今回はEC2インスタンスを利用する。|
|プロトコル|HTTP|ロードバランサーとEC2インスタンス間の通信プロトコル。|
|ポート|80|上でHTTPを設定しているのでそのまま80ポートにする。|
|VPC|{以前作成した[laravel-sample-vpc]のVPC}|このターゲットグループが作られるVPC。|

[ヘルスチェックの設定]と[ヘルスチェックの詳細設定]はデフォルトのままで問題ないです。
ALBのルーティング先のEC2インスタンスが正常かどうかをチェックするための設定です。
EC2インスタンスが正常じゃないと判定された場合、
ALBはそのEC2インスタンスにトラフィックをルーティングしなくなります。

## ターゲットグループにEC2を登録
先ほど作成したターゲットグループにEC2インスタンスを登録します。

ターゲットグループの一覧で先ほど作成した[laravel-sample-target-group]をチェックし、
ページ上部の[アクション]→[インスタンス/IPターゲットの登録と登録解除]をクリック。

表示された画面下部のEC2インスタンス一覧に、
以前作成した[laravel-sample-ec2-web-a]と[laravel-sample-ec2-web-b]があると思うので
2つをチェックし[登録済みに追加]をクリックします。

下記の状態にできたら[保存]をクリックします。
<img width="960" alt="screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-12_02_48.png" src="https://qiita-image-store.s3.amazonaws.com/0/297693/c1a81f30-12db-983c-d607-c82b67b23e31.png">


## ALBの作成
次にALBを作成します。

[EC2]のページの
サイドメニューの[ロードバランサー]をクリックし、
上部にある[ロードバランサーの作成]ボタンをクリック。

ALB、NLB、CLBの選択画面で、
ALBの[作成]ボタンをクリックします。
![screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-12_14_07.png](https://qiita-image-store.s3.amazonaws.com/0/297693/b638d067-4ec0-a991-7274-8eb42e302499.png)


### ロードバランサーの設定
下記のように設定します。
![screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-12_16_33.png](https://qiita-image-store.s3.amazonaws.com/0/297693/b3f93bf8-4075-b953-56cf-82130c3caf8f.png)

[基本的な設定]

|項目|設定値|説明|
|:------------------------|:-------------------------|:-------------------------|
|名前|laravel-sample-alb|このALBの名前。自分が管理するための任意の名前を付ける。|
|スキーム|インターネット向け|このALBはインターネットからアクセスされるか、VPC内部からのアクセスのみか。<br>今回はインターネットからアクセスできるようにする。|
|IPアドレスタイプ|ipv4|このALBのIPアドレスをipv4にするかipv6にするか。|

[リスナー]
このALBへのリクエストで許可するプロトコルを設定します。
今はHTTP通信のみ許可しますが、
後々ここの設定を変更してHTTPSでの通信もできるようにします。

[アベイラビリティーゾーン]
このALBが配置されるサブネットを選択します。
以前作成した[laravel-sample-subnet-public-a]と[laravel-sample-subnet-public-b]を選択します。
ここで間違えてprivateのサブネットを選択すると
ALBにアクセスできなくなるので注意してください。

### セキュリティ設定の構成
先ほどのリスナーの設定で[HTTPS]を選択した場合は
この画面でいろいろと設定する項目がありますが、
[HTTP]にしている場合は設定する項目はありません。

そのまま次の手順に進みます。

### セキュリティグループの設定
下記のように設定します。
<img width="960" alt="screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-12_28_48.png" src="https://qiita-image-store.s3.amazonaws.com/0/297693/1b08ef60-9c1a-4d99-a0e0-c08789daa36b.png">

これまでwebサーバ用、DBサーバ用のセキュリティグループを作成しましたが、
また新しくロードバランサー用のセキュリティグループを作成します。

自分のIPアドレスからのHTTP通信のみ許可してください。

### ルーティングの設定
このALBがトラフィックを流す先になるターゲットグループを選択します。

[ターゲットグループ]の項目で先ほど作成した[laravel-sample-target-group]を選択してください。
![screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-12_31_36.png](https://qiita-image-store.s3.amazonaws.com/0/297693/2613928b-81c4-79c8-aa04-64be281be765.png)

それ以下の項目は選択したターゲットグループの設定内容が表示されるだけなので、
触る必要のある項目はありません。

### ターゲットの登録
このページでは、
先ほど選択したターゲットグループに登録されているEC2インスタンスの一覧が表示されます。

設定する項目はありません。
![screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-12_34_58.png](https://qiita-image-store.s3.amazonaws.com/0/297693/d0e0b119-780b-5390-55a3-b19133274409.png)

### 確認
最後に設定した内容を確認し、
[作成]ボタンをクリックするとALBが作成されます。

### 動作確認
実際にALBにアクセスできるか確認します。

ロードバランサーの一覧で先ほど作成した[laravel-sample-alb]をチェックし、
ページ下部に表示される詳細情報にある[DNS名]を確認します。
<img width="960" alt="screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-12_40_26.png" src="https://qiita-image-store.s3.amazonaws.com/0/297693/c2fdaff1-852e-bd86-7e7a-8e270a10bd71.png">

このDNS名をブラウザのアドレスバーに入力すれば
ALBにアクセスできます。

アクセスすると、「504 Gateway Time-out」のエラーページになりました。

ターゲットグループの一覧で先ほど作成した[laravel-sample-target-group]をチェックし、
ページ下部に表示される詳細情報の[ターゲット]タブを開いて
ヘルスチェックの状態を確認します。
<img width="960" alt="screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-12_47_49.png" src="https://qiita-image-store.s3.amazonaws.com/0/297693/010f951b-9c90-5b38-bfd0-1139b46eed85.png">

登録したEC2インスタンスが2台ともunhealthyになっています。

つまり、サーバ構成図にあるこの部分の通信がうまくいっていないという状態です。
<img width="391" alt="3.png" src="https://qiita-image-store.s3.amazonaws.com/0/297693/752b2d20-0563-7ea8-53a5-3ab550640e54.png">

原因として
・インスタンス自体が起動していない
・Apacheが起動していない
・Laravel設定不備
などいろいろ可能性はありますが、
今回の場合は
**webサーバのセキュリティグループの設定**
が原因です。

webサーバのセキュリティグループは現在
・自分のIPからのSSHアクセス
・自分のIPからのHTTPアクセス
のみを許可している状態で
・ALBからのHTTPアクセス
は許可していないためヘルスチェックに失敗しています。

# webサーバのセキュリティグループ設定変更
[EC2]のページの
サイドメニューの[セキュリティグループ]をクリックし、
以前作成した[laravel-sample-sg-web]をチェックし、
ページ上部の[アクション]→[インバウンドルールの編集]をクリック。

・自分のIPからのHTTPアクセス許可のルールを削除する
・ALBからのHTTPアクセス許可のルールを追加する
の2点を変更します。

・ALBからのHTTPアクセス許可のルールを追加する
についてですが、
ALBはIPアドレスを持っていないため、
ソースにはALBのセキュリティグループを設定します。
セキュリティグループ一覧から[laravel-sample-sg-alb]のセキュリティグループIDを確認して設定してください。
※セキュリティグループのソースには、IPアドレスだけでなくセキュリティグループも設定することができます
<img width="601" alt="screencapture-us-east-2-console-aws-amazon-ec2-v2-home-2019-01-31-13_06_24.png" src="https://qiita-image-store.s3.amazonaws.com/0/297693/c5d06385-2a0b-6a1f-0288-924966f2fa9c.png">

## 動作確認
もう一度ALBのDNS名をブラウザに入力してアクセスします。

Laravelサンプルアプリのページが表示されれば
正常に設定完了しています。
これで、ALBを経由してwebサーバにアクセスしている状態になります。

また、EC2インスタンスのパブリックIPをブラウザに入力してアクセスすると、
こちらはタイムアウトエラーになると思います。
これは、先ほどのセキュリティグループ設定変更で
「自分のIPからのHTTPアクセス許可のルールを削除する」
を実施したためです。

EC2インスタンスへの直接アクセスはできず
ALB経由でしかアクセスできない状態になったので、
セキュリティ的に良い状態になっています。

# 次回
今回ALBを作成しwebサーバを冗長化することができましたが、
この状態でアカウント登録やログインの操作を行うと、
Laravelの「TokenMismatchException」が発生します。

詳しい原因の解説は省略しますが、
これは2台のwebサーバがそれぞれのサーバ内にセッションを保存していて、
2台のサーバ間でセッションの共有ができていないために発生しているエラーです。

この状態を解消するために、次回は
ElastiCacheのRedisを作成し、セッション共有可能にします。
[AWSでウェブアプリケーション環境構築：④ElastiCacheのRedisを作成し、セッション共有可能にする](https://qiita.com/minato-naka/items/76cd5f660d987d34f7be)