💬

「Cloud Run functions」にコンテナがデプロイできるの知ってる?

2024/12/08に公開

Cloud Run functions は Google Cloud の FaaS です。
ユーザはコンテナ、ランタイム、Web サーバーを管理することなく、コードを書くだけでデプロイすることができます。
本来はコンテナ化が不要な Cloud Run functions ですが、コンテナをデプロイできることをご存知でしょうか。

Cloud Run functions の仕組み

ユーザが Cloud Run functions にデプロイしたコードは複数の抽象化レイヤーの上で動きます。[1]

一番内側にユーザが書いたコードがあり、その下にはまず Function Framework があります。
Function Framework は Google が公開している OSS で、ユーザの関数を HTTP アプリケーションでラップします。
その下にはコンテナがあります。
Cloud Run functions の場合は関数がデプロイされると、Buildpacks を用いて Docker コンテナをビルドします。
一番下のレイヤーはコンテナをホストするプラットフォームです。
Cloud Run functions ではビルドしたコンテナを Cloud Run 上にデプロイします。

最終的には Cloud Run でコンテナとしてデプロイされるわけなので、そのコンテナを自分で作ってデプロイすることも可能です。

やってみる

準備:Cloud Run functions の作成

こちらのドキュメントに従ってサンプルの関数を作成します。

https://cloud.google.com/functions/docs/create-deploy-gcloud

サンプルリポジトリのクローン

git clone https://github.com/GoogleCloudPlatform/golang-samples.git

Cloud Run functions のサンプルコードがあるディレクトリに移動

cd golang-samples/functions/functionsv2/helloworld/

サンプル関数の実装があります。

hello_world.go
package helloworld

import (
	"fmt"
	"net/http"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
)

func init() {
	functions.HTTP("HelloGet", helloGet)
}

// helloGet is an HTTP Cloud Function.
func helloGet(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello, World!")
}

デプロイ

gcloud functions deploy go-http-function \
  --gen2 \
  --runtime=go122 \
  --region=asia-northeast1 \
  --source=. \
  --entry-point=HelloGet \
  --trigger-http \
  --no-allow-unauthenticated

デプロイ結果に URL が表示されるので動作確認します。
Hello, World!が返ります。

curl -m 70 -X POST {関数のURL} \
  -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
  -H "Content-Type: application/json" \
  -d '{}'
Hello, World!%

デプロイが完了すると Artifact Registry にビルドされたイメージが push されています。
このイメージは次節で使います。

コンテナのビルドとデプロイ

変更を確認するため、関数が返す文字列を変えておきます。

hello_world.go
func helloGet(w http.ResponseWriter, r *http.Request) {
+	fmt.Fprint(w, "Hello from custom container!")
-	fmt.Fprint(w, "Hello, World!")
}

コンテナ化には Function Framework の README を参考に、Function Framework と Buildpacks を使ってみます。
(未確認ですが普通に Web サーバと Dockerfile を書いてもいけると思います)

https://github.com/GoogleCloudPlatform/functions-framework-go

メインパッケージ用ディレクトリの作成

mkdir cmd

cmd/main.goを作成

cmd/main.go
package main

import (
	"log"
	"os"

	"github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
	// Blank-import the function package so the init() runs
	_ "github.com/GoogleCloudPlatform/golang-samples/functions/functionsv2/helloworld"
)

func main() {
	// Use PORT environment variable, or default to 8080.
	port := "8080"
	if envPort := os.Getenv("PORT"); envPort != "" {
		port = envPort
	}

	// By default, listen on all interfaces. If testing locally, run with
	// LOCAL_ONLY=true to avoid triggering firewall warnings and
	// exposing the server outside of your own machine.
	hostname := ""
	if localOnly := os.Getenv("LOCAL_ONLY"); localOnly == "true" {
		hostname = "127.0.0.1"
	}
	if err := funcframework.StartHostPort(hostname, port); err != nil {
		log.Fatalf("funcframework.StartHostPort: %v\n", err)
	}
}

ビルド

pack build \
  --builder gcr.io/buildpacks/builder:v1 \
  --env GOOGLE_FUNCTION_SIGNATURE_TYPE=http \
  --env GOOGLE_FUNCTION_TARGET=HelloGet \
  {イメージ名}

プッシュ

docker push {イメージ名}

デプロイは Cloud Run として行います。

gcloud run deploy go-http-function --region=asia-northeast1 --image {イメージ名}

動作確認
Hello from custom container!が返ってきており、自前でビルドしたコンテナが動いているのが分かります。

curl -m 70 -X POST {関数のURL} \
  -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
  -H "Content-Type: application/json" \
  -d '{}'
Hello from custom container!%

コンテナがデプロイできると何が嬉しいか

何も嬉しくありません!
Cloud Run に対する Cloud Run functions の優位性はコンテナ、ランタイム、Web サーバーをユーザが管理しなくて良いことでした。
Cloud Functions は Cloud Run functions として Cloud Run に統合されており[2]、機能面ではほとんど差がありません。
コンテナ化してデプロイすることで得られるメリットはあるかもれしれませんが、それなら初めから Cloud Run を使えば良いだけです。

脚注
  1. https://cloud.google.com/functions/docs/running/overview#abstraction_layers ↩︎

  2. https://cloud.google.com/blog/products/serverless/google-cloud-functions-is-now-cloud-run-functions ↩︎

Discussion