ポートフォリオのAWSネットワーク構成を整理した【Rails + Nginx + Unicorn + MySQL】

Railsアプリポートフォリオのネットワーク構成を頭の整理と備忘を兼ねて整理してみました。
俯瞰的に整理するものなので各リソースやネットワークの細かい設定等には触れません。
初学者なりに自分の言葉で整理したものになっていますので、おかいし点などありましたらご指摘いただけると幸いです。

# はじめに：参考書籍
AWSのネットワーク構築において以下の本を参考にさせていただきました。
初学者でも大変分かりやすく、かつ内容に従って操作することで本格的でセキュアなネットワークを構築できるハンズオン形式にもなっており、これ無くしてこのネットワーク環境は作れなかったと思います。
まだ発刊されて日も浅い（2021年3月時点）ため、ネット上の記事や古い書籍でありがちな、解説のUIと現在のUIが違って迷う、ということもほぼ無かったのも良かったです。おすすめです！

https://www.amazon.co.jp/dp/B08QTQBJKZ/ref=dp-kindle-redirect?btkr=1&ie=UTF8&language=ja_JP

# ネットワーク構成図
![ネットワーク構成図_公開用.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505169/c6576db9-6aba-097f-3b35-e1698ec1dc42.png)


# 開発環境・デプロイ
開発・テストはDockerリモートコンテナ上で実施
ソースコードはGitHubで管理、デプロイはCapistranoで行う

# IAM
IAM（Identity and Access Management）はAWSへのアクセスを安全に管理するための仕組み
AWSアカウントのデフォルトユーザであるルートユーザは権限が強く、通常の開発等で使うにはリスクが大きい
そのため別途権限を抑えたユーザを作成し、こちらでネットワークの構築を行う
また仮想デバイスを用いたMFA（多要素認証）を導入し、更にセキュアな接続を実現する

# VPC
ここは私の領域ですよ、と宣言する領域。Virtual Private Cloud
この中にAWSのネットワークを構築していく

# アベイラビリティゾーン
耐障害性を上げるために、同じ東京リージョンの中でも異なるアベイラビリティゾーンにそれぞれサブネットを構成する

# サブネット
VPCのIPアドレスの範囲を分割する範囲。分割する理由は主に以下2点

- 役割の分離：外部に公開するリソースかどうかを区別するため
- 機器の分離：AWS内での物理的な冗長化を行うため

今回は、2つのアベイラビリティゾーンにそれぞれパブリック・プライベートサブネットを用意する

# インターネットゲートウェイ

VPCで作成したネットワークとインターネット間の通信を可能にするためのもの

# NATゲートウェイ

インターネットゲートウェイを使った通信ではVPCのリソースは外部のネットワークと直接通信を行うため、VPCリソースはパブリックIPを持っている必要がある
しかしパブリックIPを持つ＝インターネットに直接公開されている状態になり、サブネットをパブリックとプライベートに分けた意味が無くなる
プライベートサブネットのリソースは、インターネットに出ていく必要はあってもインターネットから直接アクセスはされたくない
→このような要求を実現するための仕組みがNAT（ネットワークアドレス変換）

AWSではこの役割を果たすのがNATゲートウェイになる


# ルートテーブル

サブネット同士、あるいはサブネットと各ゲートとの間には、通信をするための経路がまだ無い
そのため、サブネットが別のサブネット内のリソースを見たり、サブネット外のリソースにアクセスができない
サブネット間の通信経路を設定するために、AWSにはルートテーブルという機能がある
今回はサブネットが4つある　すべてのサブネットにルートテーブルを作成する必要がある（ただし複数のサブネットで同じルートテーブルを共有することは可能

これを踏まえ今回作成するのは以下3つ

- パブリックルートテーブル：パブリックサブネット1，2共有
  - 外部とはIGW経由、プライベートサブネットとはLocalターゲットとしてアクセス
- プライベートルートテーブル1：プライベートサブネット1用
- プライベートルートテーブル2：プライベートサブネット2用
  - 外部とはNATGW経由、パブリックサブネットとはLocalターゲットとしてアクセス

# セキュリティグループ

今のままだとインターネットを通じてどんなアクセスもできてしまう
VPC内のリソースを守るため、外部からのアクセス制限をつける必要がある
→セキュリティグループという機能を用いて実現する

セキュリティグループでは、外部からのアクセスを次の2つの概念で制御できる

- ポート番号
  - サービスのアクセスで使われる80番（HTTP）、443番（HTTPS）、
  - 管理者としてサーバ接続時に使われる22番（SSHなど）
- IPアドレス

今回は以下2つのセキュリティグループが必要

- 全てのリソースに接続するための入り口となる「踏み台サーバ」
  - インバウンドルール：SSH接続（22番）のみ
- リクエストや処理を分散する「ロードバランサー」
  - インバウンドルール：HTTP（80番）とHTTPS（443番）接続のみ

# EC2（踏み台サーバ）

今回は機器の冗長化、負荷分散のために2つのアベイラビリティゾーンにサブネットを構築した
しかしリソースを増やすということはそれだけ侵入の経路が増えセキュリティ上のリスクが高まることになる
そこで全てのリソースに接続するための入り口となる踏み台（bastion）サーバを用意し、
このサーバ経由でないと各リソースに接続できないようにする

踏み台サーバは目的のリソースへの通り道となる以外の用途がないため、低スペックでもOK
EC2インスタンス上に構築する
踏み台サーバはパブリックサブネット1に設置し、先程作成した踏み台サーバ用のセキュリティグループを適用する（＝SSH接続のみ許可する。秘密鍵はローカルマシンで保持）

# EC2（Webサーバ・アプリケーションサーバ）

実際のアプリケーションを稼働させるEC2インスタンス
この中にWebサーバとしての役割を果たすNginx、Rackアプリケーション用サーバとしての役割を果たすUnicorn、アプリ本体であるRailsをインストールする
プライベートサブネット1,2にそれぞれ設置

こちらもssh認証の秘密鍵はローカルで持ち、ssh-agentを利用してセキュアに多段接続する（踏み台サーバには秘密鍵をアップしない）

ここで踏み台サーバとWebサーバの違いを整理する

|項目|踏み台|Webサーバ|
|:-|:-|:-|
|設置サブネット|パブリック|プライベート|
|パブリックIP|必要|不要|
|セキュリティグループ|デフォルト＋SSH接続|デフォルト＋HTTP, HTTPS接続|
|誰がいつ接続する？|管理者が必要に応じて|Webサービスのユーザが常時|
|接続形態|直接接続|間接接続（ロードバランサ経由）|

# ロードバランサー

ユーザが増えてくると1台のWebサーバではリクエストを捌き切れなくなる可能性がある
スケールアウトの方法として用いられるのがロードバランサーで、リクエストの分散を実現できる

設定項目は以下

- アベイラビリティゾーン：インターネットゲートウェイへの経路があるサブネット（今回はパブリックサブネット1,2）があるゾーンを指定する必要がある
- セキュリティグループ：デフォルト＋HTTP, HTTPS接続（内部向けと外部向け）
- ターゲットグループ：Webサーバ1, 2、プロトコルはHTTP

# データベース

AWSのサービスの一つであるRDSを用いて実現
RDSは以下の4点で構成される

- データベースエンジン
→ 今回はMySQLを使用）

- パラメータグループ
→ 主にデータベースエンジン固有の設定を行うためのもの

- オプショングループ
→ 主にRDS固有の設定を行うためのもの

- サブネットグループ
→ データベースサーバーを複数のアベイラビリティーゾーンに分散させて配置させる時に使われる設定
　RDSはサブネットグループを指定することで自動的に複数のアベイラビリティーゾーンにデータベースを作成し、
　耐障害性を上げてくれる（マルチAZ）

# S3

今回のアプリではユーザのアイコンや投稿機能で画像をアップロードすることが可能
その画像を保管する場所として、AWSのストレージサービスであるS3を利用する
S3はVPCの外に配置するものである
そのためIAMでS3にアクセスするためのロールを作成し、EC2に適用することでアプリからS3へのアクセスを実現する

# Amazon Route 53

Webアプリを公開するためには分かりやすくて覚えやすいドメインが必要
AWSではドメインを取得することができるRoute 53というサービスがある
こちらで希望のドメインを取得することで公開用のURLを発行することができる
（費用は$10~15/年 前後）

また、Route 53では他に以下の設定を行う

- SSLサーバー証明書の発行（Certificate Manager）
→ ドメインの正しさを保証する証明書の発行を行う。
　今回はドメイン検証済み（DV）証明書を使う
　発行した証明書を使い、HTTPSのリスナーをロードバランサーに追加する

- パブリックDNSの設定
→ 踏み台サーバとロードバランサーに対し設定する

- プライベートDNSの設定
→ 踏み台サーバ、Webサーバ1,2、DBサーバに対し設定する

# Amazon SES

SES（Simple Email Service）はメールの送受信行う機能を提供するAWSのサービス
今回のアプリではパスワードを忘れた際に再発行するためにメールの送信を行うため用意する
受信に関する設定は不要のため行わない

通常のメール送受信においては、以下プロトコルが利用される

- SMTP（Simple Mail Transfer Protocol）
→ 送信用のプロトコル

- POP3（Post Office Protocol Version 3）
→ 受信用のプロトコル。メールは受信者のローカルに取り込まれる

- IMAP4（Internet Message Access Protocol Version 4）
→ 受信用のプロトコル。メールはサーバ上に保管されるためインターネットアクセスが必要

アプリからメールを送るには、メール送信用のIAMユーザを作成し、Amazon SES API または Amazon SES SMTPインターフェイスで認証する必要がある
今回は通常のメールサーバと同じ方法で実装ができるAmazon SES SMTPインターフェイスを利用している

# CloudWatch

AWSで提供されている、各種サーバ等の運用状況を監視するツール
今回はWebサーバ・アプリサーバを包含するEC2インスタンスの死活監視、CPU使用率監視を行っている

# おわりに

CircleCI/CDを使った自動テスト・自動デプロイも導入したいと考えています。
導入できたらこちらも更新しようと思います。