VPCフローログ1年半運用で月15万円の請求に気づいた話

セキュリティ監査で導入したVPCフローログ、実は垂れ流しで月15万円の請求に。Athena×Grafanaで本番運用して気づいた費用削減と実装の落とし穴を共有します。

VPCフローログ、導入したら放置してた話

うちのチームがVPCフローログを有効化したのは2024年の中ごろ。セキュリティ監査が入るタイミングで「ネットワークトラフィックの可視化が必要」って言われたんですよね。最初は「じゃあ有効化しよう」くらいの軽い気持ちで、CloudWatch Logsに放り込んでたんです。

そしたら3ヶ月後、請求を見てぶっ倒れました。VPCフローログだけで月15万円。本来なら監視と分析に使う予定だったのに、ただ垂れ流されてるだけ。これはヤバい。

そこからチームで本気で取り組むことにして、Athena + Grafana構成に移行して、1年半本番運用してきたんです。今日はそこで学んだ実装の現実的な話をしたいと思います。

VPCフローログの費用構造——なぜ爆上がりするのか

最初、僕らが失敗したのは「VPCフローログを有効化した」で安心しちゃったこと。実際にはいくつか決定ポイントがあるんですよ。

ログの取得レベル選択

VPCフローログには大きく3つのレベルがあります:

  • ACCEPT:許可されたトラフィックのみ
  • REJECT:拒否されたトラフィックのみ
  • ALL:両方

うちは最初ALLにしてたんです。理由は「セキュリティは慎重に」という心がけだったんですが、これが費用を3倍にしちゃった。REJECTだけでも十分な異常検知ができるってことに、1年後に気づきました。正直、最初からREJECTにしておけばなって思いますね。

保存先の選択が重要

保存先によって費用体系がガラッと変わります。実際のコスト比較はこんな感じです:

保存先月額(100万フロー)クエリ費用向いてる用途
CloudWatch Logs¥30,0000リアルタイム短期監視
S3¥3,000¥5,000長期保存・分析
Kinesis Firehose¥8,000¥5,000ストリーム処理・リアルタイム

CloudWatch Logsはリアルタイム性はいいんですが、保存コストが致命的。うちは結局S3 + Athena構成に完全移行しました。保存は安いけど、クエリのたびに費用がかかるので、ここも工夫が必要なんですよ。

実装パターン——S3 + Athena + Grafanaで何が見えるか

先日チームで構成図を描き直したので、現状のアーキテクチャを紹介します。

graph TB
    subgraph VPC["VPC リージョンA"]
        ENI["ENI(複数)"]
        SG["Security Group"]
    end
    
    subgraph Logging["ログ保存層"]
        VPCFlow["VPC Flow Logs"]
        S3Raw["S3(Raw)<br/>パーティション: s3://date/hour/"]
        S3Processed["S3(Processed)<br/>Glueで整形"]
    end
    
    subgraph Query["クエリ・分析層"]
        Athena["Amazon Athena<br/>標準SQL"]
        Glue["AWS Glue<br/>ETL/カタログ"]
    end
    
    subgraph Viz["可視化・監視層"]
        Grafana["Grafana"]
        EventBridge["EventBridge<br/>異常検知ルール"]
        SNS["SNS<br/>通知"]
    end
    
    ENI --> VPCFlow
    VPCFlow --> S3Raw
    S3Raw --> Glue
    Glue --> S3Processed
    S3Raw --> Athena
    S3Processed --> Athena
    Athena --> Grafana
    Athena --> EventBridge
    EventBridge --> SNS

重要な実装のポイント

1. S3のパーティション設計

VPCフローログってめちゃくちゃ量が多いので、パーティショニングを正しくやらないとAthenaのクエリが遅くなります。うちはs3://bucket/vpc-flow-logs/region=ap-northeast-1/year=2026/month=05/day=11/hour=14/という階層構成にしてます。

-- パーティション設計が効いたクエリ例
SELECT 
  srcaddr, 
  dstaddr, 
  dstport,
  COUNT(*) as flow_count
FROM vpc_flow_logs
WHERE year = 2026 
  AND month = 5 
  AND day = 11
  AND action = 'REJECT'
GROUP BY srcaddr, dstaddr, dstport
ORDER BY flow_count DESC

パーティション指定なしだと同じクエリで30秒かかってたのが、ちゃんと指定したら2秒に短縮されました。地味に便利ですよ。

2. Glueで定型的な変換を自動化

VPCフローログのRawデータって、タイムスタンプがUNIXタイムとか、IPが数値になってたり、フォーマットが微妙に扱いづらいんです。毎回のクエリで変換してたら費用が跳ね上がる。

うちはGlue Jobで毎時間、昨日のデータを整形してS3 Processedに保存してます。

# Glue Job例
import awswrangler as wr
import pandas as pd

# Raw S3から読み込み
df = wr.s3.read_parquet(
    path='s3://bucket/vpc-flow-logs/raw/...',
    dataset=True
)

# タイムスタンプをパース
df['timestamp'] = pd.to_datetime(df['start'], unit='s')
df['srcaddr_readable'] = df['srcaddr']  # 実際にはIP逆引きも

# 整形済みで保存
wr.s3.to_parquet(
    df,
    path='s3://bucket/vpc-flow-logs/processed/...',
    dataset=True,
    mode='overwrite'
)

これだけで、Athenaのクエリ費用が月3万円削減されました。やる価値ありますね。

3. Grafanaダッシュボードで異常を即座に発見

うちが実装した監視ダッシュボードで、時間別のREJECT数を可視化するとこんな感じです:

xychart-beta
    title VPCフローログ監視:時間別REJECT数
    x-axis [00, 02, 04, 06, 08, 10, 12, 14, 16, 18, 20, 22]
    y-axis "REJECT Count" 0 --> 5000
    line [100, 120, 95, 150, 200, 180, 320, 2800, 1500, 600, 400, 280]

このグラフだと、14:00に異常な数のREJECTが出てることが一目瞭然。これが不正なポートスキャンなのか、設定ミスなのか、さっと調査できるようになったんですよ。

実運用で踏んだ落とし穴

1. False Positive地獄

半年運用してわかったのは、REJECTログのほぼ80%がノイズだということ。具体的には:

  • ヘルスチェック失敗(ELBが不正なセキュリティグループに向かった)
  • VPC内でのDNS解決失敗
  • 期限切れのNAT Gateway設定

こういった定型的なパターンばかりが引っかかるんです。ルールベース検知だけだと、運用チームが疲弊します。うちは3ヶ月目に「異常検知がうるさすぎる」ってクレームを受けました。

対策:機械学習ベースの異常検知

2025年中ごろから、Grafana Alerting + SimpleExponentialSmoothing(SES)で、「通常と大きく乖離したパターン」だけを検出するようにしました。

-- Grafanaアラートルール例(Prometheus QL)
abs(
  rate(reject_flow_count_5m[5m]) 
  - 
  avg_over_time(
    rate(reject_flow_count_5m[5m] offset 7d)[1d:5m]
  )
) > avg_over_time(
  rate(reject_flow_count_5m[5m] offset 7d)[1d:5m]
) * 0.3  -- 通常値から30%以上の乖離

これにしてからアラートが1/4に減りました。やっぱり機械学習的なアプローチが効くんですね。

2. VPCピアリングやPrivateLink経由のトラフィック

うちは複数のVPCを運用してるんですが、VPCフローログの「怖さ」は見えすぎることなんです。

ピアリング先のVPCで何かが起きると、自分たちのフローログに記録される。でも自分たちには何もしようがないんですよね。こういう時は本当にイライラします。

対策:VPC単位でログを分離し、タグで責任範囲を管理

# クエリ時点でフィルタリング
SELECT *
FROM vpc_flow_logs
WHERE vpcid = 'vpc-xxxxx'  -- 自VPCに限定
AND srcsubnetid LIKE 'subnet-xxxxx%'  -- 自サブネット限定
AND action = 'REJECT'

見える範囲を限定することで、チーム間の責任境界をはっきりさせられました。これで誰が対応するのかが明確になったんです。

3. 日中と深夜のトラフィックパターンの違い

これは監視設定を作る上で痛い失敗だったんですが、朝のデプロイラッシュと深夜の定期バッチで、トラフィックパターンが全然違うんです。

おかしなパターンを固定ルールで検知しようとすると、どっちかがアラートになる。困りますよね。

対策:時間帯別のベースライン学習

Grafanaのアラートを時間帯別に設定する、あるいは1週間の周期を学習させる。最初は手動でしたが、今は簡単なPythonスクリプトでベースラインを自動更新してます。

実装する上で注意すべきこと

コスト面でのバランス

正直なところ、全部やると費用が膨らみます。うちの現実的な費用構成はこんな感じです:

  • VPCフローログ有効化:月数千円
  • S3保存:月数千円
  • Athenaクエリ:月数万円(工夫次第)

全部合わせて月3~5万円の範囲に落ち着きました。最初の月15万円から考えると、かなり改善できたんですよ。

Athenaクエリのコツ

避けるべきパターンと推奨パターンをまとめました:

-- ❌ 避けるべき:全行スキャン
SELECT * FROM vpc_flow_logs WHERE srcaddr LIKE '%192%';

-- ✅ 推奨:パーティション指定
SELECT * 
FROM vpc_flow_logs 
WHERE year=2026 AND month=5 AND day=11 
AND srcaddr = '192.168.1.100';

同じ結果でも、10倍コストが違う。Athena Query Resultsのスキャンバイト数を常に見て、クエリを改善する癖をつけるのが大事ですね。

セキュリティ監査対応

VPCフローログは【REJECT:不正アクセス試行】を検出できるので、セキュリティ監査ではかなり評価されます。ただし、以下を運用ルール化しておかないと、監査で突っ込まれます:

  • ログの改ざん対策(S3のバージョニング、Object Lock)
  • ログの長期保存ポリシー
  • アクセス権限の制限

この辺の話はSOC2対応についての別記事にも詳しく書いてます。

異常検知の実例

1年半運用してて、実際に検出できた異常をいくつか紹介しましょう。

事例1:ポートスキャン検出

同一の送信元IPが、短時間に複数の宛先ポートへREJECTを出してた。これはセキュリティグループの設定ミスじゃなく、実際の外部からの侵入試行だったんです。早期対応で被害を防げました。良かった。

事例2:DNS設定エラー

深夜に急にREJECT数が増えた。調べたら、あるマイクロサービスがDNS解決失敗でクエリを垂れ流してた。フローログがなければ、翌朝まで気づかなかった可能性が高い。

事例3:ネットワークセグメンテーション設計ミス

想定外のVPC間通信が増えてた。これはネットワーク設計の見直しきっかけになりました。正直、この発見があっただけでもVPCフローログ導入の価値があったなって思いますね。

Grafana設定の実装例

うちが使ってるダッシュボード定義はこんな感じです(簡略版):

{
  "dashboard": {
    "title": "VPC Flow Logs Monitoring",
    "panels": [
      {
        "title": "REJECT Count by Hour",
        "targets": [
          {
            "rawSql": "SELECT DATE_TRUNC('hour', timestamp) as hour, COUNT(*) FROM vpc_flow_logs WHERE action='REJECT' GROUP BY 1 ORDER BY 1 DESC"
          }
        ]
      },
      {
        "title": "Top Rejected Ports",
        "targets": [
          {
            "rawSql": "SELECT dstport, COUNT(*) as count FROM vpc_flow_logs WHERE action='REJECT' GROUP BY dstport ORDER BY count DESC LIMIT 10"
          }
        ]
      }
    ]
  }
}

これをGrafanaのAthenaプラグインで接続するだけで、ほぼリアルタイムな可視化ができます。正直、セットアップが簡単なので驚きました。

チーム運用での工夫

責任範囲の明確化

VPCフローログは「全体のネットワークが見える」ってのが両刃の剣なんです。インフラチームの責務なのか、セキュリティチームなのか、各チームが責任を負う部分と他責な部分を整理しておかないと、誰も対応しない状況になっちゃいます。

うちは責任を次のように分離してます:

  • セキュリティチーム:REJECT異常、ポートスキャン検知
  • インフラチーム:VPC設定エラー、DNS問題対応
  • 各サービスチーム:自サービスのセキュリティグループ設定管理

こうすることで、誰が何をやるかが明確になったんですよ。

オンコール体制

VPCフローログの異常って、夜中に大量のREJECTが出ることもあります。ただし、本当に対応が必要な異常と、待てる異常を分けるのが大事。

深夜アラートは「情報通知のみ、翌朝判断」みたいなポリシーにすることで、オンコール疲弊を減らせました。やっぱり誤報が多い時間帯は無理に対応させない方が、長期的な運用品質が上がるんですね。

次のステップ:2026年の検討課題

ここからは、正直「まだ検証中」な領域ですが、個人的には気になってることです。

1. eBPFベースの可視化

VPCフローログより細粒度なネットワーク可視化ができるeBPF。コンテナセキュリティについての記事でも触れてますが、Falco、Ciliumなどのツールでアプリケーションレベルのネットワーク分析ができるようになってます。

2. VPC Latticeとの組み合わせ

EKS環境ではVPC Latticeでサービス間通信を管理するケースが増えてます。VPCフローログだけじゃ見えない部分があるんですよ。

3. ゼロトラスト・マイクロセグメンテーション

固定的なセキュリティグループルール設定じゃなく、動的にトラフィック許可を判断する方向へ進んでる。VPCフローログはそれの「監査ログ」としての位置づけに変わるかもしれません。

まとめ

VPCフローログって「有効化して終わり」だと、ただの費用垂れ流しになるんですよね。自分たちの経験から言うと、実装で重要なのはこの5点です:

  1. 保存先をCloudWatch LogsからS3に変える → 月15万から3万に削減
  2. Athenaでクエリを書く際、パーティション指定を必ずする → クエリ費用を1/3に
  3. Glueで定期的にRawデータを整形 → アクセス性と費用のバランスが改善
  4. Grafanaで時系列ダッシュボード化し、ベースラインから乖離した異常だけをアラート → False Positiveが1/4に
  5. チーム間で責任範囲を明確に → 運用の持続性が大きく改善

正直、最初の3ヶ月は手探りで、失敗も多かった。でも1年半運用してきた今、VPCフローログはうちのネットワークセキュリティ監視の「中核」になってます。

みなさんはVPCフローログ、どう運用してます?同じ課題でハマってますか?コメント待ってます。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事