こんにちは。じゅんです。
前回、1つのEC2インスタンスにSpring BootとReactの簡単メモアプリをデプロイしました。
今回はそのメモアプリを2つのEC2インスタンスにデプロイします。
2つのインスタンス間で通信を行う上で必要な設定を学んでいきたいと思います。
これまでの作業内容については以下の記事にまとめています。
全体像
今回もNginxをリバースプロキシとして使います。
EC2インスタンスを作成する
まずWebサーバーとしてReactを起動させるEC2インスタンスを作成します。
項目名 | 値 | 意味 |
---|---|---|
名前とタグ > 名前 | simple-memo -web-server(任意) | インスタンスの名前 |
キーペア (ログイン) > キーペア名 > 新しいキーペアを作成 | simple-memo | インスタンスに接続するときの認証情報 APサーバーと同じものを使用 |
ネットワーク設定 > 右上編集ボタン > ファイアウォール(セキュリティグループ) > セキュリティグループを作成する | simple-memo-web-sg | |
ネットワーク設定 > 右上編集ボタン > インバウンドセキュリティグループのルール > セキュリティグループルール 1 | タイプ:ssh ソース:3.112.23.0/29 | インスタンスを操作するための接続(ssh)をEC2 Instance Connectのみに制限(AWSコンソール上からのみ接続可) |
ネットワーク設定 > 右上編集ボタン > インバウンドセキュリティグループのルール > セキュリティグループルール 2 | タイプ:HTTP ソースタイプ:0.0.0.0/0 | Nginxへの外部からのアクセスを解放 |
次にAPサーバーとしてSpring Bootを起動させるEC2インスタンスを作成します。
項目名 | 値 | 意味 |
---|---|---|
名前とタグ > 名前 | simple-memo-ap-server(任意) | インスタンスの名前 |
キーペア (ログイン) > キーペア名 | simple-memo RSA .pem | インスタンスに接続するときの認証情報 |
ネットワーク設定 > 右上編集ボタン > ファイアウォール(セキュリティグループ) > セキュリティグループを作成する | simple-memo-ap-sg(任意) | |
ネットワーク設定 > 右上編集ボタン > インバウンドセキュリティグループのルール > セキュリティグループルール 1 | タイプ:ssh ソース:3.112.23.0/29 | インスタンスを操作するための接続(ssh)をEC2 Instance Connectのみに制限(AWSコンソール上からのみ接続可) |
ネットワーク設定 > 右上編集ボタン > インバウンドセキュリティグループのルール > セキュリティグループルール 2 | タイプ:ssh ソースタイプ:マイIP | jarファイル転送時にscpコマンドでssh接続するため自身の端末のみ許可 |
ネットワーク設定 > 右上編集ボタン > インバウンドセキュリティグループのルール > セキュリティグループルール 3 | タイプ:カスタムTCP ポート範囲:8080ソースタイプ:WebサーバーのパブリックIP | ReactからのAPI通信を受け入れるため |
Nginxの設定
今回もNginxをリバースプロキシとして使用します。
nginxのインストールと設定についてはこちらの記事を参考にしてください。
外部からのアクセスをNginxがポート80で受け取り、Reactが起動しているポート3000に代わりにリクエストを送ります。
Reactからのhttp://(WebサーバのパブリックIP)/api/
へのリクエストをNginxが受け取り、http://(APサーバのパブリックIP):8080/api
に代わりにリクエストを送ります。
server {
listen 80;
server_name localhost;
# 外部 → Nginx → React
location / {
proxy_pass http://localhost:3000;
}
# React → Nginx → APサーバのSpring Boot
location /api {
proxy_pass http://(APサーバのパブリックIP):8080/api;
}
}
nginxを起動します。
sudo systemctl start nginx
設定ファイルの導入
開発環境と本番環境では、APIのリクエスト元とリクエスト先が異なります。
環境 | リクエスト元 | リクエスト先 |
開発環境 | http://localhost:3000 | http://localhost:8080/api |
本番環境 | http://(WebサーバーのIPアドレス) | http://(WebサーバーのIPアドレス)/api |
リクエスト元の情報はSpringBootの@CrossOriginでCORS問題に対応しており、
リクエスト先の情報はReactのリクエスト作成で使用しています。
設定ファイルを導入して環境を切り替えやすくします。
SpringBoot
設定ファイルの作成
Spring Bootでは接続情報などを記載する目的でapplication.properties
という設定ファイルを使用します。
application.properties
には共通環境(開発環境と本番環境の両方)で使用する設定を記述します。
開発環境や本番環境用の設定はapplication-dev.properties
やapplication-prod.properties
という設定ファイルを作成して記述します。
# 開発環境のAPI接続元
api.endpoint=http://localhost:3000
# 本番環境のAPI接続元
api.endpoint=http://(WebサーバのIPアドレス)
環境の切り替えはapplication.properties
に記述します。
# アプリケーションの名前
spring.application.name=simple-memo
# 使用するプロパティファイルの指定(追記)
spring.profiles.active=dev
【ミニコラム: 設定ファイルの名称】
今回、devとprodを開発環境と本番環境としましたが、この部分は任意です。
なのでapplication-prd.properties
としても良い訳です。
その場合、application.properties
でactive=prdと対応させる必要はあります。
設定ファイルの読み込み
環境によって読み込む設定ファイルを動的に切り替えるために、WebConfig.java
を使用します。
@CrossOriginの引数には設定ファイルの値を埋め込むことはできないため、WebConfig.javaで動的に構成します。
WebConfig.java
はアプリケーションが起動する際に自動的に読み込まれる設定クラスです。
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");
}
}
application.properties
はアプリケーション起動時に自動的に読み込まれるのでWebConfig.java
で指定する必要はありません。
設定クラスを作成すると、コントローラクラスで個別にCORS設定をする必要がなくなります。
// @CrossOrigin(origins="localhost:8080") // 不要になる
public class SimpleMemoController {
...
}
動作確認
上記の修正を行い、開発環境で実行します。
Loggerを使用して起動直後のコンソールに接続先を出力するようにしました(下赤枠)。
上の赤枠にはapplication.properties
で設定したactiveの値が表示されます。
application.properties
をprodに変えて再実行してみます。
適切に変更されていることが確認できます。
React.envの導入
Reactでは接続情報などを記載する目的で.env
ファイルをします。
開発環境と本番環境の設定用に.env.development
と.env.production
をルートディレクトリに作成します。
REACT_APP_API_BASE_URL=http://localhost:8080
REACT_APP_API_BASE_URL=(webサーバのIPアドレス)
環境変数名はREACT_APP_
で始める必要があります。
どちらのファイルを読み込むかはReact実行方法で切り替えます(後述)。
設定ファイルの読み込み
process.env.REACT_APP_XXXとすることで.envファイルの内容を読み込みます。
// const response = await fetch("http://localhost:8080/api/memos");
const response = await fetch(`${process.env.REACT_APP_API_BASE_URL}/api/memos`);
動作確認
開発環境用の環境変数を確認するためにnpm run start
で実行します。
ブラウザの開発者ツールでURLが正しいことを確認します。
本番用の環境変数を確認するために以下のコマンドを実行します。
npm run build # 実行ファイルを作成
npm install -g serve # 軽量サーバーを提供するパッケージ
serve -s build # ビルドされた静的ファイルを軽量サーバーで実行
同じくブラウザの開発者ツールでURLが.env.production
から取得できていることを確認します。
これでコードを変更せずに環境変数を切り替えることができました。
Spring BootをAPサーバーにデプロイ
Spring Bootのみデプロイする方法はこちらで詳しくまとめています。
開発環境からjarファイルを転送したら、以下のコマンドでjarファイルを実行します。
java -jar ~/simple-memo-0.0.1-SNAPSHOT.jar
実行できたらcurlコマンドで動作確認します。
# メモを作成
$ curl -s -X POST -H "Content-Type: application/json" -d 'サンプル0' http://localhost:8080/api/memos
0
# メモ一覧を表示
$ curl -s -X GET http://localhost:8080/api/memos | jq
[
{
"id": 0,
"content": "サンプル0",
"createdAt": "2024/05/29 22:01:56",
"updatedAt": "2024/05/29 22:01:56"
}
]
コマンドの詳細については以下の記事を参考にしてください。
ReactをWebサーバーにデプロイする
Reactのみデプロイする方法はこちらで詳しくまとめています。
GitからReactアプリをクローンしたら、ビルドして起動してみます。
cd simple-memo-frontend/ # 作業ディレクトリに移動
npm install # package.jsonに記載されたパッケージをインストール
npm run build
sudo npm install -g serve # 軽量サーバーを提供するパッケージ
serve -s build # ビルドされた静的ファイルを軽量サーバーで実行
WebサーバーのパブリックIPアドレスにブラウザから接続します。
接続できました!
項目 | 値 |
APサーバ > セキュリティグループ | WebサーバのパブリックIP からのHTTPアクセスを許可しているか |
Nginx > nginx.conf | API通信のリクエスト先がhttp://(APサーバのパブリックIP):8080/api か |
SpringBoot > application.properties | spring.profiles.activeがprod か |
SpringBoot > application-prod.properties | api.endpointがWebサーバのパブリックIP か |
React > .env.production | REACT_APP_API_BASE_URLがWebサーバのパブリックIP か |
まとめ
以上がSpringBootとReactを別々にEC2インスタンスに起動して通信する方法でした。
実は開発環境に作成した10日前からこのデプロイに取り組んでいたのですが思ったより時間がかかってしまいました。
1つのEC2インスタンスに起動することにして問題を切り分けたことでNginxの仲介役としての使い方を学ぶことができました。
問題の切り分けは大事ですね。引き続き勉強していきます!
コメント