API を AWS Lambda で動かし、DB は RDS PostgreSQL に置く構成はよくあります。RDS は VPC の中にあるため、Lambda から RDS に接続するには Lambda も VPC に入れます。さらに、コストを抑えるために NAT Gateway は置かない、という判断もよくあります。
この構成は、一見すると問題なく動いているように見えます。デプロイは成功します。Function URL も作れます。死活監視用の /healthz も 200 を返します。
curl https://<function-url>/healthz
# => {"ok":true}
ところが、実際の処理だけが失敗します。
たとえば、Secrets Manager から RDS の認証情報を取ろうとするとタイムアウトします。SSM パラメーターストアから設定値を読もうとしてもタイムアウトします。CDN の KV API や LLM API のような外部 HTTP API を呼んでもタイムアウトします。
つまり、観測結果は次のようになります。
-
/healthzは成功する - RDS PostgreSQL への接続も成功する
- Secrets Manager はタイムアウトする
- SSM パラメーターストアもタイムアウトする
- 外部 HTTP API もタイムアウトする
この状態だけを見ると、原因が分かりにくく見えます。Lambda は動いている。RDS にもつながる。それなのに、Secrets Manager や外部 API だけが落ちるからです。
原因は egress、つまり外向き通信の経路です。
ここでいう egress は、VPC の外側へ出ていく通信を指します。Secrets Manager、SSM、外部 API のような接続先へ向かう通信です。一方、同じ VPC 内にある RDS への通信は、ここでいう egress には含めません。VPC の中で完結する通信だからです。
この記事では、「healthz と RDS は通るのに、Secrets Manager と外部 API だけタイムアウトする」状態を、AWS CLI の出力から切り分けます。そのうえで、NAT Gateway、VPC エンドポイント、VPC 外 Lambda のどれを選ぶべきかを整理します。
対象は次の構成です。
- AWS Lambda
- Lambda runtime: nodejs20
- architecture: x86_64
- Amazon RDS PostgreSQL
- AWS CLI v2
@aws-sdk/rds-signer
結論: 経路が無い private subnet の Lambda は VPC の外へ出られない
VPC の private subnet に置いた Lambda は、NAT Gateway と VPC エンドポイントのどちらも無いと、VPC の外へ出られません。
そのため、同じ VPC 内の RDS には届きます。/healthz のように外部依存を持たない処理も成功します。しかし、VPC の外にある Secrets Manager、SSM、外部 HTTP API には届きません。
このとき重要なのは、「AWS のサービスかどうか」ではありません。
重要なのは、次の点です。
- 通信先が VPC の内側にあるか
- VPC の外側にあるなら、そこへ出る経路があるか
RDS に届くのは、AWS のサービスだからではなく、同じ VPC 内にあるからです。Secrets Manager に届かないのは、AWS のサービスではないからではなく、VPC の外側にあるサービスエンドポイントへ出る経路が無いからです。
前提となる AWS ネットワーク用語
この記事で出てくる AWS のネットワーク用語を、最初にまとめておきます。
VPC
VPC は、AWS 上に作る自分専用のネットワークです。
会社の社内ネットワークに近いものです。その中に RDS や Lambda 用の ENI、内部 ALB、ElastiCache などを配置します。
subnet
subnet は、VPC の中をさらに分けた区画です。
VPC という大きなネットワークを、複数の小さなネットワークへ分けるために使います。よく public subnet と private subnet に分けます。
private subnet
private subnet は、外部から直接アクセスさせない前提の subnet です。
ただし、ここで注意が必要です。private subnet は「外から入れない」だけではありません。構成によっては「中から外へも出られない」状態になります。
private subnet から外へ出たいなら、NAT Gateway や VPC エンドポイントのような出口が必要です。
route table
route table は、通信をどこへ向けるかを決める表です。
たとえば、VPC 内の通信は local へ向ける、外向き通信は NAT Gateway へ向ける、というようなルールを書きます。
172.31.0.0/16 -> local
0.0.0.0/0 -> nat-xxxxxxxx
ここで 0.0.0.0/0 は「それ以外の外向き通信」と考えるとよいです。この行が private subnet の route table に無ければ、一般的な外向き通信は NAT Gateway に向かいません。
security group
security group は、通信を通してよいかどうかを決めるフィルタです。
route table が「道」だとすると、security group は「門番」です。道があっても、門番が止めれば通信は通りません。逆に、門番が許可していても、道がなければ通信は届きません。
ENI
ENI は Elastic Network Interface の略で、仮想的なネットワークカードです。
Lambda を VPC に入れると、Lambda は VPC 内に作られた ENI を通じて通信します。つまり、Lambda の通信はその ENI がある subnet の route table に従うようになります。
NAT Gateway
NAT Gateway は、private subnet からインターネットや外部 API へ出るための出口です。
private subnet の Lambda が外部 API を呼びたい場合、典型的には NAT Gateway を経由します。
VPC エンドポイント
VPC エンドポイントは、VPC の中から特定の AWS サービスへ到達するための入口です。
Secrets Manager や SSM のような AWS サービスへ、インターネットを経由せず到達したい場合に使います。ただし、インターフェースエンドポイントはサービスごとに作る必要があります。
egress
egress は、外へ出ていく通信です。
この記事では、特に VPC の外へ出ていく通信を egress と呼びます。
Lambda -> Secrets Manager
Lambda -> SSM
Lambda -> 外部 API
Lambda -> LLM API
これらは egress です。
一方、同じ VPC 内の RDS へ向かう通信は、VPC の中で完結するため、この記事で問題にしている egress とは分けて考えます。
成功する経路と失敗する経路を分けて観測する
まず、成功している経路と失敗している経路を分けます。
成功しているのは次の 2 つです。
-
GET /healthzは 200 を返す - RDS PostgreSQL への接続は通る
失敗しているのは次の 3 つです。
- Secrets Manager から RDS の認証情報を取る処理がタイムアウトする
- SSM パラメーターストアから設定を読む処理がタイムアウトする
- 外部 HTTP API への通信がタイムアウトする
この並びを見ると、失敗している処理には共通点があります。どれも VPC の外へ出ようとしています。
一方、成功している処理は VPC の外へ出ていません。/healthz は外部依存を持たず、ただ {"ok":true} を返すだけです。RDS は同じ VPC 内にあるため、VPC 内の local 経路で届きます。
ここで、返っているエラーが 403 や認証エラーではなく、タイムアウトである点も重要です。
403 なら、通信自体は相手に届いています。そのうえで、権限が足りない、ポリシーが間違っている、という話になります。
しかしタイムアウトは、そもそも相手に届いていない可能性が高いです。つまり、IAM や SDK の前に、ネットワーク経路を疑うべき状態です。
到達先は 3 種類に分ける
この問題で混乱しやすいのは、通信先を全部同じものとして見てしまうことです。
実際には、通信先は少なくとも 3 種類に分けて考えます。
| 到達先 | 例 | NAT / VPC エンドポイントなしの private subnet から届くか |
|---|---|---|
| 同一 VPC 内のリソース | RDS、内部 ALB、ElastiCache | 届く |
| AWS サービスエンドポイント | Secrets Manager、SSM、CloudWatch Logs、STS | 届かない |
| 任意の外部サービス | CDN KV、LLM API、決済 SaaS、Webhook 送信先 | 届かない |
大事なのは、Secrets Manager や SSM は AWS のサービスではあるものの、Lambda と同じ VPC の中にあるわけではない、という点です。
「AWS のサービスだから VPC 内から当然届く」と考えると、この問題を見誤ります。
判断基準は AWS かどうかではありません。
- 同じ VPC 内にあるなら local 経路で届く
- VPC の外にあるなら、外へ出る経路が必要
この基準で見ると、今回の現象は自然に説明できます。
この挙動が出る構成
この記事で扱う構成は、次のようなものです。
- Lambda は RDS PostgreSQL へ接続するため VPC に入っている
- Lambda は private subnet に配置されている
- NAT Gateway は置いていない
- VPC エンドポイントも置いていない
- RDS は同じ VPC 内にある
- Function URL で Lambda を HTTP 呼び出ししている
この構成では、Lambda から RDS への通信は VPC 内で完結します。RDS 側の security group が Lambda からの 5432 番ポートを許可していれば、DB 接続は通ります。
しかし、VPC の外へ出る経路はありません。NAT Gateway も無く、Secrets Manager 用や SSM 用のインターフェースエンドポイントも無いからです。
そのため、外部依存を持つ処理だけが実行時に失敗します。
ここで注意したいのは、デプロイ時にはこの問題が見えにくいことです。Lambda のデプロイ、IAM ロールの設定、Function URL の作成は成功します。/healthz も成功します。
問題が表面化するのは、実際に Secrets Manager や外部 API へ通信しようとしたタイミングです。
Lambda を VPC に入れると何が変わるか
Lambda を VPC に入れる前と後では、外向き通信の前提が変わります。
VPC に入れていない Lambda
VPC に入れていない Lambda は、AWS が管理するネットワーク上で動きます。
この場合、Lambda は追加の VPC 設定なしに、Secrets Manager や SSM、外部 API へ通信できます。利用者が subnet、route table、NAT Gateway を意識しなくても、AWS 側の管理ネットワークから外へ出られます。
VPC 外の Lambda
-> Secrets Manager に届く
-> SSM に届く
-> 外部 API に届く
VPC に入れた Lambda
Lambda を VPC に入れると、通信の起点が変わります。
Lambda は、指定した subnet に作られた ENI を通じて通信します。そのため、通信はその subnet の route table に従います。
VPC 内の Lambda
-> ENI
-> private subnet の route table
-> 宛先
つまり、Lambda は AWS が自動で外へ出してくれる状態ではなくなります。VPC の中に外向き経路が無ければ、Lambda も外へ出られません。
ここが、VPC Lambda で特に間違えやすい点です。
Lambda は public subnet に置けば外へ出られる、ではない
EC2 や Fargate では、public subnet に置いて public IP を割り当てれば Internet Gateway 経由で外へ出られます。
しかし、VPC Lambda では同じ考え方は使えません。Lambda の ENI には public IP を割り当てられないためです。
そのため、Lambda を public subnet に置くだけでは外へ出られません。
VPC 内の Lambda を外へ出したい場合は、基本的に次のどちらかを使います。
- NAT Gateway
- VPC エンドポイント
外部 API へ出たいなら NAT Gateway が必要です。Secrets Manager や SSM など、特定の AWS サービスだけに出たいなら VPC エンドポイントが候補になります。
なぜ RDS には届くのか
RDS が同じ VPC 内にある場合、Lambda から RDS への通信は VPC の中で完結します。
VPC には、VPC 内の CIDR 宛ての通信を local に流す経路があります。これにより、同じ VPC 内のリソース同士は通信できます。
Lambda の ENI
-> local 経路
-> RDS の private IP
もちろん、route table だけでなく security group も必要です。PostgreSQL なら、RDS 側の security group で Lambda の security group から 5432 番ポートへの接続を許可します。
この条件が揃っていれば、NAT Gateway が無くても RDS には届きます。
つまり、RDS に届くことは「外へ出られること」を意味しません。RDS は同じ VPC 内にあるため、外向き通信とは別の話です。
なぜ Secrets Manager には届かないのか
Secrets Manager は AWS のサービスですが、VPC の中にあるわけではありません。
通常、SDK は次のようなリージョン別のサービスエンドポイントへ接続します。
secretsmanager.<region>.amazonaws.com
この接続先は、private subnet の中にある RDS とは違います。VPC の外側にある AWS サービスの API です。
private subnet の Lambda からこのサービスエンドポイントへ到達するには、外へ出る経路が必要です。
NAT Gateway を使う場合は、こうなります。
Lambda
-> NAT Gateway
-> Internet Gateway
-> Secrets Manager
VPC インターフェースエンドポイントを使う場合は、こうなります。
Lambda
-> VPC Endpoint の ENI
-> PrivateLink
-> Secrets Manager
どちらも無い場合、Lambda は secretsmanager.<region>.amazonaws.com へ向かおうとしても、そこへ出る道がありません。その結果、タイムアウトします。
route table と security group を混同しない
通信できないときは、route table と security group を分けて考えます。
この 2 つは役割が違います。
| 観点 | route table | security group |
|---|---|---|
| 役割 | 通信をどこへ向けるか | 通信を通してよいか |
| たとえ | 道案内 | 門番 |
| 単位 | subnet | ENI |
| よくある失敗 | 外向き経路が無い | 443 や 5432 が許可されていない |
通信が届くには、両方が必要です。
今回のように NAT Gateway と VPC エンドポイントのどちらも無い場合、まず route table 側の問題です。そもそも VPC の外へ出る道がありません。
ただし、NAT Gateway や VPC エンドポイントを作ったあとにまだ届かない場合は、security group の確認も必要です。たとえば、VPC エンドポイントの security group が Lambda からの 443 番ポートを許可していなければ、Secrets Manager には届きません。
AWS CLI で確認する
原因を推測で決めないために、AWS CLI で確認します。
見るべきものは、まず次の 3 つです。
- VPC エンドポイントがあるか
- available な NAT Gateway があるか
- Lambda がどの subnet にいるか
VPC エンドポイントを確認する
aws ec2 describe-vpc-endpoints \
--filters Name=vpc-id,Values=<vpc-id> \
--query 'VpcEndpoints[].[ServiceName,VpcEndpointType]' \
--output table
出力が空なら、少なくともその VPC には VPC エンドポイントがありません。
エンドポイントがある場合は、VpcEndpointType を見ます。
----------------------------------------------------------------
| DescribeVpcEndpoints |
+--------------------------------------------------+-----------+
| com.amazonaws.ap-northeast-1.secretsmanager | Interface |
| com.amazonaws.ap-northeast-1.s3 | Gateway |
+--------------------------------------------------+-----------+
Interface は、Secrets Manager や SSM などのために作るエンドポイントです。サービスごとに必要です。
Gateway は、S3 と DynamoDB 用のエンドポイントです。S3 / DynamoDB は例外的に Gateway 型を使えます。
ここで大事なのは、「エンドポイントがある」だけでは不十分なことです。Secrets Manager に届きたいなら、Secrets Manager 用のエンドポイントが必要です。SSM に届きたいなら、SSM 用のエンドポイントが必要です。
NAT Gateway を確認する
aws ec2 describe-nat-gateways \
--filter Name=state,Values=available \
--query 'NatGateways[].[NatGatewayId,State]' \
--output table
available な NAT Gateway が 0 なら、稼働中の NAT Gateway はありません。
NAT Gateway は存在していても、pending や deleting では使えません。実際に外向き通信の出口として使えるのは available のものです。
ただし、NAT Gateway が available であるだけでもまだ不十分です。private subnet の route table に、次の経路が必要です。
0.0.0.0/0 -> nat-xxxxxxxx
NAT Gateway の存在と、private subnet から NAT Gateway へ向かう route table は別々に確認します。
Lambda の VPC 設定を確認する
aws lambda get-function-configuration \
--function-name <fn> \
--query 'VpcConfig'
VPC に入っている Lambda では、次のように SubnetIds と SecurityGroupIds が入ります。
{
"SubnetIds": ["subnet-xxxx", "subnet-yyyy"],
"SecurityGroupIds": ["sg-zzzz"],
"VpcId": "vpc-aaaa"
}
この SubnetIds が private subnet で、その private subnet に NAT Gateway への route がなく、VPC エンドポイントも無いなら、外へ出る経路はありません。
一方、VPC に入っていない Lambda では、次のように空になります。
{
"SubnetIds": [],
"SecurityGroupIds": [],
"VpcId": ""
}
この場合、Lambda は AWS 管理ネットワーク上で動いているため、この記事で扱っている「VPC 内 Lambda が外へ出られない」問題とは別です。
3 つの結果が揃うと egress なしと判断できる
次の 3 つが揃うと、VPC の外へ出る経路が無いと判断できます。
| 確認項目 | 出力 | 読み取れること |
|---|---|---|
| VPC エンドポイント | 空、または対象サービスが無い | AWS サービス向けの VPC 内経路が無い |
| NAT Gateway | available が 0 | 一般の外向き通信の出口が無い |
| Lambda の VPC 設定 | private subnet に配置 | Lambda は VPC の route table に従っている |
この状態で、Secrets Manager、SSM、外部 API がタイムアウトするなら、まず egress の問題として見るべきです。
route table も確認する
NAT Gateway を作っているのに届かない場合は、private subnet の route table を確認します。
aws ec2 describe-route-tables \
--filters Name=association.subnet-id,Values=<private-subnet-id> \
--query 'RouteTables[].Routes[].[DestinationCidrBlock,NatGatewayId,GatewayId]' \
--output table
外向き経路が無い場合、local 経路だけが見えます。
-----------------------------------------------
| DescribeRouteTables |
+---------------+------------+----------------+
| 172.31.0.0/16 | None | local |
+---------------+------------+----------------+
外へ出したいなら、private subnet の route table に次のような経路が必要です。
0.0.0.0/0 -> nat-xxxxxxxx
NAT Gateway を作っただけでは通信が流れません。Lambda がいる private subnet の route table から NAT Gateway へ向かう経路が必要です。
security group も確認する
経路があるのに届かない場合は、security group を確認します。
Lambda の security group の送信ルールは次で見られます。
aws ec2 describe-security-groups \
--group-ids <lambda-sg-id> \
--query 'SecurityGroups[].IpPermissionsEgress'
既定では送信は全許可になっていることが多いです。その場合、次のようなルールがあります。
[
[
{
"IpProtocol": "-1",
"IpRanges": [{ "CidrIp": "0.0.0.0/0" }]
}
]
]
送信を絞っている場合は、HTTPS なら 443、PostgreSQL なら 5432 が許可されているかを確認します。
また、VPC エンドポイントを使う場合は、エンドポイント側の security group も確認します。Lambda からエンドポイント ENI の 443 番ポートへの通信を許可していなければ、エンドポイントを作っても届きません。
NAT Gateway を追加する
外部 API も呼ぶなら、基本的には NAT Gateway が必要です。
NAT Gateway は、private subnet から外部へ出るための出口です。Secrets Manager、SSM、外部 API、LLM API、SaaS API など、幅広い宛先に到達できます。
構成としては、次の 3 つが必要です。
- public subnet に NAT Gateway を置く
- NAT Gateway に Elastic IP を割り当てる
- private subnet の route table に
0.0.0.0/0 -> NAT Gatewayを追加する
# 1) Elastic IP を確保
aws ec2 allocate-address --domain vpc
# 2) public subnet に NAT Gateway を作成
aws ec2 create-nat-gateway \
--subnet-id <public-subnet-id> \
--allocation-id eipalloc-xxxx
# 3) private subnet の route table から NAT Gateway へ向ける
aws ec2 create-route \
--route-table-id <private-rtb-id> \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id <nat-gateway-id>
図にすると、次のような流れです。
private subnet
Lambda
|
| 0.0.0.0/0 -> NAT Gateway
v
public subnet
NAT Gateway + Elastic IP
|
v
Internet Gateway
|
v
Secrets Manager / SSM / 外部 API
NAT Gateway の利点は、到達先をあまり選ばないことです。AWS サービスと外部 API のどちらにも出られます。
一方、欠点はコストです。NAT Gateway は起動している時間に対する課金と、通過するデータ量に対する課金があります。常時起動する構成では、使っていない時間にもコストが発生します。
また、可用性を考えるなら AZ ごとに NAT Gateway を置く構成も検討します。各 private subnet は、原則として同じ AZ の NAT Gateway に向けます。別 AZ の NAT Gateway を使うと、AZ 障害時の影響やクロス AZ のデータ転送料が問題になることがあります。
VPC インターフェースエンドポイントを追加する
到達先が AWS サービスだけなら、VPC インターフェースエンドポイントが候補になります。
これは、VPC の中に特定の AWS サービスへの入口を作る仕組みです。PrivateLink を使って、インターネットへ出ずに AWS サービスへ到達できます。
Secrets Manager 用のエンドポイントを作る例です。
aws ec2 create-vpc-endpoint \
--vpc-id <vpc-id> \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.<region>.secretsmanager \
--subnet-ids <private-subnet-id> \
--security-group-ids <endpoint-sg-id> \
--private-dns-enabled
private DNS が重要
--private-dns-enabled を付けると、通常の Secrets Manager のホスト名が、VPC 内のエンドポイント ENI の private IP へ解決されます。
つまり、アプリケーションコードはそのままでよいです。
secretsmanager.<region>.amazonaws.com
という同じ名前へ接続していても、VPC 内では VPC エンドポイントの private IP に向きます。
--private-dns-enabled が無いと、SDK は通常のパブリックなサービスエンドポイントへ行こうとします。その場合、NAT Gateway が無ければ結局タイムアウトします。
サービスごとに作る
インターフェースエンドポイントはサービスごとに作ります。
たとえば、Secrets Manager と SSM の両方が必要なら、それぞれ作ります。
com.amazonaws.<region>.secretsmanager
com.amazonaws.<region>.ssm
CloudWatch Logs、STS、ECR、Kinesis なども、必要ならそれぞれ検討します。
また、エンドポイント ENI には security group が付きます。Lambda の security group から 443 番ポートでアクセスできるように許可します。
S3 / DynamoDB は Gateway Endpoint
S3 と DynamoDB は例外です。
これらはインターフェースエンドポイントではなく、Gateway Endpoint を使えます。
Gateway Endpoint は route table に経路を追加する方式で、時間課金がありません。S3 や DynamoDB だけに出たいなら、Gateway Endpoint のほうが向いています。
Lambda を VPC の外に出す
RDS に直接接続する必要がないなら、Lambda を VPC の外に出す選択肢もあります。
VPC 外の Lambda は AWS 管理ネットワーク上で動くため、Secrets Manager、SSM、外部 API に追加の NAT Gateway なしで到達できます。
ただし、代わりに private subnet の RDS へ直接接続できなくなります。
そのため、DB アクセスの設計を変える必要があります。たとえば、Aurora の RDS Data API を使う、DB アクセス用の別 API を用意する、といった設計になります。
通常の RDS PostgreSQL では RDS Data API が使えない点にも注意が必要です。RDS Data API は主に Aurora 向けの機能です。
つまり、Lambda を VPC の外に出す選択肢は、外部 API との通信は簡単になりますが、RDS 直結を失うというトレードオフがあります。
一時回避策: RDS IAM 認証で Secrets Manager 呼び出しを減らす
経路を整える前に、RDS の認証情報取得だけを一時的に回避したい場合は、RDS IAM 認証を使えることがあります。
RDS IAM 認証は、固定パスワードの代わりに IAM から短命の認証トークンを作り、それを DB パスワードの代わりに使う方式です。
Node.js では @aws-sdk/rds-signer を使えます。
import { Signer } from "@aws-sdk/rds-signer";
const signer = new Signer({
region: process.env.AWS_REGION,
hostname: process.env.DB_HOST,
port: 5432,
username: process.env.DB_USER,
});
const token = await signer.getAuthToken();
ここで押さえておきたいのは、getAuthToken() は Secrets Manager に HTTP リクエストを送る処理ではない、という点です。SigV4 署名をローカルで計算して、RDS 接続用のトークンを作ります。
そのため、Secrets Manager に届かない構成でも、RDS IAM 認証用のトークン生成自体はできます。
ただし、これは万能ではありません。成立させるには、次の設定が必要です。
- RDS で IAM データベース認証を有効にする
- PostgreSQL 側で IAM 認証用ユーザーに
rds_iamロールを付ける - Lambda 実行ロールに
rds-db:connectを許可する
また、この方法で回避できるのは「RDS の認証情報を Secrets Manager から取る処理」だけです。
SSM から設定を読む処理や、外部 API を呼ぶ処理は解決しません。外部 API キーをどこから取得するかという問題も残ります。
したがって、RDS IAM 認証は一時回避策にはなりますが、egress の恒久対策ではありません。
どの対処を選ぶか
選び方は、到達先で決めます。
表にすると次のようになります。
| 要件 | 選択肢 | 向いているケース |
|---|---|---|
| 外部 API も呼ぶ | NAT Gateway | LLM API、CDN API、SaaS API などが必要 |
| Secrets Manager / SSM など AWS サービスだけ | Interface Endpoint | AWS サービスへの通信に限定できる |
| S3 / DynamoDB だけ | Gateway Endpoint | 低コストに S3 / DynamoDB へ出たい |
| RDS 直結が不要 | Lambda を VPC 外へ出す | DB アクセス設計を変えられる |
冒頭のように、RDS 直結を保ちつつ、Secrets Manager、SSM、外部 LLM API も呼ぶなら、まず NAT Gateway が候補になります。
そのうえで、Secrets Manager や SSM の通信量が多いなら、AWS サービス向けだけ VPC エンドポイントへ寄せ、外部 API だけ NAT Gateway を通す併用構成も考えられます。
この条件が揃ったら egress を最初に疑う
次の条件が揃っているなら、最初に疑うのは IAM や SDK ではなく egress です。
-
/healthzは通る - RDS への接続は通る
- Secrets Manager / SSM / 外部 API だけがタイムアウトする
- Lambda は private subnet にいる
- available な NAT Gateway が無い
- 対象サービス向けの VPC エンドポイントも無い
この状態では、失敗している通信の共通点は「VPC の外へ出ようとしていること」です。
一方、次のような場合は別の原因を疑います。
- 403 が返るなら、通信は届いているので権限を疑う
- 認証エラーなら、認証情報や IAM ポリシーを疑う
- ある外部 API だけ落ちるなら、宛先固有の制限を疑う
- NAT Gateway はあるのに届かないなら、route table と security group を確認する
「外向き通信が一様にタイムアウトする」ことが、egress なし構成の特徴です。
まとめ
VPC の private subnet に置いた Lambda は、NAT Gateway と VPC エンドポイントのどちらも無いと、VPC の外へ出る経路を持ちません。
そのため、/healthz は成功します。外部依存が無いからです。RDS 接続も成功します。同じ VPC 内にあるからです。
しかし、Secrets Manager、SSM、外部 HTTP API は失敗します。これらは VPC の外にある接続先であり、外へ出る経路が無いからです。
この問題で重要なのは、AWS のサービスかどうかではありません。
判断の基準は、VPC の内側にあるか、外側にあるならそこへ出る経路があるかです。
確認は、まず次の 3 つから始めます。
aws ec2 describe-vpc-endpoints \
--filters Name=vpc-id,Values=<vpc-id> \
--query 'VpcEndpoints[].[ServiceName,VpcEndpointType]' \
--output table
aws ec2 describe-nat-gateways \
--filter Name=state,Values=available \
--query 'NatGateways[].[NatGatewayId,State]' \
--output table
aws lambda get-function-configuration \
--function-name <fn> \
--query 'VpcConfig'
VPC エンドポイントが無い、available な NAT Gateway が無い、Lambda が private subnet にいる。この 3 つが揃れば、その Lambda は VPC の外へ出られません。
対処は到達先で選びます。外部 API も呼ぶなら NAT Gateway。AWS サービスだけなら VPC エンドポイント。S3 / DynamoDB だけなら Gateway Endpoint。RDS 直結が不要なら Lambda を VPC の外に出す選択もあります。
/healthz と RDS が通っているから Lambda 全体が正常、とは限りません。外部依存を持つ処理がタイムアウトするなら、まず「その Lambda は VPC の外へ出る道を持っているか」を確認します。
参考情報
- AWS Lambda — VPC でのネットワーク設定
- AWS Lambda — Function URL(AWS_IAM 認可)
- Amazon EC2 — ENI(Elastic Network Interface)
- AWS — VPC エンドポイント(インターフェース型 / PrivateLink)
- AWS — ゲートウェイエンドポイント(S3 / DynamoDB)
- AWS — NAT ゲートウェイ
- Amazon RDS — IAM データベース認証
- Amazon RDS — RDS Data API
- AWS — Signature Version 4 (SigV4)
- @aws-sdk/rds-signer(npm)
- AWS CLI — ec2 describe-vpc-endpoints
- AWS CLI — ec2 describe-nat-gateways
- AWS CLI — ec2 describe-route-tables
- AWS CLI — ec2 describe-security-groups
- AWS CLI — lambda get-function-configuration