【実務・中級編】 VPCエンドポイント(Interface型 / PrivateLink)のENIベース通信 – クラウド&コンテナネットワーク実践ガイド

パケットはどこへ流れるのか?VPCエンドポイント(Interface型 / PrivateLink)のENIベース通信を紐解く

クラウドのインフラ設計において、インターネットの荒波にパケットをさらすことなく、安全にマネージドサービスや別VPCのリソースへと到達させる――これはシニアSREである私たちが日々頭を悩ませ、そして腕の見せ所でもあるポイントです。

特に、AWSのVPCにおいて「VPCエンドポイント(Interface型 / AWS PrivateLink)」を導入する際、単に「マネージドコンソールでポチポチッと作成して終わり」にしていないでしょうか。裏側で何が起きているのかを知らずに設計すると、いざトラブルシューティングに直面したとき、「なぜか名前解決できない」「マルチAZで片系だけ死活監視に失敗する」といった泥沼にハマることになります。

今回は、このVPCエンドポイントの心臓部である「ENI(Elastic Network Interface)」ベースの通信が、ルーティングやDNSのレイヤーでどのように処理されているのか、そのリアルな挙動を現場の知見を交えて徹底解説します。

—

1. Interface型VPCエンドポイントの正体とENIの挙動

まず大前提として、Interface型VPCエンドポイントの本質を理解しましょう。これは、AWSがマネージドで提供するネットワークデバイス(ENI)を、「あなたのVPC内の指定したサブネットにぶら下げる機能」に他なりません。

パケットの挙動をイメージしてみましょう。
アプリケーションからAWSサービス(例えばAmazon S3やSecrets Manager、あるいは社内の別VPCで稼働する独自API)へリクエストを投げるとき、宛先として使われるのはインターネット上のグローバルIPではありません。あなたのVPCのプライベートIPアドレス、すなわちエンドポイントENIに割り当てられたプライベートIPv4アドレスです。

マルチAZ配置の鉄則

Interface型エンドポイントを作成する際、必ず複数のAZ(Availability Zone)を選択するよう求められます。これはAWSからの「可用性を担保しろ」という優しいメッセージであると同時に、ネットワーク設計上の絶対条件です。

  • クロスAZ通信の回避: アプリケーションがAZ-aにある場合、AZ-a内に配置されたエンドポイントENIへトラフィックを流すのが、レイテンシーとデータ転送コスト(クロスAZデータ転送費)の観点からベストです。
  • フェイルオーバー: 万が一、AZ-a側のENIやルートに障害が発生した場合、DNSのヘルスチェック機能やルートテーブルの制御によって、トラフィックが生きている別AZのENIへとルーティングされます。

—

2. プライベートDNSとパケットの旅(シーケンス)

「なぜ、通常のパケットがAWSのサービスに届くのか?」
その秘密は、プライベートDNS(Private DNS)の魔法にあります。

例えば、secretsmanager.ap-northeast-1.amazonaws.com というおなじみのFQDNを考えてみます。VPCエンドポイントの作成時にプライベートDNSを有効化すると、AmazonProvidedDNS(VPCのDNSサーバー、169.254.169.253 または VPC CIDRのベースIP + 2)は、このパブリックな名前に対して、パブリックIPではなくあなたのVPC内に存在するエンドポイントENIのプライベートIPアドレスを返すようになります。

通信の全体像(シーケンス)

1. 名前解決: アプリケーションが https://secretsmanager.ap-northeast-1.amazonaws.com/ にアクセスするため、DNSクエリを発行。
2. IP返却: VPCのDNSサーバーが、エンドポイントENIのプライベートIP(例: 10.0.1.50)を返す。
3. パケット送信: アプリケーションは 10.0.1.50 宛てにTCPの3ウェイハンドシェイク(SYN)を開始する。
4. ルーティングとセキュリティグループ:

  • VPC内のルートテーブルに従い、パケットは指定されたサブネットのエンドポイントENIへ直行。
  • この時、ENIにアタッチされたセキュリティグループ(SG)がインバウンドパケットを検査する。

5. AWS内部バックボーンへ: セキュリティグループを通過したパケットは、AWSの安全な内部ネットワーク(プライベートリンク基盤)へと引き渡され、ターゲットサービスへ到達する。

—

3. 実務で役立つ設定・構築・コード例

ここからは、実際にIaC(Terraform)やアプリケーションコード(Python)を用いて、この仕組みをどう扱い、どうデバッグすべきかを見ていきましょう。

3.1. TerraformによるInterface型エンドポイントの定義

実務では、セキュリティグループの絞り込みと、プライベートDNSの有効化が肝になります。以下に実用的なTerraformのコードスニペットを示します。

# Secrets Manager用 Interface型VPCエンドポイントの定義
resource "aws_vpc_endpoint" "secretsmanager" {
  vpc_id              = var.vpc_id
  service_name        = "com.amazonaws.ap-northeast-1.secretsmanager"
  vpc_endpoint_type   = "Interface"

  # アプリケーションが稼働するサブネットを指定(複数AZに配置)
  subnet_ids          = var.private_subnet_ids

  # このエンドポイント専用のセキュリティグループをアタッチ
  security_group_ids  = [aws_security_group.vpc_endpoint_sg.id]

  # プライベートDNSを有効化し、既存のAWSサービスのFQDNの名前解決を乗っ取る
  private_dns_enabled = true

  tags = {
    Name = "prod-secretsmanager-endpoint"
  }
}

# エンドポイント用セキュリティグループ(例: 同一VPC内からのHTTPSのみ許可)
resource "aws_security_group" "vpc_endpoint_sg" {
  name        = "vpc-endpoint-secretsmanager-sg"
  description = "Security group for Secrets Manager VPC Endpoint"
  vpc_id      = var.vpc_id

  ingress {
    description = "Allow HTTPS traffic from VPC CIDR"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [var.vpc_cidr] # 必要に応じて特定サブネットや別SGに絞る
  }

  egress {
    description = "Allow all outbound traffic"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

3.2. アプリケーションコードからの接続確認(Python / boto3)

VPCエンドポイントを経由している場合、コード側で特別なエンドポイントURL(endpoint_url)を指定する必要はほとんどありません(プライベートDNSが機能しているため)。しかし、デバッグ時に正しく向き先が変わっているかを確認するスニペットを知っておくと非常に便利です。

import boto3
from botocore.exceptions import ClientError


def get_secret_from_vpc():
    # セッションの作成。VPC内のEC2やECSタスク(IAMロール付与済み)で実行される想定
    session = boto3.session.Session()
    client = session.client(
        service_name="secretsmanager",
        region_name="ap-northeast-1",
        # プライベートDNSを有効化していれば、endpoint_urlの明示的指定は不要です。
        # デバッグのためにあえてローカルから確認する場合や検証時に指定することがあります。
    )

    try:
        response = client.get_secret_Value(SecretId="prod/db/credentials")
        print("Successfully retrieved secret via VPC Endpoint!")
        return response.get("SecretString")
    except ClientError as e:
        print(f"Failed to retrieve secret: {e}")
        raise e


if __name__ == "__main__":
    get_secret_from_vpc()

—

4. 現場でハマる「あるある」トラブルシューティングとTips

最後に、数々の現場で私や同僚が踏んできた「地雷」と、その回避・調査方法を共有します。

トラブル1: 「名前解決できるのに、タイムアウトする」

  • 原因の多く: エンドポイントENIにアタッチされたセキュリティグループ(SG)の設定ミス、あるいはネットワークACl(NACL)のブロック。
  • デバッグ手順:

1. 接続元のリソース(ECSタスクやEC2)から、dig secretsmanager.ap-northeast-1.amazonaws.com を実行し、返ってきたIP(ENIのプライベートIP)を確認する。
2. そのIPに対して nc -zv <ENI_IP> 443 や curl -Iv https://<ENI_IP> を叩く。
3. これでタイムアウトする場合、SGのインバウンドルール(443/tcp)で接続元のIPやSGが許可されているか、また戻りのパケットがNACLでブロックされていないかを疑う。

トラブル2: 「クロスAZ通信による想定外のデータ転送量課金」

  • 原因: マルチAZでVPCエンドポイントを作ったつもりが、特定AZのサブネットにしかENIを配置していなかったり、ルートテーブルのルーティングやアプリケーションの挙動で特定AZのENIへ負荷が集中している。
  • 対策: すべての本番用プライベートサブネットに対応するENIが存在するか、AWSコンソールやTerraformのステータスを必ず確認すること。

—

まとめ

VPCエンドポイント(Interface型 / PrivateLink)のENIベース通信は、クラウドネットワークの安全性とスケーラビリティを支える強力な仕組みです。

単に「プライベートIPで通信できる便利機能」と片付けるのではなく、「DNSがどう名前を解決し」「ENIという名の仮想NICがどのサブネットに鎮座し」「どのセキュリティグループがパケットの門番をしているか」を頭の中でパケットの動きとしてトレースできるようになれば、あなたのインフラエンジニアとしてのスキルは間違いなく一段階上のステージに引き上げられます。

現場での設計やトラブルシューティングの際に、ぜひこの知識を思い出してください。安全で堅牢なネットワークライフを!

コメント

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