サイトアイコン 協栄情報ブログ

Google Cloud Workflows の 429エラーに対するリトライ実装ハンズオン

1.はじめに

APIやマイクロサービスを呼び出す際、レート制限による429エラー(Too Many Requests)や一時的なサーバーエラー(500系)に遭遇することがあります。
こうした一時的な障害を乗り越えるために、Google Cloud Workflowsにはリトライ機能を設定することが可能です。

本記事では、Workflowsのリトライ設定を実装し、実際に429エラーが発生した場合にも対応できるハンズオンを通じて、具体的な設定方法とその動作を確認していきます。

2.ハンズオン

2.1.前提

2.1.1.実行環境

2.1.2.事前準備

2.1.3.本構成の説明

項番 リソース名 役割
1 Cloud Run 429エラーを返すエンドポイントを想定した Cloud Run
2 Workflows リトライロジックを実装した Workflows

2.2.環境設定

# 環境変数
YOUR_PROJECT_ID="あなたのプロジェクトID"

# Google Cloud CLIログイン確認
gcloud auth list

# プロジェクト設定
gcloud config set project $YOUR_PROJECT_ID

2.3.サービスアカウント作成

# ワークフロー用サービスアカウント作成
gcloud iam service-accounts create workflow-retry-sa --display-name="Workflow Retry Service Account"

# 権限付与(Cloud Run呼び出し用)
gcloud projects add-iam-policy-binding $YOUR_PROJECT_ID \
    --member="serviceAccount:workflow-retry-sa@$YOUR_PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/run.invoker"

# 権限付与(ログ書き込み用)
gcloud projects add-iam-policy-binding $YOUR_PROJECT_ID \
    --member="serviceAccount:workflow-retry-sa@$YOUR_PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/logging.logWriter"

2.4.Cloud Run作成(429エラー返却用)

# フォルダ作成
mkdir -p ~/cloudrun-test && cd ~/cloudrun-test

# package.json作成
cat > package.json << EOF
{
  "name": "cloudrun-429-test",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "express": "^4.17.1"
  }
}
EOF

# index.js作成(429エラーを返すサービス)
cat > index.js << EOF
const express = require('express');
const app = express();

app.use(express.json());

app.post('/', (req, res) => {
  // 常に429エラーを返す
  res.status(429).json({
    error: 'Too Many Requests',
    message: 'Rate limit exceeded'
  });
});

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(\`Server running on port \${PORT}\`);
});
EOF

# Dockerfile作成
cat > Dockerfile << EOF
FROM node:18-slim
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . ./
CMD [ "node", "index.js" ]
EOF

# ビルドとデプロイ
gcloud builds submit --tag gcr.io/${YOUR_PROJECT_ID}/cloudrun-429-test
gcloud run deploy cloudrun-429-test \
    --image gcr.io/${YOUR_PROJECT_ID}/cloudrun-429-test \
    --platform managed \
    --region us-central1 \
    --allow-unauthenticated

2.5.Workflowsファイル作成

# ワークフローディレクトリ作成
mkdir -p ~/retry-workflow && cd ~/retry-workflow

cat > workflow.yaml << 'EOF'
main:
  params: [input]
  steps:
    # リトライポリシー設定
    - init:
        assign:
          - retry_policy:
              predicate: ${retry_predicate} # リトライ判定を行うサブルーチンを指定
              max_retries: 5               # 最大リトライ回数
              backoff:
                initial_delay: 2           # 初回リトライまでの待機時間 (秒)
                max_delay: 60              # 最大待機時間 (秒)
                multiplier: 2              # 待機時間に乗算する係数 (指数バックオフ)

    # CloudRun呼び出し部分
    - call_cloud_run:
        try:
          call: http.post                 # HTTP POSTリクエストを実行
          args:
            url: "https://cloudrun-429-test-xxxxxxxx-uc.a.run.app" # ★★★ ご自身のCloud Run URLに置き換えてください ★★★
            body: ${input}                 # ワークフローの入力データをリクエストボディに設定
            auth:
              type: OIDC                   # OIDC認証を使用 (Workflowsのサービスアカウントを利用)
          result: api_response             # 成功時のレスポンスを変数 api_response に格納
        retry: ${retry_policy}             # initステップで定義したリトライポリシーを適用

    # 最終結果を返す
    - return_result:
        return: ${api_response}           # Cloud Runからのレスポンスをワークフローの出力として返す

# リトライ判定関数 (サブルーチン)
retry_predicate:
  params: [error]                         # http.post が失敗した場合のエラー情報を受け取る
  steps:
    # エラーコードに基づいてリトライするかどうかを判断
    - check_error_code:
        switch:                           # 条件分岐
          - condition: ${error.code == 429} # エラーコードが 429 (Too Many Requests) の場合
            next: log_and_retry           # log_and_retry ステップへ進む
          - condition: ${error.code >= 500 and error.code < 600} # エラーコードが 5xx (Server Error) の場合
            next: log_and_retry           # log_and_retry ステップへ進む
          # 上記以外のエラーコードの場合
          - condition: true
            return: false                  # リトライしない (false を返す)

    # リトライする場合のログ出力
    - log_and_retry:
        call: sys.log                     # ログを出力
        args:
          text: "リトライします"           # ログメッセージ
          severity: "INFO"                # ログレベル
        next: return_true                 # return_true ステップへ進む

    # リトライ実行を指示
    - return_true:
        return: true                      # リトライする (true を返す)
EOF

2.6.Workflows作成・デプロイ

# コマンド実行
gcloud workflows deploy retry-test-workflow \
    --source=workflow.yaml \
    --service-account=workflow-retry-sa@$YOUR_PROJECT_ID.iam.gserviceaccount.com \
    --location=us-central1

2.7.Workflow実行

2.7.1.実行コマンド

# Workflows実行コマンド
gcloud workflows run retry-test-workflow \
    --location=us-central1 \
    --data='{}'

2.7.2.レスポンス

項番 項目 説明
1 実行状態 (state) FAILED ワークフローの実行がエラーにより失敗したことを示す
2 エラーコード (error.code) 429 呼び出し先のHTTPサーバー(Cloud Run)が「Too Many Requests」エラーを返す
3 エラーメッセージ (error.payload.body.message) "Rate limit exceeded" Cloud Runサービス側で設定されたリクエスト数のレート制限を超えたため、リクエストが拒否されました。
4 エラー発生ステップ (error.context) call_cloud_run (main ルーチン内) Cloud Runを呼び出す call_cloud_run ステップでエラーが発生。
5 実行時間 (duration) 65.177827083s ワークフロー全体の実行時間。リトライ処理(最大5回、バックオフあり)を試みた合計の時間

2.8.ログでの確認

gcloud logging read "resource.type=workflows.googleapis.com/Workflow AND resource.labels.workflow_id=retry-test-workflow" \
    --project=$YOUR_PROJECT_ID \
    --format=json \
    --limit=10
  {
    "insertId": "n2jdnyf80r3bx",
    "labels": {
      "execution_id": "c3b1d5af-7f15-4f48-be8e-11cd729c215b",
      "revision_id": "000004-030"
    },
    "logName": "projects/pdf-analyzer-20250405/logs/Workflows",
    "receiveTimestamp": "2025-05-01T12:52:16.783674098Z",
    "resource": {
      "labels": {
        "location": "us-central1",
        "resource_container": "{プロジェクトID}",
        "workflow_id": "retry-test-workflow"
      },
      "type": "workflows.googleapis.com/Workflow"
    },
    "severity": "INFO",
    "textPayload": "リトライします",
    "timestamp": "2025-05-01T12:52:16.783674098Z"
  }

3.おわりに

3.1.得られた知見

モバイルバージョンを終了