NTTテクノクロス Advent Calendar 2024 シリーズ2 "7日目"の記事です。
こんにちは。NTTテクノクロスの増田です。
現在2年目のエンジニアで、AWSを用いた業務を行っています。
はじめに
本記事では、AWSのNAT Gatewayにおけるコスト削減のための作業を行ったので、その実施内容や方法などについてご紹介します。
NAT Gatewayとは
NAT Gatewayとは、VPC内に構成したプライベートサブネットからインターネットに接続するためのもので、IPアドレスとポート変換(NAPT)を行ってくれます。
NAT Gatewayのコスト
NAT Gatewayには時間単位料金とデータ処理料金があります。
時間単位料金:1時間あたり0.062USD
データ処理料金:1GBデータあたり0.062USD
NAT Gatewayを配置しているだけで時間単位料金が発生するため、不要なNAT Gatewayを削減する構成の見直しを行い、料金を抑えることにしました。
構成
NAT Gatewayの構成見直し前後の構成を以下に示します。
対処前
対処前は可用性を考慮して異なるAZにNAT Gatewayを3つ配置していましたが、開発環境や検証環境は状況によってはマルチAZ構成が不要であることもあり、コスト節約のため不要時は削除できるように改善しました。
対処後
対処後は、CloudFormationを使用して上図のように構成を変更できるようにします。なお、CloudFormationはAWSリソースをコードとして定義し、構築するためのIaC(Infrastructure as Code)ツールです。
なお、ここでは対処後の構成を以下のように定義します。
構成 | 定義内容 |
---|---|
Single構成 | 全てのAZにおけるルーティングを1つのNAT Gatewayに集約 (NAT Gateway:計1台) |
Multipl構成 | 全てのAZにおいて、NAT Gatewayを配置 (NAT Gateway:計3台) |
これにより、通常時はSingle構成の運用で、緊急時にはMultiple構成への切り替えを行うことが可能です。
CloudFormationテンプレート
構成を変更するCloudFormationテンプレートを作成します。
※テンプレート内の "VPC", "Public_Subnet00X", "Private_Subnet00X" の設定値はRefやImportValueなどで補完してください。
1. 構成選択用の環境変数を定義します
デフォルトではSingle構成にしています
Parameters:
# NAT Gatewayの構成選択用の環境変数を定義
NATGatewayType:
Description: "Type of NAT Gateway configuration (single or multiple)"
Type: String
AllowedValues:
- single
- multiple
Default: single
2. Multiple構成の条件を定義します
Conditions:
UseMultipleNatGateways: !Equals [!Ref "NATGatewayType", "multiple"]
3. NAT GatewayとElastic IPを作成します
UseMultipleNatGatewaysの条件によって、NAT Gatewayの作成数が変わります
Resources:
# NATGateway1a Create
NATGateway1a:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NATGateway1aEIP.AllocationId
SubnetId: !Ref SubnetPublic1a
Tags:
- Key: Name
Value: !Sub NATGateway1a_Name
# NATGateway1a For EIP Create
NATGateway1aEIP:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc
# NATGateway1c Create
NATGateway1c:
Type: AWS::EC2::NatGateway
# UseMultipleNatGatewaysが true の場合のみ作成
Condition: "UseMultipleNatGateways"
Properties:
AllocationId: !GetAtt NATGateway1cEIP.AllocationId
SubnetId: !Ref SubnetPublic1c
Tags:
- Key: Name
Value: !Sub NATGateway1c_Name
# NATGateway1c For EIP Create
NATGateway1cEIP:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc
# NATGateway1d Create
NATGateway1d:
Type: AWS::EC2::NatGateway
# UseMultipleNatGatewaysが true の場合のみ作成
Condition: "UseMultipleNatGateways"
Properties:
AllocationId: !GetAtt NATGateway1dEIP.AllocationId
SubnetId: !Ref SubnetPublic1d
Tags:
- Key: Name
Value: !Sub NATGateway1d_Name
# NATGateway1d For EIP Create
NATGateway1dEIP:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc
4. ルートテーブルを作成します
# PrivateRouteTable1a Create
PrivateRouteTable1a:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub PrivateRouteTable1a_Name
# PrivateRouteTable1c Create
PrivateRouteTable1c:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub PrivateRouteTable1c_Name
# PrivateRouteTable1d Create
PrivateRouteTable1d:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub PrivateRouteTable1d_Name
5. NAT Gatewayをルートテーブルへ関連付けます
UseMultipleNatGatewaysの条件によって、関連付ける対象が変わります
# Route to NATGateway1a for PrivateRouteTable1a
Private1aRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1a
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGateway1a
# Route to NATGateway1c for PrivateRouteTable1c
Private1cRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1c
DestinationCidrBlock: 0.0.0.0/0
### UseMultipleNatGatewaysが true の場合 NATGateway1c に、false の場合 NATGateway1a に接続
NatGatewayId: !If
- UseMultipleNatGateways
- !Ref NATGateway1c
- !Ref NATGateway1a
# Route to NATGateway1d for PrivateRouteTable1d
Private1dRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1d
DestinationCidrBlock: 0.0.0.0/0
### UseMultipleNatGatewaysが true の場合 NATGateway1d に、false の場合 NATGateway1a に接続
NatGatewayId: !If
- UseMultipleNatGateways
- !Ref NATGateway1d
- !Ref NATGateway1a
6. プライベートサブネットをルートテーブルへ関連付けます
# Associate SubnetPrivate1a with PrivateRouteTable1a
Private1aSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivate1a
RouteTableId: !Ref PrivateRouteTable1a
# Associate SubnetPrivate1c with PrivateRouteTable1c
Private1cSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivate1c
RouteTableId: !Ref PrivateRouteTable1c
# Associate SubnetPrivate1d with PrivateRouteTable1d
Private1dSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivate1d
RouteTableId: !Ref PrivateRouteTable1d
7. 構築したNAT Gatewayの構成を出力します
Outputs:
NatGatewayType:
Description: "The type of NAT Gateway configuration used"
Value: !Ref "NATGatewayType"
リソースマップ
作成したNat Gatewayを含むVPCのリソースマップを以下に示します。
リソースマップだとSingle構成のルーティングも分かりやすいですね。
Single構成
Multiple構成
最後にNo.1~7までを繋げて、VPCやサブネットなどを追加したテンプレート全体を以下に示します。
AWSTemplateFormatVersion: '2010-09-09'
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
VPCCIDR:
Description: VPC Cider Block for VPC
Type: String
SubnetPublic1aCIDR:
Description: Public Subnet 1a Cider Block for SubnetPublic1a
Type: String
SubnetPublic1cCIDR:
Description: Public Subnet 1c Cider Block for SubnetPublic1c
Type: String
SubnetPublic1dCIDR:
Description: Public Subnet 1d Cider Block for SubnetPublic1d
Type: String
SubnetPrivate1aCIDR:
Description: Private Subnet 1a Cider Block for SubnetPrivate1a
Type: String
SubnetPrivate1cCIDR:
Description: Private Subnet 1c Cider Block for SubnetPrivate1c
Type: String
SubnetPrivate1dCIDR:
Description: Private Subnet 1d Cider Block for SubnetPrivate1d
Type: String
# NAT-GWの構成選択用の環境変数を定義
NATGatewayType:
Description: "Type of NAT Gateway configuration (single or multiple)"
Type: String
AllowedValues:
- single
- multiple
Default: single
# ------------------------------------------------------------#
# Conditions
# ------------------------------------------------------------#
Conditions:
UseMultipleNatGateways: !Equals [!Ref "NATGatewayType", "multiple"]
Resources:
# ------------------------------------------------------------#
# VPC, Subnet, IGW
# ------------------------------------------------------------#
# VPC Create
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub VPC_Name
# SubnetPublic1a Create
SubnetPublic1a:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref SubnetPublic1aCIDR
Tags:
- Key: Name
Value: !Sub Public_Subnet1a_Name
# SubnetPublic1c Create
SubnetPublic1c:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1c
CidrBlock: !Ref SubnetPublic1cCIDR
Tags:
- Key: Name
Value: !Sub Public_Subnet1c_Name
# SubnetPublic1d Create
SubnetPublic1d:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1d
CidrBlock: !Ref SubnetPublic1dCIDR
Tags:
- Key: Name
Value: !Sub Public_Subnet1d_Name
# SubnetPrivate1a Create
SubnetPrivate1a:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref SubnetPrivate1aCIDR
Tags:
- Key: Name
Value: !Sub Private_Subnet1a_Name
# SubnetPrivate1c Create
SubnetPrivate1c:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1c
CidrBlock: !Ref SubnetPrivate1cCIDR
Tags:
- Key: Name
Value: !Sub Private_Subnet1c_Name
# SubnetPrivate1d Create
SubnetPrivate1d:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1d
CidrBlock: !Ref SubnetPrivate1dCIDR
Tags:
- Key: Name
Value: !Sub Private_Subnet1d_Name
# InternetGateway Create
IGW:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub IGW
# InternetGateway Attachment to VPC
IGWAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref IGW
VpcId: !Ref VPC
# ------------------------------------------------------------#
# NAT Gateway, EIP
# ------------------------------------------------------------#
# NATGateway1a Create
NATGateway1a:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NATGateway1aEIP.AllocationId
SubnetId: !Ref SubnetPublic1a
Tags:
- Key: Name
Value: !Sub NATGateway1a_Name
# NATGateway1a For EIP Create
NATGateway1aEIP:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc
# NATGateway1c Create
NATGateway1c:
Type: AWS::EC2::NatGateway
# UseMultipleNatGatewaysが true の場合のみ作成
Condition: "UseMultipleNatGateways"
Properties:
AllocationId: !GetAtt NATGateway1cEIP.AllocationId
SubnetId: !Ref SubnetPublic1c
Tags:
- Key: Name
Value: !Sub NATGateway1c_Name
# NATGateway1c For EIP Create
NATGateway1cEIP:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc
# NATGateway1d Create
NATGateway1d:
Type: AWS::EC2::NatGateway
# UseMultipleNatGatewaysが true の場合のみ作成
Condition: "UseMultipleNatGateways"
Properties:
AllocationId: !GetAtt NATGateway1dEIP.AllocationId
SubnetId: !Ref SubnetPublic1d
Tags:
- Key: Name
Value: !Sub NATGateway1d_Name
# NATGateway1d For EIP Create
NATGateway1dEIP:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc
# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------#
# PublicRouteTable Create
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub PublicRouteTable_Name
# PrivateRouteTable1a Create
PrivateRouteTable1a:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub PrivateRouteTable1a_Name
# PrivateRouteTable1c Create
PrivateRouteTable1c:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub PrivateRouteTable1c_Name
# PrivateRouteTable1d Create
PrivateRouteTable1d:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub PrivateRouteTable1d_Name
# PublicRoute Create
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref IGW
# Route to NATGateway1a for PrivateRouteTable1a
Private1aRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1a
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGateway1a
# Route to NATGateway1c for PrivateRouteTable1c
Private1cRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1c
DestinationCidrBlock: 0.0.0.0/0
### UseMultipleNatGatewaysが true の場合 NATGateway1c に、false の場合 NATGateway1a に接続
NatGatewayId: !If
- UseMultipleNatGateways
- !Ref NATGateway1c
- !Ref NATGateway1a
# Route to NATGateway1d for PrivateRouteTable1d
Private1dRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1d
DestinationCidrBlock: 0.0.0.0/0
### UseMultipleNatGatewaysが true の場合 NATGateway1d に、false の場合 NATGateway1a に接続
NatGatewayId: !If
- UseMultipleNatGateways
- !Ref NATGateway1d
- !Ref NATGateway1a
# Associate SubnetPublic1a with PublicRouteTable
Public1aSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPublic1a
RouteTableId: !Ref PublicRouteTable
# Associate SubnetPublic1c with PublicRouteTable
Public1cSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPublic1c
RouteTableId: !Ref PublicRouteTable
# Associate SubnetPublic1d with PublicRouteTable
Public1dSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPublic1d
RouteTableId: !Ref PublicRouteTable
# Associate SubnetPrivate1a with PrivateRouteTable1a
Private1aSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivate1a
RouteTableId: !Ref PrivateRouteTable1a
# Associate SubnetPrivate1c with PrivateRouteTable1c
Private1cSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivate1c
RouteTableId: !Ref PrivateRouteTable1c
# Associate SubnetPrivate1d with PrivateRouteTable1d
Private1dSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivate1d
RouteTableId: !Ref PrivateRouteTable1d
# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#
Outputs:
NatGatewayType:
Description: "The type of NAT Gateway configuration used"
Value: !Ref "NATGatewayType"
まとめ
以上、NAT Gatewayに関する内容をお届けしました。今回ご紹介した運用方法以外にも、未使用時はNAT Gatewayをすべて削除し、必要な時に再配置するなど、さまざまな運用方法が考えられますね。
AWS環境の構築においては、コスト削減は重要なポイントです。今後も対策に取り組みながら、参考になる情報を投稿していければと思います。
明日は @yukimuraki さんの 「綺麗なIAMポリシーを書きたくて」です。明日以降も NTTテクノクロスアドベントカレンダー2024 をお楽しみに!