【実務・中級編】 セキュリティグループのルール制限数(ルールスパイク)とパフォーマンス影響 – クラウド&コンテナネットワーク実践ガイド

セキュリティグループの「ルールスパイク」がクラウドのパケット処理を蝕む日:上限値の罠とハイパーバイザーの深層

こんにちは。クラウドの底なし沼で日々パケットの行方を追いかけているシニアSREの私です。

夜中に「突然、特定のマイクロサービス間のレイテンシが跳ね上がった」「APIの応答速度がランダムに数ミリ秒〜数十ミリ秒遅延する」というアラートに叩き起こされた経験はないでしょうか。CPU使用率は正常、メモリも枯渇していない、アプリケーションコードにも変更はない……。

そんなミステリアスなインフラ障害の裏で、ひっそりと牙を向いているのが今回取り上げる「セキュリティグループ(Security Group: SG)のルールスパイク」です。

教科書的なクラウド入門書には「セキュリティグループはインスタンス単位のステートフルなファイアウォールです。ポートやCIDRを柔軟に設定できます」と美しく書かれています。しかし、実務の現場では、マイクロサービスの増殖、セキュリティチームからの厳格なコンプライアンス要件(「この踏み台からのみアクセスを許可しろ」「社内IPレンジの全変化に追従させろ」)、そして自動化スクリプトの暴走によって、1つのSGに数百、数千ものルールが積み上げられていく「ルールスパイク」が日常茶飯事のように発生しています。

今回は、このSGのルール数が、AWSやGCPといったメガクラウドのハイパーバイザー層でどのようなパケット処理のオーバーヘッドを引き起こし、なぜレイテンシを悪化させるのか。そのメカニズムと、私たちが実務で取るべき設計上の最適化について、現場の泥臭い知見を交えて徹底的に解説します。

—

1. セキュリティグループの裏側:パケットはハイパーバイザーでどう裁かれるか

まず、クラウドのパケット処理の物理的な現実からお話ししましょう。

私たちがAWSのEC2やGCPのCompute Engine上で動かす仮想マシン(VM)は、物理サーバー上で稼働するハイパーバイザー(AWSであればNitro Systemなど)によって仮想化されています。VMが出入りするすべてのネットワークパケットは、このハイパーバイザー層を経由します。

ここで重要なのは、セキュリティグループのステートフルな判定処理(SPI: Stateful Packet Inspection)を行っているのは、OS内のカーネル(iptablesやnftablesなど)ではなく、ハイパーバイザー層の仮想スイッチ、あるいは専用のハードウェアアクセラレーターであるという点です。

ルールスパイクが引き起こす「O(N)の呪縛」

セキュリティグループに設定されたルール数は、パケットフィルタリングのアルゴリズムに直結します。

もしクラウドプロセッサがナイーブな線形探索(Linear Search)に近い処理を行っていた場合、インバウンド/アウトバウンドのルール数が $N$ に比例して、パケットごとのマッチング処理コスト(CPUサイクル)が増加します。仮にハッシュ化やツリー構造による最適化が図られていたとしても、ルール数が数千規模に膨れ上がると、キャッシュミスの確率が跳ね上がり、パケットの評価フェーズでマイクロ秒単位の遅延が発生します。

たかが「数ミリ秒の遅延」と思われるかもしれませんが、Kubernetesやマイクロサービスアーキテクチャでは、1つのユーザーリクエストが内部で10個、20個のRPC(gRPCやHTTP/REST)を直列・並列に呼び出します。このネットワークパスの各所で数ミリ秒の遅延が積み重なると、最終的なAPIのレイテンシは致命的な劣化(テールレイテンシの悪化)を引き起こすのです。

—

2. クラウド各社のルール上限値と「隠れたコスト」

主要なクラウドプロバイダーにおける、1つのセキュリティグループあたりのルール数のデフォルト上限(およびソフトリミット)を見てみましょう。

| クラウドサービス | 対象リソース | デフォルト上限(目安) | 備考 |
| :— | :— | :— | :— |
| AWS | セキュリティグループ (SG) | インバウンド 60 / アウトバウンド 60 (デフォルト) | 最大 1,000 まで申請・拡張可能 |
| GCP | ファイアウォールルール (VPC Firewall) | プロジェクトあたり 1,500 〜数千 | ルール評価順序(priority)とタグ・サービスアカウントで制御 |
| Azure | ネットワークセキュリティグループ (NSG) | サブネット/NICあたり 1,000 〜 4,000 | ルール数に応じて帯域や処理に影響を与える可能性あり |

「なんだ、AWSなら最大1,000ルールまで増やせるじゃないか」と思ったそこのあなた。ここに最大の落とし穴があります。

システム的・制限的な上限(Limit)まで設定できるからといって、パフォーマンス上の推奨値(Best Practice)であるとは限らないのです。実際にAWSの公式ドキュメントの片隅にも、「ルール数が多いほど、セキュリティグループに関連付けられたネットワークインターフェイス(ENI)のパケット処理性能に影響を与える可能性がある」という旨が記載されています。

—

3. 現場で起きた悲劇:デバッグの足取りと実証コード

私のチームが以前遭遇した障害は、まさにこのルールスパイクが原因でした。ある認証基盤APIサーバーのフロントに配置されたSGに、過去数年間のデプロイで不要になったIP制限ルールが蓄積され、なんと1つのSGあたり約850ルールに達していたのです。

この環境に対して、負荷検証ツール(Locustやwrk)を用いてスループットを上げたところ、CPUやメモリに余裕があるにもかかわらず、p99レイテンシが異常値を示し始めました。

障害切り分けのための検証スクリプト

現場で私たちがパケットの往復レイテンシを精密に計測するために使用した、Pythonによる簡易ベンチマークスクリプトの例を以下に示します。実務では、単なる ping ではなく、アプリケーションレイヤー(HTTP Keep-Aliveを有効にした状態)でレイテンシの分布を測ることが鉄則です。

import time
import statistics
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed

# ターゲットとなるAPIのエンドポイント(内部ロードバランサーや直接のENI)
TARGET_URL = "http://internal-api-service.example.com/healthz"
CONCURRENCY = 50
TOTAL_REQUESTS = 1000

def send_request():
    """
    HTTPリクエストを送信し、往復時間(Round-Trip Time)をミリ秒単位で計測する
    """
    start_time = time.perf_counter()
    try:
        # Keep-Aliveを有効にするためにSessionを使用
        with requests.Session() as session:
            response = session.get(TARGET_URL, timeout=3.0)
            # ステータスコードが200以外の場合はエラーとして扱う
            if response.status_code == 200:
                elapsed = (time.perf_counter() - start_time) * 1000.0
                return elapsed
    except requests.RequestException:
        pass
    return None

def main():
    print(f"Starting latency benchmark against {TARGET_URL} with concurrency={CONCURRENCY}...")
    
    latencies = []
    with ThreadPoolExecutor(max_workers=CONCURRENCY) as executor:
        futures = [executor.submit(send_request) for _ in range(TOTAL_REQUESTS)]
        
        for future in as_completed(futures):
            result = future.result()
            if result is not None:
                latencies.append(result)

    if latencies:
        print("\n--- Benchmark Results ---")
        print(f"Total Successful Requests: {len(latencies)} / {TOTAL_REQUESTS}")
        print(f"Min Latency:  {min(latencies):.2f} ms")
        print(f"Mean Latency: {statistics.mean(latencies):.2f} ms")
        print(f"Median (p50): {statistics.median(latencies):.2f} ms")
        # 99パーセンタイルの算出し、テールレイテンシの悪化を確認する
        latencies.sort()
        p99_idx = int(len(latencies) * 0.99)
        print(f"p99 Latency:  {latencies[p99_idx]:.2f} ms")
    else:
        print("All requests failed.")

if __name__ == "__main__":
    main()

このスクリプトをルール数が肥大化したSG環境と、適切にリファクタリングした環境(ルール数20以下)で実行したところ、p99レイテンシに歴然とした差(約18ms vs 約2.5ms)が現れました。見た目のエラーレートは0%であっても、ネットワーク層のパケット処理遅延が確実にアプリケーションの応答時間を蝕んでいたのです。

—

4. ルールスパイクを防ぐための設計上の最適化プラクティス

では、このルールスパイク問題に対し、SREやクラウドアーキテクトはどのように立ち向かうべきでしょうか。実務で即座に採用できる4つの設計アプローチを授けます。

① セキュリティグループの「参照(SG-to-SG)」を活用する

個別のアドレス(IP CIDR)を何百個も並べるのは悪手です。AWSなどのクラウドでは、セキュリティグループ同士をソースとして指定できます。

例えば、Web層からDB層へのアクセス許可を与える場合、個別のIPを書くのではなく、sg-web-tier というセキュリティグループ自体を sg-db-tier のインバウンドルールに指定します。これにより、Webサーバーの台数が何台にスケールアウトしようとも、ルール数は常に「1」のまま維持されます。

② プレフィックスリスト(Prefix Lists)の導入

外部のSaaSプロバイダーや、社内の特定拠点ネットワークなど、どうしてもIP CIDRを指定せざるを得ない場合があります。AWSでは「カスタマーマネージドプレフィックスリスト」を利用することで、最大数十〜数百のCIDRブロックを1つのオブジェクトとしてまとめ、それをSGのルール内で参照できます。

IPリストの変更が発生した場合も、SG自体のルールを書き換えるのではなく、プレフィックスリストのバージョンを更新するだけで済むため、IaC(Terraform等)の差分管理も極めてクリーンになります。

③ マイクロセグメンテーションの粒度を見直す

「何でもかんでも1つのSGに詰め込む」アンチパターンを廃し、責務ごとに適切な粒度でSGを分割、あるいは統合します。

  • 過度に細分化しすぎて、1つのENIに大量のSGをアタッチするのも、ハイパーバイザー側の評価マトリクスを複雑化させる原因になるため注意が必要です(AWSでは1つのENIあたりにアタッチできるSGの数にも制限があります)。
  • 「共通のライフサイクルを持ち、通信要件が同一のワークロード群」単位でSGを設計するのが黄金律です。

④ IaC(Terraform / Pulumi)による自動リントとガバナンス

人間が手動でAWSコンソールをポチポチ操作してルールを追加していくと、必ず「とりあえず追加したけど、もう使われていない古いルール」が放置されます。

TerraformなどのIaCコードレビュー時に、静的解析ツール(例: tflint や Checkov)や自作のカスタムLinterを組み込み、「1つのSGあたりのルール数が50を超えたらアラートを出す」「3ヶ月以上使われていない未使用のCIDRがないか監査する」といったパイプラインを構築しましょう。

—

5. まとめ:ネットワークの「見えない負荷」に目を向けよう

クラウドネイティブな世界において、ネットワークは「あって当たり前のインフラ」として抽象化されがちです。しかし、どれほどコンテナオーケストレーションが洗練され、アプリケーションのコードが高速化されても、その下を流れるパケットは必ず物理的なハイパーバイザーとクラウドのネットワーク仮想化レイヤーを通過しています。

セキュリティグループのルールスパイクは、アプリケーション開発者からは見えにくい「隠れた性能劣化の爆弾」です。

「動いているからよし」とするのではなく、定期的なルール棚卸しの文化を作り、SG-to-SGの参照やプレフィックスリストを駆使して、パケットが駆け抜ける道筋を常に美しく、スリムに保つこと。それこそが、真にスケーラブルで信頼性の高いシステムを支えるシニアSREの技量です。

今日のデプロイが終わったら、あなたのプロダクション環境のセキュリティグループのルール数を一度確認してみてはいかがでしょうか。もしかしたら、不要なルールの山が、あなたのAPIの数ミリ秒をこっそり奪っているかもしれません。

コメント

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