0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VPC Lambda が外へ出られない理由 — healthz と RDS は通るのに Secrets Manager がタイムアウトする

0
Posted at

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 つです。

  1. VPC エンドポイントがあるか
  2. available な NAT Gateway があるか
  3. 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 は存在していても、pendingdeleting では使えません。実際に外向き通信の出口として使えるのは 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 では、次のように SubnetIdsSecurityGroupIds が入ります。

{
  "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 つが必要です。

  1. public subnet に NAT Gateway を置く
  2. NAT Gateway に Elastic IP を割り当てる
  3. 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 の外へ出る道を持っているか」を確認します。

参考情報

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?