🐡
Amazon VPC設計時の要点まとめ&実践
はじめに
普段の構築作業において、VPCやサブネット、ルートテーブルは別チームが既に構築済で、私はそこにEC2インスタンスやLamdba関数、SGを作成しています。インターネットと通信可能になっているため、インスタンスのセットアップ(パッチ適用、ライブラリインストール)も難なく実行できます。便利な環境が容易されている一方で、ネットワークに関する知識に疎く、SGの作成にさえ苦労していました。
今回はVPC設計時に考慮すべき点、必要な設定項目、それに付随するネットワーク知識を簡潔にまとめ、自身が担える構築作業の幅を広げられることを目標とします。最後に、大好きなCloudFormationを用いてネットワークリソースのIaC化を行い、理解を深めたいと思います。
((っ´・ω・c))ウズウズ
学習リソース
下記を参考に執筆しています。
- Amazon VPC とは?
- Application Load Balancer とは?
- Announcing improved VPC networking for AWS Lambda functions
- 【図解AWS】ルートテーブルとは?初心者にもわかりやすく解説!
VPC設計のポイント
- VPCのCIDRブロックは/16~/28のネットマスクを指定する。
- IPアドレスの数としては
個、2^{16}=65536 個となる。2^{4}=16
- IPアドレスの数としては
- IPアドレスはRFC1918で定義されているプライベートIPアドレスの範囲で指定する(推奨)。
- RFC1918とはプライベートIPアドレスの運用方法を標準化するためのドキュメント。
- プライベートIPアドレスの範囲はIANA(Internet Assigned Numbers Authority)という組織によって予約されている。
class range prefix A 10.0.0.0 – 10.255.255.255 10.0.0.0/8 B 172.16.0.0 – 172.31.255.255 172.16.0.0/12 C 192.168.0.0 – 192.168.255.255 192.168.0.0/16
サブネット設計のポイント
- サブネットのCIDRブロックは/16~/28のネットマスクを指定する。
- IPアドレスの数としては
個、2^{16}=65536 個となる。2^{4}=16
- IPアドレスの数としては
- VPCに複数のサブネットを作成する際はCIDRブロックの重複が起こらないよう設計する。
- 2つ以上のAZにサブネットを作成する(AZに跨ってサブネットを作成することは不可)。
- 各サブネットの先頭4つのIPアドレスと最後尾のIPアドレス(計5つ)はAWSによって予約されているため、ユーザが利用することはできない。
- 利用目的別にサブネットを作成する。
- e.g. インターネットとの通信可否によってパブリックサブネットとプライベートサブネットを作成する。
VPC内で起動するAWSリソース特有の制約
- ALB
- ALBがマネージドにスケールアウトするために8個のIPアドレスを空ける。
- VPC Lamdba
- Lamdba関数のVPC設定を有効化すると、Hyperplane ENI(マネージドリソース)が自動的に作成される。
- 1つのHyperplane ENIは最大65000接続をサポートし、接続数がこれを越えた場合はHyperplane ENIがスケールアウトされる。
- 既存のHyperplane ENIは関数間、実行間で再利用されるため、サブネットのIPアドレスが枯渇するリスクが大幅に低減されている。
- Hyperplane ENIはサブネットとSGのペアで作成される。
- あるLambda関数に単一のSG、2つのサブネットを設定した場合、Hyperplane ENIが各サブネットに1つずつ作成される(合計2つ)。
ルートテーブル
- ルートテーブルとは?
- VPC内の通信を振り分けるルールの一覧。
- ルートテーブルはサブネットに関連付けて利用する。
- 1つのサブネットには1つのルートテーブルしか関連付けられない。
- 異なるサブネットに同じルートテーブルを関連付けることは可能。
- ルートテーブルの種類
- メインルートテーブル(デフォルトルートテーブル)
- VPC作成時に自動的に作成される。
- ルートテーブルが関連付けられていないサブネットに自動的に適用される。
- カスタムルートテーブル
- ユーザがカスタマイズするためのルートテーブル
- サブネットルートテーブル
- サブネットに関連付けられたルートテーブル
- ゲートウェイルートテーブル
- IGWまたはVGWに関連付けられたルートテーブル
- Transit Gatewayルートテーブル
- TGWに関連付けられたルートテーブル
- ローカルゲートウェイルートテーブル
- Outpostsローカルゲートウェイに関連付けられたルートテーブル
- メインルートテーブル(デフォルトルートテーブル)
実践
CloudFormationを用いて、VPC、サブネット、ルートテーブル等のネットワークリソースをIaC化する。
設計
-
VPC
- VPCの設計は下表の通り。
VPC Name CIDR Block Number of IPs AZ Explanation MyVpc 10.0.0.0/16 65536 - VPC Peeringやオンプレとの接続は想定しないためブロックサイズを最大の/16とする(IPの重複を考慮しない)。 -
Subnet
- サブネットの設計は下表の通り。
Subnet Name CIDR Block Number of IPs IP ranges AZ Explanation PublicSubnetLB001 10.0.0.0/20 4096 10.0.0.0 - 10.0.15.255 ap-northeast-1a LBを配置する PublicSubnetLB002 10.0.16.0/20 4096 10.0.16.0 - 10.0.31.255 ap-northeast-1c id. PublicSubnetLB003 10.0.32.0/20 4096 10.0.32.0 - 10.0.47.255 ap-northeast-1d id. PrivateSubnetApp001 10.0.48.0/20 4096 10.0.48.0 - 10.0.63.255 ap-northeast-1a アプリケーションリソースを配置する(EC2,ECS,Lamnda等) PrivateSubnetApp002 10.0.64.0/20 4096 10.0.64.0 - 10.0.79.255 ap-northeast-1c id. PrivateSubnetApp003 10.0.80.0/20 4096 10.0.80.0 - 10.0.95.255 ap-northeast-1d id. PrivateSubnetDB001 10.0.96.0/20 4096 10.0.96.0 - 10.0.111.255 ap-northeast-1a DB,ストレージを配置する PrivateSubnetDB002 10.0.112.0/20 4096 10.0.112.0 - 10.0.127.255 ap-northeast-1c id. PrivateSubnetDB003 10.0.128.0/20 4096 10.0.128.0 - 10.0.143.255 ap-northeast-1d id. ※IP数(Number of IPs)はAWS予約IPを含む。
- IP数を整理
- VPCに確保したIP数は65536個。このうちサブネットに割り当てたIPの合計数は
個、割り当たっていないIP数は4096×9=36864 個となる。65536-36864=28672 - 異なるアプローチで検算すると、PrivateSubnetDB003の次のIPは10.0.144.0から始まり、最後はVPC(10.0.0.0/16)末尾のIP(10.0.255.255)で終わることから、
個となる。(256-144)×256=28672
- VPCに確保したIP数は65536個。このうちサブネットに割り当てたIPの合計数は
-
アーキテクチャ
テンプレート
AWSTemplateFormatVersion: 2010-09-09
Description: ""
Parameters:
PjName:
Type: String
Default: zenn
Env:
Type: String
Default: dev
VpcCidrBlock:
Type: String
Default: 10.0.0.0/16
Mappings:
RegionMap:
ap-northeast-1:
az1: ap-northeast-1a
az2: ap-northeast-1c
az3: ap-northeast-1d
Resources:
##############################
### Allow resources in the public subnet to communicate over the Internet
##############################
IGW:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${PjName}-igw-${Env}
# attach an IGW to a VPC
Attachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref IGW
VpcId: !Ref MyVpc
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVpc
Tags:
- Value: Name
Key: !Sub ${PjName}-public-rtb-${Env}
RouteIgw:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref IGW
# Associates a public subnet with a route table
AssociatePublicRouteTable001:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnetLB001
AssociatePublicRouteTable002:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnetLB002
AssociatePublicRouteTable003:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnetLB003
##############################
### Allow resources in the private subnet to communicate over the Internet
##############################
# Allocate an IP address for NATGW
EIP001:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub ${PjName}-eip-natgw-${Env}
Natgw001:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIP001.AllocationId
SubnetId: !Ref PublicSubnetLB001
ConnectivityType: public
MaxDrainDurationSeconds: 350
Tags:
- Key: Name
Value: !Sub ${PjName}-natgw-001
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-private-rtb-${Env}
RouteNatgw:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref Natgw001
AssociatePrivateRouteTable001:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnetApp001
AssociatePrivateRouteTable002:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnetApp002
AssociatePrivateRouteTable003:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnetApp003
##############################
### VPC and Subnet
##############################
MyVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidrBlock
EnableDnsHostnames: true
EnableDnsSupport: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${PjName}-vpc-${Env}
PublicSubnetLB001:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az1 ]
CidrBlock: !Select [0, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-public-subnet-lb-001-${Env}
PublicSubnetLB002:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az2 ]
CidrBlock: !Select [1, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-public-subnet-lb-002-${Env}
PublicSubnetLB003:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az3 ]
CidrBlock: !Select [2, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-public-subnet-lb-003-${Env}
PrivateSubnetApp001:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az1 ]
CidrBlock: !Select [3, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-private-subnet-app-001-${Env}
PrivateSubnetApp002:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az2 ]
CidrBlock: !Select [4, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-private-subnet-app-002-${Env}
PrivateSubnetApp003:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az3 ]
CidrBlock: !Select [5, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-private-subnet-app-003-${Env}
PrivateSubnetDB001:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az1 ]
CidrBlock: !Select [6, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-private-subnet-db-001-${Env}
PrivateSubnetDB002:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az2 ]
CidrBlock: !Select [7, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-private-subnet-db-002-${Env}
PrivateSubnetDB003:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az3 ]
CidrBlock: !Select [8, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
VpcId: !Ref MyVpc
Tags:
- Key: Name
Value: !Sub ${PjName}-private-subnet-db-003-${Env}
個人的スッキリポイント
- VPCを作成するとメインルートテーブルが自動的に作成される。
- サブネットを作成する際、ルートテーブルを明示的に関連付けなかった場合はメインルートテーブルがそのサブネットに自動的に関連付けられる。
- LB向けサブネットは、IGWへのルートをもつルートテーブルと明示的に関連付けている(インターネット通信可能な状態)。
- アプリケーションリソース向けサブネットは、NATGWへのルートをもつルートテーブルと明示的に関連付けている(NATGW経由でインターネット通信可能な状態)。
- DB向けサブネットは明示的にルートテーブルとの関連付けを行っていないため、メインルートテーブルが関連付けられ、インターネットとの通信は不可能になっている。
- メインルートテーブル(下画像)には、ローカルルートと呼ばれるルート情報が含まれる。
- 送信先(通信の宛先IPアドレス)にVPC CIDR、ターゲット(通信の行先)にlocalが指定される。
- つまり、送信先IPがVPC CIDRの範囲に含まれる場合、VPC内部(local)の当該IPアドレスをもつリソースへルーティングされる。
- i.e. ローカルルートはサブネット間の通信を許可している。
- パブリックサブネットのリソースがインターネット通信するには、パブリックIPの付与、IGW、IGWへのルートをもつルートテーブルが必要。
- プライベートサブネットのリソースがインターネット通信するには、NATGW、NATGWへのルートをもつルートテーブルが必要。
- NATGWは異なるサブネットやAZのリソースからも利用できる。
- PublicSubnetLB001にのみNATGWを作成し、すべてのアプリケーションリソース向けサブネット(PrivateSubnetApp001~003)から利用できるよう設定。
- 耐障害性を高めるには、各AZごとにNATGWを作成し、各AZのリソースが同AZのNATGWを経由するようルーティング設定する。
- NATGWはデータ転送量に加え時間単位の課金が発生するため、コストと耐障害性のバランスを考慮する。
さいごに
今回はVPC設計時に必要なAWS/ネットワークの知識を整理し、実際に設計/構築を行いました。その中で「そもそもこの設定って何で必要なんだっけ??」という疑問が出てきました。これら疑問に対する回答を「個人的スッキリポイント」に書き、無事に腑に落ちることができました。これまでモヤモヤしていた部分を解消できたので、清々しい気持ちで執筆を終えられました。(๑˃̵ᴗ˂̵)و ヨシ!
次回もお楽しみに。
Discussion