AWS VPCのサブネット設計入門|パブリック・プライベート分離とルートテーブルをCLIで実装する方法

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)AWS(Amazon Linux) > AWS VPCのサブネット設計入門|パブリック・プライベート分離とルートテーブルをCLIで実装する方法
「パブリックサブネットとプライベートサブネットを分けないといけないのは分かるが、実際のCIDR設計やルートテーブルの書き方が分からない」「VPCを作ってEC2を置いたが、セキュリティ的に問題がないか不安だ」
こういう迷いは、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から段階的に構成できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜ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 アウトバウンドのみ許可
localルートはVPC作成時に自動的に追加されるため、手動設定が必要なのは「0.0.0.0/0」の向き先だけだ。

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 } }

取得した VpcId を変数に保存しておくと後続の操作が楽になる:

$ 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" } }

続けてプライベートサブネット(ap-northeast-1a)を作成する:

$ 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" } }

CIDRの設計ポイント: パブリックを10.0.1.0/24(1番台)、プライベートを10.0.11.0/24(11番台)と分けておくと一目で区別できる。複数AZに展開する場合は10.0.2.0/24(1b AZパブリック)、10.0.12.0/24(1b AZプライベート)と続けていく。

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" } }

NATゲートウェイはStateがavailableになるまで1分程度かかる。状態を確認してから次の手順へ進む:

$ aws ec2 describe-nat-gateways \ --filter Name=nat-gateway-id,Values=nat-0abc12345def67890a \ --query 'NatGateways[0].State' # availableになったら次へ "available"

availableになったらプライベートサブネット用のルートテーブルを作成してNATゲートウェイへ向ける:

# プライベート用ルートテーブルを作成する $ 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" } }

この時点で、パブリックサブネット内のEC2はIGW経由でインターネットと双方向通信できる。プライベートサブネット内のEC2はNATゲートウェイ経由でアウトバウンドのみ許可され、外部からの直接インバウンド接続は到達しない構成になっている。

マルチ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}]'

マルチAZ時のNATゲートウェイの判断:
・コスト優先の場合: 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"

「deleting」や「failed」になっていれば再作成が必要だ。

2. ルートテーブルの内容を確認する

$ aws ec2 describe-route-tables \ --filters Name=association.subnet-id,Values=subnet-0prv1a234567890cd \ --query 'RouteTables[0].Routes' # 0.0.0.0/0 のルートが存在し、NatGatewayId が正しいかを確認する

3. NATゲートウェイの配置サブネットを確認する

$ aws ec2 describe-nat-gateways \ --filter Name=nat-gateway-id,Values=nat-0abc12345def67890a \ --query 'NatGateways[0].SubnetId' "subnet-0pub1a234567890ab" # パブリックサブネットのIDが返ればOK

プライベートサブネットのIDが返ってきた場合、NATゲートウェイをプライベートサブネットに誤配置している。NATゲートウェイは必ずパブリックサブネットに配置する。

「EC2にパブリックIPが自動割り当てされない」

サブネットの「パブリックIPの自動割り当て」設定が無効になっている場合:

$ aws ec2 modify-subnet-attribute \ --subnet-id subnet-0pub1a234567890ab \ --map-public-ip-on-launch # 成功時は何も出力されない

このコマンド実行後、サブネット内で起動したEC2に自動でパブリックIPが付与されるようになる。

まとめ

やりたいこと コマンド
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
VPCサブネット設計の要点は「ルートテーブルの0.0.0.0/0が何を向いているか」に尽きる。IGWを向いていればパブリック、NATゲートウェイを向いていればプライベート(アウトバウンドのみ)、向き先がなければインターネット通信不可、という3択だ。

NATゲートウェイの配置サブネットを誤ることと、ルートテーブルのアソシエーションを忘れることが最初のつまずきポイントになりやすい。CLIで手を動かして確認することで、どのリソースがどこにあり何と繋がっているかが確実に把握できる。

サブネット設計の土台が整ったら、次はセキュリティグループとNetwork ACLを組み合わせた2層防御や、VPCエンドポイントを使ったS3へのプライベートアクセス設計へと進んでほしい。

VPCサブネット設計を「実務の型」として身につけませんか?

サブネットの切り方やルートテーブルの設定は、調べれば分かります。でも「なぜこの構成にするのか」「本番環境でマルチAZをどう設計するか」を自分の言葉で説明できますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。