AWS NAT Gateway の通信分析から始めるコスト最適化の取り組み

はじめに

こんにちは。アソビューで SRE をしている上島です。

本記事では、私たち SRE チームが取り組んだ AWS NAT Gateway における通信分析と、それに基づくコスト最適化の取り組みをご紹介します。

背景

アソビューは、遊園地や水族館、アウトドアレジャー、ものづくり体験など、全国のさまざまな遊びを予約できる日本最大級の遊び予約サイトです。土日祝やゴールデンウィーク、夏休みなどの大型連休には利用者が増え、システム全体の負荷も高まります。

アソビューのインフラは主に AWS 上に構築されており、利用者増加に比例して AWS のコストも増加していました。なかでも特に顕著だったのが NAT Gateway を経由する通信であり、コスト最適化の余地がありました。

こうした状況を踏まえ、まずは NAT Gateway に関するコストと通信状況を詳細に調査し、その結果をもとに具体的なコスト最適化に取り組むことにしました。

NAT Gateway コストの把握

NAT Gateway の通信を確認したところ、転送量(APN1-NatGateway-Bytes)は月によって変動が大きく、特に大型連休のある月は通常月と比べて月額コストが約 $1,000 増加していました。一方、使用時間(APN1-NatGateway-Hours)はほぼ横ばいで推移していました。

AWS の VPC 内リソースが外部サービスと通信する場合、基本的に NAT Gateway か VPC Endpoint のいずれかを経由します。通信を可能な限り VPC Endpoint に切り替えることでコスト最適化が期待できると考え、まずは通信量の多いサービスや時間帯を把握することにしました。

NAT Gateway 通信の把握

どのサービスやエンドポイントとの通信が多く、どの時間帯にピークが発生しているかを把握し、NAT Gateway の通信を削減する施策の検討に役立てます。

CloudWatch Metrics 確認

CloudWatch Metrics を確認したところ、いくつか特徴的な傾向が見えてきました。

利用者が少ない深夜 3 時ごろは、BytesInFromDestination(内向き通信)が BytesOutToDestination(外向き通信)の 1/3 以下にとどまります。しかしピークタイムになると状況が逆転し、内向き通信が外向き通信の 3 倍以上に達していました。

外向き通信が比較的安定している一方で、内向き通信はユーザーの利用状況に強く影響され、時間帯によって大きく変動することがわかりました。

VPC Flow Logs 確認

アソビューではすでに VPC Flow Logs が有効化され、ログは S3 バケットに保存されていました。これを Athena でクエリし、通信量の多いサービスや頻繁にアクセスされる宛先のパターンを調査しました。 なお、Athena での解析には VPC Flow Logs 用テーブルが必要ですが、事前に作成済みだったため、今回追加作業は不要でした。

VPC Flow Logs には、AWS 宛の通信であれば宛先サービス名を記録できるメタデータフィールド pkt-dst-aws-service があります。このフィールドを使うと AWS / 非 AWS の区別を含めて通信状況をより正確に把握できます。しかし、アソビューの環境ではデフォルト設定のままで、このフィールドを利用するカスタマイズが行われていませんでした。

この制約を踏まえ、pkt-dst-aws-service の有効化を優先するか、コスト最適化のため別の方法を検討するかの判断が必要になりました。 今回は後者を選択し、少し複雑な自前クエリを作成することで通信量の多いサービスを特定し、コスト最適化につなげました。 このクエリでは Athena を用いて NAT Gateway の ENI に紐づく通信ログを抽出後、宛先 IP アドレスをサービスごとに分類して集計する形で分析を行いました。

参考:


実際に Athena を用いて通信量の多いサービスを調査した内容をご紹介します。

まず、BytesInFromDestination(内向き通信)を対象に、どのサービスへの通信が多いのかを確認しました。 DynamoDB の IP アドレスが複数に分散しており全体像をつかみにくかったため、IP アドレスをグルーピングして集計し、通信量を把握しました。

クエリ

-- 共通フィルタ: 対象のVPCフローログだけを抽出
WITH filtered AS (
  SELECT *
  FROM production_vpclogs_pp
  WHERE
    region = 'ap-northeast-1'
    AND account_id = 'XXXXXXXXXXXX'
    AND day = '2025/10/01'
    AND action = 'ACCEPT
    AND interface_id IN ('eni-XXXXXXXXXXXXXXX', 'eni-XXXXXXXXXXXXXXX', 'eni-XXXXXXXXXXXXXXX') -- NAT Gateway ENI
    AND srcaddr NOT IN ('10.4.XXX.XXX', '10.4.XXX.XXX', '10.4.XXX.XXX') -- NAT Gateway IP
),

-- 全体トラフィック量を算出
total AS (
  SELECT SUM(bytes) AS total_bytes_all
  FROM filtered
),

-- 宛先ごと・送信元ごとに集計
aggregated AS (
  SELECT
    CASE
      WHEN dstaddr LIKE '10.4.%' THEN '10.4.X.X'
      ELSE dstaddr
    END AS nat_dstaddr,
    -- DYNAMODB の IP をまとめる
    CASE
      WHEN srcaddr LIKE '35.71.114.%' THEN 'DYNAMODB'
      WHEN srcaddr LIKE '13.248.70.%' THEN 'DYNAMODB'
      WHEN srcaddr LIKE '13.248.69.%' THEN 'DYNAMODB'
      WHEN srcaddr LIKE '52.94.8.%' THEN 'DYNAMODB'
      ELSE srcaddr
    END AS internet_srcaddr,
    SUM(bytes) AS total_bytes,   -- 宛先 + 送信元ごとの合計バイト数
    COUNT(*) AS connection_count -- ログ件数(接続数の目安)
  FROM filtered
  GROUP BY
    CASE
      WHEN dstaddr LIKE '10.4.%' THEN '10.4.X.X'
      ELSE dstaddr
    END,
    CASE
      WHEN srcaddr LIKE '35.71.114.%' THEN 'DYNAMODB'
      WHEN srcaddr LIKE '13.248.70.%' THEN 'DYNAMODB'
      WHEN srcaddr LIKE '13.248.69.%' THEN 'DYNAMODB'
      WHEN srcaddr LIKE '52.94.8.%' THEN 'DYNAMODB'
      ELSE srcaddr
    END
)

-- 最終出力: トラフィック割合計算・上位10件抽出
SELECT
  a.nat_dstaddr,
  a.internet_srcaddr,
  a.total_bytes,
  a.connection_count,
  ROUND(100.0 * a.total_bytes / t.total_bytes_all, 2) AS traffic_percentage
FROM aggregated a
CROSS JOIN total t
ORDER BY a.total_bytes DESC
LIMIT 10;

備考:DynamoDB の IP プレフィックスは AWS の公式 JSON から取得しました。

% curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | jq -r '.prefixes[] | select(.region=="ap-northeast-1" and .service=="DYNAMODB") | .ip_prefix'
13.248.70.0/24
35.71.114.0/24
52.94.8.0/24
13.248.69.0/24

分析結果は以下のとおりで、通信の半数以上が DynamoDB と、Cloudflare(146.75.114.208)および Fastly(199.232.150.208) への通信で占められていることがわかりました。

#    nat_dstaddr internet_srcaddr    total_bytes connection_count    traffic_percentage
1   10.4.X.X    DYNAMODB    1658127018615   435978  39.26
2   10.4.X.X    146.75.114.208  754961412736    15401649    17.88
3   10.4.X.X    199.232.150.208 743897485309    15203516    17.62
4   10.4.X.X    10.4.22.215 25298862871 115011  0.6
5   10.4.X.X    10.4.18.176 24988053076 751348  0.59
6   10.4.X.X    10.4.21.208 22521758850 944241  0.53
7   10.4.X.X    10.4.68.240 22311573048 2017128 0.53
8   10.4.X.X    10.4.39.7   22082756323 102176  0.52
9   10.4.X.X    10.4.47.208 20542447201 1323195 0.49
10  10.4.X.X    10.4.33.173 18364562660 1283315 0.43

次に、BytesOutToDestination(外向き通信)を対象に、どのサービスへの通信が発生しているのかを確認しました。 Datadog の IP アドレスが複数に分散しており全体像をつかみにくかったため、IP アドレスをグルーピングして集計し、通信量を把握しました。

クエリ

-- 共通フィルタ: 対象のVPCフローログだけを抽出
WITH filtered_out AS (
  SELECT *
  FROM production_vpclogs_pp
  WHERE
    region = 'ap-northeast-1'
    AND account_id = 'XXXXXXXXXXXX'
    AND day = '2025/10/01'
    AND action = 'ACCEPT'
    AND interface_id IN ('eni-XXXXXXXXXXXXXXX', 'eni-XXXXXXXXXXXXXXX', 'eni-XXXXXXXXXXXXXXX') -- NAT Gateway ENI
    AND srcaddr IN ('10.4.XXX.XXX', '10.4.XXX.XXX', '10.4.XXX.XXX') -- NAT Gateway IP
    AND dstaddr NOT LIKE '10.%' -- VPC 内通信は除外
),

-- 全体トラフィック量を算出
total_out AS (
  SELECT SUM(bytes) AS total_bytes_all
  FROM filtered_out
),

-- 宛先ごと・送信元ごとに集計
aggregated_out AS (
  SELECT
    CASE
      -- 3.233.144.0/20 の範囲すべてを DATADOG とする
      WHEN dstaddr BETWEEN '3.233.144.0' AND '3.233.159.255' THEN 'DATADOG'
      ELSE dstaddr
    END AS nat_dstaddr,
    SUM(bytes) AS total_bytes,
    COUNT(*) AS connection_count
  FROM filtered_out
  GROUP BY
    CASE
      WHEN dstaddr BETWEEN '3.233.144.0' AND '3.233.159.255' THEN 'DATADOG'
      ELSE dstaddr
    END
)

-- 最終出力: トラフィック割合計算・上位10件抽出
SELECT
  a.nat_dstaddr,
  a.total_bytes,
  a.connection_count,
  ROUND(100.0 * a.total_bytes / t.total_bytes_all, 2) AS traffic_percentage
FROM aggregated_out a
CROSS JOIN total_out t
ORDER BY a.total_bytes DESC
LIMIT 10;

備考:Datadog の IP プレフィックスは Datadog の公式から取得しました。

% curl -s https://ip-ranges.datadoghq.com/ | jq -r '.agents.prefixes_ipv4[]'
3.233.144.0/20

分析結果は以下のとおりで、BytesOutToDestination(外向き通信)は BytesInFromDestination(内向き通信)より少ないものの、Datadog 向けの通信が大半を占めていました。

#    nat_dstaddr total_bytes connection_count    traffic_percentage
1   DATADOG 342778815422    851024  90.42
2   146.75.114.208  12562741631 7355795 3.31
3   199.232.150.208 12239913242 7177066 3.23
4   20.27.177.113   3552549434  258 0.94
5   35.200.109.179  629592462   333245  0.17
6   142.250.207.42  472644269   6701    0.12
7   142.250.198.10  210632046   3540    0.06
8   142.250.207.10  178655666   2770    0.05
9   142.251.222.42  171598056   2954    0.05
10  142.251.42.170  147644175   3358    0.04

NAT Gateway の通信削減の取り組み

これまでの分析により、NAT Gateway 経由の通信が特定の宛先に集中していることが分かりました。特に通信量が多かったのは以下の 3 つです。

  • DynamoDB(AWS サービス)
  • Datadog
  • Imgix(Cloudflare / Fastly 経由の画像圧縮サービス)

この中でも、最も大きなコスト要因となっていたのが DynamoDB でした。

DynamoDB の VPC Endpoint 作成

当初、DynamoDB への通信はすべて NAT Gateway を経由しており、データ転送量に応じてコストが増加する構造になっていました。S3 にはすでにゲートウェイ型 VPC Endpoint が作成されていたこともあり、同様のアプローチで DynamoDB に対しても VPC Endpoint を作成することにしました。

エンドポイント作成後に改めて VPC Flow Logs を確認したところ、NAT Gateway を経由した DynamoDB の通信は完全になくなっており、期待どおりの効果が得られました。

VPC Flow Logs の pkt-dst-aws-service 有効化

分析の初期段階では pkt-dst-aws-service を利用できず、自前クエリで通信状況を分析していましたが、後に pkt-dst-aws-service を有効化したことで、通信先をサービス名として識別できるようになり、AWS / 非 AWS を明確に区別しながら、より正確に通信状況を把握できるようになりました。

これにより、NAT Gateway 経由の通信をこれまで以上に詳細に分析できるようになりました。

コンテナイメージ取得の最適化

アソビューではサービス基盤を EKS で運用しているため、EKS ノードがコンテナイメージを取得する通信は日常的に発生しています。 pkt-dst-aws-service を用いた通信分析の結果、DynamoDB への通信は解消されたものの、ECR や ECR Public Gallery、その他のパブリックコンテナレジストリからのコンテナイメージ取得は依然として NAT Gateway を経由していることがわかりました。

ECR の VPC Endpoint 作成

ECR の VPC Endpoint を作成することで、これまで NAT Gateway を経由していた ECR の通信を削減できました。DynamoDB に続き、ECR の通信も最適化できました。

Pull Through Cache 導入

ECR Pull Through Cache を導入することで、ECR Public Gallery をはじめ、パブリックコンテナレジストリ(Docker Hub、GitHub Container Registry、Google Container Registry など)宛のコンテナイメージ取得も NAT Gateway を経由せずに行えるようになり、通信コストを削減できました。

参考:Amazon ECRプルスルーキャッシュを使ってみた - DMM Developers Blog

ネクストアクション

Datadog 通信の最適化

Datadog は PrivateLink を利用することで NAT Gateway を経由せずに通信できますが、アソビューでは Datadog(US1)と AWS(APN1 東京)でリージョンが異なるため、そのまま導入するとかえってコストが増加することがわかりました。このため、現状は NAT Gateway 経由での通信を継続しています。

一方で、Datadog へのメトリクス送信やエージェント通信はデータ量が多く、通信コストへの影響も小さくありません。今後は、メトリクス送信間隔の調整、カスタムメトリクスの整理、不要メトリクスの収集停止などを通じて、Datadog まわりの通信量とコストの最適化を進めていきます。

参考:AWS PrivateLink を使って Datadog AP1(東京)サイトに接続してみた | DevelopersIO

Imgix の最適化

アソビューではページ表示速度向上のため、画像最適化 CDN「imgix」を活用しています。imgix により、端末やネットワーク環境に応じて最適な画像を自動生成・配信できるため、ユーザー体験の改善に大きく貢献しています。

一方で、imgix との通信は Cloudflare や Fastly を経由するケースが多く、NAT Gateway の転送コストが増えやすいポイントでもあります。特にアクセスが集中する時間帯には通信量が大きく変動するため、今後はキャッシュ戦略の見直しや AWS 側のネットワーク構成の最適化など、より効率的な運用方法を模索していきます。

AWS 全体の最適化

今回の取り組みにより NAT Gateway のコスト最適化は大きく前進しましたが、まだまだ改善できる領域は多く残っています。

CloudWatch や S3 はデータ量や保持期間によってコストが積み上がりやすいサービスの一例ですが、ちょっとした設定の見直しで最適化できるケースもあります。 今後はこれらに限らず、AWS 全体の運用コストを定期的に評価しながら、継続的な改善を進めていきたい思います。

おわり

今回の取り組みは、NAT Gateway のコスト削減にとどまらず、アソビューにおける AWS の利用状況や通信の実態をより深く理解するきっかけにもなりました。 今後も改善を重ねながら、サービスの安定化とコスト最適化に取り組んでいきます。

アソビュー株式会社では新しいメンバーを随時募集しています!SRE チームに少しでも興味のある方は、ぜひお気軽にカジュアル面談にお越しください!!

speakerdeck.com

www.asoview.com