0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS VPC / EC2でWebサーバ・APIサーバ構成を作った学習メモ

0
Posted at

はじめに

この記事は自分の学習用、備忘録として書いてるものです。
もし内容に間違いなどがあればご指摘ください。

AWSのVPCとEC2を使って、WebサーバとAPIサーバを分けた構成を作成した。

今回の学習で一番大事だったのは、EC2を単体で立てることではなく、AWS上でサーバ同士がどのように通信するのかを理解することだった。

AWSでは、EC2を起動するだけではシステムは成立しない。
VPC、サブネット、ルートテーブル、インターネットゲートウェイ、NATゲートウェイ、セキュリティグループなどが組み合わさって、初めて外部公開や内部通信ができる。

今回の記事では、学習した内容を自分の備忘録として整理する。


今回作った構成

今回作成した構成は以下。

インターネット
    |
    | HTTP
    v
Public Subnet
    |
    | Webサーバ EC2
    | - Nginx
    | - HTML配信
    | - APIサーバへのリバースプロキシ
    v
Private Subnet
    |
    | APIサーバ EC2
    | - APIアプリケーション
    | - 8080番ポートで起動

構成のポイントは、Webサーバは外部公開し、APIサーバは外部公開しないという点。

Webサーバはユーザーのブラウザからアクセスされるため、Public Subnetに配置する。
一方で、APIサーバは直接インターネットに公開する必要がないため、Private Subnetに配置する。

外部公開するもの   : Webサーバ
外部公開しないもの : APIサーバ、DBなど

この構成にすることで、ユーザーはWebサーバにだけアクセスし、Webサーバが内部的にAPIサーバへ通信する形になる。


VPCとは

VPC(Virtual Private Cloud)は、AWS上に作る仮想ネットワーク。

EC2やRDSなどのリソースは、基本的にVPCの中に配置する。
そのため、VPCはAWS上でシステムを構築するための土台になる。

イメージとしては以下。

VPC = AWS上に作る自分専用のネットワーク空間

VPCを作るときは、IPアドレスの範囲をCIDRで指定する。

今回のような構成では、以下のようなCIDRを使う。

VPC: 10.0.0.0/16

/16 はVPC全体でよく使われる範囲。
この範囲の中に、さらにサブネットを作っていく。


サブネットとは

サブネットは、VPCの中をさらに分割したネットワーク領域。

今回作成したサブネットは以下。

VPC: 10.0.0.0/16

Public Subnet:
10.0.1.0/24

Private Subnet:
10.0.2.0/24

サブネットを分ける理由は、リソースの役割ごとにネットワークを分離するため。

例えば、Webサーバは外部からアクセスされるためPublic Subnetに配置する。
APIサーバやDBは外部から直接アクセスさせたくないためPrivate Subnetに配置する。

Public Subnet  : インターネットからアクセスされるリソースを置く
Private Subnet : 外部公開しないリソースを置く

ここで重要なのは、サブネット自体にPublic/Privateという種類があるわけではないこと。

Public SubnetかPrivate Subnetかは、主にルートテーブルの設定で決まる。


CIDRの考え方

CIDRは、IPアドレスの範囲を表す書き方。

例:

10.0.0.0/16
10.0.1.0/24

最初は細かい計算まで理解しようとすると難しい。
まずは以下のパターンを覚えれば十分。

VPC      : /16
Subnet   : /24

/16 の例

10.0.0.0/16
→ 10.0.0.0 〜 10.0.255.255

VPC全体の範囲として使う。

/24 の例

10.0.1.0/24
→ 10.0.1.0 〜 10.0.1.255

サブネットの範囲として使う。

VPCの中にサブネットを作るため、サブネットのCIDRはVPCの範囲内である必要がある。

良い例:

VPC: 10.0.0.0/16

Subnet A: 10.0.1.0/24
Subnet B: 10.0.2.0/24

悪い例:

VPC: 10.0.0.0/16

Subnet: 192.168.1.0/24

これはVPCの範囲外なので使えない。


Public Subnetとは

Public Subnetは、インターネットと直接通信できるサブネット。

Public Subnetにするには、ルートテーブルでインターネットゲートウェイへの経路を設定する必要がある。

送信先: 0.0.0.0/0
ターゲット: Internet Gateway

0.0.0.0/0 は「すべての宛先」を意味する。

つまり、VPC内部以外への通信はインターネットゲートウェイに流す、という意味になる。

今回、Webサーバはインターネットからアクセスさせる必要があるため、Public Subnetに配置した。

ブラウザ
  ↓
Internet Gateway
  ↓
Public Subnet
  ↓
Webサーバ EC2

Private Subnetとは

Private Subnetは、インターネットから直接アクセスできないサブネット。

APIサーバやDBサーバなど、外部公開したくないリソースを置く。

今回、APIサーバはPrivate Subnetに配置した。

Private Subnet
  ↓
APIサーバ EC2

Private Subnetに配置したEC2は、基本的に外部から直接アクセスできない。
そのため、ローカルPCから直接SSH接続することもできない。

APIサーバへ接続するときは、Public Subnet上のWebサーバを踏み台として経由する。

ローカルPC
  ↓ SSH
Webサーバ
  ↓ SSH
APIサーバ

インターネットゲートウェイとは

インターネットゲートウェイは、VPCとインターネットをつなぐ出入り口。

作成しただけでは使えない。
VPCにアタッチして、ルートテーブルで通信経路を向ける必要がある。

作業の流れは以下。

1. インターネットゲートウェイを作成
2. VPCにアタッチ
3. Public Subnet用のルートテーブルに設定

ルートテーブルの設定例:

送信先: 0.0.0.0/0
ターゲット: Internet Gateway

これでPublic SubnetにあるEC2がインターネットと通信できるようになる。


NATゲートウェイとは

NATゲートウェイは、Private Subnet内のリソースがインターネットへ出ていくための仕組み。

Private Subnetのリソースは外部から直接アクセスさせたくない。
しかし、OSアップデートやパッケージインストールなどで外部通信が必要になる場合がある。

そのときにNATゲートウェイを使う。

Private Subnet
  ↓
NAT Gateway
  ↓
Internet Gateway
  ↓
Internet

ポイントは以下。

  • NATゲートウェイはPublic Subnetに配置する
  • NATゲートウェイにはElastic IPが必要
  • Private SubnetのルートテーブルでNATゲートウェイを指定する
  • Private Subnetから外へ出る通信はできる
  • インターネット側からPrivate Subnetへ直接入る通信はできない

この「外へは出られるが、外からは入れない」という性質が重要。

Private Subnet用ルートテーブルの例:

送信先: 0.0.0.0/0
ターゲット: NAT Gateway

ルートテーブルとは

ルートテーブルは、通信の行き先を決める設定。

サブネットに関連付けることで、そのサブネット内のEC2がどこへ通信を流すかを決める。

今回作成したルートテーブルは2つ。


Webサーバ用ルートテーブル

Webサーバはインターネットと通信するため、Internet Gatewayへのルートを設定する。

関連付けるサブネット:
web-subnet

ルート:
10.0.0.0/16 → local
0.0.0.0/0  → Internet Gateway

local はVPC内部通信のためのルート。
これはデフォルトで存在する。

0.0.0.0/0 → Internet Gateway があることで、インターネットと通信できる。


APIサーバ用ルートテーブル

APIサーバは外部公開しない。
ただし、必要に応じて外部へ通信できるようにNAT Gatewayへのルートを設定する。

関連付けるサブネット:
api-subnet

ルート:
10.0.0.0/16 → local
0.0.0.0/0  → NAT Gateway

この設定により、APIサーバは直接外部公開されないが、外部への通信はNAT Gateway経由で可能になる。


セキュリティグループとは

セキュリティグループは、EC2に設定する仮想ファイアウォール。

どの通信を許可するかを設定する。

基本は「許可した通信だけ通す」ホワイトリスト方式。

今回作成したセキュリティグループは以下。

web-sg
api-sg

Webサーバ用セキュリティグループ

Webサーバはブラウザからアクセスされるため、HTTPを許可する必要がある。

設定イメージ:

セキュリティグループ: web-sg

インバウンド:
HTTP 80  0.0.0.0/0
SSH  22  自分のIPアドレスなど

HTTPの 0.0.0.0/0 は、インターネット上の誰でもWebページを見られるようにするための設定。

一方、SSHの 0.0.0.0/0 は危険。
学習目的では一時的に使う場合もあるが、本番環境では自分のIPアドレスや社内ネットワークだけに制限するべき。

HTTP: 全世界に公開してよい
SSH : 管理者だけに限定するべき

APIサーバ用セキュリティグループ

APIサーバは外部から直接アクセスさせない。

そのため、Webサーバからの通信だけ許可する。

設定イメージ:

セキュリティグループ: api-sg

インバウンド:
TCP 8080  web-sg
SSH 22    web-sg

ここで重要なのは、送信元に web-sg を指定している点。

これは「web-sgが付いているEC2からの通信だけ許可する」という意味。

つまり、外部のIPアドレスから直接APIサーバへアクセスすることはできない。

ブラウザ
  ↓
Webサーバ
  ↓ 8080番
APIサーバ

APIサーバはWebサーバからだけ呼び出される。


セキュリティグループとネットワークACLの違い

セキュリティグループと似たものにネットワークACLがある。

違いは以下。

項目 セキュリティグループ ネットワークACL
適用単位 EC2単位 サブネット単位
設定内容 許可のみ 許可と拒否
評価方法 すべてのルールを評価 ルール番号順に評価
状態 ステートフル ステートレス
戻り通信 自動で許可 明示的に許可が必要
主な用途 EC2単位の通信制御 サブネット単位の通信制御

最初はセキュリティグループを中心に理解するのがよさそう。

ネットワークACLは、特定のIPアドレスを拒否したい場合や、サブネット単位でより細かく制御したい場合に使うイメージ。


EC2とは

EC2は、AWS上で仮想サーバを作成できるサービス。

正確には、EC2はサービス名で、実際に作成される仮想サーバは「インスタンス」と呼ぶ。

EC2を作るときは、以下を指定する。

- AMI
- インスタンスタイプ
- キーペア
- VPC
- サブネット
- セキュリティグループ
- ストレージ
- パブリックIPの有無

EC2は必要なときに作成でき、不要になったら停止・削除できる。
オンプレミスの物理サーバと違い、柔軟に増減できるのが特徴。


AMIとは

AMIは、EC2を起動するためのテンプレート。

OSや初期状態を決めるもの。

例:

Amazon Linux 2023
Ubuntu
Windows Server

AMIが「中身」を決めるもので、インスタンスタイプが「性能」を決めるもの。

AMI                : OSやソフトウェア構成
インスタンスタイプ : CPUやメモリなどの性能

今回のハンズオンでは、Amazon Linux 2023を使用した。


インスタンスタイプとは

インスタンスタイプは、EC2の性能を決める設定。

例:

t3.micro
m7i.large
c7i.large
r7i.large

インスタンスタイプは、ファミリー、世代、サイズで構成される。

t3.micro

t     : ファミリー
3     : 世代
micro : サイズ

ざっくりした分類は以下。

ファミリー 特徴 主な用途
t系 安価、バースト可能 学習環境、開発環境、小規模Web
m系 CPUとメモリのバランス型 一般的なWeb/APIサーバ
c系 CPU重視 高負荷処理、計算処理
r系 メモリ重視 DB、キャッシュ、大量データ処理

今回のような学習用途では t3.micro を使った。


キーペアとSSH接続

EC2にSSH接続するにはキーペアを使う。

キーペアは以下の2つで構成される。

公開鍵: AWS側に保存
秘密鍵: ローカルPCに保存

SSH接続時には秘密鍵を指定する。

Macの場合の例:

ssh -i ./my-key.pem ec2-user@<WebサーバのパブリックIP>

秘密鍵ファイルの権限が緩いと、以下のようなエラーが出る。

WARNING: UNPROTECTED PRIVATE KEY FILE!
Permissions 0644 for './my-key.pem' are too open.

この場合は、秘密鍵の権限を変更する。

chmod 400 ./my-key.pem

秘密鍵は、キーペア作成時にしかダウンロードできない。
紛失すると再ダウンロードできないため、管理に注意が必要。


EBSとは

EBSは、EC2に接続するブロックストレージ。

PCでいうHDDやSSDのようなもの。

EC2のOSやミドルウェア、アプリケーションファイルなどはEBSに保存される。

ただし、永続的に残すべき重要なデータをEC2のEBSに置くのは避けた方がよい。

理由は、EC2は増減・再作成される前提のリソースだから。

画像ファイルなど : S3
DBデータ        : RDS
一時ファイル    : EBSでも可

クラウドでは、EC2自体はいつでも作り直せるようにして、重要なデータは外部の永続化サービスに置く考え方が重要。


Elastic IPとは

EC2に自動で割り当てられるパブリックIPは、停止・開始すると変わることがある。

固定のパブリックIPが必要な場合は、Elastic IPを使う。

今回、WebサーバにElastic IPを割り当てた。

Elastic IP
  ↓
web-server

これにより、WebサーバのIPアドレスを固定できる。

ただし、Elastic IPは課金対象になるため、不要になったら解放する必要がある。


ユーザデータとは

ユーザデータは、EC2起動時に自動実行するスクリプト。

例えば、EC2起動時に以下のような処理を自動化できる。

- OSアップデート
- Nginxのインストール
- HTMLファイルの配置
- サービスの起動

毎回SSH接続して手作業でセットアップすると、作業ミスが起きやすい。
ユーザデータを使うと、EC2起動時に初期構築を自動化できる。

これはIaCや自動構築の入口になる考え方。


Webサーバの作成

Webサーバ用のEC2は、Public Subnetに配置した。

設定イメージ:

名前: web-server
AMI: Amazon Linux 2023
インスタンスタイプ: t3.micro
VPC: my-vpc
サブネット: web-subnet
パブリックIP: 有効
セキュリティグループ: web-sg

WebサーバではNginxを使った。

Nginxの役割は以下。

- ブラウザからのHTTPリクエストを受ける
- HTMLページを返す
- /api/ のリクエストをAPIサーバへ転送する

つまり、Webサーバはフロント側の入口になる。


Nginxのリバースプロキシ

Webサーバでは、Nginxを使ってAPIサーバへのリバースプロキシを設定した。

設定イメージ:

location /api/ {
    proxy_pass http://<APIサーバのプライベートIP>:8080/api/;
}

これにより、ブラウザからWebサーバに送られた /api/ のリクエストを、内部のAPIサーバへ転送できる。

ブラウザ
  ↓
Webサーバ(Nginx)
  ↓ /api/
APIサーバ

ユーザーから見るとWebサーバにアクセスしているだけ。
しかし、内部的にはWebサーバがAPIサーバへ通信している。

この構成にすることで、APIサーバを直接外部公開しなくてもAPIを利用できる。


APIサーバの作成

APIサーバ用のEC2は、Private Subnetに配置した。

設定イメージ:

名前: api-server
AMI: Amazon Linux 2023
インスタンスタイプ: t3.micro
VPC: my-vpc
サブネット: api-subnet
パブリックIP: 無効
セキュリティグループ: api-sg

APIサーバは外部公開しない。
そのため、パブリックIPは付与しない。

APIサーバでは8080番ポートでアプリケーションを起動した。

WebサーバからAPIサーバへ疎通確認するときは以下。

curl http://<APIサーバのプライベートIP>:8080/api/health

成功すると以下のようなJSONが返る。

{ "message": "API接続に成功しました" }

踏み台接続

APIサーバはPrivate Subnetにあるため、ローカルPCから直接SSH接続できない。

そのため、Webサーバを経由してAPIサーバへ接続する。

ローカルPC
  ↓ SSH
Webサーバ
  ↓ SSH
APIサーバ

ローカルPCからWebサーバへ接続:

ssh -i ./my-key.pem ec2-user@<WebサーバのパブリックIP>

WebサーバからAPIサーバへ接続:

ssh -i ./my-key.pem ec2-user@<APIサーバのプライベートIP>

ただし、実運用でWebサーバに秘密鍵を置くのは危険。
本番ではSession Managerなどを使う方が望ましい。


動作確認の流れ

最終的な動作確認は、通信経路を順番に分けて確認した。

1. WebサーバへSSH接続できるか

ssh -i ./my-key.pem ec2-user@<WebサーバのパブリックIP>

ここで接続できない場合は、以下を確認する。

- WebサーバにパブリックIPがあるか
- web-sgでSSHが許可されているか
- 秘密鍵の指定が正しいか
- 秘密鍵の権限が正しいか

2. APIサーバへ踏み台経由でSSH接続できるか

ssh -i ./my-key.pem ec2-user@<APIサーバのプライベートIP>

ここで接続できない場合は、以下を確認する。

- APIサーバがPrivate Subnetにいるか
- api-sgでweb-sgからのSSHが許可されているか
- APIサーバのプライベートIPが正しいか

3. APIサーバ自身でAPIが動いているか

APIサーバ上で確認する。

curl http://localhost:8080/api/health

成功例:

{ "message": "API接続に成功しました" }

ここで失敗する場合は、APIアプリ自体が起動していない可能性がある。

確認観点:

- アプリケーションが起動しているか
- 8080番ポートで待ち受けているか
- systemdサービスが起動しているか

4. WebサーバからAPIサーバへ通信できるか

Webサーバ上で確認する。

curl http://<APIサーバのプライベートIP>:8080/api/health

ここで失敗する場合は、WebサーバからAPIサーバへの通信が通っていない。

確認観点:

- api-sgで8080番が許可されているか
- 送信元がweb-sgになっているか
- APIサーバのプライベートIPが正しいか
- APIアプリが起動しているか

5. ブラウザからWebサーバにアクセスできるか

ブラウザで以下にアクセスする。

http://<WebサーバのパブリックIP>

またはElastic IPを割り当てている場合:

http://<Elastic IP>

ここで画面が表示されない場合は、以下を確認する。

- Nginxが起動しているか
- web-sgでHTTP 80番が許可されているか
- Public SubnetのルートテーブルがInternet Gatewayを向いているか
- WebサーバにパブリックIPがあるか

6. 画面からAPIを呼び出せるか

ブラウザ上のボタンを押して、APIのレスポンスが表示されれば成功。

API接続に成功しました

ここで失敗する場合は、Nginxのリバースプロキシ設定や、WebサーバからAPIサーバへの通信を確認する。


通信エラー時の切り分け方

AWSのネットワーク設定で詰まったときは、闇雲に設定を変えない。
通信経路を分解して、どこまで到達できているか確認する。

今回の構成なら、以下の順番で見る。

1. ブラウザ → Webサーバ
2. Webサーバ → Nginx
3. Nginx → APIサーバ
4. APIサーバ → アプリケーション
5. セキュリティグループ
6. ルートテーブル
7. サブネット
8. Internet Gateway / NAT Gateway

特に見るべきポイントは以下。

- そもそもEC2は起動しているか
- パブリックIPはあるか
- セキュリティグループでポートが開いているか
- 送信元の指定は正しいか
- ルートテーブルは正しいゲートウェイを向いているか
- アプリケーションは起動しているか
- Nginxの設定は正しいか

通信できない場合、原因は大きく3つに分けられる。

1. ネットワーク経路の問題
2. セキュリティグループの問題
3. アプリケーション起動・設定の問題

この3つに分けると切り分けしやすい。


今回理解した重要ポイント

Public/Privateはサブネット名では決まらない

サブネットに public という名前を付けただけではPublic Subnetにはならない。

Public Subnetになる条件は、ルートテーブルでInternet Gatewayへのルートがあること。

0.0.0.0/0 → Internet Gateway

Private Subnetの場合は、Internet Gatewayへ直接向けない。
必要に応じてNAT Gatewayへ向ける。

0.0.0.0/0 → NAT Gateway

APIサーバは外部公開しない方が安全

APIサーバにパブリックIPを付ければ、外部から直接アクセスできる。
しかし、それは攻撃対象を増やすことにもなる。

今回のように、外部から直接アクセスする必要がないAPIサーバはPrivate Subnetに置く。

外部ユーザー
  ↓
Webサーバ
  ↓
APIサーバ

このようにWebサーバを入口にして、APIサーバは内部通信だけにする。


セキュリティグループは最小限にする

セキュリティグループでは、必要な通信だけを許可する。

悪い例:

すべてのポートを 0.0.0.0/0 に開放する

良い例:

Webサーバ:
HTTP 80 は 0.0.0.0/0
SSH 22 は自分のIPのみ

APIサーバ:
8080 は web-sg からのみ
SSH 22 は web-sg からのみ

特にSSHは管理用の入口なので、安易に全開放しない。


ルートテーブルとセキュリティグループは役割が違う

通信できないときに混同しやすいが、役割が違う。

ルートテーブル:
通信をどこへ流すか

セキュリティグループ:
その通信を通してよいか

つまり、ルートテーブルが正しくてもセキュリティグループで拒否されれば通信できない。
逆に、セキュリティグループで許可していてもルートがなければ通信できない。

両方必要。


EC2は作って終わりではない

EC2は起動するだけなら簡単。
しかし、実際に使えるサーバにするには以下が必要。

- 適切なサブネットに配置する
- セキュリティグループを設定する
- SSH接続できるようにする
- 必要なミドルウェアを入れる
- アプリを配置する
- サービスとして起動する
- ブラウザやcurlで動作確認する

EC2を作る作業と、サーバとして使える状態にする作業は別。


実務で意識すべきこと

今回の構成は学習用としては理解しやすい。
ただし、実務ではより慎重に考える必要がある。

SSHを全開放しない

SSHの22番を 0.0.0.0/0 に開けるのは危険。

実務では以下のどちらかを使う。

- 接続元IPを限定する
- AWS Systems Manager Session Managerを使う

Session Managerを使えば、SSHポートを開けずにサーバへ接続できる。


秘密鍵をサーバに置かない

今回の学習では、APIサーバへ接続するためにWebサーバを踏み台にした。

ただし、Webサーバ上に秘密鍵を置くのはセキュリティリスクがある。
実務では、秘密鍵をサーバ上に置かない運用を考える必要がある。


NATゲートウェイは削除忘れに注意

NATゲートウェイは課金が発生しやすいリソース。

学習用に作った場合、使い終わったら削除する。

削除対象の例:

- EC2インスタンス
- NAT Gateway
- Elastic IP
- VPC

削除順も重要。
依存関係があるため、NAT GatewayやElastic IPが残っているとVPCを削除できないことがある。


リソース削除の流れ

学習後は不要なリソースを削除する。

削除の流れは以下。

1. EC2インスタンスを終了
2. NAT Gatewayを削除
3. Elastic IPを解放
4. VPCを削除

VPCを削除すると、サブネット、ルートテーブル、セキュリティグループ、インターネットゲートウェイなどもまとめて削除される場合がある。

ただし、関連リソースが残っていると削除できないことがあるため、依存関係を確認する必要がある。


まとめ

今回、VPCとEC2を使って、WebサーバとAPIサーバを分離した構成を作った。

学んだことは以下。

- VPCはAWS上の仮想ネットワーク
- サブネットでネットワークを分割する
- Public Subnetは外部公開するリソースを置く
- Private Subnetは外部公開しないリソースを置く
- Public/Privateはルートテーブルで決まる
- Internet GatewayはVPCとインターネットをつなぐ
- NAT GatewayはPrivate Subnetから外部へ出るために使う
- セキュリティグループはEC2単位の通信制御
- ネットワークACLはサブネット単位の通信制御
- EC2はAMI、インスタンスタイプ、キーペア、SG、サブネットを指定して作る
- WebサーバはPublic Subnetに置く
- APIサーバはPrivate Subnetに置く
- NginxのリバースプロキシでAPIサーバへ通信する
- 通信エラーは経路ごとに分解して切り分ける
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?