VPC (Virtual Private Cloud) について学んでいきましょう!
1. 解説動画
※動画の画質が悪い場合は、歯車から720p以上の画質を選択ください
↓構成図と下記の変更が反映されておりません。後日撮影し直します
- 名前変更:Private Subnet → Protected Subnet
- 名前変更:Local Subnet → Private Subnet
- 集約:Public Route Table と Private Route Table を Subnet ごとではなく共通に集約
※複数のシステムを作成できるように、S3ファイル名のパスを vpc.yml → [SystemName]/vpc.yml に変更させてください。まだ動画に反映されておりませんので、ご了承ください🙇♂️
2. 概要
AWSのリソースはVPCの中に作るリソース(=EC2, ELB, RDS, (一部の)Lambda, 他)と、VPCの外に作るリソース(=Route 53, S3, AMI, DynamoDB, CloudFront, (一部の)Lambda, 他)とに分かれます。
VPCはIPアドレスの範囲を定義し、インターネットに公開するか・インターネットに接続できるかも制御できる、ネットワークのリソースです。
AWSアカウントを作ると自動的に1つデフォルトのVPCが作られます。これはあくまでサンプルなので使わず、必要なネットワークを設計して作っていきます。※余談ですが、デフォルトのVPCを使うことでVPCの存在を認知や理解せずに、EC2やRDSを使われている初学者を良く見かけます...。
VPCはネットワークの知識がないと分かりづらい点がありますので、ご不明な点はSlack・Zoomでお尋ねください。
構成図
今回作成するVPCの構成図はこちらです。 ※小さくて見づらいので、右クリックして新しいタブで開いてください。
リソース一覧
リソースの種類 | 目的 | 何ごとに作るか? | 料金 | 備考 |
---|---|---|---|---|
EC2: VPC | 環境ごとにネットワークを分ける「VPC」 | 本番環境で1つ | 一部有料(同一AZ間の通信は無料、異なるAZ間の通信は有料) | S3, CloudFront, DynamoDB, Lambda など VPC外に作るリソースもあるので、VPCの中か外かは常に意識するように。 IPv6は対応しても良いが、面倒なので対応しないことが多い |
EC2: InternetGateway | VPCとインターネットを接続するための「インターネットゲートウェイ(関門、出入り口)」 | VPCごとに1つ | 一部有料(インターネットからEC2は無料、EC2からインターネットは有料) | 作った時以外は存在を忘れてかまわない。VPCにアタッチして使うが、CFnでは「VPCGatewayAttachment」でアタッチする |
EC2: RouteTable, Route | 通信経路を示す表「ルーティングテーブル」 通信経路「ルート(宛先 と ゲートウェイ の組み合わせ)」は複数書ける ※VPCの中で一番の肝! | ・Public Subnet共通で1つ ・Protected Subnetごとに1つ ・Private Subnet共通で1つ | 無料 | NAT Gatewayを複数作る場合に備えて、ProtectedのRoute TebleはSubnetごとに作っておく。Subnetに紐付けて使うが、CFnでは「SubnetRouteTableAssociation」で紐付けする |
EC2: Subnet | VPC内でネットワークをさらに細かく分ける「サブネット」 | Availability Zoneごとに、目的(Public、Protected、Private)ごとに、今回は9つ ※Protectedを作らない場合は6つ | 無料 | ・Public=中から中OK、中から外OK、外から中OK ・Protected=中から中OK、中から外OK、外から中NG ・Private=中から中OK、中から外NG、外から中NG ※中=VPCの内部、外=インターネット |
EC2: EIP (Elastic IP Address) | 固定IPアドレス「EIP」 | 固定IPが必要なリソースごとに | 一部有料 | Public Subnet内のリソースがインターネットと通信するためにはパブリックIPアドレス(グローバルIPアドレス)が必須。固定(EIP)か、自動割り当てを選べる。今回はNATゲートウェイで固定(EIP)を使う |
EC2: NatGateway | Protected Subnetでは必須の「NATゲートウェイ」※NAT=インターネットに出ていく際に、送信元IPアドレスをEIPに変換してくれる装置 | Protected Subnet共通で1つか、AZごとに作るかは任意。ただし料金が高いので注意 | 有料 | セキュリティ的にProtected Subnetは活用したい(現場では毎回作る)が、NAT Gatewayが相対的に高くて、Protectedは使わずPublicでいいんじゃない? という話もある...。NAT用のEC2インスタンスを作ればもっと安いが、それも微妙... |
EC2: VPCEndpoint (Gateway Endpoint) | DynamoDB・S3へPrivateLinkで直接接続するため「VPCエンドポイント」 | VPCごとに1つ | 無料 | DynamoDB・S3へインターネット経由ではなく、直接接続ができる。グローバルIPアドレスがなくてもOK。通信料も無料になる |
命名規則
Classmethodさんの命名規則 https://dev.classmethod.jp/articles/aws-name-rule/ に準拠します。
公式URL
AWS内部では、VPCはEC2リソースの一部として管理されているようです。リファレンスをチェックする際はご注意ください。
- Docs: Amazon VPC とは - Amazon Virtual Private Cloud
- CFn: EC2 リソースタイプのリファレンス - AWS CloudFormation
- CLI: ec2 — AWS CLI v2 Command Reference
3. 解説手順
- CFnでVPC関連のリソースを作成する ※動作確認は単独では難しいので割愛
4. CFnテンプレート
VPC
VPC, Internet Gateway, Route Table(Public, Protected※, Private), Subnet(同左), NAT Gateway※(EIPも), VPC Endpoint(DynamoDB, S3) をまとめて作成します。NAT Gatewayは意外と高いので、※が付いた項目については作成の有無を選択できます。
- [SystemName]=
awsmaster
をお好きなシステム名に置換してください。変えなくてもOKです。
CFn Stack Name: [SystemName]-prod-vpc ←必ずこの命名規則に従ってください S3 File Name: [SystemName]/vpc.yml
---
### [Change System Name] awsmaster
AWSTemplateFormatVersion: "2010-09-09"
Description: Create VPC, Internet Gateway, Subnet, Route Table, VPC Endpoint etc.
Mappings:
EnvironmentMap:
prod:
VPCCidrBlock: 10.0.0.0/19
## SubnetPublicCidrBlock = 10.0.0.0/24 - 10.0.3.0/24 (AZ A+C+D+?)
## SubnetProtectedCidrBlock = 10.0.4.0/24 - 10.0.7.0/24 (AZ A+C+D+?)
## SubnetPrivateCidrBlock = 10.0.8.0/24 - 10.0.11.0/24 (AZ A+C+D+?)
stg:
VPCCidrBlock: 10.0.32.0/19
## SubnetPublicCidrBlock = 10.0.32.0/24 - 10.0.35.0/24 (AZ A+C+D+?)
## SubnetProtectedCidrBlock = 10.0.36.0/24 - 10.0.39.0/24 (AZ A+C+D+?)
## SubnetPrivateCidrBlock = 10.0.40.0/24 - 10.0.43.0/24 (AZ A+C+D+?)
dev:
VPCCidrBlock: 10.0.64.0/19
## SubnetPublicCidrBlock = 10.0.64.0/24 - 10.0.67.0/24 (AZ A+C+D+?)
## SubnetProtectedCidrBlock = 10.0.68.0/24 - 10.0.71.0/24 (AZ A+C+D+?)
## SubnetPrivateCidrBlock = 10.0.72.0/24 - 10.0.75.0/24 (AZ A+C+D+?)
Parameters:
SystemName:
Description: System Name
Type: String
Default: awsmaster ### [Change System Name]
Environment:
Description: Environment
Type: String
Default: prod
AllowedValues:
- prod
- stg
- dev
CreateSubnetProtected:
Description: Create Protected Subnet
Type: String
Default: true
AllowedValues:
- true
- false
CidrBits:
Description: Subnet bits for the CIDR. For example, specifying a value "8" for this parameter will create a CIDR with a mask of "/24".
Type: Number
Default: 8
MinValue: 4
MaxValue: 16
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Environment Configuration"
Parameters:
- SystemName
- Environment
- CreateSubnetProtected
- CidrBits
Conditions:
ShouldCreateSubnetProtected: !Equals [ !Ref CreateSubnetProtected, true ]
Resources:
## EC2(VPC): VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ]
EnableDnsHostnames: true
EnableDnsSupport: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-vpc
## EC2(VPC): Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-igw
## EC2(VPC): VPC Gateway Attachment
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
## EC2(VPC): Route Table (Public)
RouteTablePublic:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-public-rtb
## EC2(VPC): Route (Public) (Default)
RoutePublicDefault:
Type: AWS::EC2::Route
DependsOn: VPCGatewayAttachment
Properties:
RouteTableId: !Ref RouteTablePublic
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
## EC2(VPC): Subnet (Public-A)
SubnetPublicA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [ 0, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
AvailabilityZone: !Sub ${AWS::Region}a
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-public-subnet-a
## EC2(VPC): Subnet (Public-A) Route Table (Public) Association
SubnetRouteTableAssociationPublicA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPublicA
RouteTableId: !Ref RouteTablePublic
## EC2(VPC): Subnet (Public-C)
SubnetPublicC:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [ 1, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
AvailabilityZone: !Sub ${AWS::Region}c
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-public-subnet-c
## EC2(VPC): Subnet (Public-C) Route Table (Public) Association
SubnetRouteTableAssociationPublicC:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPublicC
RouteTableId: !Ref RouteTablePublic
## EC2(VPC): Subnet (Public-D)
SubnetPublicD:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [ 2, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
AvailabilityZone: !Sub ${AWS::Region}d
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-public-subnet-d
## EC2(VPC): Subnet (Public-D) Route Table (Public) Association
SubnetRouteTableAssociationPublicD:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPublicD
RouteTableId: !Ref RouteTablePublic
## EC2(VPC): EIP
EIPNATGatewayA:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::EIP
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-nat-a
## EC2(VPC): NAT Gateway @ Public-A
NATGatewayA:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIPNATGatewayA.AllocationId
SubnetId: !Ref SubnetPublicA
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-nat-a
## EC2(VPC): Route Table (Protected-A)
RouteTableProtectedA:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-protected-rtb-a
## EC2(VPC): Route (Protected-A) (Default)
RouteProtectedADefault:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableProtectedA
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGatewayA
## EC2(VPC): Route Table (Protected-C)
RouteTableProtectedC:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-protected-rtb-c
## EC2(VPC): Route (Protected-C) (Default)
RouteProtectedCDefault:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableProtectedC
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGatewayA
## EC2(VPC): Route Table (Protected-D)
RouteTableProtectedD:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-protected-rtb-d
## EC2(VPC): Route (Protected-D) (Default)
RouteProtectedDDefault:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableProtectedD
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGatewayA
## EC2(VPC): Subnet (Protected-A)
SubnetProtectedA:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [ 4, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
AvailabilityZone: !Sub ${AWS::Region}a
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-protected-subnet-a
## EC2(VPC): Subnet (Protected-A) Route Table (Protected-A) Association
SubnetRouteTableAssociationProtectedA:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetProtectedA
RouteTableId: !Ref RouteTableProtectedA
## EC2(VPC): Subnet (Protected-C)
SubnetProtectedC:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [ 5, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
AvailabilityZone: !Sub ${AWS::Region}c
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-protected-subnet-c
## EC2(VPC): Subnet (Protected-C) Route Table (Protected-C) Association
SubnetRouteTableAssociationProtectedC:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetProtectedC
RouteTableId: !Ref RouteTableProtectedC
## EC2(VPC): Subnet (Protected-D)
SubnetProtectedD:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [ 6, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
AvailabilityZone: !Sub ${AWS::Region}d
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-protected-subnet-d
## EC2(VPC): Subnet (Protected-D) Route Table (Protected-D) Association
SubnetRouteTableAssociationProtectedD:
Condition: ShouldCreateSubnetProtected
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetProtectedD
RouteTableId: !Ref RouteTableProtectedD
## EC2(VPC): Route Table (Private)
RouteTablePrivate:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-private-rtb
## EC2(VPC): Subnet (Private-A)
SubnetPrivateA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [ 8, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
AvailabilityZone: !Sub ${AWS::Region}a
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-private-subnet-a
## EC2(VPC): Subnet (Private-A) Route Table (Private) Association
SubnetRouteTableAssociationPrivateA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivateA
RouteTableId: !Ref RouteTablePrivate
## EC2(VPC): Subnet (Private-C)
SubnetPrivateC:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [ 9, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
AvailabilityZone: !Sub ${AWS::Region}c
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-private-subnet-c
## EC2(VPC): Subnet (Private-C) Route Table (Private) Association
SubnetRouteTableAssociationPrivateC:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivateC
RouteTableId: !Ref RouteTablePrivate
## EC2(VPC): Subnet (Private-D)
SubnetPrivateD:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [ 10, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
AvailabilityZone: !Sub ${AWS::Region}d
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-private-subnet-d
## EC2(VPC): Subnet (Private-D) Route Table (Private) Association
SubnetRouteTableAssociationPrivateD:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivateD
RouteTableId: !Ref RouteTablePrivate
## EC2(VPC): VPC Endpoint (Gateway) (DynamoDB)
VPCEndpointGatewayDynamoDB:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref RouteTablePublic
- !If [ ShouldCreateSubnetProtected, !Ref RouteTableProtectedA, !Ref AWS::NoValue ]
- !If [ ShouldCreateSubnetProtected, !Ref RouteTableProtectedC, !Ref AWS::NoValue ]
- !If [ ShouldCreateSubnetProtected, !Ref RouteTableProtectedD, !Ref AWS::NoValue ]
ServiceName: !Sub com.amazonaws.${AWS::Region}.dynamodb
VpcEndpointType: Gateway
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-dynamodb-gateway-vpce
## EC2(VPC): VPC Endpoint (Gateway) (S3)
VPCEndpointGatewayS3:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref RouteTablePublic
- !If [ ShouldCreateSubnetProtected, !Ref RouteTableProtectedA, !Ref AWS::NoValue ]
- !If [ ShouldCreateSubnetProtected, !Ref RouteTableProtectedC, !Ref AWS::NoValue ]
- !If [ ShouldCreateSubnetProtected, !Ref RouteTableProtectedD, !Ref AWS::NoValue ]
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
VpcEndpointType: Gateway
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-s3-gateway-vpce
Outputs:
## EC2(VPC): VPC
VPC:
Value: !Ref VPC
Export:
Name: !Sub ${AWS::StackName}-VPC
## EC2(VPC): Internet Gateway
InternetGateway:
Value: !Ref InternetGateway
Export:
Name: !Sub ${AWS::StackName}-InternetGateway
## EC2(VPC): Route Table (Public)
RouteTablePublic:
Value: !Ref RouteTablePublic
Export:
Name: !Sub ${AWS::StackName}-RouteTablePublic
## EC2(VPC): Subnet (Public-A)
SubnetPublicA:
Value: !Ref SubnetPublicA
Export:
Name: !Sub ${AWS::StackName}-SubnetPublicA
## EC2(VPC): Subnet (Public-C)
SubnetPublicC:
Value: !Ref SubnetPublicC
Export:
Name: !Sub ${AWS::StackName}-SubnetPublicC
## EC2(VPC): Subnet (Public-D)
SubnetPublicD:
Value: !Ref SubnetPublicD
Export:
Name: !Sub ${AWS::StackName}-SubnetPublicD
## EC2(VPC): EIP
EIPNATGatewayA:
Condition: ShouldCreateSubnetProtected
Value: !Ref EIPNATGatewayA
Export:
Name: !Sub ${AWS::StackName}-EIPNATGatewayA
## EC2(VPC): NAT Gateway @ Public-A
NATGatewayA:
Condition: ShouldCreateSubnetProtected
Value: !Ref NATGatewayA
Export:
Name: !Sub ${AWS::StackName}-NATGatewayA
## EC2(VPC): Route Table (Protected-A)
RouteTableProtectedA:
Condition: ShouldCreateSubnetProtected
Value: !Ref RouteTableProtectedA
Export:
Name: !Sub ${AWS::StackName}-RouteTableProtectedA
## EC2(VPC): Route Table (Protected-C)
RouteTableProtectedC:
Condition: ShouldCreateSubnetProtected
Value: !Ref RouteTableProtectedC
Export:
Name: !Sub ${AWS::StackName}-RouteTableProtectedC
## EC2(VPC): Route Table (Protected-D)
RouteTableProtectedD:
Condition: ShouldCreateSubnetProtected
Value: !Ref RouteTableProtectedD
Export:
Name: !Sub ${AWS::StackName}-RouteTableProtectedD
## EC2(VPC): Subnet (Protected-A)
SubnetProtectedA:
Condition: ShouldCreateSubnetProtected
Value: !Ref SubnetProtectedA
Export:
Name: !Sub ${AWS::StackName}-SubnetProtectedA
## EC2(VPC): Subnet (Protected-C)
SubnetProtectedC:
Condition: ShouldCreateSubnetProtected
Value: !Ref SubnetProtectedC
Export:
Name: !Sub ${AWS::StackName}-SubnetProtectedC
## EC2(VPC): Subnet (Protected-D)
SubnetProtectedD:
Condition: ShouldCreateSubnetProtected
Value: !Ref SubnetProtectedD
Export:
Name: !Sub ${AWS::StackName}-SubnetProtectedD
## EC2(VPC): Route Table (Private)
RouteTablePrivate:
Value: !Ref RouteTablePrivate
Export:
Name: !Sub ${AWS::StackName}-RouteTablePrivate
## EC2(VPC): Subnet (Private-A)
SubnetPrivateA:
Value: !Ref SubnetPrivateA
Export:
Name: !Sub ${AWS::StackName}-SubnetPrivateA
## EC2(VPC): Subnet (Private-C)
SubnetPrivateC:
Value: !Ref SubnetPrivateC
Export:
Name: !Sub ${AWS::StackName}-SubnetPrivateC
## EC2(VPC): Subnet (Private-D)
SubnetPrivateD:
Value: !Ref SubnetPrivateD
Export:
Name: !Sub ${AWS::StackName}-SubnetPrivateD
## EC2(VPC): VPC Endpoint (Gateway) (DynamoDB)
VPCEndpointGatewayDynamoDB:
Value: !Ref VPCEndpointGatewayDynamoDB
Export:
Name: !Sub ${AWS::StackName}-VPCEndpointGatewayDynamoDB
## EC2(VPC): VPC Endpoint (Gateway) (S3)
VPCEndpointGatewayS3:
Value: !Ref VPCEndpointGatewayS3
Export:
Name: !Sub ${AWS::StackName}-VPCEndpointGatewayS3
【変更履歴】
- 2022-05-23:EIPから
Domain: vpc
を削除 - 2022-06-20:SubnetのCIDRブロックの指定方法を、Cidr組み込み関数を使った方式に変更
- 2023-04-05:MetaDataに
CidrBits
を追加(追加漏れ) - 2023-04-14:コメント文を全般的に修正
- 2025-01-16:VPCエンドポイントにNameタグを追加(いつの間にかできるようになっていた)
5. 予習
- CFnでVPCのリソースを作成してください ※CFnでエラーになった場合は、EventsタブのスクリーンショットをSlackでください (AWSの画面が変わっている場合は同じ設定項目を探してください)
- CFnで作成したリソースを全て、手でも作ってください。作成中に分からなかった設定項目については調べたり、質問のためにメモしておいてください
※順番も大事なので、CFnの記載順を参考にしてください
※命名は
SystemName
の箇所をCFnと別名にしてください。awsmaster
とawsmaster2
のように - 【宿題】下記の項目について調べて、次回のZoomで説明してください (公式の情報が優先で、分からない場合はブログ等を参照ください)
- Region(リージョン)とは何か? Regionごとに何か違いはあるか?
- Availability Zone(アベイラビリティゾーン)とは何か?
- ①インターネットからEC2への通信、②EC2からインターネットへの通信、③同一AZ間の通信、④異なるAZ間の通信、⑤VPC Endpoint(Gateway型)、⑥NAT Gatewayの各料金体系。またNAT Gatewayを1ヶ月連続で動かした場合はいくらかかるか?(計算上、1ヶ月=30日×24時間とする)
- サブネットマスク計算(IPv4)/サブネット一覧(早見表) でIPアドレスの範囲(ネットワークアドレス~ブロードキャストアドレス)が次の式のようになっていることを確認 10.0.0.0/24 + 10.0.1.0/24 = 10.0.0.0/23 >10.0.0.0~10.0.0.255(256個) + 10.0.1.0~10.0.1.255(256個) = 10.0.0.0~10.0.1.255(512個) 10.0.2.0/24 + 10.0.3.0/24 = 10.0.2.0/23 >上と同様に調べてみてください 10.0.0.0/23 + 10.0.2.0/23 = 10.0.0.0/22 >上と同様に調べてみてください
- サブネットマスク /19 の中に /24 は何個入るか?
- Public, Protected, Private Subnet用の各Route Tableに書かれている全部のRouteの意味(例えばPublicには4つのRouteがありますが、それぞれの意味は? Protected, Privateも同様に)
- (7.~9. は想定でお答えください)https://ipinfo.io/ にアクセスすると、接続元IPアドレスが表示されます。Public Subnetに配置したEC2インスタンスからアクセスすると、IPアドレスは何になるでしょうか?
- 同様にProtected Subnetに配置したEC2インスタンスからはどうでしょうか?
- 同様にPrivate Subnetに配置したEC2インスタンスからはどうでしょうか?
- 【応用課題】余裕のある方は実際に挑戦して頂くと勉強になると思います! ※時間の都合でZoomで解説はしませんので、答え合わせをされたい方はSlackでDMください
- 上記7. を実際に試してみてください。Public SubnetのEC2は⑤EC2の回を先行して進めば試せます。コマンド
curl ipinfo.io
で結果を確認できます - 上記8. を実際に試してみてください。Protected SubnetのEC2は⑤EC2を参考に手で作れば試せます
- 上記9. を実際に試してみてください。ただしPrivate SubnetではEC2に接続するところから難易度が高いです。接続方法から考えてみてください
- VPC Endpoint(Interface型)の料金体系
6. FAQ(随時追加)
NAT Gatewayは「停止」ができないので「削除」するしかありません。もしCFnで作ったNAT Gatewayを削除されたい場合は、次の通り操作してください。
- Protected Subnetに作ったEC2などがあれば削除する
- CloudFormationの画面でVPCのスタックを開く
- Updateボタンを押す
- User current templateを選択したまま、Nextボタンを押す
- CreateSubnetProtectedを
false
に変更して、Nextボタンを押す - Nextボタンを押す
- 末尾のChangesでEIPNATGatewayAなど多数のリソースがRemoveされることを確認して、Update stackボタンを押す
- 今後⑤EC2以降、CFnでStackを作成する際はUseSubnetProtectedを必ず
false
にしてください!(そうしないとエラーになりますのでお願いいたします)
ただしZoomではNAT GatewayやProtected Subnetを確認させて頂きますので、
- ④VPCの学習が全部終わってからNAT Gatewayを削除する
または
- ④VPCを学習するZoomが終わった後にNAT Gatewayを削除し、翌週のZoomが始まる前に上記手順5.で「CreateSubnetProtectedを
true
に変更」してNAT Gatewayを復活させる。これを④VPCを学習中は毎週繰り返す
のいずれかでご対応ください。よろしくお願いいたします。
- まず 3つのサブネットの違いは分かりますか?(この答えは敢えて書かないので、ご自身で整理してみてください)
- その上で次のような使い分けを行います。
- Public:インターネットに公開するなどグローバルIPアドレスが必要なEC2, ALB, NAT Gatewayを置く
- Protected:インターネットに公開はしないが、接続はしたいEC2を置く
- Private:インターネットと接続したくないRDS, ElastiCacheを置く
- ElastiCache以外は今後教材の中で取り上げていきますので、都度どのサブネットに作るのか意識してください。
- インターネットに公開する、つまりデフォルトルートがInternet GatewayであるサブネットをPublic Subnetと呼ぶ
- それ以外の、つまりインターネットには公開されないサブネットをPrivate Subnetと呼ぶ
ここまではAWS公式を含めて世界共通の認識ですが、問題は
- デフォルトルートをNAT Gatewayにする"Private Subnet"
- デフォルトルートを設定しない"Private Subnet"
をどう呼び分けるか? です。2つの"Private Subnet"の呼び分けが一般的に定まっていません。
国内海外を問わず、ネット上のAWS構成図からピックアップしてみると本当に様々です。
- Private Subnet ― Private Subnet ※呼び分けを放棄?
- Private Subnet (NAT) ― Private Subnet
- Private Subnet A ― Private Subnet B
- Frontend Private Subnet ― Backend Private Subnet
- Apps Private Subnet ― Database Private Subnet
- Private Service Subnet ― Private DB Subnet
- Private Subnet ― Secure Subnet
- Private Subnet ― Intra Subnet
- Protected Subnet ― Private Subnet ※弊社で使っているAWSリソースの命名規則を紹介します | DevelopersIO
- Private Subnet ― Isolated Subnet ※AWSリソースの命名規則を考えてみた (2024年版) | DevelopersIO
私も教材作成時に散々悩んだ結果、Public, Protected, Private の 3パターンにしました。3つとも Pで始まるのがきれいなのと、他のリソースも含め教材全体としてClassmethodさんの命名規則 https://dev.classmethod.jp/articles/aws-name-rule/ に準拠すると決めた方が迷いがないと考えたためです。
確かに国内ではClassmethodさんの投稿を参考にしたのか、この 3パターンが多いようです。海外ではあまり見かけませんでしたが。2つの"Private Subnet"はきっと現場では様々な呼ばれ方をされていると思います!
ネットワークは分かると面白いので私は好きなのですが、意外と難しいです。インフラエンジニアでも(クラウドエンジニアならなおさら)正確にTCP/IPを理解している方は、実は少ないと感じています。
以下「小学生でもわかる」と釣り文句になっていますが、下記の公開YouTube動画も参考になれば幸いです。
ぜひ高評価と、よろしければ動画にコメントも頂けると大変嬉しいです!
CidrBits
と!Cidr
組み込み関数がよく分からないので、教えてください。まず下記ParameterのCidrBits
(サブネットビット)
CidrBits:
Description: Subnet bits for the CIDR. For example, specifying a value "8" for this parameter will create a CIDR with a mask of "/24".
Type: Number
Default: 8
MinValue: 4
MaxValue: 16
ですが、ホスト部のビット数を表し、サブネットマスクの逆です。Description
の通り、例えば8
を指定すると、32-8=24でサブネットマスクは/24
という意味になります。サブネットマスク(ネットワーク部/固定値)+サブネットビット(ホスト部/可変値)=32(IPv4アドレスのビット数)に必ずなります。
その上でサブネットのCidrBlock
には(一例として)次のように書いています。
CidrBlock: !Select [ 0, !Cidr [ !FindInMap [ EnvironmentMap, !Ref Environment, VPCCidrBlock ], 12, !Ref CidrBits ] ]
↓まずはParametersの値で!Ref
を置換します。
CidrBlock: !Select [ 0, !Cidr [ !FindInMap [ EnvironmentMap, prod, VPCCidrBlock ], 12, 8 ] ]
↓次にEnvironmentMapの値で!FindInMap
を置換します。
CidrBlock: !Select [ 0, !Cidr [ 10.0.0.0/19, 12, 8 ] ]
↓次に!Cidr
は公式ドキュメントの通り、10.0.0.0/19
をサブネットビット8
(=サブネットマスク/24
)で分割し、頭から12
個分のCIDRブロックのリスト(配列)になります。イメージとしては次の通りです。
CidrBlock: !Select [ 0, [ 10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24, (8個分を省略), 10.0.11.0/24 ] ]
↓最後に!Select
は公式ドキュメントの通り、リストから0
番目(つまりリストの一番最初)を選択します。
CidrBlock: 10.0.0.0/24
以上のようになります。他のサブネットも同様です。
このようにすればVPCCidrBlock
とCidrBits
を指定するだけで、自動的に複数のサブネットのCIDRブロックを自動判定してくれるので大変便利です。分かりづらいのが難点ですが…。
結論から書きますと、有効にしてもメリットはほぼなく、むしろ設計・管理・運用コストが上がってデメリットばかりだからです。私も実務で使ったことはなく、周りのエンジニアに聞いても使ったことがない人ばかりでした。
IPv4アドレスの枯渇が叫ばれて早20年以上経ちましたが、いまだにIPv6の普及率は50%に届きません。詳細は割愛しますが、IPv4とIPv6は互換性が全くないために普及が遅れています。つまりIPv4はIPv4同士でしか、IPv6はIPv6同士でしか通信ができないために、世界中全てのサーバとクライアントがIPv6で通信できるようになるまでIPv4を廃止することができません。生きている間にIPv4の廃止に立ち会える日が来るのでしょうか...。
ちなみに今ご覧のPCやスマホがIPv6に対応しているかどうかは https://test-ipv6.com/ を開くと分かります。PCやスマホは当然対応していますが、インターネット回線(プロバイダー)やルーターの対応次第です。最近のプロバイダー契約ではIPv6も使えることが一般的になりましたが、古い契約では使えないままのことが多いので契約変更や機器の変更が必要になります。
余談はさておき、Webサイトとして公開するだけなら(VPCはIPv4のみでも)CloudFrontを使えばIPv6に対応できるので、実務上それで十分です。
それでもなおVPCでIPv6を有効にしたいという場合は、ざっと調べた限り、次の4点に注意しましょう。
- AWS VPCのIPv6は基本グローバルIPアドレス・パブリックIPアドレスです。インターネットから直接アクセスされたくない場合は「Egress-Only インターネットゲートウェイ」が必要です。参考:Egress-Only インターネットゲートウェイ - Amazon Virtual Private Cloud
0.0.0.0/0
がIPv4アドレス全てを指し、::/0
がIPv6アドレス全てを指します。ポートをフルオープンする場合、セキュリティグループにその両者を追加する必要があります。任意でない場合はIPv4、IPv6両方のアドレスを追加するように注意してください(通信相手がどちらで来るか分からない場合に特に注意が必要です)参考:VPC のセキュリティグループ - Amazon Virtual Private Cloud- ルートテーブルでもIPv4だけでなく、IPv6のルートも必要になります。前述のEgress-Only インターネットゲートウェイを使う場合は
::/0
のターゲットに設定してください。参考:VPC のルートテーブル - Amazon Virtual Private Cloud - Route 53でDNSレコードを追加する際、
A
レコードはIPv4、AAAA
レコードはIPv6のアドレスを返します。EC2やALBを公開する際に前者だけ追加すると、IPv6非対応になりますのでご注意ください(CloudFrontでIPv6対応する際も忘れがちです)
最後にAWS公式で2020年のIPv6対応状況のプレゼン資料がありましたので、共有いたします。
https://www.iajapan.org/ipv6/summit/TSU2020/pdf/kikuchi_tsu2020.pdf
いえ、実は存在するんですが、選択できない状況です。その経緯を順番にたどってみます。
①東京リージョンは世界で5番目のリージョンとして、2011年3月にローンチ(=サービス開始)されましたが、当初A・BのAZしか存在しませんでした。
②早くも2012年9月には3つ目のAZが誕生した結果、A〜CのAZが使えていたようです。
アマゾン ウェブ サービスが東京リージョンに新しいアベイラビリティゾーン開設によりさらにデータセンター群を追加 https://aws.amazon.com/jp/about-aws/whats-new/2012/09/14/announcing-launch-new-tokyoregion/
③ただ2013年頃にAZの1つで新機能が提供されず、旧世代のEC2インスタンスしか使用できないような状況が発生したようです。データセンターの物理スペースが足りなくてサーバーを追加できなかったのか、原因は不明ですが。
↓ 2014年11月の記事です。
利用可能なAZをワンライナーで取得してみた | DevelopersIO https://dev.classmethod.jp/articles/one-liner-for-getting-available-az/
より引用。
1年以上前からAWSを使用されている方は、東京リージョンでEC2を起動する時にAZを3個から選択できるのではないでしょうか。しかし、この中の1個のAZは新機能が提供されておらず、旧世代のEC2インスタンスしか使用できず、EBSもMagneticのみの提供となっています。 そのため、できる限り早く新機能が提供されていないAZから、別の利用可能なAZへ移行すべきです。
AWS 東京regionに一体何個のAZがあるのか? #AWS - Qiita https://qiita.com/dennis_wang/items/4651ce61105e182e4307
のコメント欄より引用。
ap-northeast-1bは、2013年の時点で既にリソースが足りず、特に新しいインスタンスは起動できない状態でした。
④上記の記述からすると、2014年頃以降に契約したAWSアカウントではA・CのAZだけが使える状態になったようです。私も2015年からAWSを使い始めましたが、A・CのAZだけが使えて、BのAZが使えた記憶はないです。
⑤2018年1月に4番目のAZが誕生した結果、2013年頃までに契約したAWSアカウントではA〜DのAZ 4つが、2014年頃以降のAWSアカウントではA・C・DのAZ 3つが使える状態になりました。
↓ 2018年1月の記事です。
東京リージョンの新しいアベイラビリティゾーン「ap-northeast-1d」がリリースされました。 | DevelopersIO https://dev.classmethod.jp/articles/new-az-ap-northeast-1d/
というわけで「なぜBのAZが存在するのに、使えないのか?」ですが、③で書きましたように4つのAZのうち、1つだけ新機能に対応しないAZがあるため、実質的に3つのAZしか使えないことが原因のようです。その実質的に使えないAZがBという名称となり、結果的にA・C・DのAZだけが使える状況として残ったわけです。
私のインフラエンジニア歴は14年ですが、断然「命名規則」と答えます。名前は本当に大事です。プログラミングのコードでも同じだと思いますが、インフラ管理の「いの一番」です。DeNAさん https://www.itmedia.co.jp/news/articles/2204/15/news088.html を見ても同じでした。
なぜ大事かというと、名前を見た瞬間にそのリソースが何か分かると認識齟齬や操作ミスが減りますので、事故(システム障害)になるリスクが確実に減ります。また使っているか使っていないか分からないようなゴミの発生を減らし、無駄なコストやセキュリティリスクも防げます。
さらに後から簡単に名前変更できるものなら良いのですが、リソースや設定箇所によっては作成後に変更できず、作成し直しになる場合もあります。その点でも会社全体・チーム全体で「命名規則」を決め、それに従ってリソースを作っていくことが大事です。
ちなみにこの教材ではClassmethodさんの命名規則 https://dev.classmethod.jp/articles/aws-name-rule/ を参考にしています。
余談ですが、それと同じくらい大事なのは「IPアドレス体系」ですね。こちらも名前と同様に会社全体でルールを一番最初に決めるのが理想です。
【↓⑤EC2・IAM RoleのFAQより抜粋】
セキュリティグループと似たようなネットワークのフィルタ設定に「ネットワークアクセスコントロールリスト(略してネットワークACL)」があります。違いや使い分けを表でまとめておきます。
ただ両方使うと、通信できなかった時の問題切り分けが面倒になりますし、ネットワークACLはインスタンス個別設定ができないので、現場では使わないことが大半です。
観点 | セキュリティグループ | ネットワークACL |
---|---|---|
オンプレで言えば | OSのファイアウォール | ルーターのACL |
適用場所 | 正確にはENIだが、EC2, ELB, RDSなどのリソースごとという認識でOK | サブネット(つまりサブネット内の全てのリソースに自動適用される) |
複数適用可能か | 1つのリソースで複数適用可能(ただし分かりづらくなるので複数適用はオススメしない) | 1つのサブネットで複数適用不可(1つのみ適用可能) |
ルールの許可/拒否 | 許可ルールのみ追加 | 許可/拒否ルールの両方組み合わせ可能 |
ルールのチェック | 通信が許可されるかどうか、全てのルールがチェックされる | 通信が許可されるかどうか、番号の小さい順にルールがチェックされる(該当したらそれ以降は無視) |
インバウンドルールのデフォルト(セキュリティグループは新規作成を使い、ネットワークACLはデフォルトのまま使うと想定) | 新規作成すると、全ての通信を拒否(許可ルールが1つもないので、暗黙のDenyで全て拒否) | デフォルトのは、全ての通信を許可(ルール番号100)(ちなみに新規作成すると、許可設定がないので全て拒否) |
アウトバウンドルールのデフォルト(同上) | 新規作成すると、全ての通信を許可(全て許可するルールが入っている) | デフォルトのは、全ての通信を許可(同上) |
戻りの通信は許可されるか | ステートフル(行き(=Request)の通信がルールで許可されれば、戻り(=Response)の通信も自動的に許可される) | ステートレス(通信の行き戻り関係なく、許可/拒否のルールが厳密に適用される) |
良くある使い方(本教材でも同じ) | デフォルトのは使わず、リソースごとに新規作成する。インバウンドで必要な許可ルールのみ追加、アウトバウンドはデフォルトのまま全て許可 | デフォルトのまま使う。インバウンドもアウトバウンドもデフォルトのまま全て許可 |
参考(1) AWS公式ドキュメント
- Amazon VPC でのインターネットワークトラフィックのプライバシー - Amazon Virtual Private Cloud
- VPC のセキュリティグループ - Amazon Virtual Private Cloud
- ネットワーク ACL - Amazon Virtual Private Cloud
参考(2) Classmethod
以上、お疲れさまでした!