🐁

Go ランタイム Cloud Functions の開発環境を考える

2023/09/10に公開

はじめに

Cloud Functions 便利だけど構成とかってどうするのがいいんだろうか?を考えてみた記事となります。
デプロイなどの動作確認は行なっていますが、実際の運用などは行なってはいません。あくまで参考程度に見ていただければと思います。あしからず...

実行環境

uname -a
Darwin MacBook-Pro-7.local 22.6.0 Darwin Kernel Version 22.6.0: Wed Jul  5 22:22:52 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T8103 arm64
go version
go version go1.21.1 darwin/arm64
docker version
Client:
 Cloud integration: v1.0.35-desktop+001
 Version:           24.0.5
 API version:       1.43
 Go version:        go1.20.6
 Git commit:        ced0996
 Built:             Fri Jul 21 20:32:30 2023
 OS/Arch:           darwin/arm64
 Context:           desktop-linux

Server: Docker Desktop 4.22.1 (118664)
 Engine:
  Version:          24.0.5
  API version:      1.43 (minimum version 1.12)
  Go version:       go1.20.6
  Git commit:       a61e2b4
  Built:            Fri Jul 21 20:35:38 2023
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.6.21
  GitCommit:        3dce8eb055cbb6872793272b4f20ed16117344f8
 runc:
  Version:          1.1.7
  GitCommit:        v1.1.7-0-g860f061
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
gcloud version
Google Cloud SDK 437.0.1
bq 2.0.93
core 2023.06.30
gcloud-crc32c 1.0.0
gsutil 5.24

構成

ディレクトリ構成は以下となります。

.
├── LICENSE
├── Makefile            # タスクランナー
├── README.md
├── e2e
│   ├── bar_test.go     # bar エンドポイントへのE2Eテスト
│   └── foo_test.go     # foo エンドポイントへのE2Eテスト
├── functions
│   ├── bar
│   │   ├── bar.go      # クラウド環境に対するエントリーポイント
│   │   ├── go.mod
│   │   ├── go.sum
│   │   └── local
│   │       └── main.go # ローカル環境に対するエントリーポイント
│   └── foo
│       ├── foo.go      # クラウド環境に対するエントリーポイント
│       ├── go.mod
│       ├── go.sum
│       └── local
│           └── main.go # ローカル環境に対するエントリーポイント
├── go.mod
├── go.sum
├── go.work
├── go.work.sum
├── local
│   └── main.go         # ローカル環境でのルートエントリーポイント
└── script
    ├── add.sh          # 新たに Cloud Functions を追加するためのスクリプト
    ├── deploy.sh       # Cloud Functions をデプロイするためのスクリプト
    └── destory.sh      # Cloud Functions をデストロイするためのスクリプト

ポイント: go.work を活用したモノレポ構成

go.work を活用したモノレポ構成となっています。
(Cloud Functions ごとに go.mod が必要みたいなので... go.mod なしでデプロイできる方法があれば知りたいです。)

https://go.dev/doc/tutorial/workspaces

go.work を設定すれば VSCode でルートディレクトリで作業し続けることができます。
(go.work がないと警告が出た気がします。)

ポイント: functions-framework-go を活用したローカルでの開発

ローカル環境で動作確認を実施するために functions-framework-go というライブラリを活用します。

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

このライブラリを使ったローカル用のサーバーは Docker で起動するように設定しています。

Dockerfile

また起動には air というライブラリを利用することで半(?)ホットリロードになるようにしています。

https://github.com/cosmtrek/air

完全なホットリロードにしたいですが、現状はエントリーポイントの main.go を更新することでしかリロードされないです...

ポイント: ローカル環境向けの proxy サーバー実装

functions-framework-go を使うと Cloud Functions ごとにサーバーが立ち上がります。
これだとポート番号を Cloud Functions ごとに使い分ける必要があり使いづらかったので proxy を使うことにしました。
docker compose にて各 Cloud Functions を管理しdockerネットワーク内に閉じ込めます。

compose.yaml

それぞれの Cloud Functions はポート番号 8080 で起動させておき隠蔽します。
そして proxy サーバーから service 名で接続するように設定します。
proxy のコードも極力新しい Cloud Functions を追加してもコードを変更しないように実装しています。

proxy server main.go

環境変数でエントリーポイントとなる関数名(?)を追加して proxy を設定しています。

なぜこんなことをしたかったかというとクラウド環境にデプロイしたときに払い出されるエンドポイントとの差異を無くしたかったからです。
こうしておくことでパスを切り替えるだけで接続することができます。

E2Eテストのエンドポイントを環境変数で切り替えて実施できるようになります。

E2Eテスト

Cloud Functions を追加する

以下のゴリ押しスクリプトを組んで追加できるようにしてみました。go.work の使い方とかも多少はわかるかもしれません...(解説省略してすみません。)

add.sh

M1 Mac でしか動作確認を実施していないのでご了承ください。

おわりに

Cloud Functions は手軽にデプロイして動作確認できますが、なんだかんだローカル環境での動作確認もしたいかと思います。モノレポ構成でいい感じに考えることができたのではないでしょうか。
共通化して利用するための pkg を root において各 module から参照する方法とかもあるので時間があったら追記します。(replace 使ってごにょごにょする)

今回考えた構成は以下のリポジトリに置いておきます。ご参考まで。

https://github.com/otakakot/emutate-cloud-functions

Discussion