【実務・中級編】 VPCエンドポイント(Interface型/AWS PrivateLink)のENI配置とIPアドレス消費 – クラウド&コンテナネットワーク実践ガイド

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

クラウドインフラの規模が拡大するにつれて、避けて通れなくなるのが「IPアドレス管理(IPAM)」と「VPCのサブネット設計」の泥臭い現実です。特に、セキュリティ要件が厳しいシステムでAWS PrivateLink(Interface型VPCエンドポイント)を導入した途端、「あれ、可用性を高めるためにマルチAZですべてのサブネットにエンドポイントを置いたら、割り当てられるプライベートIPが足りなくなったぞ……?」という悲鳴を、これまで何度も現場で耳にしてきました。

今回は、Interface型VPCエンドポイントが内部でどのようにENI(Elastic Network Interface)を配置し、なぜIPアドレスを猛烈に消費するのか、そのメカニズムとパケットの挙動、そして現場で役立つサイジングの勘所を徹底的に解説します。

—

1. 現場の罠:なぜInterface型VPCエンドポイントでIPが枯渇するのか?

多くのエンジニアが最初にハマる罠が、「VPCエンドポイントを作ると、VPC全体で1つだけENIができるんでしょ?」という誤解です。

現実のAWS PrivateLink(Interface型)は、「あなたがエンドポイントを配置すると指定したアベイラビリティゾーン(AZ)のサブネットごとに、専用のENIを必ず1つずつ作成する」という仕様になっています。

パケットとENIのリアルな挙動

アプリケーションからAWSサービス(例えば s3.us-east-1.amazonaws.com や、PrivateLink経由のSaaS)へリクエストが飛ぶとき、DNSの名前解決(Route 53プライベートホストゾーンなど)によって、クライアントはエンドポイントENIに割り当てられたプライベートIPアドレス(Aレコード)を返されます。

ここで、もしあなたが冗長化(HA)を考慮して、3つのAZにまたがるサブネットすべてにInterface型VPCエンドポイントを有効化した場合、どうなるでしょうか?

  • AZ-a のサブネット: ENIが1つ作成され、IPを1つ消費
  • AZ-b のサブネット: ENIが1つ作成され、IPを1つ消費
  • AZ-c のサブネット: ENIが1つ作成され、IPを1つ消費

これだけで 合計3つのプライベートIPアドレス が消費されます。
さらに、これが1つのサービスだけでなく、S3、DynamoDB(Gateway型とは別にInterface型を使う場合)、Secrets Manager、Systems Manager(SSM)、そしてサードパーティのSaaSへと増えていくとどうでしょう?

「たかがENI、されどENI」です。気づいた時には /24 や /26 といった手塩にかけて切ったサブネットの空きIPが風前の灯火となり、新規インスタンスの立ち上げ時に InsufficientIpAddressCapacity エラーに悩まされることになります。

—

2. Interface型VPCエンドポイントのサイジング計算式

実務でVPCやサブネットを設計する際、Interface型VPCエンドポイントによるIP消費量は以下の計算式で厳密に見積もる必要があります。

$$\text{消費IP数} = (\text{エンドポイントを作成するサービスの数}) \times (\text{配置するAZの数})$$

実務でありがちな設計ミスと対策

例えば、システム要件として以下のような構成を組むとします。

1. 対象サービス群: Secrets Manager, SSM, KMS, CloudWatch Logs, S3 (Interface), 独自SaaSの計6つ
2. 可用性要件: 3つのAZ(ap-northeast-1a, 1c, 1d)ですべて冗長化

この場合、$6 \times 3 = 18$ 個 のプライベートIPがエンドポイント専用に固定で消費されます。
もしサブネットが /28(利用可能IP数 11個)しかなければ、最初のサービス群を配置した瞬間に溢れ返ります。最低でも /26(利用可能IP数 59個)以上、できればアプリケーションワーカーとは別に エンドポイント専用の独立したサブネット(Endpoint Subnet) を切り出すのが、大規模SREの定石です。

—

3. 実践:AWS CLIによるエンドポイント構築とENI確認

言葉だけではイメージしにくいので、実際にAWS CLIを使ってInterface型VPCエンドポイントをデプロイし、ENIがどのように配置されるかを確認する手順を見てみましょう。

以下のスクリプトは、指定したVPCとサブネットに対して、AWS Systems Manager (SSM) のInterface型エンドポイントを作成する例です。

#!/bin/bash
set -euo pipefail

# パラメータ定義
VPC_ID="vpc-0123456789abcdef0"
REGION="ap-northeast-1"
SERVICE_NAME="com.amazonaws.${REGION}.ssm"
# 冗長化のために複数のサブネットIDを指定(ここでIPが複数消費される)
SUBNET_IDS="subnet-aaaaaa111111,subnet-bbbbbb222222"
SECURITY_GROUP_ID="sg-0123456789abcdef0"

echo "==> Interface型VPCエンドポイントを作成中..."
ENDPOINT_ID=$(aws ec2 create-vpc-endpoint \
    --region "${REGION}" \
    --vpc-id "${VPC_ID}" \
    --vpc-endpoint-type Interface \
    --service-name "${SERVICE_NAME}" \
    --subnet-ids ${SUBNET_IDS} \
    --security-group-ids "${SECURITY_GROUP_ID}" \
    --query 'VpcEndpoint.VpcEndpointId' \
    --output text)

echo "Created VPC Endpoint: ${ENDPOINT_ID}"

echo "==> 割り当てられたENIとプライベートIPアドレスを確認..."
aws ec2 describe-vpc-endpoints \
    --region "${REGION}" \
    --vpc-endpoint-ids "${ENDPOINT_ID}" \
    --query 'VpcEndpoints[0].NetworkInterfaceIds' \
    --output text

このスクリプトを実行すると、指定した2つのサブネット(SUBNET_IDS)に対してそれぞれENIが生成され、各サブネットのCIDRブロックからプライベートIPが1つずつ割り当てられます。

—

4. アプリケーションコードからの接続確認とデバッグ

エンドポイントが無事に配置され、ENIにIPが割り当てられたら、次はアプリケーションからの疎通確認です。
ここでは、Pythonの requests ライブラリを使用して、プライベートにSSM APIへリクエストを投げるコードスニペットを紹介します。

DNS設定(PrivateDnsEnabled: true)が正しく有効になっていれば、アプリケーションコード側は特別なエンドポイントURLを意識せずとも、通常のパブリックAPIのエンドポイントを叩くだけで、トラフィックはVPC内のPrivateLink(ENI)を経由します。

import boto3
import requests
from botocore.exceptions import ClientError

def check_ssm_endpoint_connectivity():
    region = "ap-northeast-1"
    
    # Boto3クライアントの初期化(内部的にPrivateLink経由で通信される)
    ssm_client = boto3.client('ssm', region_name=region)
    
    try:
        # Systems Managerのパラメータストアから値を取得するテスト
        # トラフィックはインターネットに出ず、VPCエンドポイントのENIを通過する
        response = ssm_client.describe_parameters(
            MaxResults=5
        )
        print("SUCCESS: SSMエンドポイント経由での通信に成功しました。")
        for param in response.get('Parameters', []):
            print(f" - Parameter Name: {param['Name']}")
            
    except ClientError as e:
        print(f"ERROR: AWS APIの呼び出しに失敗しました: {e}")
        # 【デバッグのヒント】
        # ここでタイムアウトする場合は、以下の3点を確認すること:
        # 1. セキュリティグループがインバウンド・アウトバウンドでポート443を許可しているか
        # 2. ルートテーブルに適切なルーティングが存在するか
        # 3. エンドポイントを配置したサブネットのIPアドレスが枯渇していないか

if __name__ == "__main__":
    check_ssm_endpoint_connectivity()

現場で役立つデバッグTips

もしアプリケーションからエンドポイントへの通信がタイムアウトしたり、名前解決に失敗したりする場合は、以下の手順でトラブルシューティングを行ってください。

1. DNSの名前解決チェック:
EC2インスタンスやコンテナ内から dig ssm.ap-northeast-1.amazonaws.com を実行し、返ってくるIPアドレスが、対象サブネットのENIに割り当てられたプライベートIP(RFC 1918のプライベートレンジ)と一致しているか確認します。パブリックIPが返ってくる場合は、プライベートホストゾーンの紐づけ(EnableDnsHostnames や EnableDnsSupport のVPC設定)が漏れています。
2. セキュリティグループの精査:
エンドポイントにアタッチされたセキュリティグループは、クライアントからのインバウンド通信(通常はTCP 443)を許可していなければなりません。また、クライアント側のセキュリティグループがアウトバウンドをブロックしていないかも合わせて確認します。

—

まとめ:SREが守るべきVPCエンドポイント設計の鉄則

Interface型VPCエンドポイントは、セキュリティとプライバシーを向上させるための強力な武器である一方、設計を誤るとIP枯渇というインフラストラクチャの致命傷を引き起こします。

  • 鉄則1: アプリケーション用サブネットとは別に、「エンドポイント専用サブネット(Endpoint Subnet)」を独立して切り出す。
  • 鉄則2: AZ数とサービス数の掛け算でIP消費量を事前にスプレッドシート等で厳密にサイジングする。
  • 鉄則3: DNSとセキュリティグループの挙動(パケットの流れるパス)を頭に叩き込んでおく。

クラウドの便利さの裏側にある「ネットワークの物理的制約」を正しく理解し、余裕を持った美しいインフラ設計を心がけましょう。あなたのシステムが、将来のスケールアップにもびくともしない堅牢なものになることを応援しています。

コメント

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