はじめに
AWSを用いたサーバーレス開発において、最も頻繁に遭遇し、かつ設計に頭を悩ませるのが「LambdaからVPC内のリソース(RDSなど)にアクセスしつつ、外部のインターネット(外部APIなど)にも接続したい」というユースケースです。
「LambdaをVPCに入れたらインターネットにつながらなくなった」「RDSにはつながるが、今度は外部システムとのデータ連携でエラーが出る」といったトラブルは、インフラエンジニアの登竜門とも言えます。本記事では、この課題を解決するための正しいネットワーク設計、セキュリティグループのベストプラクティス、そして具体的な設定手順を、初心者から中級者向けにステップバイステップで解説します。
前提知識と必要な理由
なぜLambdaをVPCに入れる必要があるのか?
デフォルト状態のLambda関数は、AWSが管理するシステムVPC上で実行されます。この状態ではインターネット上のリソースにはアクセスできますが、セキュリティ上の理由からプライベートサブネット内に配置されたRDS(Relational Database Service)やElastiCacheなどのリソースには直接アクセスできません。これらに安全にアクセスするためには、Lambda関数を自社管理のVPC(仮想プライベートクラウド)内に配置(VPC Lambda化)する必要があります。
VPC Lambdaが抱える「インターネット接続」の課題
Lambda関数をVPC(特にプライベートサブネット)に配置すると、今度はデフォルトでインターネットへのルートが失われます。LambdaにパブリックIPアドレスが割り当てられないため、直接インターネットゲートウェイ(IGW)を経由して外に出ることができないからです。したがって、RDSなどのデータベースと外部API(決済ゲートウェイや外部SaaSなど)の両方に同時接続するためには、適切なルートテーブルの設計と、NAT(ネットワークアドレス変換)デバイスの設置が必須となります。
具体的な設定手順・設計方法
今回構築する構成の全体像
本ハンズオンでは、以下の堅牢なアーキテクチャを構築します。
- VPC: 10.0.0.0/16
- プライベートサブネット(Lambda/RDS用): 10.0.1.0/24, 10.0.2.0/24(マルチAZ構成)
- パブリックサブネット(NAT Gateway用): 10.0.11.0/24, 10.0.12.0/24
- NAT Gateway: パブリックサブネットに配置し、Lambdaからのアウトバウンド通信をインターネットへ仲介
- セキュリティグループ: Lambda用とRDS用をそれぞれ作成し、最小権限で疎通を許可
ステップ1:VPCとサブネットの作成
まず、LambdaとRDS、およびNAT Gatewayを配置するためのネットワークインフラを整備します。AWS管理コンソール、またはAWS CLIで以下の通り設定します。
- AWS管理コンソールのVPCダッシュボードを開き、「VPC を作成」をクリックします。
- 「VPC など」を選択し、以下のパラメータを入力して作成すると、サブネットやルートテーブルが自動生成されて便利です。
- IPv4 CIDR ブロック:
10.0.0.0/16 - パブリックサブネット数: 2 (マルチAZ用)
- プライベートサブネット数: 2 (マルチAZ用)
- NAT ゲートウェイ: 「1つのAZ内」または「AZごと(可用性重視)」を選択
- VPC エンドポイント: 「なし」(必要に応じて後から追加)
- IPv4 CIDR ブロック:
ステップ2:セキュリティグループの設定
次に、セキュリティの要となるセキュリティグループ(SG)を2つ作成します。実務では「すべてのトラフィックを許可(0.0.0.0/0)」にするのではなく、セキュリティグループ同士の「相互参照(インバウンド連携)」を設定するのがベストプラクティスです。
1. Lambda用のセキュリティグループ (sg-lambda)
- インバウンドルール: なし(Lambdaはトリガーされて起動するため、外部からの直接接続は不要)
- アウトバウンドルール: すべてのトラフィック (0.0.0.0/0) を許可
2. RDS用のセキュリティグループ (sg-rds)
- インバウンドルール:
- タイプ: PostgreSQL(またはMySQLなど、使用するDBに合わせてポートを指定。例: 5432)
- ソース: カスタム ->
sg-lambdaのセキュリティグループIDを指定
- アウトバウンドルール: 任意(デフォルトのままで可)
ステップ3:Lambda用のIAMロールの作成
VPC内でLambdaを実行するためには、LambdaサービスがElastic Network Interface (ENI) を作成・管理するための権限(IAMポリシー)が必要です。この権限がないと、LambdaはVPC内で起動できません。
IAMロールを作成し、AWS管理ポリシーである AWSLambdaVPCAccessExecutionRole をアタッチしてください。このポリシーには以下の権限が含まれています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
"ec2:AssignPrivateIpAddresses",
"ec2:UnassignPrivateIpAddresses"
],
"Resource": "*"
}
]
}
ステップ4:Lambda関数の作成とVPC設定
Lambda関数を作成し、これまでに作成したVPC、サブネット、セキュリティグループを紐付けます。
- Lambdaコンソールを開き、「関数の作成」をクリックします。
- 「一から作成」を選択し、関数名を入力、ランタイムに「Python 3.11」などを選択します。
- 「デフォルトの実行ロールの変更」を展開し、ステップ3で作成したIAMロールを選択します。
- 「詳細設定」を展開し、「VPC を有効化」にチェックを入れます。
- VPC: ステップ1で作成したVPCを選択
- サブネット: プライベートサブネットを2つ選択(※絶対にパブリックサブネットを選択しないでください)
- セキュリティグループ:
sg-lambdaを選択
- 「関数の作成」をクリックします。
ステップ5:動作確認用コードの実装
Lambda関数から、内部のRDS(PostgreSQL)に接続しつつ、外部のパブリックAPI(今回はテスト用のHTTPSエンドポイント)へリクエストを送信する検証用Pythonコードです。
import json
import urllib.request
import psycopg2 # 事前にLambdaレイヤー等でライブラリの追加が必要です
def lambda_handler(event, context):
# 1. 外部APIへの接続テスト(インターネット疎通確認)
external_url = "https://httpbin.org/get"
try:
with urllib.request.urlopen(external_url, timeout=5) as response:
html = response.read().decode('utf-8')
print("外部API接続成功:")
print(html[:100]) # ログ出力制限のため一部のみ
except Exception as e:
print(f"外部API接続失敗: {e}")
raise e
# 2. VPC内RDS(PostgreSQL)への接続テスト
db_config = {
"host": "your-rds-endpoint.amazonaws.com",
"database": "mydb",
"user": "dbuser",
"password": "securepassword",
"port": 5432
}
try:
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
cur.execute("SELECT version();")
db_version = cur.fetchone()
print(f"RDS接続成功: {db_version}")
cur.close()
conn.close()
except Exception as e:
print(f"RDS接続失敗: {e}")
raise e
return {
'statusCode': 200,
'body': json.dumps('両方の接続テストに成功しました!')
}
実務での注意点(トラブルシューティング・設計ベストプラクティス)
1. プライベートサブネットの「IPアドレス枯渇」に注意
過去の仕様とは異なり、現在のAWS(Hyperplane ENIの導入後)では、Lambda関数がスケールアウトしても起動ごとに新しいENIが作成されることはなくなりました。しかし、同一のサブネット/セキュリティグループの組み合わせごとに1つの共有ENIが作成される仕組みであるため、依然としてIPアドレスの設計は重要です。他のシステムと共有するサブネットを設定する場合は、CIDRを十分に(最低でも/24以上)確保しておきましょう。
2. NAT Gatewayのコストに配慮する
NAT Gatewayは1時間あたりの起動コスト(約$0.045/時)と、データ処理量(GBあたり約$0.045)がかかるため、検証環境であっても維持費が割高になります。
コスト最適化のアプローチ:
- 本番環境以外: NAT Gatewayの代わりに、安価なEC2インスタンス(NAT Instance)を利用して構築する。
- VPCエンドポイントの活用: 外部接続の目的が「S3へのファイルアクセス」や「Systems Manager(SSM)の利用」だけであれば、NAT Gatewayを使わずにVPCエンドポイント(Gateway型/Interface型)を配置する方が、データ転送コストを劇的に抑えられます。