Spring Boot × React アプリを AWS に本番デプロイ!前編:EC2 + RDS + Dockerで構築してみた
はじめに
本記事では、Spring Boot & React で開発したイベント予約アプリを AWS 本番環境にデプロイする手順を紹介します。
構成は Auto Scaling と ALB(Application Load Balancer)を組み合わせ、アクセス集中にもある程度耐えられる環境を構築しました。
セキュリティ設定についてはまだ課題が多く、今回は「とにかく AWS 上でアプリを動かす」ことを優先しています。
AWS 上でアプリケーションを動かしてみたい方や、Auto Scaling・ALB の構成を試してみたい方に向けた記事となっています。
※記事の内容が多くなってしまったのでこの記事ではオートスケールとロードバランサーの設定は触れません。
アプリの開発過程や学習の流れについては、以下の記事でまとめています。
まだ読んでいない方は、あわせてご覧いただくことで、本記事の内容がより理解しやすくなるかと思います。
これまでの学習・開発ログ
- 学習計画を立てた話
- Javaの基礎とオブジェクト指向を学んでみた感想
- Spring Bootのローカル環境構築
- Spring BootでのEntity + Repository実装
- Spring Bootで予約機能を実装してみた|バリデーション・例外処理
- Spring Boot × React で作るイベント予約管理システム(フロントエンド編)
- Docker Composeで開発環境を一体化
今回の最終的な構成
下記の図は、今回構築したインフラ構成のイメージです。
※図はあくまで大まかな構成を表したもので、細かい構成や設定は省略しています。
インターネットゲートウェイから入ってきた通信は、まずロードバランサー(ALB)で受け取られます。
リクエストは、オートスケーリング設定された EC2 インスタンスへと振り分けられます。
この EC2 は、あらかじめデプロイ用に構築・設定したインスタンスから作成した AMI をベースに起動されるため、
新しいインスタンスが立ち上がっても、すぐにアプリケーションを動作させることができます。
また、データベース(PostgreSQL)はプライベートサブネット内に配置しており、外部から直接アクセスできないようになっています。
シングルAZ構成でも、RDSを作成する際には「2つ以上のサブネット」が必要なので使わないプライベートサブネットがあります。
アプリケーションからのみ通信できる構成にすることで、セキュリティを少しでも高めるよう意識しています。
VPCとサブネットの作成
VPC は、AWS 上に自分専用のネットワーク空間(=仮想の土地)を作るようなイメージです。
この「土地」の中に、アプリを置くためのサーバー(EC2)やデータベース(DB)を建てるための「区画(サブネット)」を作っていきます。
今回は以下の構成で VPC を作成しました:
- VPC(CIDR:10.0.0.0/16)
- パブリックサブネット ×2(EC2・ALB用)
- プライベートサブネット ×2(DB用)
VPCの作成手順
1. VPCの作成
- AWSマネジメントコンソールで「VPC ダッシュボード」にアクセス
- 「VPC を作成」をクリック
- 以下の設定で入力
- 作成するリソース:VPC のみ
-
名前タグ:
event-reservation-vpc
(任意) -
IPv4 CIDR ブロック:
10.0.0.0/16
(任意) - IPv6 CIDR ブロック:なし
- テナンシー:デフォルト
- 「VPCを作成」をクリック
2. サブネットの作成(4つ)
- 「VPC ダッシュボード」→「サブネット」→「サブネットを作成」
- 各サブネットを以下の通り作成する(1つずつ)
名前 | AZ | IPv4 CIDR ブロック |
---|---|---|
public-subnet-1a |
ap-northeast-1a |
10.0.1.0/24 |
public-subnet-1c |
ap-northeast-1c |
10.0.2.0/24 |
private-subnet-1a |
ap-northeast-1a |
10.0.101.0/24 |
private-subnet-1c |
ap-northeast-1c |
10.0.102.0/24 |
- 各作成時の設定
- VPC:ステップ1で作成した VPC を選択
- サブネット名:上記表を参照(任意)
- アベイラビリティゾーン:表の AZ を選択
- IPv4 CIDR ブロック:表の値を入力
- 「サブネットを作成」をクリック
💡 補足:この時点ではまだパブリック/プライベートの性質はありません。
後ほど行う「ルートテーブル」や「インターネットゲートウェイ」の設定で決まります。
3. サブネットの自動パブリックIP割り当て設定(パブリックのみ)
- VPC ダッシュボードから「サブネット」を選択
-
public-subnet-1a
を選び、「アクション」→「サブネットの設定を編集」 - 「自動でパブリック IPv4 アドレスを割り当てる」に チェックを入れる
- 「保存」をクリック
- 同様に
public-subnet-1c
にも同じ設定を行う
✅ ポイント:
プライベートサブネット側にはこの設定をしないように注意!
パブリックIPが割り当てられると、外部公開されてしまうため、DBなどには不要です。
インターネットゲートウェイとルートテーブルの設定
次に、VPC をインターネットに接続できるようにするための設定を行います。
-
インターネットゲートウェイ(Internet Gateway) は、
インターネットと VPC をつなぐ “空港のようなゲート” の役割です。 -
ルートテーブル(Route Table) は、
空港から目的の場所(サブネット)へどうやって行くかを案内する「道順マップ」のようなものです。
これらを正しく設定することで、外部からアプリにアクセスできるようになります。
1. インターネットゲートウェイ(IGW)の作成
- 「VPC」→「インターネットゲートウェイ」→「インターネットゲートウェイを作成」
- 名前タグを設定(例:
event-reservation-igw
) - 作成後、「アクション」→「VPC にアタッチ」→ 先ほど作成した VPC を選択してアタッチ
2. ルートテーブルの作成
- 「VPC」→「ルートテーブル」→「ルートテーブルを作成」
- 名前タグを設定(例:
public-route-table
) - 対象の VPC を選択して作成
3. ルートの追加(0.0.0.0/0 → IGW)
- 作成したルートテーブルを開く
- 「ルート」タブを開き、「ルートを編集」
- 以下のようにルートを追加:
- 宛先:
0.0.0.0/0
(全てのインターネット通信) - ターゲット:インターネットゲートウェイ(先ほど作成したものを選択)
- 宛先:
- 保存
4. パブリックサブネットとルートテーブルの関連付け
- ルートテーブル詳細画面 →「サブネットの関連付け」→「サブネットを編集」
- パブリックサブネット1・2 を選択して保存
✅ これでようやく「パブリックサブネット」になります!
この設定によって、サブネット内のインスタンスはインターネットに出入りできるようになります。
5. プライベートサブネット用ルートテーブルの作成
RDS(PostgreSQL)などの DB を置くプライベートサブネットにも、
通信ルートを設定するためのルートテーブルを作っておきましょう。
- 「ルートテーブル」→「ルートテーブルを作成」
- 名前タグを設定(例:
private-route-table
) - 対象の VPC を選択して作成
※ ルートの追加(0.0.0.0/0)は不要です。
6. プライベートサブネットとルートテーブルの関連付け
- 作成したルートテーブルを開く
- 「サブネットの関連付け」→「サブネットを編集」
- プライベートサブネット1・2 を選択して保存
🔒 この設定で、外部からはアクセスできないけれど、VPC内部の通信は可能なサブネットになります。
セキュリティグループの設計
セキュリティグループは、AWSにおけるファイアウォールのような存在です。
インスタンスに対して「どこから」「どのポートで」アクセスを許可するかを定義できます。
例えば、EC2を「お店」、DBを「倉庫」と考えてみましょう。
セキュリティグループは「鍵のついた出入口の扉」のようなもので、お店や倉庫への出入り口を守っています。
ここでは以下の2つのセキュリティグループを作成します。
- sg-ec2:アプリケーションをホストするEC2用(お店の入り口)
- sg-db:PostgreSQL(RDS)用(倉庫の入り口)
1. EC2用セキュリティグループ(sg-ec2)
-
AWSマネジメントコンソールで「セキュリティグループ」→「セキュリティグループを作成」
-
名前タグ:
sg-ec2
-
説明:アプリケーション用EC2のセキュリティグループ
-
VPC:作成したVPCを選択
-
インバウンドルールを追加:
- タイプ:HTTP、ポート:80、送信元:0.0.0.0/0(世界中からお店の入り口へ入れる)
- タイプ:カスタムTCP、ポート:8080、送信元:0.0.0.0/0(アプリのポートに合わせて)
- タイプ:SSH、ポート:22、送信元:自分のIPアドレス(管理者だけがお店の裏口から入れる鍵)
-
アウトバウンドルール:デフォルトのままでOK(お店から自由に出ていける)
2. DB用セキュリティグループ(sg-db)
-
「セキュリティグループ」→「セキュリティグループを作成」
-
名前タグ:
sg-db
-
説明:PostgreSQL用セキュリティグループ
-
VPC:作成したVPCを選択
-
インバウンドルールを追加:
- タイプ:PostgreSQL、ポート:5432、送信元:
sg-ec2
(お店の扉を持つ人だけが倉庫に入れる)
- タイプ:PostgreSQL、ポート:5432、送信元:
-
アウトバウンドルール:デフォルトのままでOK(倉庫からの出入りは自由)
🔐 ポイント:
- お店(EC2)の扉は広く開けてお客様を迎え入れます。
- 倉庫(DB)はお店のスタッフ(EC2)だけが鍵を持って入れるように厳重に守ります。
- こうすることで外部から直接倉庫にアクセスされるリスクを防げて、安全な設計になります。
本番環境ではこのように「お店の扉は広く、倉庫の扉は鍵付き」にすることがセキュリティ的にとても安心です。
EC2の作成と設定
EC2インスタンスの作成手順
- 「EC2」→「インスタンス」→「インスタンスを起動」
- 名前:
app-server
など - AMI:Ubuntu(使いなれたOSでいいです。)
- Amazon マシンイメージ (AMI)の右下あたりにユーザー名があります。Tera Termのログイン時に使うユーザー名なので大切。
- インスタンスタイプ:
t2.micro
(無料枠)やt3.small
など適宜選択 - キーペア名:何でもよい キーペア:新規作成(SSH用) タイプ:ED25519(これ選ばないとteraTermにログイン出来ないかも) プライベートキーファイル形式:.pem → このときにダウンロードされるファイルは絶対になくさないように
- ネットワーク設定:
- VPC:作成済みのVPC
- サブネット:パブリックサブネット(例:
public-subnet-1a
) - パブリックIP:自動割り当て(有効)
- セキュリティグループ:
sg-ec2
を選択
- ストレージ:デフォルトでOK(必要に応じて増やす)
- 「起動」でインスタンスを作成
EC2インスタンスの設定
- 作成したEC2のパブリック IPv4 アドレスを覚える
- TeraTerm接続 下記の設定でOK
- セキュリティー警告が出るが気にせず続行
- 先ほど覚えておいたユーザー名と認証方式で秘密鍵でキーペア作成時にダウンロードされたpemを選ぶ
- 下記のコマンドを打っていく
# パッケージ更新
sudo apt update && sudo apt upgrade -y
# Dockerインストール & 起動
sudo apt install -y docker.io
docker --version
sudo systemctl start docker
sudo systemctl enable docker
# docker-composeインストール
sudo apt install -y docker-compose
docker-compose --version
# Docker Hub からアプリのイメージを pull
docker pull hirumadevlog/event-reservation-backend:latest
docker-compose は公式のバージョンによりURLが異なるため、必要に応じて最新版を確認してください。
.envとdocker-compose.yml の作成 Docker起動
- .envのファイル作成(EC2上の docker-compose.yml があるのと同じディレクトリ に作成)
SPRING_DATASOURCE_URL=jdbc:postgresql://<RDSのエンドポイント>:5432/<DB名>
SPRING_DATASOURCE_USERNAME=<DBのユーザー名>
SPRING_DATASOURCE_PASSWORD=<DBのパスワード>
- docker-compose.yml の作成
version: '3'
services:
app:
image: hirumadevlog/event-reservation-backend:latest
container_name: event-reservation-app
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://<RDSのエンドポイント>:5432/<DB名>
- SPRING_DATASOURCE_USERNAME=<DBのユーザー名>
- SPRING_DATASOURCE_PASSWORD=<DBのパスワード>
データベースの作成
-
「データベース」→「データベースを作成」
-
モード:標準作成
-
エンジンのタイプ:PostgreSQL
-
バージョン:安定した最新バージョンを選択
-
DBインスタンス識別子:
my-event-db
など -
認証情報設定:
- マスターユーザー名:
postgres
(または任意) - パスワード:任意(後で接続に使うので控えておく)
- マスターユーザー名:
-
インスタンスクラス:
db.t3.micro
(検証目的なら無料枠対応) -
ストレージ:デフォルトでOK(20GBなど)
-
接続:
- VPC:作成済みのVPC
- サブネットグループ:上で作成した
my-db-subnet-group
- パブリックアクセス:無効
- セキュリティグループ:
sg-db
を選択(EC2 からのみ接続許可)
-
その他の設定はデフォルトでOK(または適宜調整)
-
「データベースを作成」で起動
ブラウザでアプリを確認
- Dockerアプリを再起動(任意)
# docker-compose があるディレクトリで
docker-compose down
docker-compose up -d
- EC2が起動中&Dockerアプリが動作中かを確認
# EC2 に SSH 接続後
docker ps
アプリケーションのコンテナ(Spring Boot & React)が STATUS に「Up」と表示されていればOKです。
2. EC2のパブリックIPを確認
AWS マネジメントコンソール → EC2 → 対象のインスタンスを選択
→「IPv4 パブリックIP」の値をコピーします。
例:18.176.123.45
3. ブラウザでアクセスしてみる
ブラウザで以下のURLにアクセスします:
http://18.176.123.45:8080/
- 表示されたら成功!
- トップ画面やイベント一覧、フォーム画面などが表示されればOKです!
- 登録処理や一覧取得など、DBと連携している動作も確認できればより安心です。
まとめ
ここまでで、以下の構成をベースにアプリケーションを AWS 上に立ち上げるところまでを進めてきました。
- VPC・サブネットの作成
- インターネットゲートウェイ・ルートテーブルの設定
- RDS(PostgreSQL)の配置
- EC2 からアプリの起動と DB への接続確認
- ブラウザからアプリケーションが動作することの確認
次回
次回は、より本番運用を意識した構成に踏み込んでいきます。
- Application Load Balancer(ALB)の作成と設定
- Auto Scaling Group の構成
- AMI を使った起動テンプレートの作成
- 複数インスタンスへのトラフィック分散とスケーリング対応
- ALB経由でのアプリアクセス(DNSでアクセス可能に)
Discussion