【実務・中級編】 VPCフローログのパケットサンプリング仕様と記録されない通信 – クラウド&コンテナネットワーク実践ガイド

こんにちは。SREチームのシニアエンジニアです。

本番環境で突然のレイテンシ急上昇や、原因不明の疎通不良(コネクション・タイムアウト)に直面したとき、あなたは何を頼りにパケットの足取りを追いますか? 多くのエンジニアがまず手を伸ばすのが、AWSのVCPフローログやGCPのVPCフローログ(以下、VPCフローログ)でしょう。

「あのマイクロサービス間通信は、本当に意図したルーティングを通っているのか?」
「タイムアウトしたAPIリクエストは、どこでドロップされたのか?」

インフラ運用の現場において、VPCフローログはまさに「夜道のヘッドライト」です。しかし、このヘッドライト、実は映る景色と映らない景色があることをご存知でしょうか? 「すべてのパケットが漏れなく記録されている」という幻想を抱いていると、障害調査の最中に痛い目を見ることになります。

今回は、VPCフローログの裏側にある「サンプリングの物理的現実」と「あえてログに残らないトラフィックの正体」について、パケットの挙動を追いながら徹底的に解説します。

—

1. なぜ「全パケット」は記録されないのか? VPCフローログのサンプリング仕様

まず大前提として知っておくべきなのは、VPCフローログはパケットキャプチャツール(tcpdumpやWiresharkなど)のミラーリングとは異なり、ハイパーバイザーレイヤーやソフトウェアDefinedな仮想ルーターの統計情報として非同期に集計されているという点です。

AWS(Amazon VPC)を例に取ると、フローログのデータはElastic Network Interface (ENI) を通過するトラフィックからサンプリングされます。ここで重要なのは、「サンプリングされるウィンドウ(集計期間)」と「記録のタイムラグ」です。

0秒で届かない「集計ウィンドウ」のリアル

VPCフローログのデフォルトの集計間隔(Maximumaggregation interval)は、AWSでは通常10分(または1分を選択可能)、GCPでも数秒〜数分単位のバッチ処理を挟みます。
つまり、実務で curl を叩いた瞬間にCloudWatch LogsやBigQueryへログがプッシュされるわけではありません。

[Client / API] ---> (HTTP Request) ---> [ENI (Hypervisor)]
                                              |
                                              v  (数分間のバッチ・集計バッファへ蓄積)
                                     [VPC Flow Logs Pipeline] ---> [CloudWatch Logs / S3]

この仕様により、短命なコネクション(例えば、ヘルスチェックや数発の疎通確認用Pingなど)は、集計のタイミングやサンプリングの確率によっては完全に統計の網から漏れ落ちることがあります。「さっきテストでリクエストを送ったのに、フローログに全くヒットしないんだけど……」という現象の多くは、この集計ウィンドウとサンプリングの仕様が原因です。

—

2. ログに残らない「幽霊パケット」たち:暗黙の除外トラフィック

ネットワークエンジニアとして最も注意しなければならないのが、「通信は正常に行われているのに、VPCフローログには1行も記録されないパケット」の存在です。これらはAWS/GCPのインフラストラクチャを維持するための制御用トラフィックであり、セキュリティや監査の観点から意図的に除外されています。

実務上、特にハマりやすい代表的な除外トラフィックを以下に挙げます。

1. デフォルトDNSサーバーへのクエリ

  • VPC内のEC2やGKEノードから、VPCの予約IP(例: VPCのベースIP + 2、AWSなら 169.254.169.253 や AmazonProvidedDNS)に対して行われるDNSクエリ。

2. DHCPトラフィック

  • インスタンスがIPアドレスを取得・更新するために行う DHCP discover / offer などのブロードキャスト/ユニキャスト通信。

3. メタデータサービスへのアクセス

  • AWSの 169.254.169.254(IMDSv1 / IMDSv2)に対するインスタンスからのメタデータ取得リクエスト。

4. Windowsライセンス認証(KMS)トラフィック

  • Windowsインスタンスが内部的に行うKMSサーバーへのアクティベーション通信の一部。

5. VPCルーター(デフォルトゲートウェイ)宛てのトラフィックの大部分

特に「DNSクエリ」がフローログに残らないという仕様は、マイクロサービス間で名前解決のトラブル(例: 内部ドメインの浸透漏れや、CoreDNSの負荷起因の応答遅延)をデバッグする際に致命的な見落としを生む原因になります。「アプリケーションはホスト名で接続しにいっているはずなのに、フローログには宛先IPへの SYN さえない」と悩んだときは、DNS名前解決のレイヤー(アプリ内のリゾルバや /etc/resolv.conf)を疑う必要があります。

—

3. 実践:VPCフローログ設定とパケット検証の作法

ここからは、実務でVPCフローログを正しく活用するための具体的な設定と、アプリケーション側からの検証アプローチを見ていきましょう。

AWS Terraformによる高度なフローログ設定

単にログを取るだけでなく、実務では「拒否されたパケット(REJECT)」や「カスタムフォーマット」を適切に設計することが求められます。以下のTerraformコードは、パケットの双方向(ALL)をキャプチャし、トラブルシューティングに必要なフィールドを網羅した推奨設定です。

# ターゲットとなるVPCのIDを指定
resource "aws_flow_log" "main_vpc_flow_log" {
  iam_role_arn    = aws_iam_role.flow_logs_role.arn
  log_destination = aws_cloudwatch_log_group.flow_logs_group.arn
  traffic_type    = "ALL" # ACCEPT(許可)と REJECT(拒否)の両方を記録
  vpc_id          = aws_vpc.main.id

  # 実務のデバッグで役立つカスタムログフォーマットの定義
  # 始点、終点、ポート番号に加え、パケットの向き(tcp-flags)やフロー方向を詳細に記録
  log_format = "$version $account-id $interface-id $srcaddr $dstaddr $srcport $dstport $protocol $packets $bytes $start $end $action $log-status $tcp-flags"

  tags = {
    Name        = "production-vpc-flow-log"
    Environment = "production"
  }
}

# CloudWatch Logs グループの定義
resource "aws_cloudwatch_log_group" "flow_logs_group" {
  name              = "/aws/vpc/production-flow-logs"
  retention_in_days = 30 # 本番環境では最低30日以上の保持を推奨
}

アプリケーション側からの疎通検証コード(Python)

サンプリングとログのタイムラグを考慮しつつ、APIクライアントからターゲットサーバーへリクエストを送り、その挙動をシミュレートするPythonスクリプトの例です。

import time
import urllib.request
import urllib.error

# 接続先のエンドポイント(内部ALBやマイクロサービスのIP/ドメイン)
TARGET_URL = "http://internal-api.example.local/healthz"

def test_api_connectivity():
    print(f"[{time.strftime('%Y-%m-%d %H:%M:%S' )}] 疎通テストを開始します: {TARGET_URL}")
    
    req = urllib.request.Request(
        TARGET_URL,
        headers={"User-Agent": "SRE-Network-Validator/1.0"}
    )
    
    try:
        # タイムアウトを3秒に設定し、ハングアップを検知できるようにする
        with urllib.request.urlopen(req, timeout=3.0) as response:
            status_code = response.getcode()
            print(f"-> 成功: HTTPステータスコード {status_code}")
            
    except urllib.error.HTTPError as e:
        # 4xxや5xx系エラーの場合も、ネットワーク層としては通信できている(REJECTではなくACCEPT)
        print(f"-> HTTPエラー発生: {e.code} (ネットワーク層の疎通は成功しています)")
        
    except urllib.error.URLError as e:
        # ここでタイムアウトや名前解決失敗が発生した場合、
        # VPCセキュリティグループやNACLによる「REJECT」または「DNS除外」の可能性を疑う
        print(f"-> 接続失敗(ネットワーク/DNS異常の可能性): {e.reason}")
        print("   ※ TIPS: この種の瞬断や短命なエラーは、VPCフローログの集計ウィンドウ(数分)のタイムラグに注意してください。")

if __name__ == "__main__":
    test_api_connectivity()

—

4. 現場のシニアが教えるトラブルシューティング・Tips

最後に、現場で数々のネットワーク障害を切り抜けてきた私から、VPCフローログを扱う上での実践的なTipsをいくつか授けます。

1. 「REJECT」ログが出たらまずセキュリティグループとNACLの両方を見る

  • フローログの action フィールドが REJECT になっている場合、AWSのセキュリティグループ(SG)か、サブネット単位のネットワークACL(NACL)のどちらで弾かれたのかを特定する必要があります。NACLはステートレス(往復の制御が必要)なため、エフェメラルポート(高位ポート)の戻りパケットがブロックされているケースが非常に多いです。

2. 短いパケットのロストには tcpdump や AWS VPC Reachability Analyzer を併用する

  • サンプリングや集計遅延の壁にぶぶんだときは、VPCフローログだけで原因究明をしようとせず、一時的にホスト側で tcpdump -nnvvS を実行するか、AWS公式の「VPC到達可能性アナライザー(Reachability Analyzer)」を使いましょう。静的なルーティングとポリシーの不備をミリ秒単位で一発特定できます。

3. DNSやメタデータの挙動不審はフローログを「見ない」

  • 前述の通り、これらはログに乗らないため、アプリケーション層のログ(例: Pythonの socket.gaierror や、Goの lookup i/o timeout)を直接解析することが解決への近道です。

まとめ

VPCフローログは強力な武器ですが、「万能の全知全能のログ」ではありません。
サンプリングの仕組み、集計のタイムラグ、そしてインフラ制御用トラフィックが持つ「映らない」という特性を深く理解しておくこと。それこそが、難解なクラウドネットワークの障害からシステムを守り抜く、真のSREのスキルなのです。

次回のネットワーク設計や障害対応の際には、ぜひ今回の話を思い出してみてください。あなたのインフラストラクチャが、より堅牢で透明性の高いものになることを願っています。

コメント

タイトルとURLをコピーしました