AWSのVPC内からマネージドサービス(S3やDynamoDB、Secrets Managerなど)を呼び出すとき、あなたはどうしていますか?
「とりあえずNATゲートウェイ経由でインターネットに出していれば動くからヨシ!」……そう思ってコスト請求書を見て冷や汗をかいた経験、インフラエンジニアなら一度や二度ではないはずです。パブリックなインターネットを迂回させず、AWSのバックボーンネットワーク内でトラフィックを完結させる。そのための切り札が AWS PrivateLink(Interface型VPCエンドポイント) です。
今回は、このInterface型VPCエンドポイントの心臓部である ENI(Elastic Network Interface)の挙動 と プライベートDNS解決の裏側のメカニズム について、パケットの気持ちになりながら徹底的に紐解いていきましょう。
—
1. Interface型VPCエンドポイントの本質:ENIの正体
Interface型VPCエンドポイントは、指定したVPC内のサブネットにAWSが管理する ENI(Elastic Network Interface) を配置することで機能します。
ここでよくある誤解が、「エンドポイント=仮想ルーターのような特別なハードウェアやアプライアンスが置かれている」というものです。違います。実態は、AWSのマネージドサービス側が所有・運用するバックエンドのフリート(群)へ直結するための「プライベートIPアドレスを持ったただの仮想NIC」が、あなたのVPCのサブネットにポンと置かれる、ただそれだけのことです。
ENIのIPアドレスとセキュリティグループ
VPCエンドポイント用のENIを作成すると、指定したサブネットのCIDRブロックからプライベートIPアドレスが割り当てられます。ここにアタッチされるのが セキュリティグループ(SG) です。
- インバウンドルール(Inbound): あなたのVPC内(アプリケーションサーバーなど)から、エンドポイントのポート(通常はHTTPSの
443)への通信を許可する必要があります。 - アウトバウンドルール(Outbound): デフォルトではすべての送信先への通信が許可されていますが、セキュリティを厳格化する場合はAWSサービスのバックエンドIPレンジに絞ることも可能です(通常はデフォルトのままで運用されることが多いです)。
現場でのトラブルシューティングで最も多いのが、「アプリケーションからエンドポイントに接続できない(タイムアウトする)」というケースです。大抵の場合、原因はこのENIにアタッチされたセキュリティグループが、呼び出し元からの 443 ポートの通信をブロックしていることにあります。パケットキャプチャを取る前に、まずセキュリティグループのインバウンドを確認するのがシニアの鉄則です。
—
2. クロスAZ冗長化の罠とプライベートDNSの魔法
高可用性(HA)を担保するため、本番環境のVPCでは複数のアベイラビリティーゾーン(AZ)にサブネットを切っているはずです。Interface型VPCエンドポイントも、当然ながらマルチAZで構築すべきです。
クロスAZの挙動
エンドポイントを作成する際、どのAZのサブネットにENIを配置するかを選択できます。
例えば、ap-northeast-1a と ap-northeast-1c の両方でエンドポイントを有効化すると、それぞれのサブネットにENIが作成され、それぞれにプライベートIPが割り当てられます。
ここで重要なのが 「AZ間のデータ転送費(Cross-AZ Data Transfer Fee)」 です。
もしアプリケーションが ap-northeast-1a にあるインスタンスから、ap-northeast-1c に配置されたエンドポイントのENIに向かって通信した場合、AZを跨ぐことになり、余計なデータ転送コストが発生します。
そのため、アプリケーションが存在するすべてのAZにエンドポイントのENIを配置する(フルメッシュに近い形にする) のがベストプラクティスです。
プライベートDNSホスト名の自動解決メカニズム
AWSのマネージドサービス(例えばSecrets Managerなら secretsmanager.ap-northeast-1.amazonaws.com)のデフォルトのDNS名前解決を行うと、通常はインターネット側を向いたパブリックIPアドレスが返されます。
しかし、Interface型VPCエンドポイントを作成する際に 「プライベートDNS名(Private DNS)」 を有効にすると、AWSがマジカルなことをやってくれます。
1. AWSが管理するVPC内のVPCロカライズされたAmazon Provided DNS(Route 53 Resolver)が、*.amazonaws.com へのクエリをインターセプトします。
2. 通常のパブリックIPではなく、あなたのVPC内のサブネットに配置されたENIのプライベートIPアドレス を返答するようにルーティングを書き換えます。
これにより、アプリケーション側のコードや接続先エンドポイントのURLを一切変更することなく、トラフィックをVPC内(PrivateLink)に強制的に引き込むことができるのです。
—
3. 実践:AWS CLIによるエンドポイント構築とパラメータの理解
理屈が分かったところで、実際にTerraformやAWS CLIでどのような設定が必要かを見てみましょう。ここでは手動検証やシェルスクリプトでよく使うAWS CLIの例を挙げます。
# Secrets Manager用のInterface型VPCエンドポイントを作成する例
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0123456789abcdef0 \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.ap-northeast-1.secretsmanager \
--subnet-ids subnet-01111111111111111 subnet-02222222222222222 \
--security-group-ids sg-03333333333333333 \
--private-dns-enabled
パラメータの解説
--vpc-endpoint-type Interface: ゲートウェイ型(S3/DynamoDB用)ではなく、Interface型を指定します。--service-name: 接続先サービスのプレフィックスです。リージョンごとに異なるため、公式ドキュメントやaws ec2 describe-vpc-endpoint-servicesで正確な文字列を確認してください。--subnet-ids: ENIを配置するサブネットのリストです。冗長性を考慮し、最低2つ以上のAZを指定することを強く推奨します。--private-dns-enabled: これが肝です。trueに設定することで、既存のAWSサービスのドメイン名名前解決がVPCエンドポイントのプライベートIPに向くようになります。
—
4. アプリケーションコードからの接続確認とデバッグ
プライベートDNSが正しく機能しているか、そしてENI経由でAWSサービスと通信できているかを、Pythonのコードやシェルから確認してみましょう。
DNS解決の確認(digコマンド)
まずは名前解決がパブリックIPではなく、VPC内のプライベートIPを返しているかをコンテナ内やEC2から確認します。
# Secrets Managerのエンドポイントを引いてみる
dig secretsmanager.ap-northeast-1.amazonaws.com
# 【期待される出力のイメージ】
# 応答の中に、VPCのサブネットCIDRに含まれるプライベートIP(例: 10.0.1.X や 172.16.X.X)が返ってくれば成功!
もしここでインターネット側のグローバルIPが返ってくる場合は、VPCの属性である enableDnsHostnames や enableDnsSupport が無効になっていないか、あるいはエンドポイント作成時の --private-dns-enabled が漏れていないかを疑ってください。
Python (Boto3) による接続テスト
アプリケーションコード側では、特別なエンドポイントURLを指定しなくても、プライベートDNSのおかげで自動的にENIへルーティングされます。
import boto3
from botocore.exceptions import ClientError
def get_secret_from_vpc_endpoint():
# リージョンを明示的に指定
region_name = "ap-northeast-1"
# Boto3のクライアントを作成
# プライベートDNSが有効な場合、endpoint_urlの明示的な指定は不要です。
# すべてのトラフィックは自動的にVPCエンドポイントのENIを経由します。
client = boto3.client(
service_name='secretsmanager',
region_name=region_name
)
try:
response = client.get_secret_value(
SecretId='prod/database/password'
)
print("Successfully retrieved secret via VPC Endpoint!")
return response['SecretString']
except ClientError as e:
print(f"Failed to connect or retrieve secret: {e}")
raise e
if __name__ == "__main__":
get_secret_from_vpc_endpoint()
—
シニアからの実務Tips:ハマりどころとデバッグの極意
最後に、現場で泣く泣くトラブルシュートにあたった先輩たちからの教訓をいくつかシェアします。
1. ルートテーブルの設定は不要(ここがGateway型との違い)
S3などで使う「Gateway型」VPCエンドポイントはVPCルートテーブルに明示的なルーティング追加が必要でしたが、Interface型(PrivateLink)はルートテーブルの設定を一切必要としません。 パケットはOSの標準的なルーティング(プライベートIP宛ての通信)に従ってENIへ到達します。
2. ネットワークACL(NACL)の罠
セキュリティグループだけでなく、サブネットに紐づくNACL(Network ACL)がインバウンド/アウトバウンドの通信を遮断していないかも必ず確認してください。特にエフェメラルポート(1024-65535)の戻りパケットをNACL側でブロックしているケースは非常に多いです。
3. カスタムドメインやオンプレミスからの接続
Direct ConnectやVPNを経由してオンプレミス環境からAWSのマネージドサービスにアクセスする場合、デフォルトではプライベートDNS名でVPCエンドポイントのIPを名前解決することはできません(Route 53 Resolverの条件付き転送などの追加設定が必要になります)。オンプレからのアクセス時は、エンドポイントのENIに割り当てられたプライベートIPを直接指定するか、オンプレ側のDNSサーバーにフォワーダーを設定するアーキテクチャ設計が求められます。
クラウドのネットワークは、パケットの流れる道筋を頭の中で正確にトレースできれば、どんな難解なトラブルも必ず原因に辿り着けます。セキュアで無駄のないVPC設計の武器として、Interface型VPCエンドポイントをマスターしてください!
コメント