対象読者: AWSをこれから触る人。 AWS勉強シリーズの6本目で、今回はVPCです。全体の地図は索引記事にあります。
EC2を立てたのに、こういう症状で止まった人向けに書きます。
- 「サーバーは起動しているのに、外からアクセスできない」
- 「サブネットって何個作ればいいのか、そもそも何なのか分からない」
このどちらも、VPCの5つの部品(建物・部屋・玄関・案内板・警備員)のどれかが欠けているか、繋がっていないのが原因です。

VPCの部品をマンションに例えた図(筆者作成)。この記事の全部がこの1枚に入っています。
VPCとは何か
Amazon VPC(Virtual Private Cloud)は、AWSの中に自分専用のネットワークを区切って作るサービスです。EC2やRDSといったサーバー系のサービスは、必ずどこかのVPCの中に置きます。上の図でいうとVPCは「マンションの建物全体」で、その中に部屋(サブネット)を作り、玄関(インターネットゲートウェイ)を付け、案内板(ルートテーブル)で通り道を決め、各部屋のドアに警備員(セキュリティグループ)を立てます。VPC自体の料金は無料です(中に置くEC2やNATゲートウェイには料金がかかります)。
何に使うか
- 外に見せる部分と隠す部分を分ける: Webサーバーは外から見える部屋に、データベースは外から見えない部屋に置く。DBに直接インターネットから届かない構造にする
- 自分のIPアドレスの範囲を決める: 10.0.0.0/16 のように、社内ネットワークと被らない範囲を自分で選ぶ
- 通信の入口・出口を1か所に絞る: 玄関(インターネットゲートウェイ)を通らないと外に出られない、という構造で守る
- オンプレミスや別VPCと繋ぐ: VPN・Direct Connect・VPCピアリングの土台になる(この記事では扱いません)
重要用語
| 用語 | マンションで言うと | 意味 |
|---|---|---|
| VPC | 建物全体 | 自分専用のネットワークの範囲。IPの範囲(CIDR)を1つ持つ |
| サブネット | 部屋 | VPCの中をさらに区切った小さな範囲。1つのアベイラビリティゾーン(データセンター群)に属する |
| インターネットゲートウェイ(IGW) | 外への玄関 | VPCとインターネットを繋ぐ出入口。1つのVPCに1つ |
| ルートテーブル | 案内板 | 「この宛先はどこへ送るか」の表。サブネットごとにどの案内板を見るか決める |
| セキュリティグループ(SG) | 部屋のドアの警備員 | サーバー単位で「どのポートを誰に開けるか」を決める |
| パブリックサブネット | 外に面した部屋 | ルートテーブルにIGW向けの経路がある部屋 |
| プライベートサブネット | 内側の部屋 | IGW向けの経路がない部屋。外から直接届かない |
| CIDR | 部屋番号の範囲 |
10.0.0.0/16 のようなIP範囲の書き方。/16 は約65,000個、/24 は256個 |
仕組みの要点
初心者が一番つまずくのがここです。パブリックとプライベートの違いは、サブネットの種類ではなく、ルートテーブルの中身で決まります。 サブネットを作った時点では全部同じで、そのサブネットが見ている案内板に「0.0.0.0/0(全部の外)→IGW」という行があれば外に出られる部屋、無ければ出られない部屋になります。
実際に、ルートを何も設定していないサブネットが見ているルートテーブルの中身を取ると、こうなっています。
10.0.0.0/16 -> local
「VPCの中同士は通じる」の1行だけで、外への経路がありません。だからこの部屋のサーバーは、起動していてもインターネットから届きません。「サーバーは動いているのに外から繋がらない」の正体の一つがこれです。
もう一つ、警備員(セキュリティグループ)は通したいものだけを書く方式です。何も書かなければ全部止めます。443を開ける行を書いて初めてHTTPSが通ります。
このコードを動かす前提
この記事のコードはPython(boto3)です。動かす方法は2通りあります。
A. AWSアカウント無しで試す(おすすめ): motoというAWSのそっくりさんを使うと、課金もサインアップもなしでPCの中だけで動きます。
python3 -m venv venv
./venv/bin/pip install boto3 moto
from moto import mock_aws
@mock_aws # この中のboto3呼び出しは全部ローカルの偽AWSに行く
def main() -> None:
... # 以下の記事のコードをここに入れる
main()
B. 本物のAWSで動かす: 先にIAMユーザーとアクセスキーを作って aws configure で登録しておく必要があります。手順はIAMの記事にあります。
使い方
「建物→部屋→玄関→案内板→警備員」の順に作ります。Python(boto3)での最小の形です。
import boto3
ec2 = boto3.client("ec2", region_name="ap-northeast-1")
# ① 建物: VPCを作る(IPの範囲を決める)
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
# ② 部屋: サブネットを作る(VPCの範囲の中から切り出す)
sub = ec2.create_subnet(VpcId=vpc, CidrBlock="10.0.1.0/24",
AvailabilityZone="ap-northeast-1a")["Subnet"]["SubnetId"]
# ③ 玄関: インターネットゲートウェイを作ってVPCに付ける
igw = ec2.create_internet_gateway()["InternetGateway"]["InternetGatewayId"]
ec2.attach_internet_gateway(InternetGatewayId=igw, VpcId=vpc)
# ④ 案内板: 「全部の外→玄関」の行を書いて、部屋に紐付ける
rt = ec2.create_route_table(VpcId=vpc)["RouteTable"]["RouteTableId"]
ec2.create_route(RouteTableId=rt, DestinationCidrBlock="0.0.0.0/0", GatewayId=igw)
ec2.associate_route_table(RouteTableId=rt, SubnetId=sub)
# ⑤ 警備員: 443番だけ通す
sg = ec2.create_security_group(GroupName="web", Description="web", VpcId=vpc)["GroupId"]
ec2.authorize_security_group_ingress(
GroupId=sg,
IpPermissions=[{"IpProtocol": "tcp", "FromPort": 443, "ToPort": 443,
"IpRanges": [{"CidrIp": "0.0.0.0/0"}]}])
④まで終わった時点で、この部屋はパブリックサブネットになっています。④をやらなければ、同じ部屋がプライベートサブネットのままです。違いはコード1行(create_route)だけです。
実際に叩くと止まるところ
作り方を間違えたときにどんなエラーが返るかを、5パターン実際に叩きました。エラー文を読めれば、どの部品が悪いか分かります。
| やったこと | 返ってきたエラー | 意味 |
|---|---|---|
| VPCの範囲(10.0.0.0/16)の外にサブネット(10.1.0.0/24)を作る | InvalidSubnet.Range: The CIDR '10.1.0.0/24' is invalid. |
部屋は建物の中にしか作れない |
| 同じ範囲(10.0.1.0/24)のサブネットをもう1つ作る | InvalidSubnet.Conflict: conflicts with another subnet |
部屋番号の範囲は重ねられない |
| 1つのIGWを2つ目のVPCにも付ける | Resource.AlreadyAssociated |
玄関は1つのVPC専用。VPCごとに作る |
| サブネットが残っているVPCを消す | DependencyViolation: has dependencies and cannot be deleted. |
部屋を全部消してからでないと建物は壊せない |
| 存在しないSGにルール追加 | InvalidGroup.NotFound |
IDの打ち間違いや、別リージョンのSGを指している |
エラー名の頭(InvalidSubnet / DependencyViolation)がそのまま原因を言っている。まずエラー名で当たりを付けるのが早い。
サブネットは256個ぶんでも251個しか使えない
10.0.1.0/24 は計算上256個のIPを含みますが、実際に使えるのは251個でした。
10.0.1.0/24 の AvailableIpAddressCount = 251
AWSが各サブネットの先頭4つと末尾1つ、計5個を予約しているためです(ネットワークアドレス、VPCルーター、DNS、将来用、ブロードキャスト)。小さいサブネットを切るときは、この5個ぶん少なく見積もる。/28(16個)だと使えるのは11個しかない。
CIDRの決め方。/16から/28まで
建物(VPC)の広さと部屋(サブネット)の広さは、CIDRの後ろの数字で決まります。数字が小さいほど広い。実測で、VPCに許される範囲は /16〜/28 でした。
create_vpc(CidrBlock="10.0.0.0/8") → InvalidVpc.Range: The CIDR '10.0.0.0/8' is invalid.
create_vpc(CidrBlock="10.0.0.0/29") → InvalidVpc.Range: The CIDR '10.0.0.0/29' is invalid.
| CIDR | 計算上のIP数 | 実際に使える数(実測) | 用途の目安 |
|---|---|---|---|
| /16 | 65,536 | 65,531 | VPC本体。迷ったらこれ |
| /24 | 256 | 251 | サブネット1部屋。入門はこれで足りる |
| /28 | 16 | 11 | 最小。踏み台や小さな検証用 |
使える数が5個少ないのは前の節の通りです。IPの頭は、社内ネットワークと被らないプライベート用の3系統(10.0.0.0/8・172.16.0.0/12・192.168.0.0/16)から選びます。デフォルトVPCが 172.31.0.0/16 を使っているので、自作は 10.0.0.0/16 にしておくと被りません。
サブネットはVPCの範囲の中から切り出すので、入れ子になります。10.0.0.0/16 の中に 10.0.1.0/24、10.0.2.0/24、…と部屋を並べる形で、/16の中には/24が256部屋まで入ります。
デフォルトVPCの中身
AWSアカウントには最初からデフォルトVPCがあります。中身を取るとこうなっています(東京リージョン)。
VPC: 172.31.0.0/16 (IsDefault=True)
サブネット: ap-northeast-1a 172.31.0.0/20 MapPublicIpOnLaunch=True
ap-northeast-1c 172.31.16.0/20 MapPublicIpOnLaunch=True
ap-northeast-1d 172.31.32.0/20 MapPublicIpOnLaunch=True
部屋はAZごとに1つ、全部パブリック(起動したEC2に自動でパブリックIPが付く設定がON)で、案内板には外への行き先(0.0.0.0/0→IGW)が入っています。「EC2を立てたら何も設定していないのに外から繋がった」のはこれが理由です。逆に言うと、デフォルトVPCには「外から見えない部屋」が無いので、DBを置く練習には向きません。自分でVPCを作る意味はここにあります。
案内板は2種類ある。メインとサブネット用
VPCを作った瞬間に、案内板(ルートテーブル)は1枚だけ自動で付いてきます。これがメインルートテーブルで、中身は 10.0.0.0/16 -> local の1行だけ。どの案内板にも紐付けていないサブネットは、自動でこのメインを見ます。
create_vpc直後のルートテーブル: 1枚 (Main=True) 10.0.0.0/16 -> local
「使い方」の④で作ったのはサブネット用のルートテーブルで、それをパブリックにしたい部屋にだけ紐付けました。紐付けなかった部屋はメインを見続けるので、プライベートのままです。メインに0.0.0.0/0→IGWを書くと全部屋がパブリックになるので、メインは触らず、外に出したい部屋にだけ専用の案内板を付けるのが定石です。
プライベートの部屋から外に出る。NATゲートウェイ
外から届かない部屋(プライベート)のサーバーでも、パッケージの更新などで外に「出る」必要はあります。そのための出口がNATゲートウェイです。パブリックの部屋に置いて、プライベートの案内板を「0.0.0.0/0→NAT」にします。
# ⑥ Elastic IP(固定の公開IP)を1つ確保してNATゲートウェイに付ける。NATはパブリックの部屋に置く
eip = ec2.allocate_address(Domain="vpc")
nat = ec2.create_nat_gateway(SubnetId=sub, AllocationId=eip["AllocationId"])["NatGateway"]["NatGatewayId"]
# ⑦ プライベートの部屋用の案内板。「全部の外→NAT」
prv = ec2.create_subnet(VpcId=vpc, CidrBlock="10.0.2.0/24",
AvailabilityZone="ap-northeast-1a")["Subnet"]["SubnetId"]
prt = ec2.create_route_table(VpcId=vpc)["RouteTable"]["RouteTableId"]
ec2.create_route(RouteTableId=prt, DestinationCidrBlock="0.0.0.0/0", NatGatewayId=nat)
ec2.associate_route_table(RouteTableId=prt, SubnetId=prv)
できあがった2枚の案内板を並べると、違いは行き先の1語だけです。
パブリックの案内板: 10.0.0.0/16 -> local, 0.0.0.0/0 -> igw-xxxx
プライベートの案内板: 10.0.0.0/16 -> local, 0.0.0.0/0 -> nat-xxxx
矢印の向きに注意してください。IGWは双方向(外から入れるし、中から出られる)、NATは中から外への一方通行(外から中には入れない)。この違いが「DBは外から見えないが、DB自身はアップデートを取りに行ける」を作っています。NATゲートウェイは時間課金で、作ったまま忘れると月数千円かかります。検証が終わったら消す。
セキュリティグループの後ろにもう1枚。ネットワークACL
部屋のドアの警備員(SG)とは別に、部屋の入口にネットワークACLという門番がいます。作ったVPCの既定のACLを取ると、全部通す設定です。
既定のネットワークACL
入ってくる: 100 allow 0.0.0.0/0 / 32767 deny 0.0.0.0/0
出ていく: 100 allow 0.0.0.0/0 / 32767 deny 0.0.0.0/0
番号の小さい順に評価して、100番で全部許可、最後の32767番(触れない既定行)で全部拒否。つまり既定では素通しで、SGだけが効いている状態です。SGとの違いは3つ。
| セキュリティグループ | ネットワークACL | |
|---|---|---|
| 効く単位 | サーバー(ENI)ごと | サブネットごと |
| 書くもの | 許可だけ | 許可と拒否の両方 |
| 戻りの通信 | 自動で通る(ステートフル) | 戻りも書く必要がある(ステートレス) |
入門の範囲ではACLは触らず、SGだけで組みます。「特定のIPをサブネット丸ごと締め出したい」が出てきたときに、ACLの拒否行を思い出してください。
外と繋ぐ部品の一覧
VPCの外と繋ぐ部品は他にもあります。入門で使うのは上2つだけです。
| 部品 | 繋ぐ相手 | 向き | 料金 |
|---|---|---|---|
| インターネットゲートウェイ | インターネット | 双方向 | 無料 |
| NATゲートウェイ | インターネット | 中→外のみ | 時間+データ量 |
| VPCエンドポイント | S3やDynamoDBなどAWSのサービス | 中→サービス(インターネットを通らない) | ゲートウェイ型(S3/DynamoDB)は無料 |
| VPCピアリング | 別のVPC | 双方向 | データ転送量 |
| VPN / Direct Connect | 会社のネットワーク | 双方向 | 時間+転送量 |
VPCエンドポイントだけ補足すると、プライベートの部屋からS3を読みたいとき、NATを経由してインターネット越しに行くより、エンドポイントでAWS内を直接通した方が速くて安い。S3とDynamoDBのゲートウェイ型は無料なので、NATを立てる前にこれで足りないか確認する価値があります。
似た概念との使い分け
| 迷うところ | 答え |
|---|---|
| セキュリティグループとネットワークACL | SGはサーバー単位の警備員で、通したいものだけ書く(戻りの通信は自動で通る)。ネットワークACLはサブネット単位の門番で、許可と拒否を両方書く。最初はSGだけで足りる |
| パブリックとプライベート、どっちに置く | 外から直接アクセスされるもの(Webサーバー、ロードバランサ)はパブリック。それ以外(DB、内部処理)はプライベート |
| プライベートの中から外に出たいとき | NATゲートウェイをパブリック側に置いて、プライベート側の案内板を「0.0.0.0/0→NAT」にする。NATは有料 |
| VPCは何個作る | 最初は1つで十分。環境(本番/検証)を分けたくなったら分ける |
料金の考え方
VPC・サブネット・ルートテーブル・IGW・SGは無料です。料金がかかるのは、中に置くEC2などのリソースと、NATゲートウェイ(時間課金+データ量課金)、VPN接続、VPCピアリングのデータ転送などです。「VPCを作ったら課金される」ことはありません。初心者がVPC周りで請求を食らう典型はNATゲートウェイの消し忘れなので、そこだけ覚えておいてください。
よくある注意点
- デフォルトVPCがある。 AWSアカウントには最初からデフォルトVPCが1つ用意されていて、何も指定せずEC2を立てるとそこに入ります。学習用途ならそれで動きますが、中身を理解するには自分でVPCを1つ作ってみるのが早いです
- サブネットはアベイラビリティゾーンをまたげない。 冗長化するなら、別のAZにもう1つサブネットを作ります
- IGWを付けただけでは外に出られない。 案内板(ルートテーブル)に0.0.0.0/0→IGWの行を足して、それをサブネットに紐付けるまでがセットです
- 消す順番は作った逆。 NATゲートウェイ→Elastic IPの解放→SG→ルート→IGWのデタッチ→サブネット→VPC。NATは消えるまで数十秒かかり、消える前にEIPを解放しようとすると失敗します。順番を間違えるとDependencyViolationで止まります
まとめ
冒頭の2つの症状に戻ります。
- 「起動しているのに外からアクセスできない」→ ルートテーブルにIGW向けの行があるか、SGにそのポートの許可があるか。この2つで大半は決まります
- 「サブネットが何なのか分からない」→ VPCの中の部屋で、パブリックかプライベートかは案内板(ルートテーブル)の1行で決まる。部屋自体に種類はない
覚えるのは5部品(VPC・サブネット・IGW・ルートテーブル・SG)の関係だけです。上の図に全部入っています。プライベートの部屋から外に出たくなったらNATゲートウェイ、S3に行くだけならVPCエンドポイント、を足します。
次回はLambda(サーバーレス)を予定しています。
参考
- Amazon VPC 公式ドキュメント
- 前回: 【図解】AWS EC2とは
- シリーズ索引: AWSとは何か