EventarcとCloud WorkflowsでCloudサービス間を少しずつ連携させる

クラウドピタゴラスイッチは好きですか。ぼくはきらいです。アレは下手に使うと無駄に複雑になってしまうと思う。でも、これくらいの使い勝手のものならむしろ好ましいのかも、と思える組み合わせもあるなと思うきっかけがあって、その時に利用したツール周りの話をまとめてみることにした。
Eventarc
Eventarc Advanced になると少し込み入った話が増えるので、いったん Standard で紹介しておく。
- Google Cloud はもともと各リソースが直接 Functions を叩いたり、Pub/Sub にメッセージを送信したり、event に対応したプログラムを実行する基盤があった
- それらの中に今も有効なものはあるが、基本的には Eventarc に統合されつつある
- Eventarc はイベントをシリアライズした情報を送信する HTTP POST request をあちこちに送信できるパッケージ
- body は Eventarc 独自の JSON
- header に CloudEvents の情報が載っている
- 実際のイベントの伝播には Pub/Sub など他のサービスを利用している
昔はイベント側が独自にイベントをエンコードして Functions を kick し、Functions 側は独自のイベントをデコードする必要があり、そのために SDK も用意されていたが、今はもう不要になったと言ってよい。
全体はこんな感じで、
Trigger って何よと思うかもしれないけど、Standard で言うと以下のようになっている。
Event providers and destinations | Eventarc Standard | Google Cloud
本当にいろんな provider が対応している。ただし、対応方法に種類があって、
- Audit Logs 経由
- Direct ( Pub/Sub 経由 )
がある。
実は Standard ではほとんどが Audit Logs 経由での対応になる。もう一つの Direct が昔からあるやつのように見える。今回はそこを深掘りしたいわけじゃないのでいったん置いておく。
Cloud Workflows
で、この Eventarc の Target に Workflows があるんだけど、Workflows から利用できるさまざまな API connector が用意されているので、
Connectors reference | Workflows | Google Cloud
これで Google Cloud のいろんなリソース同士を結びつけることができる。普通にコードが動くプラットフォームもあれば、そうでないものも利用できる。
つまり、Eventarc + Workflows を利用することで何らかのサービスの何らかのイベントを他のサービスの API 呼び出しに変換できる1。
そして、このためにわざわざ HTTP を受け取ってサービスの API を正しく叩くだけのコードを頑張って書いてデプロイ、テストする必要がないということになる23。
endpointが複数あるGoogle APIのconnectorは注意が必要
例えば Cloud Build には以下の2種類の API があるが、
- Method: googleapis.cloudbuild.v1.projects.triggers.run | Workflows | Google Cloud
- Method: googleapis.cloudbuild.v1.projects.locations.triggers.run | Workflows | Google Cloud
なんとこれ引数が違ってて、数字を対応づけて挙げると
1 は global trigger で、
{
projectId: string,
triggerId: string,
name: string,
body: RepoSource
}
2 は regional trigger で、
{
name: string,
body: RunBuilderTriggerRequest
}
になっている。RunBuilderTriggerRequest は
{
projectId: string,
source: RepoSource,
triggerId: string
}
になっている。最終的に RepoSource にはたどり着くんだけど、途中の構造は全然違う。
で、これ Gemini for Google Cloud も混乱してたりする。今のところ Gemini に限らず生成 AI はこういうよく似た API の微妙な違いがほんと苦手なので注意が必要、という話でした。いやほんとこういうの罠。
具体的に活用するうえで考えること
そもそもどういう使い方から始めるとよさそうか
こういうイベント駆動のシステムって、よく話題に出るのはイベントをストリームで処理してスケーラブル!ってやつなんだけど、実際には ID 管理してチェックして、ってやってると無限にはスケールしなくて、ボトルネックになる ID とそれで制約をチェックするストレージが出てきたりする。あとそもそもイベントで処理しなくてもよくない? みたいなものもある。
基本的に向いてるのはアプリケーションレイヤーから外れる変化を、他のシステムに繋げるものだと思う。少なくとも Eventarc で扱うイベントはビジネスロジックで言うところのイベントではなく、クラウドリソースのイベントなので。
よくある例は画像ファイルのアップロード完了をきっかけにその変換処理を始めるもの。似たものとしては動画や音声からデータを抜き出すとか CSV をアップロードしたら BigQuery にインポートするとか。
ただ、最近の本格的な Web アプリからファイルアップロードをオブジェクトストレージへのダイレクトアップロードで実現する形だと、Web アプリ側で整合性を保つために 1) JavaScript アップロード + 2) フォーム送信での関連レコードの CRUD になっているんじゃないかと思う4。2 が完了した段階で後続の処理を kick できるし、処理後のデータについても元レコードに紐づけて管理しやすいのではないかと思うので、わざわざこういう仕組みは必要なさそう。使わずに済むなら使わない方が複雑さは減るのでその方がよいと思う。
もう少しライトというか雑多なというか、ユーザーの直接的なリクエストとは関係ないファイルが自動的に生成され、そこから後続の処理が始まる、みたいなケースの方がよくマッチしそう。ということはバッチ処理とか、その辺りか、もう少しインフラ寄りの話の方が本当は便利なんだと思う。
at-least-once問題
言い換えると、
そのイベントを2回以上処理しても大丈夫ですか問題。
これは(あんまりはっきり書かれていないが)Eventarc の基盤が Pub/Sub の push ( HTTP ) notification だからって話と、そもそもイベント駆動ってそういうの考えないとダメだよね、がある。
そのままで2回以上処理することを具体例で表すとこういう感じ。
- [OK] 特定の ID のデータを unique 制約のある DB に保存する
- [NG] イベントで渡ってくる情報をもとにメールを送信する
1 は、2回以上実行しても「保存済みかどうか確認する」だけで済む。2 は、送信先に2件以上のメールが送信されてしまう可能性がある。
2 の場合も 1 と同じように送信履歴自体が DB に保存されていて、「送信済みかどうかチェックできるなら」流してしまっても ok と言える。5
素朴な生イベントじゃないものにするとよい可能性も
もう一つ引っかかるのが素朴なイベントをそのまま扱うのがよいとは限らない点。上の例だと雑多なバッチ処理の成果物なんかがそうで、例えば
- オブジェクトストレージに(何らかの処理後のデータを)保存した
- そのイベントからそのオブジェクトを利用した何かを始める
これを複数のファイルが生成された場合にその都度実行されてよいか?がある。上の at-least-once 問題と似ているけど違う。こちらは実際に発生するイベントが複数になった場合にそのまま複数処理して大丈夫か?問題。
これがそれぞれの成果物で処理が異なっているなら別に問題ないが、結果的に成果物をひとまとめで扱う場合は無駄に回数だけ増えることになる。数分おきにポツポツ生成される程度なら気にしなくてよいかもしれないが、数秒の間に何十個も生成されるようになると話はだいぶ変わってくる。さらに桁が変わる可能性もある。
これについては
- 実際の成果物
- 処理完了を知らせるためのファイル6
を分けて、2 のイベントを Eventarc で伝えて後続の処理を始めるとよさそう。
これは非常に分かりやすい例だけど、他にも
- windowing
- aggregation
- grouping
- debounce
- convolution
などいろんな概念があるらしい。この辺は低レイヤー方面の知識の多い人の方が強そう。
図の source と trigger の関係が上と変わってしまっているが、設定画面は trigger が主になるが、関係としては source -> target だろうということで少し入れ替えてある。 ↩
これはどうせローカルでまともに動かないコードなので準備が面倒なだけ。 ↩
ただし、Workflows 上で API 呼び出しがうまくいっているかどうかの確認をくり返す行程は必要なので、確認しやすい何かを準備しておく必要はある。これはなくせない。 ↩
少なくとも ActiveStorage はそう ↩
メールを例に出してしまったこと自体がアレで、メールって送信処理に入ったかどうかは分かっても向こうのサーバに拒否されたか、spam判定されたか、実際に着信したかどうかは分からない。分からないが、ここのケアの話は本題ではないので置いておく。 ↩
これをシグナルファイル、マーカーファイルなどと呼ぶらしい。 ↩