kakakakakku blog

Weekly Tech Blog: Keep Learning!

NAT Gateway のコスト最適化に使える NAT インスタンス「fck-nat」を試す

はじめに

AWS で NAT Gateway のコスト最適化を考えようとすると NAT インスタンス という選択肢が出てくると思う.しかし AWS から提供されていた NAT AMI は Amazon Linux ベースで既にサポートが終了していて,ドキュメントでは NAT Gateway への移行もしくは独自の NAT AMI を作る選択肢が推奨されている.

docs.aws.amazon.com

fck-nat

そこで「fck-nat」という選択肢がある.fck-nat は Amazon Linux 2023 ベースで構築された NAT インスタンス用の AMI で,arm64 の AMI も提供されている💡Amazon EC2 の制約でインターネット向けの帯域幅は最大 5Gbps という上限はあるけど,多くの用途で問題ない気がするし,NAT Gateway と比較してコストを大幅に下げることができる.たとえば開発環境の NAT Gateway を置き換える用途だったらバッチリハマりそう👌

github.com

ちなみに fck-nat には「HA モード」という可用性を高める仕組みがある.あくまで AZ (Availability Zone) 内で Min 1 / Max 1 の Auto Scaling Group を作るイメージで,一般的には「オートヒーリング」と呼ばれる構成になる.よって,fck-nat で起動した NAT インスタンスに障害があった場合は Auto Scaling Group の判断で自動的に NAT インスタンスが作り直される♻️

fck-nat.dev

ということは fck-nat で HA モードを使ったとしても NAT インスタンスの障害時にはダウンタイムが発生することになる.fck-nat のドキュメントにも書いてある通り,それ以上に可用性を求めるのであれば NAT Gateway を使うべきだと思う.

If your workload requires five 9s of uptime, then Managed NAT Gateway is likely a better option for you.

環境

  • Terraform: v1.16.2
  • Terraform AWS Provider: v6.63.0
  • terraform-aws-fck-nat: v1.6.1

Terraform module for fck-nat

fck-nat のドキュメントに CDK・Terraform・CloudFormation・マネジメントコンソールの構築手順がまとまっている.

fck-nat.dev

今回は Terraform module for fck-nat を使って試す.

github.com

Amazon VPC を構築する

まずは検証用の Amazon VPC を構築する.検証用なので Multi-AZ にはせず,

  • Amazon VPC
  • Internet Gateway
  • パブリックサブネット (1a)
  • プライベートサブネット (1a)
  • パブリックルートテーブル
  • プライベートルートテーブル (1a)

というシンプルな構成にした.

ちなみに,通常はプライベートルートテーブルに設定する 0.0.0.0/0 への経路はあえて設定せず,Terraform module for fck-nat の update_route_tables パラメータを使って設定するところがポイント💡

resource "aws_vpc" "main" {
  cidr_block = "10.100.0.0/16"
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
}

resource "aws_subnet" "public_1a" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.100.1.0/24"
  availability_zone       = "ap-northeast-1a"
  map_public_ip_on_launch = true
}

resource "aws_subnet" "private_1a" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.100.2.0/24"
  availability_zone = "ap-northeast-1a"
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
}

resource "aws_route" "public_default" {
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.main.id
}

resource "aws_route_table_association" "public_1a" {
  subnet_id      = aws_subnet.public_1a.id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table" "private_1a" {
  vpc_id = aws_vpc.main.id
}

resource "aws_route_table_association" "private_1a" {
  subnet_id      = aws_subnet.private_1a.id
  route_table_id = aws_route_table.private_1a.id
}

fck-nat を構築する(HA モードなし)

まずは HA モードなしで fck-nat を構築する.ha_mode = false を設定すれば OK👌

resource "aws_eip" "main" {
  domain = "vpc"
}

module "fck_nat" {
  source  = "RaJiska/fck-nat/aws"
  version = "1.6.1"

  name      = "sandbox-fck-nat-ha-false"
  vpc_id    = aws_vpc.main.id
  subnet_id = aws_subnet.public_1a.id

  ha_mode       = false
  instance_type = "t4g.nano"

  eip_allocation_ids = [aws_eip.main.id]

  update_route_tables = true
  route_tables_ids = {
    private-1a = aws_route_table.private_1a.id
  }
}

terraform apply コマンドを実行すると,プライベートルートテーブルに 0.0.0.0/0 のエントリー(fck-nat の ENI 宛)ができていた.

動作確認(HA モードなし)

動作確認のためにプライベートサブネットに Amazon EC2 インスタンスを構築する.

AWS Systems Manager Session Manager で Amazon EC2 インスタンスに接続して,送信元 IP アドレスを確認すると期待通りに fck-nat インスタンスに設定した Elastic IP になっていた❗️そもそも AWS Systems Manager Session Manager を使えている時点で fck-nat インスタンスが正常に動いているとも言える👌

$ aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=sandbox-fck-nat-ha-false" "Name=instance-state-name,Values=running" \
    --query 'Reservations[].Instances[].[InstanceId,PublicIpAddress]'
[
    [
        "i-09464414e4c6c847f",
        "18.180.50.191"
    ]
]

$ aws ssm start-session --target i-02492860cdfc85ff2

sh-5.2$ curl -s https://checkip.amazonaws.com
18.180.50.191

fck-nat を構築する(HA モード)

今度は HA モードで fck-nat を構築する.ha_mode = true を設定すれば OK👌(今回はわかりやすく name も変えている)

resource "aws_eip" "main" {
  domain = "vpc"
}

module "fck_nat" {
  source  = "RaJiska/fck-nat/aws"
  version = "1.6.1"

  name      = "sandbox-fck-nat-ha-true"
  vpc_id    = aws_vpc.main.id
  subnet_id = aws_subnet.public_1a.id

  ha_mode       = true
  instance_type = "t4g.nano"

  eip_allocation_ids = [aws_eip.main.id]

  update_route_tables = true
  route_tables_ids = {
    private-1a = aws_route_table.private_1a.id
  }
}

terraform apply コマンドを実行すると,同じようにプライベートルートテーブルに 0.0.0.0/0 のエントリー(fck-nat の ENI 宛)ができていて,さらに Auto Scaling Group も Min 1 / Max 1 で構築されていた👌

動作確認(HA モード)

HA モードの fck-nat でも同じように動作確認ができた👌

$ aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=sandbox-fck-nat-ha-true" "Name=instance-state-name,Values=running" \
    --query 'Reservations[].Instances[].[InstanceId,PublicIpAddress]'
[
    [
        "i-02a94f53e0307ba61",
        "35.76.164.235"
    ]
]

$ aws ssm start-session --target i-02492860cdfc85ff2

sh-5.2$ curl -s https://checkip.amazonaws.com
35.76.164.235

さらに fck-nat の HA モードを確認するために aws ec2 terminate-instances コマンドで意図的に fck-nat インスタンスを落とす.

$ aws ec2 terminate-instances --instance-ids i-02a94f53e0307ba61
{
    "TerminatingInstances": [
        {
            "InstanceId": "i-02a94f53e0307ba61",
            "CurrentState": {
                "Code": 32,
                "Name": "shutting-down"
            },
            "PreviousState": {
                "Code": 16,
                "Name": "running"
            }
        }
    ]
}

すると Auto Scaling Group によって自動的に fck-nat インスタンスが作り直されて「オートヒーリング」の挙動になっていることを確認できた❗️

数分間のダウンタイムはあるけど,ENI (Elastic Network Interface) と Elastic IP はそのまま作り直された fck-nat インスタンスに引き継がれていた.

$ aws ssm start-session --target i-02492860cdfc85ff2

sh-5.2$ curl -s https://checkip.amazonaws.com
35.76.164.235

まとめ

NAT Gateway のコスト最適化に使える NAT インスタンス「fck-nat」を試してみた.HA モードにすれば Auto Scaling Group を使ったオートヒーリングにも対応してて,fck-nat インスタンスの障害発生時のダウンタイムを許容できる前提であれば NAT Gateway と比較してコストを大幅に下げることができる.選択肢の1つとして覚えておこう \( 'ω')/

関連ポスト