こんにちは。じゅんです。
今回はReact×Spring Boot×MySQLをAWSにデプロイします。
VPCの作成から動作確認までこの記事でカバーしています。
これまでの作業内容については以下の記事にまとめています。
全体像
VPCの作成
EC2やRDSを配置する仮想ネットワークを用意するためVPCを作成します。
VPCとは広範囲のIPアドレスを持つIPアドレス空間のことです。
IPアドレスは住所に例えられることがあります。
その例えに従うとVPCは複数の住所を持つ都道府県のようなイメージです。
以下の設定で作成します。
項目 | 値 | 備考 |
VPCの設定 > 作成するリソース | VPCのみ | 「VPCなど」を選択すると同時にサブネットやNATゲートウェイを作成できます。 サブネットは別で作成するため「VPCのみ」を選択します。 |
名前タグ | simple-memo-vpc(任意) | VPCの名前 |
IPv4 CIDRブロック | IPv4 CIDRの手動入力 | |
IPv4 CIDR | 10.0.0.0/16 | VPCのIPアドレス範囲を10.0.0.0~10.0.255.255とします。 |
サブネットの作成
VPC内でさらにIPアドレス範囲を分割したサブネットを作成します。
サブネットはVPC(都道府県)の中の具体的なエリアを示す市区町村のようなイメージです。
市区町村ごとに外部からのアクセスに対して異なる対応をするためにサブネットを作成します。
パブリックサブネット
ReactとSpring Bootを起動したWebサーバーを配置するパブリックサブネットを作成します。
パブリックサブネットは外部からのアクセスを許可します。
パブリックサブネットは外部の人々が訪れることができる観光地や商業地のようなイメージです。
以下の設定で作成します。
項目 | 値 | 備考 |
VPC > VPC ID | simple-memo-vpc | 作成してVPCを選択します。 |
サブネットの設定 > サブネット名 | simple-memo-public-subnet(任意) | パブリックサブネットの名前 |
サブネットの設定 > アベイラビリティーゾーン | アジアパシフィック(東京)/ ap-northeast-1a | 現実世界のどこのエリアのハードウェアを使用するかを表す。 |
サブネットの設定 > IPv4 サブネット CIDR ブロック | 10.0.0.0/24 | VPCに割り当てられたIPアドレス空間の中で、パブリックサブネットに割り当てるIPアドレスの範囲を定義。 IPアドレス範囲を10.0.0.0~10.0.0.255とします。 |
作成直後はパブリック IPv4 アドレスを自動割り当て
がいいえ
となっており、このサブネット内で起動したEC2インスタンスにパブリックIPアドレスが自動的に付与されません。そのため作成後に設定を編集する必要があります。
アクション > サブネットの設定を編集
から変更します。
プライベートサブネット
RDSを起動したDBサーバーを配置するプライベートサブネットを作成します。
RDSを利用する際には、マルチAZ配置にして可用性を高めるために異なるAZに属するサブネットを2つ用意する必要があります。
プライベートサブネットは外部の人々が直接アクセスできない住宅地や工業地帯のようなイメージです。
プライベートサブネットは外部からのアクセスを拒否して、パブリックサブネットからのアクセスのみ許可します。
以下の設定で作成します。
項目 | 値 | 備考 |
VPC > VPC ID | simple-memo-vpc | 作成してVPCを選択します。 |
サブネットの設定 > サブネット名 | 01: simple-memo-private-subnet-01 02: simple-memo-private-subnet-02(任意) | プライベートサブネットの名前 |
サブネットの設定 > アベイラビリティーゾーン | 01: アジアパシフィック(東京)/ ap-northeast-1a 02: アジアパシフィック(東京)/ ap-northeast-1c | 現実世界のどこのエリアのハードウェアを使用するかを表す。 可用性向上のため異なるAZを使用する |
サブネットの設定 > IPv4 サブネット CIDR ブロック | 01: 10.0.1.0/24 02: 10.0.2.0/24 | VPCに割り当てられたIPアドレス空間の中で、プライベートサブネットに割り当てるIPアドレスの範囲を定義。 1つ目はIPアドレス範囲を10.0.1.0~10.0.1.255とします。 2つ目はIPアドレス範囲を10.0.2.0~10.0.2.255とします。 |
サブネット作成時点ではパブリックとプライベートの違いは対象のCIDRブロックのみです。
この後作成するセキュリティグループでアクセス制御を行います。
インターネットゲートウェイの作成
VPCと外部のインターネットの出入口となるインターネットゲートウェイを作成します。
VPC(都道府県)とインターネットの外部(国外)をつなぐ空港のようなイメージです。
以下の設定で作成します。(VPCとの紐付けは作成後に行います)
項目 | 値 | 備考 |
名前タグ | simple-memo-igw | インターネットゲートウェイの名前 |
作成後、アクション > VPCにアタッチ
からVPCと紐づける設定を行います。
ルートテーブルの設定
VPC内の通信を振り分けるためのルートテーブルを設定します。
都道府県(VPC)内の交通(通信)を適切に誘導するための地図や交通標識のようなイメージです。
ルートテーブルはこれまでの操作の過程で既に作成されています。
この設定は、送信先が10.0.0.0/16
に該当するIPアドレス、つまりVPC内部での通信に関する設定を表しています。
ターゲットがlocal
とあるため、同じVPC内のリソースに接続されるルールが設定されています。
このままではWebサーバーがインターネットへレスポンスを返す際にVPCから出られません。
外部への通信を行えるようにするために、送信先が0.0.0.0/0
でターゲットが作成したインターネットゲートウェイであるルートを追加します。
また、追加したルートに関連づけるためにパブリックサブネットを関連付けます。
これでWebサーバーからのレスポンスがインターネットに返るようになりました。
セキュリティグループの作成
Webサーバー用
ルートテーブルの設定では、Webサーバーがインターネットにレスポンスを返すための設定を行いました。
Webサーバー用のセキュリティグループではインターネットからのリクエストを許可する設定を行います。
以下の設定で作成します。
項目名 | 値 | 意味 |
---|---|---|
セキュリティグループ名 | simple-memo -web-sg(任意) | |
説明 | SSH & HTTP(任意) | |
VPC | simple-memo-vpc | |
セキュリティグループルール 1 | タイプ:ssh ソース:3.112.23.0/29 | インスタンスを操作するための接続(ssh)をEC2 Instance Connectのみに制限 |
セキュリティグループルール 2 | タイプ:ssh ソースタイプ:マイIP | jarファイル転送時にscpコマンドでssh接続するため使用しているPCのみ許可 |
セキュリティグループルール 3 | タイプ:HTTP ソースタイプ:0.0.0.0/0 | Nginxへのアクセスを解放 |
DBサーバー用
ルートテーブルでは、DBサーバーを起動するプライベートサブネットへの外部からのアクセスは許可していません。
DBサーバー用のセキュリティグループには、Webサーバーからのリクエストを許可するように設定する必要があります。
以下の設定で作成します。
項目名 | 値 | 意味 |
---|---|---|
セキュリティグループ名 | simple-memo -db-sg(任意) | |
説明 | For RDS(任意) | |
VPC | simple-memo-vpc | |
セキュリティグループルール | タイプ:MYSQL/Aurora ソース:0.0.0.0/0 | 任意のIPアドレスからの通信を許可します。 |
任意のIPアドレスからの通信を許可するのは、Webサーバー用のインスタンスのパブリックIPアドレスは起動ごとに変更されるためです。
ルートテーブルの設定でインターネットからプライベートサブネットへの通信は拒否されるためセキュリティグループで任意のIPアドレスからの通信を許可してもインターネットからRDSへは到達しません。
(セキュリティ的には最適とは言えません...)
DBサブネットグループの作成
RDSのマルチAZ化のためにDBサブネットグループを作成します。
RDS > サブネットグループ
から以下の設定で作成します。
項目名 | 値 | 意味 |
---|---|---|
セキュリティグループ名 | simple-memo -db-subnet-group(任意) | |
説明 | For RDS subnet group(任意) | |
VPC | simple-memo-vpc | |
アベイラビリティゾーン | ap-northeast-1a ap-northeast-1c | 作成した2つのプライベートサブネットのアベイラビリティゾーンを選択します |
サブネット | (作成した2つのプライベートサブネット) |
EC2インスタンスの作成
WebサーバーとしてのEC2インスタンスを作成します。
以下の設定で作成します。
項目名 | 値 | 備考 |
---|---|---|
名前とタグ > 名前 | simple-memo -web-server(任意) | |
キーペア (ログイン) > キーペア名 > 新しいキーペアを作成 | simple-memo | |
ネットワーク設定 > 右上編集ボタン > VPC | simple-memo-vpc | 作成したVPC |
ネットワーク設定 > 右上編集ボタン > サブネット | simple-memo-public-subnet | 作成したパブリックサブネット |
ネットワーク設定 > 右上編集ボタン > サブネット | simple-memo-web-sg | 作成したセキュリティグループ |
RDSインスタンスの作成
DBサーバーとしてのRDSインスタンスを作成します。
以下の設定で作成します。
項目名 | 値 | 備考 |
---|---|---|
データベース作成方法 | 標準作成 | |
エンジンのタイプ | MySQL | |
テンプレート | 無料利用枠 | |
DB インスタンス識別子 | simple-memo-db | DBインスタンスの名前 |
認証情報の設定 > マスターユーザー名 | admin | MySQLのユーザー名 |
認証情報の設定 > 認証情報の管理 | セルフマネージド | MySQLの認証情報を自分で管理します |
接続 > コンピューティングリソース | EC2コンピューティングリソースに接続しない | VPCやインターネットゲートウェイなど手動で設定しているため不要です |
接続 > VPC | simple-memo-vpc | 作成したVPC |
接続 > DB サブネットグループ | simple-memo-db-subnet-group | 作成したDBサブネットグループ |
接続 > アベイラビリティーゾーン | ap-northeast-1a | マルチAZの主となるAZを選択します |
セットアップ・動作確認
ここからはWebサーバー役のEC2インスタンス上で作業を行います。
EC2 Instance Connectを使用します。
今回、ReactとSpring BootをWebサーバーに起動する上で、Nginxをリバースプロキシとして使用します。
Webサーバーのデプロイに関する以下の操作はこちらの記事で詳しく解説しています。
Nginx
EC2 Instance Connectを使用して、以下の設定でNginxを起動します。
Nginxのセットアップについてはこちらで詳しく解説しています。
server {
listen 80;
server_name localhost;
# 外部 → Nginx → React
location / {
proxy_pass http://localhost:3000;
}
# React → Nginx → Spring Boot
location /api {
proxy_pass http://localhost:8080/api;
}
}
実行コマンド
sudo systemctl start nginx
React
ReactプロジェクトについてはGitからクローンしてきます。
Reactのデプロイについてもこちらで詳しく解説しています。
なお、API接続先は以下の通りです。
const response = await fetch(`http://(WebサーバーのパブリックIPアドレス)/api/memos`);
実行コマンド
cd simple-memo-frontend/ # 作業ディレクトリに移動
npm install # package.jsonに記載されたパッケージをインストール
npm run build
npm install -g serve # 軽量サーバーを提供するパッケージ
serve -s build # ビルドされた静的ファイルを軽量サーバーで実行
npm run buildがEC2上で終わらなかったのでビルドファイルをscp転送しました。
Spring Boot
Spring Bootは開発環境からjarファイルをscp転送します。
Spring Bootのデプロイについてはこちらで詳しく解説しています。
なお、MySQLの接続先情報は以下の通りです。
spring.datasource.url=jdbc:mysql://(RDSのエンドポイント):3306/simple_memo
spring.datasource.username=admin
spring.datasource.password=xxxxxxxx
また、CORS対策としてWebサーバーのパブリックIPアドレスを許可しておきます。@Value("${api.endpoint}")
でapplication.propertiesで設定したapi.endpoint
が取得できます。
package com.example.simplememo.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private static final Logger logger = LoggerFactory.getLogger(WebConfig.class);
@Value("${api.endpoint}")
private String apiEndpoint;
@Override
public void addCorsMappings(CorsRegistry registry) {
// apiEndpointの値をログに出力して確認
logger.info("Configured API Endpoint for CORS: {}", apiEndpoint);
registry.addMapping("/api/**")
.allowedOrigins(apiEndpoint)
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true);
}
}
# 上述したMySQL接続情報
spring.datasource.url=jdbc:mysql://(RDSのエンドポイント):3306/simple_memo
spring.datasource.username=admin
spring.datasource.password=xxxxxxxx
# CORS対策: リクエスト元を指定
api.endpoint=http://54.249.13.31
前回(こちらの記事の時)はNginxでリバースプロキシすることでCORSを回避できたのですが、今回は上手く動きませんでした。
理由は分かりませんが許可するとAPI通信が正常に実行できました。
実行コマンド
java -jar ~/simple-memo-0.0.1-SNAPSHOT.jar
MySQL
EC2 Instance Connect上からMySQLに接続します。
mysql -h (RDSのエンドポイント) -u admin -p
MySQLのセットアップはこちらの通りに「simple_memo」データベースを作成し、memosテーブルを作成します。
以下のコマンド実行結果が得られればセットアップ完了です。
上記4サービスをセットアップすることで、WebサーバーのパブリックIPアドレスにアクセスすると以下のようにアプリケーションが表示されるはずです。
まとめ
今回はReact×SpringBoot×MySQLをAWSにデプロイしてみました。
「Reactだけ」「SpringBootだけ」「React×SpringBoot」と段階的にデプロイしてきましたが、初めよりだいぶ本番環境の理解が進んだと思います。
ReactのHello Worldから記事にしているので順番にやっていけば同じようにできるはずです!
次回からはフロントエンドの学習トピックに注力していきたいと思います。
なお、今回はポートフォリオを公開するのに必要最低限のアーキテクチャを選択しています。
フロントエンドをS3とCloudFrontに、SpringBootをECSにデプロイすることでよりパフォーマンスとスケーラビリティを向上することができます。
また機会を設けてインフラの勉強を続けたいと思います。
コメント