Terraform で Cloud Functions をデプロイする

2021年2月20日

概要

やりたいこと

Terraform で CloudFunctions をデプロイしたい。

前回の GCP チュートリアルの続き。

CloudFunctions のデプロイ

GCP 版 AWS Lambda。デプロイ方法は以下を参考にした。

流れ。

  1. ローカルでプログラムファイルを作成・修正
  2. Terraform で zip 化
  3. Terraform で zip ファイルを GCS へアップロード
  4. CloudFunctions が GCS から zip を取得し解凍

Terraform

Terraform 準備

前回参照。

Python で Hellow World

CloudFunctions で動かすプログラムは Python を選択。基本の Hello World。

main.py という名前で作成しないといけない。

vi main.py
def hello_world(request):
    """Responds to any HTTP request.
    Args:
        request (flask.Request): HTTP request object.
    Returns:
        The response text or any set of values that can be turned into a
        Response object using
        `make_response <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>`.
    """
    request_json = request.get_json()
    if request.args and 'message' in request.args:
        return request.args.get('message')
    elif request_json and 'message' in request_json:
        return request_json['message']
    else:
        return f'Hello World!'

Terraform で CloudFunctions

ランタイムは Python3.7 を指定する。

vi cloudfunctions.tf
data "archive_file" "function_archive" {
  type        = "zip"
  source_dir  = "code" # main.pyやrequirement.txtが入ってるdir
  output_path = "code/main.zip" # zipファイルの出力パス
}

resource "google_storage_bucket" "bucket" {
  name = "test-bucket-hello-world-test"
}

resource "google_storage_bucket_object" "archive" {
  name   = "hellow_${data.archive_file.function_archive.output_md5}.zip"
  bucket = google_storage_bucket.bucket.name
  source = data.archive_file.function_archive.output_path
}

resource "google_cloudfunctions_function" "function" {
  name        = "function-test"
  description = "My function"
  runtime     = "python37"

  available_memory_mb   = 128
  source_archive_bucket = google_storage_bucket.bucket.name
  source_archive_object = google_storage_bucket_object.archive.name
  trigger_http          = true
  entry_point           = "hello_world"
}

resource "google_cloudfunctions_function_iam_member" "invoker" {
  project        = google_cloudfunctions_function.function.project
  region         = google_cloudfunctions_function.function.region
  cloud_function = google_cloudfunctions_function.function.name

  role   = "roles/cloudfunctions.invoker"
  member = "allUsers"
}

デプロイ。

terraform apply

curl から呼び出せることを確認。

url https://us-central1-<PROJECT_ID>.cloudfunctions.net/function-test

エラー

ERROR: error fetching storage source: generic::unknown: retry budget exhausted (3 attempts): fetching gcs source: unpacking source from gcs: source fetch container exited with non-zero status: 9

ローカルの python ファイルを zip に圧縮しないで指定していた。

resource "google_storage_bucket_object" "archive" {
  name   = "index.zip"
  bucket = google_storage_bucket.bucket.name
  source = "./slack_bot.py"
}

python ファイルを zip で圧縮し、zip ファイルを指定するようにした。

resource "google_storage_bucket_object" "archive" {
  name   = "index.zip"
  bucket = google_storage_bucket.bucket.name
  source = "./slack_bot.py.zip"
}

Error: Error waiting for Creating CloudFunctions Function: Error code 3, message: Function failed on loading user code. This is likely due to a bug in the user code. Error message: File main.py that is expected to define function doesn’t exist

main.py という名前で python ファイルを作成しないといけなかった。

Error: Error waiting for Creating CloudFunctions Function: Error code 3, message: Function failed on loading user code. This is likely due to a bug in the user code. Error message: File main.py is expected to contain a function named helloGET

python のメソッド名が Terraform の endpoint と一致していない( helloGET というメソッドがない)

Terraform 側の endpoint 名を変更した。

resource "google_cloudfunctions_function" "function" {
  name        = "function-test"
  description = "My function"
  runtime     = "python37"

  available_memory_mb   = 512
  source_archive_bucket = google_storage_bucket.bucket.name
  source_archive_object = google_storage_bucket_object.archive.name
  trigger_http          = true
  entry_point           = "hello_http"
}

Error: Error applying IAM policy for cloudfunctions cloudfunction “projects/<PROJECT_ID>/locations/us-central1/functions/function-test”: Error setting IAM policy for cloudfunctions cloudfunction “projects/<PROJECT_ID>/locations/us-central1/functions/function-test”: googleapi: Error 403: Permission ‘cloudfunctions.functions.setIamPolicy’ denied on resource ‘projects/<PROJECT_ID>/locations/us-central1/functions/function-test’ (or resource may not exist).

IAM Policy を CloudFunctions にセットできない。

パブリックアクセスを許可する設定部分。

resource "google_cloudfunctions_function_iam_member" "invoker" {
  project        = google_cloudfunctions_function.function.project
  region         = google_cloudfunctions_function.function.region
  cloud_function = google_cloudfunctions_function.function.name

  role   = "roles/cloudfunctions.invoker"
  member = "allUsers"
}

cloudfunctions.functions.setIamPolicy 権限がないためとのこと。

Terraform 用のサービスロールに Cloud Functions Admin を追加した。

参考

コスト管理の自動レスポンスの例

GCP予算アラートをSlackに通知する

Terraformを使ってCloudFunctionsのzip化とdeployを行う方法