こういう迷いは、AWS入門の最初の壁でもある。
VPCのサブネット設計は、AWSインフラのセキュリティと可用性を決める土台だ。ここの判断が曖昧なままだと、意図せずRDSがインターネットに露出したり、プライベートサブネット内のEC2からパッケージ更新ができなくなったりと、後から直すコストが高い問題に発展する。
この記事では、VPCのパブリック・プライベートサブネット分離の設計思想を整理し、AWS CLIを使った実装手順を段階的に解説する。CIDRブロックの設計から、インターネットゲートウェイ・NATゲートウェイの配置、ルートテーブルの設定、マルチAZ展開のパターンまで、実務判断の根拠を示しながら説明していく。
この記事のポイント
・ルートテーブルの0.0.0.0/0がIGWを向いているかどうかで「パブリック」かが決まる
・プライベートサブネットのアウトバウンドはNATゲートウェイ経由で確保する
・NATゲートウェイ自体はパブリックサブネットに配置する(IGWが必要なため)
・aws ec2 create-subnetとルートテーブル設定でCLIから段階的に構成できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜVPCのサブネット設計が重要なのか
VPCはAWSが提供する仮想ネットワーク空間で、その内部にサブネットを切り、EC2・RDS・ElastiCacheなどのリソースを配置する。「どのサブネットにどのリソースを置くか」という判断が、そのままセキュリティアーキテクチャの境界線を決める。セキュリティグループやNetwork ACLで細かくアクセス制御を行うとしても、サブネット設計の誤りは根本的な防御の穴になる。具体的な問題例を挙げると:
・RDSインスタンスをパブリックサブネットに置くと、インターネットからDBポート(3306等)への直接接続を試みられるリスクが生まれる
・プライベートサブネット内のEC2にNATゲートウェイへのルートがないと、yum / apt によるパッケージ更新ができず、セキュリティパッチ適用に支障をきたす
・NATゲートウェイをプライベートサブネットに誤って配置すると、インターネットへの通信経路が成立せず「pingは通るのに外には出られない」という状況になる
正しく設計されたVPCは「外に見せるもの(パブリック)」と「内部に閉じるもの(プライベート)」を明確に分け、各リソースが必要最小限の通信経路だけを持つ構成になっている。
AWSのLinux実務を体系的に学びたい場合は、AWS(Amazon Linux)の基礎から実践まで解説した入門ガイドも参照してほしい。
VPCとサブネットの基本概念
1. VPCとCIDRブロックの決め方
VPCには/16~/28のCIDRブロックを割り当てる(RFC 1918のプライベートアドレス帯を使用)。実務では10.0.0.0/16が最もよく使われる理由は、拡張性にある。/16だとIPアドレスが65,536個あり、/24のサブネット(256アドレス)を最大256個切り出せる。RDSやALBはENI(Elastic Network Interface)を複数消費するため、サブネットは/24以上の広さを推奨する。/28(16アドレス)では可用性を確保しながら稼働させるリソースの数が制限される。
CIDRを決める際の追加確認ポイント:
・オンプレミスとVPNやDirect Connectで繋ぐ予定があるか → アドレス帯が重複しないよう注意
・複数VPCをピアリングする予定があるか → 全VPCで重複しないCIDRを事前に確保する
・VPCのCIDRは後から変更できない(追加は可能)。設計段階で十分な広さを確保すること
2. パブリックサブネットとプライベートサブネット
AWSでは「パブリックサブネット」「プライベートサブネット」という区別は、コンソールのスイッチではなく、そのサブネットに関連付けたルートテーブルの内容で決まる。・ルートテーブルに「0.0.0.0/0 → インターネットゲートウェイ(IGW)」がある → パブリックサブネット
・ルートテーブルに「0.0.0.0/0 → NATゲートウェイ」がある → プライベートサブネット(アウトバウンドのみ)
・0.0.0.0/0のルートが存在しない → インターネット通信不可
この仕組みを理解していないと、「プライベートと思っていたサブネットが実はパブリックだった」という見落としが起きる。
用途別の配置目安:
・パブリックサブネット: ALB(アプリケーションロードバランサー)、踏み台EC2、NATゲートウェイ
・プライベートサブネット: アプリケーションEC2、RDS、ElastiCache、ECS Fargateタスク
3. ルートテーブルの役割
ルートテーブルは「宛先IPアドレス範囲 → 転送先ゲートウェイ」を定義するテーブルだ。各サブネットに1つのルートテーブルを関連付ける。VPC作成時にメインルートテーブルが自動生成され全サブネットに適用されるが、サブネットごとに個別のルートテーブルを割り当てることで上書きできる。パブリックサブネット用ルートテーブルの構成例:
| 宛先 | ターゲット | 用途 |
|---|---|---|
| 10.0.0.0/16 | local | VPC内の通信(自動追加) |
| 0.0.0.0/0 | igw-xxxxxxxxxx | インターネット向け通信 |
| 宛先 | ターゲット | 用途 |
|---|---|---|
| 10.0.0.0/16 | local | VPC内の通信(自動追加) |
| 0.0.0.0/0 | nat-xxxxxxxxxx | アウトバウンドのみ許可 |
CLIでサブネット構成を実装する
以下の手順は Amazon Linux 2023 / Ubuntu 24.04 LTS で動作確認している。前提としてaws configure でアクセスキーと東京リージョン(ap-northeast-1)が設定済みであること。1. VPCを作成する
$ aws ec2 create-vpc \ --cidr-block 10.0.0.0/16 \ --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=prod-vpc}]' # 実行結果 { "Vpc": { "CidrBlock": "10.0.0.0/16", "State": "pending", "VpcId": "vpc-0a1b2c3d4e5f60001", "IsDefault": false } }
$ VPC_ID=vpc-0a1b2c3d4e5f60001
2. サブネットを作成する
パブリックサブネット(ap-northeast-1a)を作成する:$ aws ec2 create-subnet \ --vpc-id $VPC_ID \ --cidr-block 10.0.1.0/24 \ --availability-zone ap-northeast-1a \ --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=pub-subnet-1a}]' # 実行結果 { "Subnet": { "SubnetId": "subnet-0pub1a234567890ab", "VpcId": "vpc-0a1b2c3d4e5f60001", "CidrBlock": "10.0.1.0/24", "AvailabilityZone": "ap-northeast-1a", "State": "available" } }
$ aws ec2 create-subnet \ --vpc-id $VPC_ID \ --cidr-block 10.0.11.0/24 \ --availability-zone ap-northeast-1a \ --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=prv-subnet-1a}]' # 実行結果 { "Subnet": { "SubnetId": "subnet-0prv1a234567890cd", "VpcId": "vpc-0a1b2c3d4e5f60001", "CidrBlock": "10.0.11.0/24", "AvailabilityZone": "ap-northeast-1a", "State": "available" } }
3. インターネットゲートウェイを設定する
# IGWを作成する $ aws ec2 create-internet-gateway \ --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=prod-igw}]' # 実行結果 { "InternetGateway": { "InternetGatewayId": "igw-0abc12345def67890", "State": "available" } } # 作成したIGWをVPCにアタッチする $ aws ec2 attach-internet-gateway \ --internet-gateway-id igw-0abc12345def67890 \ --vpc-id $VPC_ID # 成功時は何も出力されない(エラーがなければOK)
4. パブリックサブネットのルートテーブルを設定する
# パブリック用ルートテーブルを作成する $ aws ec2 create-route-table \ --vpc-id $VPC_ID \ --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=pub-rtb}]' # 実行結果 { "RouteTable": { "RouteTableId": "rtb-0pub1234567890abc", "VpcId": "vpc-0a1b2c3d4e5f60001" } } # 0.0.0.0/0 をIGWへ向けるルートを追加する $ aws ec2 create-route \ --route-table-id rtb-0pub1234567890abc \ --destination-cidr-block 0.0.0.0/0 \ --gateway-id igw-0abc12345def67890 # 実行結果 { "Return": true } # パブリックサブネットにルートテーブルを関連付ける $ aws ec2 associate-route-table \ --route-table-id rtb-0pub1234567890abc \ --subnet-id subnet-0pub1a234567890ab # 実行結果 { "AssociationId": "rtbassoc-0abc123456789def0", "AssociationState": { "State": "associated" } }
5. NATゲートウェイでプライベートサブネットのアウトバウンドを確保する
プライベートサブネット内のEC2がyumやapt updateでパッケージを取得するには、アウトバウンド(外向き)の通信経路が必要だ。NATゲートウェイはパブリックサブネット上に配置し、Elastic IP(静的グローバルIP)を割り当てる。# Elastic IPを取得する $ aws ec2 allocate-address --domain vpc # 実行結果 { "PublicIp": "54.249.xxx.xxx", "AllocationId": "eipalloc-0abc1234def567890", "Domain": "vpc" } # NATゲートウェイをパブリックサブネット上に作成する $ aws ec2 create-nat-gateway \ --subnet-id subnet-0pub1a234567890ab \ --allocation-id eipalloc-0abc1234def567890 \ --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=prod-nat}]' # 実行結果 { "NatGateway": { "NatGatewayId": "nat-0abc12345def67890a", "State": "pending", "SubnetId": "subnet-0pub1a234567890ab" } }
$ aws ec2 describe-nat-gateways \ --filter Name=nat-gateway-id,Values=nat-0abc12345def67890a \ --query 'NatGateways[0].State' # availableになったら次へ "available"
# プライベート用ルートテーブルを作成する $ aws ec2 create-route-table \ --vpc-id $VPC_ID \ --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=prv-rtb}]' { "RouteTable": { "RouteTableId": "rtb-0prv1234567890def" } } # 0.0.0.0/0 をNATゲートウェイへ向けるルートを追加する $ aws ec2 create-route \ --route-table-id rtb-0prv1234567890def \ --destination-cidr-block 0.0.0.0/0 \ --nat-gateway-id nat-0abc12345def67890a { "Return": true } # プライベートサブネットにルートテーブルを関連付ける $ aws ec2 associate-route-table \ --route-table-id rtb-0prv1234567890def \ --subnet-id subnet-0prv1a234567890cd { "AssociationId": "rtbassoc-0def123456789abc0", "AssociationState": { "State": "associated" } }
マルチAZ設計パターン
本番環境では、1つのAZで障害が発生してもサービスが継続できるよう、最低2つのAZにサブネットを分散させる。マルチAZ冗長設計の詳細については、AWSマルチAZ冗長設計の実践ガイドも参照してほしい。CIDRの設計例(2AZ構成):
・pub-subnet-1a: 10.0.1.0/24(ap-northeast-1a)
・pub-subnet-1b: 10.0.2.0/24(ap-northeast-1b)
・prv-subnet-1a: 10.0.11.0/24(ap-northeast-1a)
・prv-subnet-1b: 10.0.12.0/24(ap-northeast-1b)
1b AZ用のサブネットを追加する:
$ aws ec2 create-subnet \ --vpc-id $VPC_ID \ --cidr-block 10.0.2.0/24 \ --availability-zone ap-northeast-1b \ --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=pub-subnet-1b}]' $ aws ec2 create-subnet \ --vpc-id $VPC_ID \ --cidr-block 10.0.12.0/24 \ --availability-zone ap-northeast-1b \ --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=prv-subnet-1b}]'
・コスト優先の場合: 1a AZにのみNATゲートウェイを置き、1b AZのプライベートサブネットも1a NATへルーティングする(1a AZ障害時にプライベートサブネットのアウトバウンドが止まるリスクあり)
・可用性優先の場合(推奨): 各AZにNATゲートウェイを1台ずつ設置し、AZ間の依存を排除する
ALBを2つのパブリックサブネットに展開するには、subnetを両方指定する:
$ aws elbv2 create-load-balancer \ --name prod-alb \ --subnets subnet-0pub1a234567890ab subnet-0pub1b234567890ef \ --security-groups sg-0xxxxxxxxxxxxxxxx \ --type application
よくある設計ミスとトラブルシュート
「プライベートサブネットのEC2がインターネットに出られない」
原因の切り分けは以下の順で確認する:1. NATゲートウェイのStateを確認する
$ aws ec2 describe-nat-gateways \ --filter Name=nat-gateway-id,Values=nat-0abc12345def67890a \ --query 'NatGateways[0].State' "available"
2. ルートテーブルの内容を確認する
$ aws ec2 describe-route-tables \ --filters Name=association.subnet-id,Values=subnet-0prv1a234567890cd \ --query 'RouteTables[0].Routes' # 0.0.0.0/0 のルートが存在し、NatGatewayId が正しいかを確認する
$ aws ec2 describe-nat-gateways \ --filter Name=nat-gateway-id,Values=nat-0abc12345def67890a \ --query 'NatGateways[0].SubnetId' "subnet-0pub1a234567890ab" # パブリックサブネットのIDが返ればOK
「EC2にパブリックIPが自動割り当てされない」
サブネットの「パブリックIPの自動割り当て」設定が無効になっている場合:$ aws ec2 modify-subnet-attribute \ --subnet-id subnet-0pub1a234567890ab \ --map-public-ip-on-launch # 成功時は何も出力されない
まとめ
| やりたいこと | コマンド |
|---|---|
| VPCを作成する | aws ec2 create-vpc --cidr-block 10.0.0.0/16 |
| サブネットを作成する | aws ec2 create-subnet --vpc-id vpc-xxx --cidr-block 10.0.1.0/24 --availability-zone ap-northeast-1a |
| IGWを作成してVPCにアタッチする | aws ec2 attach-internet-gateway --internet-gateway-id igw-xxx --vpc-id vpc-xxx |
| ルートテーブルにIGWルートを追加する | aws ec2 create-route --route-table-id rtb-xxx --destination-cidr-block 0.0.0.0/0 --gateway-id igw-xxx |
| ルートテーブルをサブネットに関連付ける | aws ec2 associate-route-table --route-table-id rtb-xxx --subnet-id subnet-xxx |
| NATゲートウェイを作成する | aws ec2 create-nat-gateway --subnet-id subnet-xxx --allocation-id eipalloc-xxx |
| NATゲートウェイのStateを確認する | aws ec2 describe-nat-gateways --filter Name=nat-gateway-id,Values=nat-xxx --query 'NatGateways[0].State' |
| プライベートサブネットのルートにNATを設定する | aws ec2 create-route --route-table-id rtb-xxx --destination-cidr-block 0.0.0.0/0 --nat-gateway-id nat-xxx |
| パブリックIP自動割り当てを有効にする | aws ec2 modify-subnet-attribute --subnet-id subnet-xxx --map-public-ip-on-launch |
NATゲートウェイの配置サブネットを誤ることと、ルートテーブルのアソシエーションを忘れることが最初のつまずきポイントになりやすい。CLIで手を動かして確認することで、どのリソースがどこにあり何と繋がっているかが確実に把握できる。
サブネット設計の土台が整ったら、次はセキュリティグループとNetwork ACLを組み合わせた2層防御や、VPCエンドポイントを使ったS3へのプライベートアクセス設計へと進んでほしい。
VPCサブネット設計を「実務の型」として身につけませんか?
サブネットの切り方やルートテーブルの設定は、調べれば分かります。でも「なぜこの構成にするのか」「本番環境でマルチAZをどう設計するか」を自分の言葉で説明できますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:AWS Network ACLとセキュリティグループで実装するVPCセキュリティ設計|ステートレス・ステートフルの違いと2層防御の実践パターン
- この記事の属するカテゴリ:AWS(Amazon Linux)へ戻る

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