Cloud FunctionsでGo 1.11がβリリースされたので試したときのメモです。
GCPの課金情報をCloud StorageにJSON形式でexportするようにし、
Cloud Functions/GoでSlackに通知するコードを書いて試してみました。
関数の実装方法
関数の実装方法は以下の2種類に分けられるようです。
新しく覚えることはほとんどなくどちらも非常に簡単です。
HTTP functions
HTTPリクエストをトリガーにして実行される関数です。
実装は簡単で一般的なWebアプリケーションと同様にハンドラを定義すればよいです。
// function.go
package function
import "net/http"
func F(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Write([]byte(r.Header.Get("X-Forwarded-For")))
}
background functions
今回はこちらを使いました。
イベントをトリガーにして実行される関数です。
利用可能なイベントは、公式ドキュメントに記載があるので見てみてください。
- Cloud Storage
- Cloud Pub/Sub
- Cloud Firestore
- Google Analytics for Firebase
- Firebase Realtime Database
- Firebase Authentication
第二引数で各イベントに対応した構造体を受け取るように実装する必要があります。
現時点では構造体は自前で定義する必要がありますが、上記ドキュメントに記載されているものを参考にすれば良さそうです。
// function.go
// Package function includes an example of processing a GCS event.
package function
import (
"context"
"log"
)
// GCSEvent holds event data from a Google Cloud Storage Event.
type GCSEvent struct {
Bucket string `json:"bucket"`
Name string `json:"name"`
}
func F(ctx context.Context, e GCSEvent) error {
log.Printf("Processing file: %s", e.Name)
return nil
}
事前準備
- GCP
- IAMでCloud FunctionsのAPIの有効化
- Cloud SDKを最新にする
gcloud components update && gcloud components install beta
- Cloud Storageに適当なバケットを作成
- 課金情報をJSON形式で作成したバケットに出力するように設定
- その他
- SlackのWebhook URLをメモしておく
実装してみよう
作成したコードはこちらです。
概要
あまり難しいコードではないので要点だけ説明します。
GCPのサンプルコードも参考になるので併せて参照してください。
まず、Cloud Storageからのイベント情報を構造体で定義します。
こちらはGCPのサンプルコードを参考にしました。
type GCSEvent struct {
Bucket string `json:"bucket"`
Name string `json:"name"`
Metageneration string `json:"metageneration"`
ResourceState string `json:"resourceState"`
TimeCreated time.Time `json:"timeCreated"`
Updated time.Time `json:"updated"`
}
あとは、第一引数にcontext.Context
と第二引数に上記で定義した構造体を受け取る関数を定義するだけです。
簡単ですね!!
func F(ctx context.Context, e GCSEvent) error {
// contextからメタ情報を取得することができる
meta, err := metadata.FromContext(ctx)
if err != nil {
return fmt.Errorf("metadata.FromContext: %v", err)
}
log.Printf("Event ID: %v\n", meta.EventID)
log.Printf("Event type: %v\n", meta.EventType)
// ...省略
// Slackにwebhookで通知
return webhook(ctx, webhookURL, buildMessage(e.Name, b))
}
第二引数で受け取ったイベント情報(バケット名とファイル名)を使ってCloud Storageからファイルの内容を読み取ります。
今回は、JSON形式で課金情報が記載されているので、必要な情報を抜き出してSlackに通知しています。
func readFromGCS(ctx context.Context, bucket, name string) (io.ReadCloser, error) {
return storageClient.
Bucket(bucket).
Object(name).
NewReader(ctx)
}
依存ライブラリ
依存ライブラリの管理には、go modules
かvendoring
が利用可能です。
両方同時に利用することができないと記載があるので注意してください。
One-time initialization
初期化の方法が紹介されています。
DBのコネクションなど一度初期化しておけばよいものはまとめて実装しておきましょう。
func init()
とsync.Once.Do()
の2パターンが紹介されていますので、それぞれ用途に合わせて利用しましょう。
私は、Cloud Storageのクライアント初期化や正規表現のコンパイルをfunc init()
で行いました。
func init() {
var err error
storageClient, err = storage.NewClient(context.Background())
if err != nil {
log.Fatalf("storage.NewClient: %v", err)
}
webhookURL = os.Getenv("WEBHOOK")
regexB = regexp.MustCompile(`billing-(.*).json`)
}
デプロイしてみよう
ドキュメントを参考にしてgcloud cliでデプロイします。
gcloud functions deploy FUNCTION_NAME --runtime go111 --entry-point F --trigger-resource \
TRIGGER_BUCKET_NAME --trigger-event google.storage.object.finalize \
--region asia-northeast1 --project=PROJECT_ID --set-env-vars WEBHOOK=WEBHOOK_URL
FUNCTION_NAME
は、実装とは関係なくデプロイした関数の名前になるので好きなものを指定してください。
--entry-point
に呼び出して欲しい関数名を指定します。
--trigger-resource
にバケット名を指定します。
--trigger-event
には、イベントのタイプを指定します。
今回は、バケットにファイルが生成されたタイミングで関数を実行して欲しいのでgoogle.storage.object.finalize
を指定しています。
google.storage.object.finalize
は、以下のように説明されています。
This event is sent when a new object is created (or an existing object is overwritten, and a new generation of that object is created) in the bucket.
環境変数の設定
設定には環境変数を利用したいですよね。
Using Environment Variablesに記載があります。
先のコマンド例では、--set-env-vars WEBHOOK=WEBHOOK_URL
のようにデプロイ時にwebhook先のURLを環境変数として設定しています。
扱い方の注意事項などいろいろ記載があるので読んでみてください。
例えば、Variable lifecycle
には、環境変数はデプロイ時のみ設定が可能とあります。
デプロイ後にUI等で変更することはできないようです。
Environment variables are bound to a deployment of a Cloud Function, and can only be set or changed with a deployment. If a deployment fails for any reason, any changes to environment variables will not be applied. Environment variable changes require a successful deployment.
まとめ
Cloud Functions/Goを試してみました。
今後SDKが拡充されてもっと実装しやすくなることを期待します。
普段はApp Engineでサクッと済ませることが多いのですが、Cloud StorageやFirestoreなどで発生したイベントをもとに処理したいときには便利そうです。
また、Cloud Pub/SubやCloud Schedulerと組み合わせることでいろいろとおもしろい使い方ができそうです。
みなさんも試してみてください!!