07. Lambda / API Gateway
サーバを管理せずに HTTP API を作る最小構成。Lambda(実行)+API Gateway HTTP API(HTTP 受付)+IAM ロール(権限)の 3 点セット。
この章の目次
リクエスト処理の流れ
[クライアント] ───HTTPS──→ [API Gateway HTTP API]
│ AWS_PROXY 統合
↓
[Lambda 関数]
│ IAM Role の権限で
↓
[DynamoDB / S3 / RDS など]
料金は 呼ばれた回数 × 実行時間 のみ。リクエストが来ない夜中は $0。
関数コードの zip 化
関数のコードは zip にして Lambda に渡します。Terraform の archive_file で生成可。
data "archive_file" "hello" {
type = "zip"
source_dir = "${path.module}/lambda/hello"
output_path = "${path.module}/build/hello.zip"
}
例: lambda/hello/index.js
exports.handler = async (event) => {
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: "Hello from Lambda!",
path: event.rawPath,
}),
};
};
Lambda 用 IAM ロール
data "aws_iam_policy_document" "lambda_assume" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
resource "aws_iam_role" "lambda" {
name = "lambda-hello"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
}
# CloudWatch Logs に書く最小権限
resource "aws_iam_role_policy_attachment" "lambda_basic" {
role = aws_iam_role.lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
aws_lambda_function
resource "aws_lambda_function" "hello" {
function_name = "hello"
role = aws_iam_role.lambda.arn
filename = data.archive_file.hello.output_path
source_code_hash = data.archive_file.hello.output_base64sha256
runtime = "nodejs20.x"
handler = "index.handler"
memory_size = 256 # MB
timeout = 10 # 秒
architectures = ["arm64"] # Graviton(同性能で 20% 安い)
environment {
variables = {
LOG_LEVEL = "info"
}
}
}
Lambda の主要属性
| 属性 | デフォルト | 用途 |
|---|---|---|
memory_size | 128 | 大きいほど CPU 性能も上がる |
timeout | 3 秒 | 最大 15 分 |
architectures | x86_64 | arm64 推奨(Graviton で割安) |
vpc_config | 無し | VPC 内のリソースにアクセスする時のみ |
reserved_concurrent_executions | 無し | 同時実行数の上限 |
layers | 無し | 共通ライブラリの分離(ARN 指定) |
aws_apigatewayv2_api(HTTP API)
API Gateway には REST API(v1)と HTTP API(v2)がありますが、新規は HTTP API(v2)が標準。料金が約 70% 安く、構成もシンプル。
resource "aws_apigatewayv2_api" "main" {
name = "hello-api"
protocol_type = "HTTP"
cors_configuration {
allow_origins = ["*"]
allow_methods = ["GET", "POST"]
allow_headers = ["Content-Type", "Authorization"]
max_age = 86400
}
}
integration / route / stage
# Integration: 「API → Lambda」の接続
resource "aws_apigatewayv2_integration" "hello" {
api_id = aws_apigatewayv2_api.main.id
integration_type = "AWS_PROXY"
integration_uri = aws_lambda_function.hello.invoke_arn
payload_format_version = "2.0"
timeout_milliseconds = 10000
}
# Route: 「GET /hello → 上の integration」
resource "aws_apigatewayv2_route" "hello" {
api_id = aws_apigatewayv2_api.main.id
route_key = "GET /hello"
target = "integrations/${aws_apigatewayv2_integration.hello.id}"
}
# Stage: 公開環境(HTTP API は $default を使うのが便利)
resource "aws_apigatewayv2_stage" "default" {
api_id = aws_apigatewayv2_api.main.id
name = "$default"
auto_deploy = true
access_log_settings {
destination_arn = aws_cloudwatch_log_group.api.arn
format = jsonencode({
requestId = "$context.requestId"
sourceIp = "$context.identity.sourceIp"
requestTime = "$context.requestTime"
httpMethod = "$context.httpMethod"
routeKey = "$context.routeKey"
status = "$context.status"
responseLength = "$context.responseLength"
})
}
}
resource "aws_cloudwatch_log_group" "api" {
name = "/aws/apigw/hello-api"
retention_in_days = 14
}
aws_lambda_permission
API Gateway が Lambda を呼ぶ権限を 明示 する必要があります。これを忘れると 500 エラーで詰まります。
resource "aws_lambda_permission" "apigw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.hello.function_name
principal = "apigateway.amazonaws.com"
# この API & 任意のステージ/メソッド/リソース から
source_arn = "${aws_apigatewayv2_api.main.execution_arn}/*/*"
}
完成形と動作確認
output "api_url" {
value = aws_apigatewayv2_stage.default.invoke_url
}
$ terraform apply
$ curl "$(terraform output -raw api_url)/hello"
{"message":"Hello from Lambda!","path":"/hello"}