必要な時だけNAT Gatewayを作成する方法
検証用のVPCを作成しておき、必要な時だけNAT Gatewayがあればいいのになぁ、と思っていたので簡単にNAT Gatewayを作成、削除できるCloudFormationテンプレートを作成しました。
NAT GatewayはVPCと違って利用可能となっている時間で料金が発生するので、できるだけ利用可能となっている時間を減らしたいわけです。
構成
今回作成するVPCは下記のようになっています。NAT Gatewayを必要に応じて作成、削除を行います。NAT GatewayはAZ障害を考慮して冗長化していますが、お好みでカスタマイズしてください。
- Frontend subnet
- パブリックサブネットになります。インターネットゲートウェイを通じて外部との通信が可能です。
- ELBや踏み台サーバを配置する想定です
- Application subnet
- NAT Gatewayが無い場合は外部アクセス不可のプライベートサブネットになります。ある場合はVPC内からの通信のみが可能になります。その状態を図ではProtectedとしています。
- APサーバなどを配置する想定です
- DataStore subnet
- 外部アクセス不可なプライベートサブネットになります
- RDSなどを配置する想定です
解説
NAT Gatewayの部分だけ抜粋して説明していきます。全体のCloudFormationテンプレートは記事の最後に記載しています。
Parameters
NAT Gatewayを作成するかどうかをパラメータとして受け取ります。デフォルトでは作成しないになっています。
Parameters: EnableNatGateway: Description: Enable NAT Gateway. Type: String Default: false AllowedValues: [true, false]
Conditions
NAT Gatewayを作成するかどうかの条件を作成します。
Conditions: EnableNatGateway: !Equals [true, !Ref EnableNatGateway]
Resources
作成した条件が真であれば、NAT GatewayとEIPを作成します。
Resources: ・・・ NatGateway1: Type: AWS::EC2::NatGateway Condition: EnableNatGateway Properties: AllocationId: !GetAtt NatGatewayEIP1.AllocationId SubnetId: !Ref FrontendSubnet1 Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-ngw1 NatGatewayEIP1: Type: AWS::EC2::EIP Condition: EnableNatGateway Properties: Domain: vpc NatGateway2: Type: AWS::EC2::NatGateway Condition: EnableNatGateway Properties: AllocationId: !GetAtt NatGatewayEIP2.AllocationId SubnetId: !Ref FrontendSubnet2 Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-ngw2 NatGatewayEIP2: Type: AWS::EC2::EIP Condition: EnableNatGateway Properties: Domain: vpc ・・・
NAT Gatewayが作成されていれば、各Application subnetのルートテーブルにNAT Gatewayへのルートを作成しています。
Resources: ・・・ ApplicationRoute1: Type: AWS::EC2::Route Condition: EnableNatGateway Properties: RouteTableId: !Ref ApplicationRouteTable1 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway1 ApplicationRoute2: Type: AWS::EC2::Route Condition: EnableNatGateway Properties: RouteTableId: !Ref ApplicationRouteTable2 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway2 ・・・
スタックを作成する
まずは記事の最後にあるテンプレートをデフォルト設定のままスタックを作成します。パラメータは下図のとおりです。作成が完了したらNAT Gateway、EIP、ルートテーブルを確認し、NAT Gatewayの設定が無いことを確認します。
NAT Gatewayを作成する
作成したVPCにNAT Gatewayを作成してみます。コンソールからのポチポチだけでできます。
先程作成したスタックを選択し、「更新する」をクリックします。
「現在のテンプレートを使用」を選択します
EnableNatGateway
をtrue
に変更し、NAT Gatewayが作成されるようにします。あとは「次へ」をクリックしていきます。
変更セットのプレビューでNAT Gateway、EIP、ルートが追加されることが分かります。「スタックの更新」をクリックします。
スタックの更新が完了したらNAT Gatewayが作成されているか確認して完了です。
NAT Gatewayを削除する
削除する場合はEnableNatGateway
をfalse
にして更新すればOKです。
変更セットを見れば下記のように、NAT Gateway関連のリソースが削除されることがわかると思います。
テンプレート全体
今回使用したテンプレートになります。
--- AWSTemplateFormatVersion: '2010-09-09' Description: Network Layer Template #------------------------------------------------------------------------------ Parameters: #------------------------------------------------------------------------------ SystemName: Description: This value is used as the resource prefix. Type: String MinLength: 1 Default: example Env: Description: Environment Name Type: String Default: dev AllowedValues: - dev - prd VpcCidr: Description: First and Second Octet of VPC, For example (10.0/172.16/192.168) Type: String Default: 10.0 AllowedPattern: "^(10\\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|172\\.(1[6-9]|2[0-9]|3[0-1])|192\\.168)$" ConstraintDescription: xxx.xxx EnableNatGateway: Description: Enable NAT Gateway. Type: String Default: false AllowedValues: [true, false] #------------------------------------------------------------------------------ Conditions: #------------------------------------------------------------------------------ EnableNatGateway: !Equals [true, !Ref EnableNatGateway] #------------------------------------------------------------------------------ Mappings: #------------------------------------------------------------------------------ VpcConfig: dev: Vpc : .0.0/16 FrontendSubnet1 : .0.0/24 FrontendSubnet2 : .1.0/24 ApplicationSubnet1 : .10.0/24 ApplicationSubnet2 : .11.0/24 DatastoreSubnet1 : .20.0/24 DatastoreSubnet2 : .21.0/24 #------------------------------------------------------------------------------ Resources: #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ Vpc: #------------------------------------------------------------------------------ Type: AWS::EC2::VPC Properties: CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, Vpc ]}] EnableDnsSupport: true EnableDnsHostnames: true InstanceTenancy: default Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-vpc #------------------------------------------------------------------------------ InternetGateway: #------------------------------------------------------------------------------ Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-igw AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref Vpc InternetGatewayId: !Ref InternetGateway #------------------------------------------------------------------------------ FrontendRouteTable: #------------------------------------------------------------------------------ Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-frontend-rtb FrontendRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref FrontendRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway #------------------------------------------------------------------------------ ApplicationRouteTable1: #------------------------------------------------------------------------------ Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-application1-rtb ApplicationRoute1: Type: AWS::EC2::Route Condition: EnableNatGateway Properties: RouteTableId: !Ref ApplicationRouteTable1 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway1 #------------------------------------------------------------------------------ ApplicationRouteTable2: #------------------------------------------------------------------------------ Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-application2-rtb ApplicationRoute2: Type: AWS::EC2::Route Condition: EnableNatGateway Properties: RouteTableId: !Ref ApplicationRouteTable2 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway2 #------------------------------------------------------------------------------ DataStoreRouteTable: #------------------------------------------------------------------------------ Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-datastore-rtb #------------------------------------------------------------------------------ NetworkACL: #------------------------------------------------------------------------------ Type: AWS::EC2::NetworkAcl Properties: Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-nacl VpcId: !Ref Vpc NetworkACLEntryEgress: Type: AWS::EC2::NetworkAclEntry Properties: CidrBlock: 0.0.0.0/0 Egress: true NetworkAclId: !Ref NetworkACL Protocol: -1 RuleAction : allow RuleNumber : 100 NetworkACLEntryIngress: Type: AWS::EC2::NetworkAclEntry Properties: CidrBlock: 0.0.0.0/0 Egress: false NetworkAclId: !Ref NetworkACL Protocol: -1 RuleAction : allow RuleNumber : 100 #------------------------------------------------------------------------------ FrontendSubnet1: #------------------------------------------------------------------------------ Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 0, "Fn::GetAZs": { Ref: "AWS::Region" } ] CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, FrontendSubnet1 ]}] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-frontend1-subnet VpcId: !Ref Vpc FrontendSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref FrontendSubnet1 RouteTableId: !Ref FrontendRouteTable FrontendSubnet1NACLAssociation: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: !Ref FrontendSubnet1 NetworkAclId: !Ref NetworkACL #------------------------------------------------------------------------------ FrontendSubnet2: #------------------------------------------------------------------------------ Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 1, "Fn::GetAZs": { Ref: "AWS::Region" } ] CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, FrontendSubnet2 ]}] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-frontend2-subnet VpcId: !Ref Vpc FrontendSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref FrontendSubnet2 RouteTableId: !Ref FrontendRouteTable FrontendSubnet2NACLAssociation: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: !Ref FrontendSubnet2 NetworkAclId: !Ref NetworkACL #------------------------------------------------------------------------------ ApplicationSubnet1: #------------------------------------------------------------------------------ Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 0, "Fn::GetAZs": { Ref: "AWS::Region" } ] CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, ApplicationSubnet1 ]}] Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-application1-subnet VpcId: !Ref Vpc ApplicationSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref ApplicationSubnet1 RouteTableId: !Ref ApplicationRouteTable1 ApplicationSubnet1NACLAssociation: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: !Ref ApplicationSubnet1 NetworkAclId: !Ref NetworkACL #------------------------------------------------------------------------------ ApplicationSubnet2: #------------------------------------------------------------------------------ Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 1, "Fn::GetAZs": { Ref: "AWS::Region" } ] CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, ApplicationSubnet2 ]}] Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-application2-subnet VpcId: !Ref Vpc ApplicationSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref ApplicationSubnet2 RouteTableId: !Ref ApplicationRouteTable2 ApplicationSubnet2NACLAssociation: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: !Ref ApplicationSubnet2 NetworkAclId: !Ref NetworkACL #------------------------------------------------------------------------------ DatastoreSubnet1: #------------------------------------------------------------------------------ Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 0, "Fn::GetAZs": { Ref: "AWS::Region" } ] CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, DatastoreSubnet1 ]}] Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-datastore1-subnet VpcId: !Ref Vpc DatastoreSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref DatastoreSubnet1 RouteTableId: !Ref DataStoreRouteTable DatastoreSubnet1NACLAssociation: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: !Ref DatastoreSubnet1 NetworkAclId: !Ref NetworkACL #------------------------------------------------------------------------------ DatastoreSubnet2: #------------------------------------------------------------------------------ Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 1, "Fn::GetAZs": { Ref: "AWS::Region" } ] CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, DatastoreSubnet2 ]}] Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-datastore2-subnet VpcId: !Ref Vpc DatastoreSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref DatastoreSubnet2 RouteTableId: !Ref DataStoreRouteTable DatastoreSubnet2NACLAssociation: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: !Ref DatastoreSubnet2 NetworkAclId: !Ref NetworkACL #------------------------------------------------------------------------------ NatGateway1: #------------------------------------------------------------------------------ Type: AWS::EC2::NatGateway Condition: EnableNatGateway Properties: AllocationId: !GetAtt NatGatewayEIP1.AllocationId SubnetId: !Ref FrontendSubnet1 Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-ngw1 NatGatewayEIP1: Type: AWS::EC2::EIP Condition: EnableNatGateway Properties: Domain: vpc #------------------------------------------------------------------------------ NatGateway2: #------------------------------------------------------------------------------ Type: AWS::EC2::NatGateway Condition: EnableNatGateway Properties: AllocationId: !GetAtt NatGatewayEIP2.AllocationId SubnetId: !Ref FrontendSubnet2 Tags: - Key: Name Value: !Sub ${SystemName}-${Env}-ngw2 NatGatewayEIP2: Type: AWS::EC2::EIP Condition: EnableNatGateway Properties: Domain: vpc #------------------------------------------------------------------------------ Outputs: #------------------------------------------------------------------------------ Vpc: Value: !Ref Vpc Export: Name: !Sub ${SystemName}-${Env}-vpc FrontendSubnet1: Value: !Ref FrontendSubnet1 Export: Name: !Sub ${SystemName}-${Env}-frontend1-subnet FrontendSubnet2: Value: !Ref FrontendSubnet2 Export: Name: !Sub ${SystemName}-${Env}-frontend2-subnet ApplicationSubnet1: Value: !Ref ApplicationSubnet1 Export: Name: !Sub ${SystemName}-${Env}-application1-subnet ApplicationSubnet2: Value: !Ref ApplicationSubnet2 Export: Name: !Sub ${SystemName}-${Env}-application2-subnet DatastoreSubnet1: Value: !Ref DatastoreSubnet1 Export: Name: !Sub ${SystemName}-${Env}-datastore1-subnet DatastoreSubnet2: Value: !Ref DatastoreSubnet2 Export: Name: !Sub ${SystemName}-${Env}-datastore2-subnet
まとめ
NAT Gatewayを簡単に作成、削除できることが確認できました。条件の使い過ぎは可読性や保守性が落ちるのでおすすめしませんが、用途によっては非常に便利なことがわかりました。
なにかの参考になれば幸いです。