LoginSignup
4
0

More than 3 years have passed since last update.

はじめに

こんにちは、SREエンジニアをやっています。@hayaosatoです。
今回はサーバレスなインフラを構築する上で、Cloud FunctionsとAWS Lambda(以下、Lambda)についてTerraformにて管理する観点で比較してみようと思います。
構築する構成はそれぞれ以下のようになります。
コードはこちら

Cloud Function

terraform_cloud_functions.jpg

Lambda

torm_.jpg

どちらの構成もバケットにファイルがアップロードされた事をトリガに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がしんどい。

参考

4
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
4
0