mitsuのぶろぐ

基本的にはプログラミングの話のつもり。

Terraformで簡単なNode.jsアプリケーションをCloud Run作ってみた

表題の通りです。

Cloud RunでのNode.jsのアプリケーションのサンプルはあったり、

Terraformでのサンプルとかはあったりしたのですが、上の2つの組み合わせがあまりなかったので試してみました。

Terraform

# main.tf
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "5.40.0"
    }
  }
}

resource "google_artifact_registry_repository" "example_repository" {
  project       = var.project_id
  location      = var.location
  repository_id = "example"
  format        = "DOCKER"
}

resource "google_cloud_run_v2_service" "example_cloud_run_service" {
  project  = var.project_id
  location = var.location
  name     = "example"
  ingress  = "INGRESS_TRAFFIC_ALL"

  template {
    scaling {
      min_instance_count = 0
      max_instance_count = 1
    }
    containers {
      image = "${google_artifact_registry_repository.example_repository.location}-docker.pkg.dev/${var.project_id}/${google_artifact_registry_repository.example_repository.repository_id}/init"
    }
  }
}

resource "google_cloud_run_v2_service_iam_member" "example_cloud_run_service_iam_member" {
  location = var.location
  project  = var.project_id
  role     = "roles/run.invoker"
  member   = "allUsers"
  service  = google_cloud_run_v2_service.example_cloud_run_service.name
}
#variables.tf
variable "project_id" {
  type = string
}

variable "location" {
  default = "asia-northeast1"
}

Artifact RegistryとCloud Runをつなぐ

Google Cloudによせたかったので、imageのregistryにはArtifact Registryを利用しました。

Artifact Registryで作ったものをCloud Runのimage部分に参照させます。

今回試している間は上のコードをterraform applyして毎回当てていましたが、長期的なことを考えると初回の適用だけterraform apply経由で行い、その後は別の形でimageをupし、そのupsしたイメージをCloud Runにあてるよう修正する必要があると思いました。

Cloud RunのAPIを利用したい場合

Cloud Runは作成したタイミングで独自のURLを持っています。

あまりそれ自身をそのまま使うことはないでしょうが、今回はそこまでかしこまったアプリケーションを作りたかったわけではなかったので、動作確認にそのURLを叩く形を取りました。

いざ作成が完了したあとにそのURLを叩いてみたところ

Your client does not have permission to get URL / from this server.

という表示がでました。

なにも意識せずCloud Runのスタックを作った場合、認証部分は「認証が必要」な状態で作られていました。

画面上から切り替えてもよかったのですが、せっかくなのでそちらもTerraformに起こしてみました。

その結果は上にあるコードの以下の部分です

resource "google_cloud_run_v2_service_iam_member" "example_cloud_run_service_iam_member" {
  location = var.location
  project  = var.project_id
  role     = "roles/run.invoker"
  member   = "allUsers"
  service  = google_cloud_run_v2_service.example_cloud_run_service.name
}

こちらのiam_memberの設定を上のようにすると未認証で叩ける状態を実現できます。

他のパターンで疎通を試したい場合以下の記事が参考にできるかと思います。

blog.g-gen.co.jp

困ったポイント

実際にdeployした際にこのような表示に悩まされました。

 Error: Error waiting to create Service: Error waiting for Creating Service: Error code 13, message: Revision 'example-00001-9b8' is not ready and cannot serve traffic. The user-provided container failed to start and listen on the port defined provided by the PORT=8080 environment variable. Logs for this revision might contain more information.

Logs URL: https://console.cloud.google.com/logs/viewer?project=xxx&resource=cloud_run_revision/service_name/example/revision_name/example-00001-9b8&advancedFilter=resource.type%3D%22cloud_run_revision%22%0Aresource.labels.service_name%3D%22example%22%0Aresource.labels.revision_name%3D%example-00001-9b8%22 

For more troubleshooting guidance, see https://cloud.google.com/run/docs/troubleshooting#container-failed-to-start

Cloud Runのアプリケーションが外とうまくつながっていないようでした。

環境変数PORT は基本的に 8080 で設定されているようなので、自分の場合はDockerfile内で EXPOSE をするのを忘れていたのが一つ目の原因でした。

そしてEXPOSEしても動かないなと思い改めて以下の記事を眺めていました。

cloud.google.com

コンテナ ランタイムの契約に従って、コンテナ イメージを 64 ビット Linux 用にコンパイルされていることを確認します。

結論から申し上げるとdocker buildの際にplatformの指定をする必要があった、ということでした

docker build --platform linux/amd64 -t asia-northeast1-docker.pkg.dev/${PROJECT_ID}/example/${IMAGE_TAG}:latest .

気軽にCloud Runを試せるようになったので、もっといろいろ試していきたいと思います。