💰

fluent-bit と kinesis の通信をインターフェースVPCエンドポイントに置き換えて通信料金を月$560削減した話

2024/10/22に公開

前提条件

本記事では以下のバージョンを前提としています。

概要

サービスの成長に伴い、AWSのコストが増加するのは避けられないことですが、想定していた予算を超える状況になっていたため、インフラチームでコスト削減に注力することにしました。
最近、いくつかの効果的なコスト削減を実現できたのですが、今回は「NatGateway-Bytes」の削減について、原因の特定から改善、効果測定までの事例をご紹介します。
前回記事では「DataTransfer-Regional-Bytes」の削減について紹介したので、よろしければそちらも見てください。
https://zenn.dev/socialplus/articles/1a03f157abc114

AWS Cost Explorer で削減できそうな項目を特定する

前回記事同様、AWS Cost Explorer を活用し、大きくコストのかかっている項目として「EC2 Other」を特定します。
「EC2 Other」だけだと具体的に何にコストがかかっているのか詳細がわからないため、レポートパラメータに以下の値を設定します。

  • 時刻
    • 日付範囲
      • 任意の値を設定
    • 粒度
      • 任意の値を設定
  • グループ化の条件
    • ディメンション
      • 使用タイプ
  • 適用フィルター
    • サービス
      • EC2 Other

フィルターを設定した結果、特に以下2つが高額であることが判明しました。

  • DataTransfer-Regional-Bytes
    • 同一リージョン内の別ゾーンとの通信に対して発生するコスト
    • $0.01/1GB
  • NatGateway-Bytes
    • NAT Gateway を介した通信に対して発生するコスト
    • $0.062/1GB

今回の記事では「NatGateway-Bytes」に絞って調査していきます。

NatGateway-Bytes の発生箇所を特定する

まず、NatGateway-Bytes がどの通信で膨らんでいるのかを特定します。
調査ツールの候補はいくつかありましたが、弊社では Datadog を活用しているので、Datadog の機能のネットワークパフォーマンスモニタリングを使い調査することにしました。
ネットワークパフォーマンスモニタリングを有効化して、検索項目に server_gateway_type:aws_nat_gateway と入力した上で Auto-grouped traffic フィルターをつけると、NAT Gateway の通信内容が通信量と共に列挙されます。
NAT Gateway には「アップロード」と「ダウンロード」の2パターンの経路がありますが、どちらも個別に確認することができ、例えばアップロード側を降順で確認したい場合は「CLIENT->SERVER」と記載されている下側の「VOLUME」をクリックすることで、降順に切り替えることができます。

実際に調べてみると以下のような結果が得られました。
NAT Gateway アップロード側(降順)

NAT Gateway ダウンロード側(降順)

アップロードもダウンロードも fluent-bit->kinesis の通信量がトップで、ここを改善すれば大幅にコスト削減ができそうです。

kinesis の通信方式を調査

現状、EKS上に起動した fluent-bit が kinesis に通信するために、NAT Gateway を介してインターネットを経由している状況です。
インターネットから出ずに直接 kinesis にアクセスできる方法がないか調査したところ、AWS の以下のドキュメントに辿り着きました。
インターフェイスVPCエンドポイントで Amazon Kinesis Data Streams を使用する
インターフェースVPCエンドポイントとは、AWS PrivateLink の一部で AWS のサービスに対してプライベートに接続するためのインターフェースを提供するものです。
そのインターフェースVPCエンドポイントが kinesis に対応しているようなので、導入すれば NAT Gateway を介した通信を回避することができそうです。

インターフェースVPCエンドポイントの料金を調査

公式でチェックすると通信にかかる料金は以下の通りです。

AWS リージョンで 1 か月に処理されるデータ 処理データ 1 GB あたりの料金 (USD)
最初の 1 PB 0.01 USD
次の 4 PB 0.006 USD
5 PB以上のもの 0.004 USD

弊社の使い方だと 1ヶ月に 1PB を超えることはないので、$0.01/1GB で固定と考えて良さそうです。
NAT Gateway の通信料金は $0.062/1GB なので、インターフェースVPCエンドポイントを利用するだけで通信料だけなら約84%は割安となる計算ですが、インターフェースVPCエンドポイント1つに対して以下の料金が発生します。

各 AZ の VPC エンドポイント 1 つあたりの料金 (USD/時間)
USD 0.014

例えば kinesis streams 用にインターフェースVPCエンドポイントを 3AZ 分用意した場合、1ヶ月の料金は 0.014 * 24 * 30 * 3 = $30.24 となりそうです。
つまり、新たにインターフェースVPCエンドポイントを作成する場合、損益分岐点となる通信量が存在します。
NAT Gateway と インターフェースVPCエンドポイントの通信料金の差分は $0.052/1GB となるので、$30.24 のベースコストの元を取るには1ヶ月あたり 30.24 / 0.052 = 約582GB 以上の通信量が必要そうです。

ネットワークパフォーマンスモニタリング による調査では、1時間あたり 5.6GB の通信が kinesis間で発生していたので、概算ですが1月あたり 5.6 * 24 * 30 = 4032GB 程度の通信は発生していそうです。
仮に概算から下振れしたとしても損益分岐点である 582GB は確実に超えていそうなので、インターフェースVPCエンドポイントの導入を決定しました。

実装

以下が Terraform のコードになります。
インターフェースVPCエンドポイントに紐付けるサブネットや、セキュリティグループの設定は各自の環境に最適化してください。

# aws_vpc_endpoint.service_name に入る値を取得する
data "aws_vpc_endpoint_service" "kinesis_streams" {
  service      = "kinesis-streams"
  service_type = "Interface"
}

resource "aws_vpc_endpoint" "socialplus-private-kinesis-streams" {
  vpc_id            = aws_vpc.main.id
  # インターフェース型を選択する
  vpc_endpoint_type = "Interface"
  # com.amazonaws.ap-northeast-1.kinesis-streams が展開されます
  service_name      = data.aws_vpc_endpoint_service.kinesis_streams.service_name
  # プライベートDNSを有効化しないと通信経路によってはインターネットを経由してしまうので有効化する
  private_dns_enabled = true
  # 3AZ の private subnet id を紐づける
  subnet_ids = [
    module.private_subnet["b"].aws_private_subnet_id,
    module.private_subnet["c"].aws_private_subnet_id,
    module.private_subnet["d"].aws_private_subnet_id,
  ]
  security_group_ids = [
    aws_security_group.vpc-interface-endpoint-kinesis-streams.id,
  ]
  tags = {
    Name = "vpc-interface-endpoint-kinesis-streams"
  }
}

resource "aws_security_group" "vpc-interface-endpoint-kinesis-streams" {
  name   = "vpc-interface-endpoint-kinesis-streams"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "TCP"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "vpc-interface-endpoint-kinesis-streams"
  }
}

疎通確認

インターフェースVPCエンドポイントの設置が完了したら、疎通確認を行います。
今回は fluent-bit(EKS EC2) -> kinesis なので、EKS の Node に入って dig コマンドを打ちます。
以下のように、インターフェースVPCエンドポイントのネットワークインターフェイスに紐づくIPアドレスが返却されれば疎通成功です。

# dig +short kinesis.ap-northeast-1.amazonaws.com.
10.0.20.97
10.0.10.5
10.0.21.111

実装後の通信量削減効果確認

実装前は一番上に来ていた kinesis が完全に消えたことがわかります。
通信経路から NAT Gateway を完全に外すことに成功したようです。

CloudWatch の BytesOutToDestination で NAT Gateway 全体の通信量を確認してみると、目視で確認できるほどベースラインが下がっていることが確認できます。
これはコスト削減効果も期待できそうです。

実装後のAWSコスト削減効果確認

実装後の8/7移行とそれ以前で比較すると、元々$16 ~ $25 程度だったのが $7 ~ $8 程度に収まるようになっています。

8月のデータは月毎の比較としては使いづらいので、実装前の7月と実装後の9月で比較します。

7月の NatGateway-Bytes

9月の NatGateway-Bytes

月あたり $560 程コストカットできたことがわかります。
インターフェースVPCエンドポイントのコスト は $30.24 なので、余裕で元が取れました。
サービス成長次第で通信量が増えてくることが考えられるので、今後より一層恩恵を受けられそうです。

まとめ

  • NAT Gateway は便利だが、通信量にかかる課金が高めの設定($0.062/GB)なので、定期的な見直しが必要
  • 通信量によってはインターフェースVPCエンドポイントを新規に立ち上げた方が NAT Gateway より安価
  • Datadog のネットワークパフォーマンスモニタリングを活用することで NatGateway-Bytes の内訳を簡単に調べられる
SocialPLUS Tech Blog

Discussion