【AWS】ALB-EC2-RDSで3層アーキテクチャを作成する
概要
AWS&インフラ初心者です。
AWSやインフラの理解を深めるため、AWSのALB,EC2,RDSを使って、3層アーキテクチャの実装をしてみます。
- ロードバランサー(ALB)
- アプリケーションサーバ(EC2にnginxコンテナを立てる)
- データベース(RDS)
アプリケーションサーバと書きましたが、アプリケーションを作ることが目的ではないため、EC2インスタンス内でWebサーバのnginxコンテナを立ち上げるのみとします。
- ALBにアクセスすると、EC2にルーティングされて、nginxのデフォルトページが表示されること
- EC2で立ち上げたnginxコンテナからRDS(mysql)に接続できること
の2点を目標としたいと思います。
インフラ構成図
1. ALB (Application Load Balancer)
そもそもALBとは
AWSが提供する負荷分散(LB)のサービスです。HTTP/HTTPSトラフィックに特化しています。今回のケースでは、インターネットからの通信を受け付け、アプリサーバにトラフィックを振り分ける役割を担います。
ALB用のサブネットを作成する
ALB作成時には最低2つのサブネットを選択する必要があります。また、この時、2つのサブネットは、異なるアベイラビリティゾーン(AZ)に属する必要があります。異なるAZに属するサブネットが存在しない場合、サブネットが2つ選択できないため、ALBも作成できないようです。
複数のAZに跨ることが必須なのは、主に可用性の問題です。ALBが複数のAZにまたがってデプロイされていれば、万が一1つのAZで障害が起きても、別のAZでALBが稼働しているのでサービスが継続できます。
以上のことから、ALB用のサブネットを2つ作成します。
-
サブネット名:alb-subnet
-
アベイラビリティーゾーン:ap-northeast-1a
-
IPv4 CIDRブロック:10.0.0.64/26
-
サブネット名:alb-subnet2
-
アベイラビリティーゾーン:ap-northeast-1c
-
IPv4 CIDRブロック:10.0.1.0/26
ルートテーブルの設定
ALBはインターネットからの通信を受けますので、ALBのサブネットのルートテーブルにはインターネットゲートウェイ(IGW)へのルートが必要です。忘れないように設定しておきます。
- alb-subnetのルートテーブルを選択
- ルートを編集 > ルートを追加
- 送信先:0.0.0.0/0 / ターゲット:対象VPCのインターネットゲートウェイを選択
- 変更を保存
ALB用のセキュリティグループを作成する
ALBは、インターネットからのHTTPによるアクセスを全て許可したいので、下記のような設定とします。
- セキュリティグループ名:alb-sg001
- VPC:subnetを作成したVPCと同じものを選択する
- インバウンドルール
- タイプ:HTTP
- ソース:0.0.0.0/0
ターゲットグループを作成する
ALBの前に作成する必要があるリソースがまだあります。それはターゲットグループです。
ターゲットグループは、ALBがトラフィックを振り分ける対象先を定義するリソースです。EC2インスタンスやLamda関数やIPアドレスなどを指定できます。
この後作成するALBでリスナールールというものを設定できますが、様々な条件でトラフィックを振り分けることができます。その振り分け先となるのがこのターゲットグループです(振り分け先は、ターゲットグループ以外にURLなども指定できます)。
- 基本設定
- ターゲットタイプ:EC2インスタンス
- ターゲットグループ名:app-target-group(名前は任意)
- プロトコル:HTTP / ポート:3030
- EC2内でコンテナを立ち上げる際、ホストマシン(EC2)のポートに3030を指定します
- VPC:対象となるEC2インスタンスが属するネットワークを選択します
- ターゲットの登録
- 対象とするEC2インスタンスを選択
- 後ほど、このインスタンス内にnginxを立ち上げます
- 対象とするEC2インスタンスを選択
ALBを作成する
ようやくALBを作成します。
- EC2 > ロードバランサー > ロードバランサーの作成
- ロードバランサータイプ:Application Load Balancer
- 基本設定
- ロードバランサー名:sample-alb(名前は任意)
- スキーム:インターネット向け
- IPアドレスタイプ:IPv4
- ネットワークマッピング
- VPC:subnetを作成したVPCと同じものを選択します
- subnet:alb-subnet
- セキュリティグループ
- 上で作成したalb-sg001を選択します
- リスナー
- プロトコル:HTTP
- HTTP/HTTPSのみ選択可能
- ポート:80
- デフォルトアクション:app-target-group(アプリケーションーサーバ(EC2)をターゲットにしたターゲットグループ)を指定します
- プロトコル:HTTP
今回は、httpアクセスをapp-target-groupにルーティングするように設定しました。
リスナールールを追加することで、より詳細な条件に基づいてトラフィックをルーティングすることもできます。
例えば、パスが/api
の場合は別のAPI用のサーバにルーティングしたいケースがあるとします。
その場合は、パスパターンが/api
の時、別のターゲットグループ(API用のサーバをターゲットとして設定したもの)に転送するリスナールールを追加します。
2. EC2(アプリケーションサーバ)の準備
EC2インスタンス上にnginxコンテナを立ち上げます。
nginxはWebサーバなので、この後ろにアプリケーション用のサーバが存在するのが一般的(?)とは思います。ただ、「簡単に3層アーキテクチャを作ってみよう」が主旨なので、実際のビジネスロジックを持つサーバはなしで、nginxを1つ置くだけとします。
アプリケーションサーバ用のサブネットの作成
下記の通り、アプリケーションサーバ用のサブネットを作成します。
- サブネット名:app-subnet
- アベイラビリティーゾーン:ap-northeast-1a
- IPv4 CIDRブロック:10.0.0.0/26
nginxコンテナを準備する
前提として、EC2インスタンスは既に作成済みです。下記の記事で作成したEC2をそのまま使います。
EC2内でnginxコンテナを起動していきます。
まず、該当のEC2にSSH接続します。
nginx
ディレクトリを作成し、そこに以下のdocker-compose.yml
を作成します。
version: '3'
services:
nginx:
image: nginx:latest
ports:
- "3030:80"
ymlファイルを作成したら、nginx
ディレクトリに移動し、コンテナを起動します。
[ec2-user@ip-****** ]$ cd nginx
[ec2-user@ip-****** nginx]$ sudo docker-compose up -d
[+] Running 2/2
⠿ Network nginx_default Created 0.1s
⠿ Container nginx-nginx-1 Started 0.9s
[ec2-user@ip-****** nginx]$ sudo docker-compose ps -a
NAME COMMAND SERVICE STATUS PORTS
nginx-nginx-1 "/docker-entrypoint.…" nginx running 0.0.0.0:3030->80/tcp, :::3030->80/tcp
ホスト(EC2)の3030ポートとコンテナの80ポートのポートマッピングされていますので、EC2の3030ポートにアクセスがあれば、コンテナの80ポートにトラフィックが転送されます。
セキュリティグループの作成
アプリケーションサーバにはALBからのアクセスのみを受け付けるようにしたいので、ソースにALBのセキュリティグループ(alb-sg001)を指定します。
- セキュリティグループ名:sg001
- VPC:subnetを作成したVPCと同じものを選択する
- インバウンドルール
- タイプ:カスタムTCP
- ポート範囲:3030
- ソース:ALBのセキュリティグループを設定(alb-sg001)
- IPアドレスでも指定できますが、セキュリティグループを指定するのが一般的です
作成後、このセキュリティグループをEC2に関連付けます。
NATゲートウェイ
NATゲートウェイとは
プライベートサブネット内のインスタンスがインターネットと通信するためのサービスです。
プライベートサブネット内のインスタンスからの通信をインターネットに転送し、返ってきたトラフィックを元のインスタンスにルーティングします。
当たり前ですが、プライベートサブネット内のインスタンスからインターネットに直接アクセスすることはできません。しかし、プライベートサブネット内のインスタンスがインターネットと通信したいケースがあります(内部リソースのソフトウェアのアップデートなど)。そのような場合にNATゲートウェイを使います。
NATゲートウェイにより、プライベートサブネット内のリソース(EC2インスタンスなど)から外のインターネットにアクセスできるが、外からはアクセスできないという状態を実現できるのです。
より具体的には、プライベートIPアドレスをNATゲートウェイのパブリックIPアドレスに変換することで、プライベートサブネットのインスタンスがインターネットと通信できるようになります。
ルートテーブルの紐付けを変更
EC2がNATゲートウェイを介してインターネットにアクセスできるようにするため、app-subnet(EC2が属するプライベートサブネット)のルートテーブルを変更します。
- "app-subnet"のルートテーブルを選択
- ルートを編集
- 送信先:0.0.0.0/0
- ターゲット:nat-gateway(上で作成したNAT)を選択
ALBからEC2への疎通確認
これまでの設定によって、ALBにアクセスするとnginxコンテナを起動中のEC2インスタンスにルーティングされます。
結果として、下記のようなnginxのデフォルトのページが表示されるはずです。
ここで、ALBのドメインに(HTTP)アクセスがあったときの詳細な流れを追ってみます。
- インターネットからigwを通じてトラフィックがパブリックサブネットのルートテーブルに到達
- この時、ルートテーブルはigwのルートを持つ必要がある。ないと、インターネットからの通信をルーティングできないから
- トラフィックがALBにルーティングされる
- ALB用のセキュリティグループは、HTTPアクセス(80番ポート)が開放された状態のため、トラフィックは問題なくALBに到達
- ALBがEC2に対して、トラフィックをルーティングする
- ALBの設定で、HTTPアクセスはEC2の3030ポートにルーティングされる(リスナールールで設定した部分)
- nginxコンテナの80番ポートへトラフィックがルーティングされる
- EC2の3030ポートへのアクセスはnginxコンテナに転送されるようにポートマッピングされているため、nginxコンテナにトラフィックが到達
- nginxのデフォルトページが表示される
3. RDS(Amazon Relational Database Service)
DB用のサブネットの作成
RDSを作成するにあたっては、サブネットグループというものを選択する必要があります。
このサブネットグループは、異なるアベイラビリティゾーンに属する複数のサブネットを持つ必要があります。
そのため、サブネットを2つ作成し、それぞれ異なるアベイラビリティゾーンを選択します。
- DB用のサブネットを作成
- サブネット名:db-subnet
- アベイラビリティーゾーン:ap-northeast-1a
- IPv4 CIDRブロック:10.0.0.128/26
- 2つ目のDB用のサブネットを作成
- サブネット名:db-subnet2
- アベイラビリティーゾーン:ap-northeast-1c
- IPv4 CIDRブロック:10.0.0.192/26
サブネットグループの作成
上で作成した2つのサブネットを持つサブネットグループを作成します。
- サブネットグループの詳細
- 名前:db-subnet-group
- VPC:subnetを作成したVPCと同じものを選択する
- サブネットを追加
- アベイラビリティゾーン:ap-northeast-1a,ap-northeast-1c
- サブネット:db-subnet,db-subnet2
セキュリティグループの作成
このセキュリティグループをこの後作成するDBに関連付けることによって、EC2のみがDBにアクセスできるようになります。
- セキュリティグループ名:db-sg001
- VPC:subnetを作成したVPCと同じものを選択する
- インバウンドルール
- タイプ:MySQL/Aurora
- ソース:sg001
- EC2に関連付けしたセキュリティグループ(sg001)を指定します
RDSの作成
AWSコンソールのサービスメニューからRDSを選択し、データベースの作成ボタンを押します。
- データベース作成方法を選択:標準作成
- エンジンのオプション
- エンジンのタイプ:MySQL
- エディション:MySQL Community(デフォルト)
- エンジンのバージョン:MySQL 8.0.33(デフォルト)
- テンプレート:無料利用枠
- 可用性と耐久性:無料利用枠の場合は選択できません
- 設定
- DBインスタンス識別子:任意の名前(AWSリージョン内で一意の必要あり)
- マスターユーザー名:admin
- マスターパスワード:任意
- インスタンスの設定
- db.t2.micro(無料枠内)
- ストレージ
- テスト用なので、共に最小値を選択します
- 汎用SSD(gp2)
- ストレージ割り当て:20
- ストレージの自動スケーリング:無効
- テスト用なので、共に最小値を選択します
- 接続
- コンピューティングリソース:EC2コンピューティングリソースに接続しない
- ※ EC2に接続を選択した場合、自動的にセキュリティグループの設定が変更されます。今回は既に手動設定済だったため接続しないを選択しました
- VPC:subnetを作成したVPCと同じものを選択します
- DB サブネットグループ:事前に作成済みの"db-subnet-group"を選択します
- パブリックアクセス:なし
- 既存のVPCセキュリティグループ:事前に作成済みの"db-sg001"を選択します
- コンピューティングリソース:EC2コンピューティングリソースに接続しない
- 追加設定
- データベースの選択肢
- 最初のデータベース名:sample
- ※ ここを設定すると、RDSインスタンス起動時に自動的にデータベースを作成してくれます
- データベースの選択肢
EC2からRDSインスタンスへの疎通確認
ここまでの設定によって、EC2からRDSインスタンスにアクセスできるようになりました。EC2内のnginxコンテナからRDSインスタンスに接続できることを確認してみます。
nginxコンテナに入り、MySQLクライアントをインストールします。
[ec2-user@ip-10-0-0-7 nginx]$ sudo docker exec -it コンテナ名 /bin/sh
$ sudo apt update
All packages are up to date.
$ apt install default-mysql-client
$ mysql -V
mysql Ver 15.1 Distrib 10.11.3-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper
MySQLクライアントがインストールできたので、データベースに接続してみます。
RDSのエンドポイントは、RDSインスタンスの詳細ページに記載されています。パスワードはRDSインスタンス作成時に設定したものを使ってください。
$ mysql -h "RDSのエンドポイント" -u admin -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 294
Server version: 8.0.33 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 [(none)]> show databases
-> ;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| sample |
+--------------------+
5 rows in set (0.012 sec)
nginxコンテナからデータベースへの接続が確認できました。
RDSインスタンス作成時に設定した"sample"というデータベースが作成されているのも確認できます。
Discussion