この記事は、Supershipグループ Advent Calendar 2024の4日目の記事になります。
はじめに
Google CloudのWorkflowsを業務で使用する機会があったため、そこで得た知見を共有したいと思います。
Workflows をあまり触ったことがない人向けです。
事例
今回は、GCSの任意のorig
フォルダに動画をアップロードしたときに動画変換を行うワークフローをWorkflowsを使って実現しました。
柔軟なトリガーを使いたい時は Cloud Audit Logs 経由のイベントを指定する
さっそくWorkflowsというよりEventarcの話になりますが…。
GCSの任意のフォルダへアップロードしたときにトリガーを発火させたい場合は、Eventarcのパスパターンが有効でした。
例えば、今回の事例ではフォルダ orig
を含むときに発火させたかったので、パスパターンは以下のようになりました。
/projects/_/buckets/バケット名/**/orig/*
このパスパターンですが、イベントタイプによって使用できない場合があります。
今回の事例でいうと、GCSをイベントプロバイダ、Workflowsをイベントの宛先とする場合、Eventarcトリガーで使用できるイベントタイプには以下の2種類があります。
- Cloud Audit Logsイベント
- 直接イベント
この2つについてパスパターンの適用可否を確認してみます。
Cloud Audit Logsイベントの場合は、pathPatternSupported: true
とありますので、パスパターンを適用できることがわかります。
gcloud eventarc providers describe cloudaudit.googleapis.com --location=asia-northeast1
displayName: Cloud Audit Logs
eventTypes:
- description: An audit log is created that matches the trigger's filter criteria.
filteringAttributes:
- attribute: methodName
description: The identifier of the service's operation.
required: true
- attribute: resourceName
description: The complete path to a resource. Used to filter events for a specific
resource.
pathPatternSupported: true <-
- attribute: serviceName
description: The identifier of the Google Cloud service.
required: true
- attribute: type
required: true
type: google.cloud.audit.log.v1.written
name: projects/プロジェクトID/locations/asia-northeast1/providers/cloudaudit.googleapis.com
一方で直接イベントの場合は、pathPatternSupported: true
が見当たらないので、パスパターンを適用できないことがわかります。
$ gcloud eventarc providers describe storage.googleapis.com --location=asia-northeast1
displayName: Cloud Storage
eventTypes:
- description: The live version of an object has become a noncurrent version.
filteringAttributes:
- attribute: bucket
description: The bucket name being watched.
required: true
- attribute: type
required: true
type: google.cloud.storage.object.v1.archived
- description: An object has been permanently deleted.
filteringAttributes:
- attribute: bucket
description: The bucket name being watched.
required: true
- attribute: type
required: true
type: google.cloud.storage.object.v1.deleted
- description: A new object is successfully created in the bucket.
filteringAttributes:
- attribute: bucket
description: The bucket name being watched.
required: true
- attribute: type
required: true
type: google.cloud.storage.object.v1.finalized
- description: The metadata of an existing object changes.
filteringAttributes:
- attribute: bucket
description: The bucket name being watched.
required: true
- attribute: type
required: true
type: google.cloud.storage.object.v1.metadataUpdated
name: projects/プロジェクトID/locations/asia-northeast1/providers/storage.googleapis.com
以上より、GCSでイベントプロバイダとしてパスパターンによる柔軟なトリガーを使いたい場合は、Cloud Audit Logs経由のイベントを指定する必要があります。
prefix指定のみできれば良い場合は、直接イベントで行う方法もあるようですね!
ちなみに2024年10月31日にPreview版になったEventarc Advancedの方が、より詳細な制御ができるそうですが、GCSはサポートされておらず、また東京リージョンでは使えませんでした。
サブワークフローは変数のスコープが分断される
公式ベストプラクティスにもある通り、サブワークフローを使用するとワークフローを分割することができ、ワークフローの複雑化を回避することができます。
可視化されるフロー図もサブワークフロー単位となるので実行の流れも追いやすくなります。
エラーハンドリングのような共通で使用するワークフローは、特にサブワークフローとして切り出すと良さそうです。
ただし変数のスコープが分かれるので、サブワークフローで使用する変数をparamsフィールドで受け取る必要があります。
Subworkflows do not have access to any variables from the main block of a workflow or other subworkflows. A subworkflow receives values passed in a call, and assigns those values to new variables in the subworkflow scope. Note that all step names in a subworkflow must be unique, even if they are in a scope.
https://cloud.google.com/workflows/docs/reference/syntax/variables#variable-scope
例えば、発生したエラーをFirestoreに格納したい場合、格納するdocument_pathを参照するためにエラーが発生するワークフローからdocument_pathを引き回す必要がありました。
main:
params: [event]
steps:
- init:
assign:
- document_path: ...
- call_convert_file:
call: convert_file
args:
...
document_path: ${document_path}
- call_upload_converted_file:
call: upload_converted_file
args:
...
document_path: ${document_path}
...
convert_file:
params: [..., document_path]
steps:
- post_convert:
try:
...
except:
as: error
steps:
- known_error:
switch:
- condition: ${error.body != null and error.body.status != null}
steps:
- store_known_error:
call: store_error
args:
error_code: ${error.body.code}
error_message: ${error.body.message}
document_path: ${document_path}
next: end
- unknown_error:
call: store_error
args:
error_code: "unknown"
error_message: ${error.message}
document_path: ${document_path}
next: end
...
upload_converted_file:
params: [..., document_path]
steps:
...
store_error:
params: [error_code, error_message, document_path]
steps:
- init_store_error:
assign:
- project_id: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
- store_error_to_firestore:
call: googleapis.firestore.v1.projects.databases.documents.patch
args:
name: ${"projects/"+project_id+"/databases/(default)/documents/"+document_path}
updateMask:
fieldPaths:
- "errorCode"
- "errorMessage"
body:
fields:
errorCode:
stringValue: ${error_code}
errorMessage:
stringValue: ${error_message}
- raise_error:
raise:
error_code: ${error_code}
error_message: ${error_message}
サブワークフローはプログラミング言語の関数に似ていますが、グローバル変数のようには変数を使えないと思って分割を考える必要があります。
環境変数で環境差分を吸収する
各環境でアクセスするドメイン名やSecret Managerのシークレット名などが異なる場合は、環境変数を使用してYAMLの共通化を図ることができます。
env.prod.yaml や env.stg.yaml として保存しておき、--env-vars-file
オプションでそのパスを指定すれば読み込むことができます。
$ gcloud workflows deploy WORKFLOW_NAME --env-vars-file ./env.prod.yaml
環境変数をWorkflowsのコード上から参照するときは、sys.get_env()を使用します。
main:
steps:
- init:
assign:
- keyValue: ${sys.get_env("KEY1")}
- returnResult:
return: ${keyValue}
使用できる環境変数は最大で20個、4KiBまでです。
長時間実行には向いているが処理には向いていない
Workflowsは、実行可能な最長時間が1年と非常に長いため、タイムアウトを気にする必要がほとんどありません。
料金形態もステップごとの課金のため、長時間実行による課金はありません。
プロジェクトあたりの同時実行数も定期的に増加されており、現在では10,000となっています1。
今回の事例では動画変換状態のポーリングをしていて変換には5〜10分以上かかるため、Cloud Functionsではタイムアウト時間 9分2 を超える恐れがありましたが、Workflowsではタイムアウトの心配をせずに済みました。
その一方で、Workflowsは、変数に512KBまでしかデータを持つことができません。
今回の事例では変換後の動画がS3に格納されており、それをGCSにアップロードする必要がありましたが、512KBまでしかデータを持つことができず、それ以前にHTTPレスポンスサイズも2MBまでなので、ダウンロード時点でエラーになってしまいました。
仕方ないので大人しく、アップロード処理のみCloud Functionsで実装しました。
他にも、割り当て(assign)できる変数の上限が50個であることも考慮する必要があります。
以下の記事でも言及されているように、基本的に処理はCloud Functionsに任せてワークフローの実装に集中するのが良さそうですね。
おわりに
Workflowsを使用してみて、Google Cloudサービスであれば認証情報を渡さずとも正しいロールが設定してあれば簡単にオーケストレーションできるのがやはり便利でした。
公式サンプルが充実しているのもありがたいですね。
最後に宣伝です。
Supershipではプロダクト開発やサービス開発に関わる人を絶賛募集しております。
ご興味がある方は以下リンクよりご確認ください。
Supership 採用サイト
是非ともよろしくお願いします。
-
日本語ドキュメントでは未更新のためか7,500になっているので注意: https://cloud.google.com/workflows/quotas?hl=ja#quotas ↩
-
https://cloud.google.com/functions/docs/configuring/timeout?hl=ja ↩