Terraform でも AWS Lambda 開発したい
こんにちは、build サービスチームで Platform エンジニアをやっているt.g.です。
最近、Terraform を再度触り始めたので、そこで気になったことを共有します。
(背景) AWS Lambda の開発は sam が基本
かつては、ビルドしたコードを Zip 圧縮して、S3バケットに保存し、(S3上の)ZipへのパスをCloudFormation に記述して、ソースコードを変更したらまた同じ作業を・・・
という絶え間ない手作業が必要でした。
それを変えたのが sam です。
まず、CloudFormation テンプレートに Lambda 定義 (AWS::Serverless::Function という拡張リソース)を記述して、ソースコードへのパスを定義します。
AccessDynamoDBFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/access_dynamodb.lambda_handler
Role: !GetAtt LambdaFunctionRole.Arn
Events:
ReadDynamoDB:
Type: Api
Properties:
Path: /read
Method: get
WriteDynamoDB:
Type: Api
Properties:
Path: /write
Method: get
Environment:
Variables:
TABLE_NAME: !Ref DynamoTable
KMS_CMK_IDS: !Ref EncryptionKey
ENCRYPTION_CONTEXT: !Ref ENCRYPTIONCONTEXT
そして、以下のようなコマンドでローカル起動、AWSへデプロイなどを行います。
# ビルド
sam build [-t <テンプレート>, etc.]
# ローカルで起動
sam local invoke <関数名> [-t <テンプレート>, etc.]
# AWS へデプロイ
sam deploy --stack-name <スタック名> ...
では IaC ツールに CloudFormation ではなく Terraform を使う場合は、どうするのが良いんだろうというのが今回の話です
なお、自分が Lambda 開発に必要な要件は以下だと思っています
トライ1: aws-sam-terraform-examples
去年11月に出たパブリックプレビューな AWS 本家の機能です。
Terraform で定義した AWS Lambda を sam で制御できるようになります
https://aws.amazon.com/jp/blogs/compute/better-together-aws-sam-cli-and-hashicorp-terraform/
https://github.com/aws-samples/aws-sam-terraform-examples
内部的には、"null_resource" を使って、Lambda本体のデプロイ前にビルド用スクリプトを実行するようになっています
サンプルスクリプト抜粋
resource "aws_lambda_function" "hello-terraform" {
filename = "${local.building_path}/${local.lambda_code_filename}"
handler = "index.lambda_handler"
runtime = "python3.8"
function_name = "hello-terraform"
role = aws_iam_role.iam_for_lambda.arn
timeout = 30
depends_on = [
null_resource.build_lambda_function
]
}
resource "null_resource" "sam_metadata_aws_lambda_function_hello_terraform" {
triggers = {
resource_name = "aws_lambda_function.hello-terraform"
resource_type = "ZIP_LAMBDA_FUNCTION"
original_source_code = "${local.lambda_src_path}"
built_output_path = "${local.building_path}/${local.lambda_code_filename}"
}
depends_on = [
null_resource.build_lambda_function
]
}
resource "null_resource" "build_lambda_function" {
triggers = {
build_number = "${timestamp()}" # TODO: calculate hash of lambda function. Mo will have a look at this part
}
provisioner "local-exec" {
command = substr(pathexpand("~"), 0, 1) == "/"? "./py_build.sh \"${local.lambda_src_path}\" \"${local.building_path}\" \"${local.lambda_code_filename}\" Function" : "powershell.exe -File .\\PyBuild.ps1 ${local.lambda_src_path} ${local.building_path} ${local.lambda_code_filename} Function"
}
}
以下のような実行スクリプトで起動しました
# build
sam build --hook-name terraform --beta-features
# ローカル起動
sam local invoke aws_lambda_function.publish_book_review -e events/new-review.json --beta-features
[注意点] ディレクトリ構成によっては動かないことアリ
以下のように、Terraform テンプレートと Lambda ソースコードを別ディレクトリに配置したところ、ビルドスクリプトの実行で失敗しました
├── lambdas
│ └── TestPythonLambda1
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── templates
│ ├── main.tf
│ ├── provider.tf
│ ├── py_build.sh
│ └── variables.tf
local-exec を実行すると、テンプレートファイル群を /private/var/folders/XXX ディレクトリにコピーしてそこで実行されます。
その時、templates ディレクトリ外のディレクトリは持ってきてくれてないので、ソースコードが見つからずビルドスクリプトでエラーが出ていました。
`working-dir` オプションで指定しても、思ったような効果は得られず。 lambdaソースコードは、templates 配下に置く必要がありそうです
トライ2: terraform-aws-lambda
serverless.tf が作ったモジュールが公開されています
https://registry.terraform.io/modules/terraform-aws-modules/lambda/aws/latest
Terraformの定義は以下のようになりました。module のおかげできれいに隠蔽化されてます
module "lambda_function" {
source = "terraform-aws-modules/lambda/aws"
function_name = "my-lambda1"
description = "My awesome lambda function"
handler = "app.lambda_handler"
runtime = "python3.9"
architectures = [ "arm64" ]
source_path = "../lambdas/TestPythonLambda1"
}
以下のようなコマンドで起動を確認できました
# ローカル起動
sam local invoke --hook-name terraform module.lambda_function.aws_lambda_function.this[0] --beta-features
# AWS へデプロイはterraformで実行
terraform apply
[おまけ] 大量のオプションあり
"lambda_function" にはオプションがいっぱいありました。[inputs]
かゆいところに手がとどくオプションだったり、制限回避のためだったり・・
目を通しておくと Lambda ライフが充実します
- cloudwatch_logs_retention_in_days: 本家にほしい
- create_role = false : 自前のIAM RoleをLambdaに使いたい場合、これが必要
[注意点?] almost..?
どうやら Lambda の全機能は網羅できてないようです。未サポートの機能はなにか見つけられませんでしたが、使う前にドキュメント読んでおく必要アリですね
まとめ
今、Lambda を使うならterraform-aws-lambda がベストだ思います
その際、inputsなどのドキュメントを読んでおくと開発効率はさらに上がるはず
AWS本家の機能も気になるので、今後アップデートがあったら試してみます!