🐡

【AWS】ALB-EC2-RDSで3層アーキテクチャを作成する

2023/09/18に公開

概要

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を立ち上げます

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アクセスを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をそのまま使います。

https://zenn.dev/shimiyu/articles/9763c6ac23cab2#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)アクセスがあったときの詳細な流れを追ってみます。

  1. インターネットからigwを通じてトラフィックがパブリックサブネットのルートテーブルに到達
    • この時、ルートテーブルはigwのルートを持つ必要がある。ないと、インターネットからの通信をルーティングできないから
  2. トラフィックがALBにルーティングされる
    • ALB用のセキュリティグループは、HTTPアクセス(80番ポート)が開放された状態のため、トラフィックは問題なくALBに到達
  3. ALBがEC2に対して、トラフィックをルーティングする
    • ALBの設定で、HTTPアクセスはEC2の3030ポートにルーティングされる(リスナールールで設定した部分)
  4. nginxコンテナの80番ポートへトラフィックがルーティングされる
    • EC2の3030ポートへのアクセスはnginxコンテナに転送されるようにポートマッピングされているため、nginxコンテナにトラフィックが到達
  5. 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"を選択します
  • 追加設定
    • データベースの選択肢
      • 最初のデータベース名: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