はじめに
こんにちは、SREエンジニアをやっています。@hayaosatoです。
今回はサーバレスなインフラを構築する上で、Cloud FunctionsとAWS Lambda(以下、Lambda)についてTerraformにて管理する観点で比較してみようと思います。
構築する構成はそれぞれ以下のようになります。
コードはこちら
Cloud Function
Lambda
どちらの構成もバケットにファイルがアップロードされた事をトリガにSlack通知をするアプリケーションが動くようになっています。
Cloud Functions
まずはCloud Functionを作成していきます。コードは以下の通り
resource "google_cloudfunctions_function" "default" {
name = var.service_name
description = "Function of ${var.service_name}"
runtime = "python37"
available_memory_mb = var.available_memory_mb
source_archive_bucket = google_storage_bucket.source.name
source_archive_object = google_storage_bucket_object.source.name
event_trigger {
event_type = "google.storage.object.finalize"
resource = google_storage_bucket.trigger.name
}
entry_point = var.entry_point
environment_variables = {
SLACK_API_KEY = var.SLACK_API_KEY
}
}
Cloud Storageからのトリガを受け取って起動するためには event_trigger
を定義する必要があります。
その中でevent_type
にトリガのタイミングを指定します。今回はファイルがアップロードされた時に起動したいのでfinalize
を指定しました。
Cloud Storage
今回作成するCloud Storageは2つです。
一つはCloud Functionのソースコードzipファイルを格納するバケットでもう一つはCloud Functionのトリガのためのバケットです。
それぞれ見ていきましょう。
ソースコード保存用バケット
ソースコード保存用バケットでは、Terraformにてソースコードのアップまでをやっておきましょう。
data "archive_file" "source" {
type = "zip"
source_dir = "src"
output_path = "${var.service_name}.zip"
}
resource "google_storage_bucket" "source" {
name = "${var.service_name}_source"
location = "${var.region}"
labels = {
service = var.service_name
source = "true"
}
}
resource "google_storage_bucket_object" "source" {
name = "${var.service_name}.zip"
bucket = google_storage_bucket.source.name
source = "${var.service_name}.zip"
}
かなりすっきり書けて良い感じですね。
しかし、ここで気をつけなければいけないのがソースコードのアップデートです。
一度 terraform apply
でCloud Functionを作成した後にCloud Storageのソースコードを更新したところで
ソースコードの更新をCloud Functionに反映することがTerraformでは出来ないです。
(TerraformでCloud Functionソースコード更新出来る方法知っている方いたら教えてください。。。)
トリガ用バケット
resource "google_storage_bucket" "trigger" {
name = "${var.service_name}_trigger"
location = "${var.region}"
labels = {
service = var.service_name
}
force_destroy = true
}
Cloud FunctionsではこのようにTerraformで定義することが出来ました。
Lambda
続いてLambdaです。Lambdaの場合はIAMroleの指定が必須となるので、ロールもTerraformで管理するかどうかで大変さが変わりますが、
今回ではロールもTerraformで管理しようと思います。
また、ログの保存ためにCloud Watch Logも作成します。
Lambda関数
resource "aws_iam_role" "default" {
name = var.service_name
description = "IAM Rolw for ${var.service_name}"
assume_role_policy = file("policies/${var.service_name}-role.json")
}
resource "aws_iam_policy" "default" {
name = var.service_name
description = "IAM Policy for ${var.service_name}"
policy = file("policies/${var.service_name}-policy.json")
}
resource "aws_iam_role_policy_attachment" "default" {
role = aws_iam_role.default.name
policy_arn = aws_iam_policy.default.arn
}
resource "aws_cloudwatch_log_group" "default" {
name = "/aws/lambda/${var.service_name}"
retention_in_days = 7
}
data archive_file "default" {
type = "zip"
source_dir = "src"
output_path = "${var.service_name}.zip"
}
resource "aws_lambda_function" "default" {
filename = "${var.service_name}.zip"
function_name = var.service_name
role = aws_iam_role.default.arn
handler = "main.main"
source_code_hash = data.archive_file.default.output_base64sha256
runtime = "python3.7"
environment {
variables = {
SLACK_API_KEY = var.SLACK_API_KEY
}
}
}
AWS Lambdaの場合はIAMやCloudWatchもTerrraformで作成しないといけないので複数リソースを定義する必要がありますね
ただし、ソースコードはLambda自身が持っているのでソースコードの更新はterraform apply
のみで再作成なしに更新することができます。
(pip install -t .
をやらなければいけない辛さはありますが、、、)
トリガ用バケット
resource "aws_lambda_permission" "default" {
statement_id = "AllowExecutionFromS3Bucket"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.default.function_name
principal = "s3.amazonaws.com"
source_arn = aws_s3_bucket.default.arn
}
resource "aws_s3_bucket" "default" {
bucket = var.service_name
}
resource "aws_s3_bucket_notification" "default" {
bucket = aws_s3_bucket.default.id
lambda_function {
lambda_function_arn = aws_lambda_function.default.arn
events = ["s3:ObjectCreated:*"]
}
}
トリガ用バケットはこのようになります。バケットの作成自体は割とすっきりしているのですが、
定義するリソース数は多いなという印象を受けました。
まとめ
Cloud StorageとAWS Lambdaにて同等の機能を持ったアプリケーションを実装してみたのですが、
僕個人の感想としては、
Cloud Storage
定義するリソースが必要最低限で済むので、わざわざ定義しなくてもStack Driverとかでログが見れたりするので非常に楽で入門しやすい。
ただし、ソースコードのアップデートがterraformからだと現状できなさそうなので、そこがとてもつらい。
AWS Lambda
基本的に全部定義しなければいけないので、定義がめんどくさいしソースコードが長くなりがちだが、まぁそこまで辛くはなさそう。
ソースコードのアップデート自体は楽だが、(Lambda自体の話ですが)外部ライブラリを使う際のpip install
がしんどい。