そう感じているエンジニアは多いはずです。VPC(Virtual Private Cloud)はAWSの基盤となるネットワーク機能ですが、
CIDRブロック・サブネット・セキュリティグループ・NATゲートウェイと用語が多く、最初は混乱しがちです。
この記事では、aws vpc 設計の基礎からマルチAZ構成まで、実際の設計パターンをハンズオン形式で解説します。
Webサービスの本番環境で主流になっている「3層アーキテクチャ(ALB・EC2・RDS)」での実装パターンを中心に、VPCフローログを使った通信の可視化とセキュリティ監査の方法も含めて、「まず動くVPCを作りたい」「セキュリティ設定が正しいか確認したい」という初級~中級者に向けて体系的にカバーします。
設計の詳細な冗長化・マルチAZ応用については、AWS冗長設計入門もあわせて参照してください。
この記事のポイント
・VPCはAWS上の仮想ネットワーク。CIDRブロックで範囲を決める
・サブネットはパブリック(ALB)・アプリプライベート(EC2)・DBプライベート(RDS)の3層に分ける
・セキュリティグループはALB-SG→EC2-SG→RDS-SGの順にSG参照で連携させる
・VPCフローログでACCEPT/REJECTを記録し、SG/NACLの設定検証と通信監査に活用する
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
AWS VPCとは何か(基本概念)
AWS VPC(Virtual Private Cloud)は、AWSクラウド上に作成する論理的な仮想ネットワーク空間です。
物理的なデータセンターでいう「社内LAN」を、クラウド上に丸ごと再現するイメージを持つと分かりやすいです。
VPCを使うと、以下のことが実現できます。
・IPアドレスの範囲を自分で決められる(CIDRブロック)
・インターネットに公開するゾーンと非公開ゾーンを分けられる(パブリック・プライベートサブネット)
・どのIPからどのポートへのアクセスを許可するか細かく制御できる(セキュリティグループ・ネットワークACL)
・複数のアベイラビリティゾーン(AZ)にまたがる冗長構成を組める(マルチAZ)
・VPCフローログでネットワーク通信のACCEPT/REJECTを記録して可視化できる
AWSの多くのサービス(EC2・RDS・ELBなど)はVPC内に配置するため、
aws vpc 設計の基礎を押さえることが、AWSの実践スキル全体に直結します。
VPCの主な構成要素
VPCを構成する要素を整理しておきましょう。
| 要素 | 役割 |
|---|---|
| VPC | 仮想ネットワーク空間全体。リージョン単位で作成する |
| サブネット | VPC内を細かく区切ったIPアドレス範囲。AZ単位で作成する |
| インターネットゲートウェイ(IGW) | VPCとインターネットを接続する出入り口 |
| ルートテーブル | サブネットごとの通信経路を定義する |
| セキュリティグループ | インスタンス単位のファイアウォール(ステートフル) |
| ネットワークACL(NACL) | サブネット単位のファイアウォール(ステートレス) |
| NATゲートウェイ | プライベートサブネットからのアウトバウンド通信を中継する |
| VPCフローログ | ENIを通過するIPトラフィックのACCEPT/REJECTを記録する |
| VPC DNSホスト名 | EC2インスタンスにDNSホスト名を付与する(RDS接続に必須) |
VPC設計の基本:CIDRブロックの考え方
VPCを作成する際に最初に決めるのが「CIDRブロック」です。
CIDR(Classless Inter-Domain Routing)は、IPアドレスの範囲を「アドレス/プレフィックス長」で表す記法です。
例えば 10.0.0.0/16 と指定すると、10.0.0.0~10.0.255.255 の65,536個のIPアドレスを使えます。
AWSでは /16(最大65,536アドレス)から /28(最小16アドレス)まで指定できます。
1. CIDRブロックの設計指針
実務でよく使われるVPC CIDRの目安を示します。
| CIDRブロック | 使用可能IPアドレス数 | 適したケース |
|---|---|---|
10.0.0.0/16 |
65,536個 | 本番環境・大規模構成。サブネット分割の余裕が大きい |
10.0.0.0/24 |
256個 | 学習・検証環境。小規模で素早く試したい場合 |
172.16.0.0/16 |
65,536個 | オンプレ環境とIPが重複しないよう別レンジを使いたい場合 |
後からCIDRブロックを変更するのは非常に困難なため、最初に「少し大きめ」に設定しておくことが鉄則です。
本番環境では 10.0.0.0/16 を基本とし、サブネットに /24(256アドレス)を割り当てる設計がよく使われます。
また、ALBやRDSはENI(Elastic Network Interface)を複数消費するため、/28のような小さいサブネットは避けてください。
2. プライベートIPアドレス範囲(RFC 1918)を使う理由
VPC内では必ずプライベートIPアドレス範囲を使います。
インターネットには直接ルーティングされない専用の範囲であり、安全にVPC内部で自由にIPを割り振れます。
| プライベートIPレンジ | アドレス範囲 |
|---|---|
10.0.0.0/8 |
10.0.0.0 ~ 10.255.255.255 |
172.16.0.0/12 |
172.16.0.0 ~ 172.31.255.255 |
192.168.0.0/16 |
192.168.0.0 ~ 192.168.255.255 |
3. AWS CLIでVPCを作成する
AWSコンソールだけでなく、CLIからも作成できます。手順を覚えておくとインフラのコード化(IaC)に役立ちます。
# VPCを作成する(東京リージョン・ap-northeast-1を前提) $ aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=web-prod-vpc}]' # 実行結果(抜粋) { "Vpc": { "VpcId": "vpc-0a1b2c3d4e5f60001", "CidrBlock": "10.0.0.0/16", "State": "available" } } # VPCのDNSホスト名を有効にする(RDSのエンドポイント名解決に必要) $ aws ec2 modify-vpc-attribute --vpc-id vpc-0a1b2c3d4e5f60001 --enable-dns-hostnames
パブリックサブネットとプライベートサブネットの設計
VPCの設計で最も重要な概念が「パブリックサブネット」と「プライベートサブネット」の使い分けです。
この2種類を適切に分けることで、インターネットに公開すべきリソースと非公開にすべきリソースを安全に管理できます。
1. パブリックサブネットの役割
パブリックサブネットは、インターネットゲートウェイ(IGW)へのルートを持ち、
インターネットと直接通信できるサブネットです。
典型的な用途は以下の通りです。
・Webサーバー(EC2インスタンス)
・ロードバランサー(ALB/NLB)
・踏み台サーバー(Bastion Host)
・NATゲートウェイ本体(後述)
# ルートテーブルに以下のルートを追加する # 送信先: 0.0.0.0/0 → ターゲット: インターネットゲートウェイ(igw-xxxxxxxxx) # これによりパブリックサブネット内のインスタンスはインターネットと通信可能になる # また、EC2インスタンスには「パブリックIPの自動割り当て」を有効にする # または Elastic IP(固定パブリックIP)を割り当てる
2. プライベートサブネットの役割
プライベートサブネットは、IGWへの直接ルートを持たず、インターネットから直接アクセスできないサブネットです。
典型的な用途は以下の通りです。
・データベースサーバー(RDS)
・アプリケーションサーバー(APIサーバーなど)
・バッチ処理サーバー
・機密データを扱うサービス
プライベートサブネットに置いたリソースは直接インターネットからアクセスできないため、セキュリティが大幅に向上します。
ただしソフトウェアのアップデートなどでインターネットへのアウトバウンド通信が必要な場合は、NATゲートウェイを使います(後述)。
3. CIDRブロックの分割例
VPCを 10.0.0.0/16 で作った場合のサブネット割り当て例です。
| サブネット名 | CIDRブロック | AZ | 種別 |
|---|---|---|---|
| public-subnet-1a | 10.0.1.0/24 | ap-northeast-1a | パブリック |
| public-subnet-1c | 10.0.2.0/24 | ap-northeast-1c | パブリック |
| private-subnet-1a | 10.0.11.0/24 | ap-northeast-1a | プライベート |
| private-subnet-1c | 10.0.12.0/24 | ap-northeast-1c | プライベート |
パブリックは 10.0.1.x~10.0.9.x、プライベートは 10.0.10.x 以降のような「番号で種別を見分けやすい」設計にしておくと、
後から見返したときに混乱しにくくなります。
4. 3層アーキテクチャでのサブネット分割(Web・AP・DB層)
本番品質のWebシステムでは、プライベートサブネットをさらに「アプリ層(EC2)」と「DB層(RDS)」に分割するのがベストプラクティスです。
アプリ層とDB層を同じサブネットに混在させると、セキュリティグループの制御が複雑になり、
EC2が侵害された場合にRDSへも直接アクセスされるリスクが生じます。
| 層 | 役割 | AWSサービス | 配置サブネット例 |
|---|---|---|---|
| Web層 | HTTPS受付・SSL終端・負荷分散 | ALB(Application Load Balancer) | パブリック(10.0.1.0/24, 10.0.2.0/24) |
| アプリ層 | ビジネスロジック実行・DBアクセス | EC2(Auto Scalingグループ) | アプリプライベート(10.0.11.0/24, 10.0.12.0/24) |
| DB層 | データ永続化・トランザクション | RDS(マルチAZ有効) | DBプライベート(10.0.21.0/24, 10.0.22.0/24) |
AWS CLIでアプリ層とDB層のプライベートサブネットを作成する例です。
# アプリ層用プライベートサブネット(ap-northeast-1a) $ aws ec2 create-subnet --vpc-id vpc-0a1b2c3d4e5f60001 --cidr-block 10.0.11.0/24 --availability-zone ap-northeast-1a --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=app-prv-1a}]' # アプリ層用プライベートサブネット(ap-northeast-1c) $ aws ec2 create-subnet --vpc-id vpc-0a1b2c3d4e5f60001 --cidr-block 10.0.12.0/24 --availability-zone ap-northeast-1c --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=app-prv-1c}]' # DB層用プライベートサブネット(ap-northeast-1a) $ aws ec2 create-subnet --vpc-id vpc-0a1b2c3d4e5f60001 --cidr-block 10.0.21.0/24 --availability-zone ap-northeast-1a --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=db-prv-1a}]' # DB層用プライベートサブネット(ap-northeast-1c) $ aws ec2 create-subnet --vpc-id vpc-0a1b2c3d4e5f60001 --cidr-block 10.0.22.0/24 --availability-zone ap-northeast-1c --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=db-prv-1c}]'
セキュリティグループとネットワークACLの使い分け
VPCのセキュリティ制御は「セキュリティグループ」と「ネットワークACL(NACL)」の2層構造です。
この2つは似て非なるものであり、役割を理解して使い分けることが大切です。
なお、設定後はVPCフローログのactionフィールドで実際のACCEPT/REJECTを確認する習慣をつけると、意図通りに動作しているかすぐ検証できます(後述)。
1. セキュリティグループ(Security Group)
セキュリティグループは、EC2インスタンスなどのリソースに直接アタッチするファイアウォールです。
主な特徴は以下の通りです。
・ステートフル:インバウンドで許可した通信のレスポンスは自動的に許可される
・許可ルールのみ:「拒否」のルールは設定できない(ルールにないものは自動的に拒否)
・インスタンス単位:1つのインスタンスに複数のセキュリティグループを適用可能
# インバウンドルール(受信許可)設定例(Webサーバー用セキュリティグループ) # ルール1: HTTP (TCP 80) 送信元: 0.0.0.0/0 インターネットからのアクセスを許可 # ルール2: HTTPS (TCP 443) 送信元: 0.0.0.0/0 HTTPS接続を許可 # ルール3: SSH (TCP 22) 送信元: 203.0.113.50/32 自分のPCのIPアドレスのみ許可 # アウトバウンドルール(送信許可) # デフォルト: すべてのアウトバウンドを許可(0.0.0.0/0) # ステートフルのため、インバウンドで受けた通信のレスポンスは別途許可不要
2. ネットワークACL(NACL)
ネットワークACLはサブネット全体に適用するファイアウォールです。セキュリティグループとの違いをまとめます。
| 比較項目 | セキュリティグループ | ネットワークACL |
|---|---|---|
| 適用対象 | インスタンス単位 | サブネット単位 |
| ステート | ステートフル | ステートレス |
| 拒否ルール | 設定不可 | 設定可能 |
| ルールの評価 | 全ルールをまとめて評価 | 番号順に評価(マッチしたら終了) |
| デフォルト動作 | 全拒否(許可ルールのみ追加) | 全許可(デフォルトNACL) |
実務上はセキュリティグループを主軸に設計し、NACLは追加の防御層として使うケースが多いです。
特定のIPアドレスを明示的にブロックしたい場合(DDoS対策など)は、NACLの「拒否ルール」が有効です。
NACLはステートレスのため、インバウンドを許可した通信のレスポンスについても
アウトバウンドのルールで明示的に許可する必要があります。
エフェメラルポート(一般的に1024~65535)を戻り通信用に開ける必要がある点に注意してください。
3. セキュリティグループの設計ベストプラクティス
セキュリティグループを設計する際の実践的なポイントをまとめます。
・最小権限の原則:必要なポートのみ、必要なソースIPのみを許可する
・SSH(22番ポート)は必ず特定のIPに制限:0.0.0.0/0(全世界)に開放しない
・セキュリティグループ間参照を使う:WebサーバーからDBサーバーへのアクセスは、IPではなくWebサーバー用SGを送信元に指定する
・名前・説明文を丁寧に書く:後から見てどのリソース用か分かるように管理する
# DBサーバーのセキュリティグループのインバウンドルール # ポート: MySQL/Aurora (TCP 3306) # 送信元: sg-0123456789abcdef0 (Webサーバー用セキュリティグループのID) # →「WebサーバーのSGがアタッチされたインスタンスからのみ3306番を許可」という意味 # IPアドレスではなくSGを参照することで、インスタンスのIP変動に依存しない柔軟な設計になる
4. 3層アーキテクチャでのセキュリティグループ連携設計
3層アーキテクチャ(ALB・EC2・RDS)のセキュリティグループ設計の核心は、送信元をIPアドレスではなくセキュリティグループIDで指定する点です。
これにより、EC2インスタンスのIPが動的に変わってもルールが自動で追従します。
# ステップ1: ALB-SG(インターネットからHTTPS/HTTPを受け付ける) $ aws ec2 create-security-group --group-name alb-sg --description "SG for ALB web layer" --vpc-id vpc-0a1b2c3d4e5f60001 # 実行結果: {"GroupId": "sg-0alb1234567890ef0"} # インターネットからHTTPS(443)とHTTP(80)を許可する $ aws ec2 authorize-security-group-ingress --group-id sg-0alb1234567890ef0 --protocol tcp --port 443 --cidr 0.0.0.0/0 $ aws ec2 authorize-security-group-ingress --group-id sg-0alb1234567890ef0 --protocol tcp --port 80 --cidr 0.0.0.0/0 # ステップ2: EC2-SG(ALB-SGからのみアプリポートを受け付ける) $ aws ec2 create-security-group --group-name ec2-app-sg --description "SG for EC2 app layer" --vpc-id vpc-0a1b2c3d4e5f60001 # 実行結果: {"GroupId": "sg-0ec21234567890fg0"} # --source-group でSGIDを指定(IPアドレスではなくSG参照) $ aws ec2 authorize-security-group-ingress --group-id sg-0ec21234567890fg0 --protocol tcp --port 8080 --source-group sg-0alb1234567890ef0 # → EC2へのアクセスはALBを経由したものだけに限定される # ステップ3: RDS-SG(EC2-SGからのみDBポートを受け付ける) $ aws ec2 create-security-group --group-name rds-db-sg --description "SG for RDS DB layer" --vpc-id vpc-0a1b2c3d4e5f60001 # 実行結果: {"GroupId": "sg-0rds1234567890gh0"} $ aws ec2 authorize-security-group-ingress --group-id sg-0rds1234567890gh0 --protocol tcp --port 3306 --source-group sg-0ec21234567890fg0 # ルール確認(IpRangesが空でUserIdGroupPairsのみが正しい状態) $ aws ec2 describe-security-groups --group-ids sg-0rds1234567890gh0 --query 'SecurityGroups[0].IpPermissions'
このSG連携設計(ALB-SG→EC2-SG→RDS-SGのチェーン)により、
・EC2へはALBを経由した通信だけが届く(直接攻撃をALB手前でブロック)
・RDSへはEC2からの通信だけが届く(EC2が侵害されてもRDSへは直接到達できない)
という2段階の防御が自動的に機能します。
NATゲートウェイの役割と設計パターン
プライベートサブネット内のEC2インスタンスは、直接インターネットと通信できません。
しかしソフトウェアの更新(yum/apt)や外部APIへのアクセスなど、インターネットへのアウトバウンド通信が必要な場面は多くあります。
このときに使うのが「NATゲートウェイ(NAT Gateway)」です。
1. NATゲートウェイの仕組み
NAT(Network Address Translation)ゲートウェイは、プライベートIPアドレスをパブリックIPアドレスに変換して
インターネットとの通信を中継するサービスです。
# 通信フロー(プライベートサブネットからインターネットへ) # プライベートサブネット内のEC2(10.0.11.10) # → NATゲートウェイ(パブリックサブネット・10.0.1.x) # → インターネットゲートウェイ(IGW) # → インターネット(yumリポジトリ等) # 設定のポイント: # 1. NATゲートウェイ自体はパブリックサブネットに置く(必須) # 2. NATゲートウェイにはElastic IP(固定パブリックIP)を割り当てる # 3. プライベートサブネットのルートテーブルに追加するルート: # 送信先: 0.0.0.0/0 → ターゲット: nat-xxxxxxxxxxxxxxxxx
2. NATゲートウェイの設定手順(概要)
AWSコンソールでの設定手順を示します。
# Step 1: Elastic IPを取得する # VPCコンソール → Elastic IP → 新しいElastic IPの割り当て # Step 2: NATゲートウェイを作成する # VPCコンソール → NATゲートウェイ → NATゲートウェイを作成 # - サブネット: パブリックサブネットを選択(プライベートサブネットではない) # - 接続タイプ: パブリック # - Elastic IP割り当てID: Step1で取得したEIPを選択 # Step 3: プライベートサブネットのルートテーブルを更新する # VPCコンソール → ルートテーブル → プライベートサブネット用ルートテーブルを選択 # → ルートを編集 → ルートの追加 # 送信先: 0.0.0.0/0 / ターゲット: NATゲートウェイ(nat-xxxxxxxxxxxxxxxxx)
3. NATゲートウェイのコスト注意点
NATゲートウェイは有料サービスであり、時間課金(約0.062 USD/時間)+データ転送量課金がかかります。
開発環境や検証環境では、コスト削減のためにNATゲートウェイの代わりに
「NATインスタンス(EC2インスタンスでNATを行う)」を使う選択肢もあります。
ただし可用性・運用負荷を考えると、本番環境ではNATゲートウェイを使うことを強くすすめます。
また、検証が終わったらNATゲートウェイを削除し、Elastic IPも解放しておくことも忘れずに。
削除し忘れると課金が続くため注意してください。
マルチAZ構成で可用性を高める
AWSではデータセンターのような物理施設を「アベイラビリティゾーン(AZ)」と呼びます。
東京リージョン(ap-northeast-1)には複数のAZがあり、それぞれが物理的に独立した電源・ネットワークを持ちます。
マルチAZ構成とは、複数のAZにサブネット・リソースを分散配置することで、
1つのAZに障害が発生しても残りのAZでシステムを継続稼動させる設計です。
1. マルチAZ構成の基本パターン
典型的なWebアプリケーションのマルチAZ構成を示します。
| レイヤー | AZ-1a(東京) | AZ-1c(東京) |
|---|---|---|
| パブリックサブネット | 10.0.1.0/24(ALB・踏み台) | 10.0.2.0/24(ALB・NATゲートウェイ) |
| プライベートサブネット(Web) | 10.0.11.0/24(EC2 Webサーバー) | 10.0.12.0/24(EC2 Webサーバー) |
| プライベートサブネット(DB) | 10.0.21.0/24(RDSプライマリ) | 10.0.22.0/24(RDSスタンバイ) |
この構成のポイントは次の通りです。
・ALB(Application Load Balancer)を2つのAZのパブリックサブネットに配置し、Webサーバーへのトラフィックを分散
・Webサーバーは2つのAZのプライベートサブネットに配置(ALB配下でオートスケーリング対応)
・RDSはマルチAZで配置し、プライマリに障害発生時にスタンバイへ自動フェールオーバー
なお、NATゲートウェイは各AZに1つずつ配置するのがベストプラクティスです。
1つのAZにNATゲートウェイが集中すると、そのAZに障害が起きたときに
他のAZからのインターネットアウトバウンド通信も止まってしまいます。
2. ALBのマルチAZ設定
ALBはインターネット向け(internet-facing)で作成し、2つのパブリックサブネットを指定します。
ALBは指定したサブネットのAZに自動でノードを配置し、マルチAZのリクエスト分散を行います。
# ALBを作成する(2つのパブリックサブネットを指定) $ aws elbv2 create-load-balancer --name web-prod-alb --subnets subnet-0pub1a234567890ab subnet-0pub1c234567890cd --security-groups sg-0alb1234567890ef0 --scheme internet-facing --type application # 実行結果(抜粋) { "LoadBalancers": [{ "DNSName": "web-prod-alb-1234567890.ap-northeast-1.elb.amazonaws.com", "AvailabilityZones": [ {"ZoneName": "ap-northeast-1a", "SubnetId": "subnet-0pub1a234567890ab"}, {"ZoneName": "ap-northeast-1c", "SubnetId": "subnet-0pub1c234567890cd"} ], "State": {"Code": "active"} }] } # ターゲットグループを作成する(EC2のアプリポート8080に転送) $ aws elbv2 create-target-group --name web-prod-tg --protocol HTTP --port 8080 --vpc-id vpc-0a1b2c3d4e5f60001 --health-check-path /health --health-check-interval-seconds 30
ヘルスチェックパス(/health)はEC2アプリが200を返すエンドポイントを指定します。
ここで200が返らないとALBはそのEC2をターゲットから除外するため、アプリ起動後に必ず疎通確認を行ってください。
ALBのDNS名(web-prod-alb-xxx.ap-northeast-1.elb.amazonaws.com)がアプリのエンドポイントになるので、
Route 53でカスタムドメインをAliasレコードでこのDNS名に向けるのが定石です。
3. Auto ScalingグループによるEC2のマルチAZ分散
EC2をマルチAZに分散するには、Auto ScalingグループにVPCゾーンID(プライベートサブネット)を複数指定します。
# Auto Scalingグループを作成する(2つのプライベートサブネットを指定) $ aws autoscaling create-auto-scaling-group --auto-scaling-group-name web-prod-asg --launch-template LaunchTemplateId=lt-0abcdef1234567890,Version='$Latest' --min-size 2 --max-size 6 --desired-capacity 2 --vpc-zone-identifier "subnet-0app1a234567890ef,subnet-0app1c234567890gh" --target-group-arns arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/web-prod-tg/abc --health-check-type ELB --health-check-grace-period 60 # EC2インスタンスが2つのAZに分散されているか確認する $ aws ec2 describe-instances --filters "Name=tag:aws:autoscaling:groupName,Values=web-prod-asg" --query 'Reservations[*].Instances[*].{ID:InstanceId,AZ:Placement.AvailabilityZone,State:State.Name}' --output table # 実行結果 # +-------------------+------------------+------------------+ # | AZ | ID | State | # +-------------------+------------------+------------------+ # | ap-northeast-1a | i-0abc1234ef56 | running | # | ap-northeast-1c | i-0def5678gh90 | running | # +-------------------+------------------+------------------+
--min-size 2 にすることで、ASGは常に2台以上のEC2を維持します。
2台が異なるAZに分散されるため、1AZが落ちても残りのAZのEC2がリクエストを引き続き処理できます。
4. 踏み台サーバー(Bastion Host)の配置
プライベートサブネット内のサーバーにSSHで接続する際は、「踏み台サーバー」を経由する方法が一般的です。
踏み台サーバーはパブリックサブネットに配置し、自分のPCから踏み台にSSH、踏み台からプライベートサブネット内のサーバーにSSHという2段階の接続を行います。
# 踏み台サーバー経由でプライベートEC2に接続するSSH設定例(~/.ssh/config) Host bastion HostName 203.0.113.10 # 踏み台サーバーのElastic IP User ec2-user IdentityFile ~/.ssh/mykey.pem Host private-web HostName 10.0.11.20 # プライベートサブネット内のEC2プライベートIP User ec2-user IdentityFile ~/.ssh/mykey.pem ProxyJump bastion # 使い方(自分のPCから実行) # $ ssh private-web # → 踏み台(203.0.113.10)を経由してプライベートEC2(10.0.11.20)に接続される
踏み台サーバーのセキュリティグループは、
SSH(22番ポート)のインバウンドを「自分のPCのIPアドレス/32」のみに制限してください。
0.0.0.0/0に開放することは絶対に避けてください。
なお近年は踏み台サーバーを使わずに、AWS Systems Manager Session Managerを使ってSSHなしでEC2に接続する方法も増えています。SSHポートの開放が不要なため、セキュリティをさらに高めたい場合は検討してください。
VPCフローログで通信を可視化・監査する
VPCを設計して動かし始めたとき、「セキュリティグループは意図通りに動いているか」「プライベートサブネットから想定外の通信が出ていないか」を確認する手段が必要です。
そのために使うのが「VPCフローログ(VPC Flow Logs)」です。
VPCフローログは、VPC内のネットワークインターフェース(ENI: Elastic Network Interface)を通過するIPトラフィックのACCEPT/REJECTを記録するサービスです。
実際のパケットの中身(ペイロード)は記録されませんが、送信元IP・宛先IP・ポート・プロトコル・通信量・判定結果が記録されます。
フローログは「問題が起きてから有効にしても遅い」サービスです。
本番環境ではVPC設計の段階でフローログの出力先を決めておくことを強くすすめます。
1. フローログが記録する主要フィールド
フローログのレコードには以下のフィールドが含まれます。
| フィールド | 内容 | 主な活用場面 |
|---|---|---|
| srcaddr | 送信元IPアドレス | 不審なアクセス元の特定 |
| dstaddr | 宛先IPアドレス | 想定外の通信先の検出 |
| srcport / dstport | 送信元・宛先ポート | 使用サービス・プロトコルの確認 |
| protocol | プロトコル番号(6=TCP、17=UDP、1=ICMP) | 通信種別の把握 |
| action | ACCEPT または REJECT | セキュリティグループ・NACLの動作確認 |
| bytes | 転送バイト数 | 異常な大量転送の検出 |
| log-status | OK / NODATA / SKIPDATA | ログ欠損の確認 |
2. 出力先の設計(S3 vs CloudWatch Logs)
フローログの出力先は「S3」か「CloudWatch Logs」のどちらかを選択します。
| 観点 | S3出力 | CloudWatch Logs出力 |
|---|---|---|
| コスト | 低い(長期保存向き) | 高い(リアルタイム向き) |
| 分析ツール | Athena・QuickSight | CloudWatch Insights |
| リアルタイム性 | 約5分の遅延 | 約2分の遅延 |
| アラート連携 | Lambda経由で可能 | Metric Filter + Alarmで直接可能 |
| 推奨ユースケース | 監査・コスト分析・長期保存 | リアルタイム異常検知 |
長期保存・コスト効率を重視するならS3(Parquet形式)、
REJECTスパイクをリアルタイムで検知したいならCloudWatch Logs+Metric Filter+Alarmが定石です。
本番環境では両方を組み合わせるケースもあります。
3. フローログレコードの読み方
実際のフローログのレコードはスペース区切りで記録されます。
# フローログレコードの例(v2フォーマット: version account-id interface-id srcaddr dstaddr srcport dstport protocol packets bytes start end action log-status) # 203.0.113.45からポート443へのHTTPS通信(ACCEPT) 2 123456789012 eni-0a1b2c3d4e5f 203.0.113.45 10.0.1.50 52341 443 6 15 1823 1688000000 1688000059 ACCEPT OK # 198.51.100.22からSSH(22番ポート)への接続試行(REJECT) # → SGでSSHの送信元IPが制限されているため弾かれた 2 123456789012 eni-0a1b2c3d4e5f 198.51.100.22 10.0.1.50 54321 22 6 3 180 1688000000 1688000059 REJECT OK # EC2(10.0.1.50)からRDS(10.0.2.30)の3306番への通信(ACCEPT) # → プライベートサブネット間の正常な通信 2 123456789012 eni-0a1b2c3d4e5f 10.0.1.50 10.0.2.30 49152 3306 6 8 1024 1688000000 1688000059 ACCEPT OK
・予期しないREJECT:外部から特定ポートへのREJECTが大量に記録されている場合、攻撃スキャンの可能性あり
・予期しないACCEPT:本来ブロックすべき通信がACCEPTされていた場合、SGの設定漏れを疑う
・NACLとSGの切り分け:NACLがREJECTした通信はSGに到達する前に遮断される。どちらが遮断したかをフローログのactionフィールドで確認できる
VPC設計でよくあるトラブルと対処法
aws vpc 設計を初めて行う際に、実際によく直面するトラブルと対処法をまとめます。
フローログが有効になっていれば、切り分け作業がより確実に進められます。
1. プライベートサブネットのEC2がインターネットに繋がらない
最も多いトラブルです。次の順番で確認してください。
# チェックリスト(上から順に確認する) # 1. NATゲートウェイが「利用可能」状態か確認 # AWSコンソール → VPC → NATゲートウェイ → ステータスを確認 # 2. プライベートサブネットのルートテーブルを確認 # 「0.0.0.0/0 → nat-xxxxxxxxxx」のルートが存在するか確認 # 3. NATゲートウェイがパブリックサブネットに配置されているか確認 # NATゲートウェイがプライベートサブネットに作られているとアウトバウンドが通らない # 4. EC2からpingで確認 # $ ping 8.8.8.8 # → 到達できれば外部通信OK。できなければルートテーブルかセキュリティグループを再確認 # 5. フローログが有効な場合:プライベートサブネットのENIのフローログを確認 # EC2からNATゲートウェイへの通信がACCEPTされているか確認する
2. セキュリティグループの設定後もSSHで繋がらない
セキュリティグループでSSHを許可したのに接続できない場合、よくある原因は以下です。
・自分のPCのグローバルIPアドレスが変わっている:プロバイダの都合でIPが変動することがある。現在のIPを確認して再設定する
・踏み台サーバー経由が必要なのに直接接続しようとしている:プライベートサブネットのEC2にはパブリックIPがない
・キーペアが間違っている:EC2作成時に指定したキーペア(.pemファイル)と一致しているか確認する
# 自分のPCの現在のグローバルIPアドレスを確認する $ curl -s https://checkip.amazonaws.com 203.0.113.50 # このIPアドレスをセキュリティグループのインバウンドルールに設定する # 送信元: 203.0.113.50/32 ポート: SSH(22) # フローログが有効な場合:踏み台サーバーのENIのフローログで # 自分のIPからSSH(22番)への通信がREJECTされていないか確認する
3. RDSにアプリケーションから繋がらない
アプリケーションサーバーからRDSへの接続が失敗する場合は、次の点を確認してください。
・RDSのセキュリティグループ:アプリケーションサーバー用SGを送信元として3306番(MySQL)や5432番(PostgreSQL)が許可されているか
・サブネットグループ:RDSのサブネットグループにアプリケーションサーバーと同じVPCのサブネットが含まれているか
「セキュリティグループは設定した」という場合でも、送信元をIPではなくSGで指定しているかを確認してください。
間違えてアプリサーバーのIPを直接指定すると、IPが変動したときに接続が切れます。
フローログを使って切り分ける場合は次の手順が有効です。
・ステップ1:EC2のENIのフローログを確認。RDSの3306番への通信がREJECTされているか確認する
・ステップ2:RDSのENIのフローログを確認。EC2からのパケットがRDS側のENIに到達しているか確認する
・ステップ3:NACLがステートレスのため、アウトバウンドのエフェメラルポート(1024~65535)の許可漏れも疑う
# RDS-SGの設定確認(UserIdGroupPairsにEC2-SGのみが入っているか) $ aws ec2 describe-security-groups --group-ids sg-0rds1234567890gh0 --query 'SecurityGroups[0].IpPermissions[?FromPort==`3306`]' # RDSのエンドポイントDNS名を名前解決して疎通確認する $ nslookup web-prod-mysql.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com # プライベートIP(10.0.21.x)が返れば名前解決は正常 # 失敗する場合はVPCのenableDnsHostnames/enableDnsSupportを確認する $ aws ec2 describe-vpc-attribute --vpc-id vpc-0a1b2c3d4e5f60001 --attribute enableDnsHostnames
4. ALBのヘルスチェックが失敗してEC2がunhealthyになる
ALBのターゲットグループでEC2が「unhealthy」になる場合、次の順で確認してください。
# 確認1: EC2-SGでALB-SGからのアクセスが許可されているか $ aws ec2 describe-security-groups --group-ids sg-0ec21234567890fg0 --query 'SecurityGroups[0].IpPermissions' # ポート8080・送信元sg-0alb1234567890ef0 のルールが存在するか確認する # 確認2: EC2上でアプリが指定ポートでlistenしているか # (AWS Systems Manager Session Managerでポート確認) $ ss -tlnp | grep 8080 # 正常時の出力例: # LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=1234,fd=42)) # 何も返らない場合はアプリが起動していないか別ポートでlistenしている # 確認3: ヘルスチェックパスが200を返すか $ curl -o /dev/null -s -w "%{http_code}" http://localhost:8080/health # 200が返れば正常。200以外ならアプリのヘルスチェック実装を確認する
まとめ:VPC設計のベストプラクティス
この記事で解説したaws vpc 設計のポイントをまとめます。
| 設計項目 | ベストプラクティス |
|---|---|
| CIDRブロック | 余裕を持って 10.0.0.0/16 を選択。後から変更は困難。/24より小さいサブネットは避ける |
| サブネット分割 | パブリック(ALB)・アプリプライベート(EC2)・DBプライベート(RDS)の3層に分ける |
| ALB | パブリックサブネットのマルチAZに配置。ターゲットグループのヘルスチェックを設定する |
| セキュリティグループ | ALB-SG→EC2-SG→RDS-SGのSG参照チェーンで3層防御。SSHは特定IPのみ |
| ネットワークACL | セキュリティグループを主軸に。明示的なブロックが必要な場合のみ活用 |
| NATゲートウェイ | パブリックサブネットに配置。本番はAZ数分用意する。不要時は削除 |
| マルチAZ | 本番環境は必ず複数AZにサブネットを分散。RDS・NATゲートウェイも各AZに |
| 踏み台サーバー | パブリックサブネットに配置。SSHアクセスは自分のPCのIPに限定 |
| VPCフローログ | VPC設計の段階で有効化先を決める。S3(長期保存)かCloudWatch Logs(リアルタイム)を選択 |
| VPC DNS設定 | enableDnsHostnames/enableDnsSupportを有効にする(RDSエンドポイント名解決に必須) |
VPC設計は「最初に正しく作ると後が楽」なインフラの基盤です。
この記事の構成を参考に、まずは検証環境で一度手を動かしてみてください。
一度作ってみると、各コンポーネントの関係がぐっと分かりやすくなります。
より複雑な冗長設計(マルチリージョン・VPC Peering等)は
AWS冗長設計入門で解説しています。あわせてご覧ください。
Linux無料マニュアルを受け取る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 次のページへ:AWS Auto ScalingとELBで作る高可用性アーキテクチャ|EC2自動スケーリングとロードバランサーの設計パターン入門
- 前のページへ:AWSでマルチAZ冗長構成を設計する方法|止まらないシステムの作り方入門
- この記事の属するカテゴリ:AWS(Amazon Linux)へ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます