こんにちは。じゅんです。
前回ローカル環境にSpring BootとReactの簡単メモアプリを作成しました。
今回はそのメモアプリを1つのEC2インスタンスにデプロイしていこうと思います。
WebサーバとAPサーバを分けてデプロイした際の記事はこちらです。
これまでの作業内容については以下の記事にまとめています。
事前準備
AWSアカウントの開設・セットアップ
ローカル環境でのアプリの開発
全体像
Nginxをリバースプロキシとして使います。
インスタンスの作成
ReactとSpring Bootを起動するインスタンスを作成します。
項目名 | 値 | 意味 |
---|---|---|
名前とタグ > 名前 | simple-memo -web-server(任意) | インスタンスの名前 |
キーペア (ログイン) > キーペア名 > 新しいキーペアを作成 | simple-memo | インスタンスに接続するときの認証情報 APサーバーと同じものを使用 |
ネットワーク設定 > 右上編集ボタン > ファイアウォール(セキュリティグループ) > セキュリティグループを作成する | simple-memo-web-sg | |
ネットワーク設定 > 右上編集ボタン > インバウンドセキュリティグループのルール > セキュリティグループルール 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へのアクセスを解放 |
Nginxの設定
今回、Nginxをリバースプロキシとして使います。
外部からのアクセスをNginxがポート80で受け取り、Reactが起動しているポート3000に代わりにリクエストを送ります。
ReactとSpringBoot間のAPI通信もNginxを介して行います。
Reactからのhttp://(パブリックIP)/api/
へのリクエストをNginxが受け取り、http://localhost:8080/api
に代わりにリクエストを送ります。
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;
}
}
React(ポート3000)とSpringBoot(ポート8080)が直接通信を行わない(Nginxが仲介する)ためCORS問題を回避することができます。
nginxを起動します。
sudo systemctl start nginx
ソースコードの修正
Nginxで通信の仲介を行うためReactとSpringBootのソースコードに修正が必要です。
React
開発環境ではSpringBootに直接リクエストを送っていましたが、本番環境ではNginxがhttp://(パブリックIP)/api/
で待ち受けているためURLを修正します。
// const response = await fetch(`http://localhost:8080/api/memos`);
const response = await fetch(`http://xxx.xxx.xxx.xxx/api/memos`);
Spring Boot
Nginxが仲介してCORS問題が発生しないため@CrossOriginアノテーションは不要です。
// @CrossOrigin(origins = "http://localhost:3000")
public class SimpleMemoController {
// メソッドなど
}
Spring Bootのデプロイ
Spring Bootのみデプロイする方法はこちらで詳しくまとめています。
開発環境からjarファイルを転送したら、以下のコマンドでjarファイルを実行します。
java -jar ~/simple-memo-0.0.1-SNAPSHOT.jar
実行できたらcurlコマンドで動作確認します。
なお、EC2 Instance Connectではコンソールが1画面しかないので私はタブを複製して実行しました。
コマンドの詳細については以下の記事を参考にしてください。
# メモを作成
$ curl -s -X POST -H "Content-Type: application/json" -d 'サンプル0' http://(パブリック
IP)/api/memos
0
# メモ一覧を表示
$ curl -s -X GET http://(パブリックIP)/api/memos | jq
[
{
"id": 0,
"content": "サンプル0",
"createdAt": "2024/05/27 11:19:47",
"updatedAt": "2024/05/27 11:19:47"
}
]
Reactのデプロイ
Reactのみデプロイする方法はこちらで詳しくまとめています。
GitからReactアプリをクローンしたら、ビルドして起動してみます。
exit # ec2-userに戻ります
cd simple-memo-frontend/ # 作業ディレクトリに移動
npm install # package.jsonに記載されたパッケージをインストール
npm run build
sudo npm install -g serve # 軽量サーバーを提供するパッケージ
serve -s build # ビルドされた静的ファイルを軽量サーバーで実行
インスタンスのパブリックIPに接続します。
起動できました。
API通信も確認できました!
【最も苦しんだ箇所】
ReactからのAPI接続先をlocalhostにしていたところAPI通信ができなくて悪戦苦闘しましたが、結局インスタンスのパブリックIPを設定することで解決しました。
その理由を調べたのですが調べ方が悪いのか上手くヒットしませんでした。
個人的な仮説としては、Reactが送信するlocalhostは
Reactが起動しているサーバーのパブリックIPアドレス
ではなく
ブラウザが起動している端末のIPアドレス
として展開されるのではないか、ということです。
まとめ
以上がSpring BootとReactで作成したアプリケーションを1つのEC2インスタンスにデプロイする方法でした。
Nginxで通信を仲介することでCORS問題を回避する点やクライアント側のlocalhostの中身についての考察など、得られるものが多い回でした。
次回はSpring BootとReactを2つのインスタンスに分けてデプロイしてみようかなと思います。
おまけ
今回からiPad Pro 12.9インチを出先の作業のお供にしているのですが、非常に快適です。
少しでも作業効率を上げたい方やiPad Pro12.9インチ(2018年モデル)に興味がある方はぜひこちらの記事も見てみてください。
コメント