Workflows を使用したサーバーレス オーケストレーションの概要

1. はじめに

c9b0cc839df0bb8f.png

Workflows を使用すると、一連のサーバーレス タスクを定義した順序でリンクするサーバーレス ワークフローを作成できます。Google Cloud の API、Cloud Functions や Cloud Run などのサーバーレス プロダクト、外部 API の呼び出し機能を組み合わせて、柔軟なサーバーレス アプリケーションを作成できます。

Workflows ではインフラストラクチャの管理が不要で、ゼロへのスケールダウンも含め、需要に応じてシームレスにスケーリングできます。料金モデルは従量課金制で、料金は実行時間に対してのみ発生します。

この Codelab では、さまざまな Google Cloud サービスと外部 HTTP API を Workflows に接続する方法を学びます。具体的には、2 つのパブリック Cloud Functions サービス(一方のプライベート Cloud Run サービスと外部の公開 HTTP API)をワークフローに接続します。

学習内容

  • Workflows の基礎」へようこそ。
  • パブリック Cloud Functions の関数を Workflows に接続する方法。
  • 限定公開の Cloud Run サービスを Workflows に接続する方法。
  • Workflows で外部 HTTP API を接続する方法。

2. 設定と要件

セルフペース型の環境設定

  1. Cloud Console にログインし、新しいプロジェクトを作成するか、既存のプロジェクトを再利用します(Gmail アカウントまたは G Suite アカウントをお持ちでない場合は、アカウントを作成する必要があります)。

H_hgylo4zxOllHaAbPKJ7VyqCKPDUnDhkr-BsBIFBsrB6TYSisg6LX-uqmMhh4sXUy_hoa2Qv87C2nFmkg-QAcCiZZp0qtpf6VPaNEEfP_iqt29KVLD-gklBWugQVeOWsFnJmNjHDw

dcCPqfBIwNO4R-0fNQLUC4aYXOOZhKhjUnakFLZJGeziw2ikOxGjGkCHDwN5x5kCbPFB8fiOzZnX-GfuzQ8Ox-UU15BwHirkVPR_0RJwl0oXrhqZmMIvZMa_uwHugBJIdx5-bZ6Z8Q

jgLzVCxk93d6E2bbonzATKA4jFZReoQ-fORxZZLEi5C3D-ubnv6nL-eP-iyh7qAsWyq_nyzzuEoPFD1wFOFZOe4FWhPBJjUDncnTxTImT3Ts9TM54f4nPpsAp52O0y3Cb19IceAEgQ

プロジェクト ID を忘れないようにしてください。プロジェクト ID はすべての Google Cloud プロジェクトを通じて一意の名前にする必要があります(上記の名前はすでに使用されているので使用できません)。以降、このコードラボでは PROJECT_ID と呼びます。

  1. 次に、Google Cloud リソースを使用するために、Cloud Console で課金を有効にする必要があります。

このコードラボを実行しても、費用はほとんどかからないはずです。このチュートリアル以外で請求が発生しないように、リソースのシャットダウン方法を説明する「クリーンアップ」セクションの手順に従うようにしてください。Google Cloud の新規ユーザーは $300 の無料トライアル プログラムをご利用いただけます。

Cloud Shell の起動

Google Cloud はノートパソコンからリモートで操作できますが、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。

GCP Console で右上のツールバーにある Cloud Shell アイコンをクリックします。

STgwiN06Y0s_gL7i9bTed8duc9tWOIaFw0z_4QOjc-jeOmuH2TBK8l4udei56CKPLoM_i1yEF6pn5Ga88eniJQoEh8cAiTH79gWUHJdKOw0oiBZfBpOdcEOl6p29i4mvPe_A6UMJBQ

プロビジョニングと環境への接続にはそれほど時間はかかりません。完了すると、次のように表示されます。

r6WRHJDzL-GdB5VDxMWa67_cQxRR_x_xCG5xdt9Nilfuwe9fTGAwM9XSZbNPWvDSFtrZ7DDecKqR5_pIq2IJJ9puAMkC3Kt4JbN9jfMX3gAwTNHNqFmqOJ-3iIX5HSePO4dNVZUkNA

この仮想マシンには、必要な開発ツールがすべて用意されています。永続的なホーム ディレクトリが 5 GB 用意されており、Google Cloud で稼働します。そのため、ネットワークのパフォーマンスと認証機能が大幅に向上しています。このラボでの作業はすべて、ブラウザから実行できます。

3. ワークフローの概要

基本

ワークフローは、Workflows YAML ベースの構文を使用して記述された一連のステップで構成されます。これがワークフローの定義です。Workflows YAML 構文の詳細については、構文リファレンス ページをご覧ください。

ワークフローが作成されるとデプロイされ、ワークフローを実行できるようになります。実行とは、ワークフローの定義に含まれるロジックを 1 回実行することです。ワークフローの実行はすべて独立しており、プロダクトは多数の同時実行をサポートしています。

サービスを有効にする

この Codelab では、Cloud Functions(Cloud Run サービス)を Workflows に接続します。また、サービスの構築時に Cloud Build と Cloud Storage も使用します。

必要なサービスをすべて有効にします。

gcloud services enable \
  cloudfunctions.googleapis.com \
  run.googleapis.com \
  workflows.googleapis.com \
  cloudbuild.googleapis.com \
  storage.googleapis.com

次のステップでは、2 つの Cloud Functions の関数をワークフローで接続します。

4. 最初の Cloud Functions の関数をデプロイする

1 つ目の関数は Python の乱数ジェネレータです。

関数コード用のディレクトリを作成し、そのディレクトリに移動します。

mkdir ~/randomgen
cd ~/randomgen

このディレクトリに、次の内容の main.py ファイルを作成します。

import random, json
from flask import jsonify

def randomgen(request):
    randomNum = random.randint(1,100)
    output = {"random":randomNum}
    return jsonify(output)

この関数は HTTP リクエストを受信すると 1 ~ 100 の乱数を生成し、JSON 形式で呼び出し元に返します。

この関数は HTTP 処理に Flask に依存しているため、これを依存関係として追加する必要があります。Python における依存関係は pip で管理され、requirements.txt というメタデータ ファイルで表現されます。

同じディレクトリに、次の内容の requirements.txt ファイルを作成します。

flask>=1.0.2

次のコマンドで、HTTP トリガーと未認証リクエストを許可して関数をデプロイします。

gcloud functions deploy randomgen \
    --runtime python37 \
    --trigger-http \
    --allow-unauthenticated

関数がデプロイされると、コンソールの httpsTrigger.url プロパティまたは gcloud functions describe コマンドで関数の URL を確認できます。

次の curl コマンドを使用して、関数の URL にアクセスすることもできます。

curl $(gcloud functions describe randomgen --format='value(httpsTrigger.url)')

ワークフローで関数を使用できるようになりました。

5. 2 つ目の Cloud Functions の関数をデプロイする

2 つ目の関数は乗数です。受け取った入力を 2 倍にする。

関数コード用のディレクトリを作成し、そのディレクトリに移動します。

mkdir ~/multiply
cd ~/multiply

このディレクトリに、次の内容の main.py ファイルを作成します。

import random, json
from flask import jsonify

def multiply(request):
    request_json = request.get_json()
    output = {"multiplied":2*request_json['input']}
    return jsonify(output)

HTTP リクエストを受信すると、この関数は JSON 本文から input を抽出して 2 を乗算し、JSON 形式で呼び出し元に返します。

同じディレクトリに、次の内容で同じ requirements.txt ファイルを作成します。

flask>=1.0.2

次のコマンドで、HTTP トリガーと未認証リクエストを許可して関数をデプロイします。

gcloud functions deploy multiply \
    --runtime python37 \
    --trigger-http \
    --allow-unauthenticated

関数がデプロイされたら、次の curl コマンドを使用して、関数のその URL にアクセスすることもできます。

curl $(gcloud functions describe multiply --format='value(httpsTrigger.url)') \
-X POST \
-H "content-type: application/json" \
-d '{"input": 5}'

ワークフローで関数を使用できるようになりました。

6. 2 つの Cloud Functions の関数を接続する

最初のワークフローでは、2 つの関数を接続します。

次の内容の workflow.yaml ファイルを作成します。

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- returnResult:
    return: ${multiplyResult}

このワークフローでは、最初の関数から乱数を取得し、2 番目の関数に渡します。結果は乱数を掛け合わせたものです。

最初のワークフローをデプロイします。

gcloud workflows deploy workflow --source=workflow.yaml

最初のワークフローを実行します。

gcloud workflows execute workflow

ワークフローの実行後、前のステップで指定した実行 ID を渡すことで結果を確認できます。

gcloud workflows executions describe <your-execution-id> --workflow workflow

出力には、resultstate が含まれます。

result: '{"body":{"multiplied":108},"code":200 ... } 

...
state: SUCCEEDED

7. 外部 HTTP API を接続する

次に、ワークフローで math.js を外部サービスとして接続します。

math.js では、次のような数式を評価できます。

curl https://api.mathjs.org/v4/?'expr=log(56)'

今回は、Cloud コンソールを使用してワークフローを更新します。Google Cloud コンソールで Workflows を見つけます。

7608a7991b33bbb0.png

ワークフローを見つけて、[Definition] タブをクリックします。

f3c8c4d3ffa49b1b.png

ワークフロー定義を編集して、math.js の呼び出しを含めます。

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- returnResult:
    return: ${logResult}

ワークフローは、乗算関数の出力を math.js のログ関数呼び出しにフィードするようになりました。

UI に表示される手順に沿ってワークフローの編集とデプロイを行います。デプロイが完了したら、Execute をクリックしてワークフローを実行します。実行の詳細が表示されます。

b40c76ee43a1ce65.png

ステータス コード 200 とログ関数の出力を含む body に注目してください。

ワークフローに外部サービスを統合できました。

8. Cloud Run サービスをデプロイする

最後のパートでは、限定公開の Cloud Run サービスを呼び出してワークフローを完了します。つまり、Cloud Run サービスを呼び出すにはワークフローの認証が必要です。

Cloud Run サービスは、渡された数値の math.floor を返します。

サービスコード用のディレクトリを作成し、そのディレクトリに移動します。

mkdir ~/floor
cd ~/floor

このディレクトリに、次の内容の app.py ファイルを作成します。

import json
import logging
import os
import math

from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=['POST'])
def handle_post():
    content = json.loads(request.data)
    input = float(content['input'])
    return f"{math.floor(input)}", 200

if __name__ != '__main__':
    # Redirect Flask logs to Gunicorn logs
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)
    app.logger.info('Service started...')
else:
    app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

Cloud Run はコンテナをデプロイするため、Dockerfile が必要であり、コンテナを 0.0.0.0 環境変数と PORT 環境変数にバインドする必要があるため、上記のコードが必要になります。

HTTP リクエストを受信すると、この関数は JSON 本文から input を抽出し、math.floor を呼び出して呼び出し元に結果を返します。

同じディレクトリに次の Dockerfile を作成します。

# Use an official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.7-slim

# Install production dependencies.
RUN pip install Flask gunicorn

# Copy local code to the container image.
WORKDIR /app
COPY . .

# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
CMD exec gunicorn --bind 0.0.0.0:8080 --workers 1 --threads 8 app:app

コンテナを構築します。

export SERVICE_NAME=floor
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}

コンテナがビルドされたら、Cloud Run にデプロイします。no-allow-unauthenticated フラグに注目してください。これにより、サービスは認証済みの呼び出しのみを受け入れるようになります。

gcloud run deploy ${SERVICE_NAME} \
  --image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
  --platform managed \
  --no-allow-unauthenticated

デプロイが完了すると、サービスはワークフローで使用できるようになります。

9. Cloud Run サービスを接続する

限定公開の Cloud Run サービスを呼び出すように Workflows を構成する前に、Workflows が使用するサービス アカウントを作成する必要があります。

export SERVICE_ACCOUNT=workflows-sa
gcloud iam service-accounts create ${SERVICE_ACCOUNT}

サービス アカウントに run.invoker ロールを付与します。これにより、サービス アカウントは認証済みの Cloud Run サービスを呼び出すことができます。

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member "serviceAccount:${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
    --role "roles/run.invoker"

workflow.yaml のワークフロー定義を更新して、Cloud Run サービスを含めます。また、Workflows が Cloud Run サービスの呼び出しで認証トークンを渡すように、auth フィールドも追加しています。

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- floorFunction:
    call: http.post
    args:
        url: https://floor-<random-hash>.run.app
        auth:
            type: OIDC
        body:
            input: ${logResult.body}
    result: floorResult
- returnResult:
    return: ${floorResult}

ワークフローを更新します。今度はサービス アカウントを渡しています。

gcloud workflows deploy workflow \
    --source=workflow.yaml \
    --service-account=${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

ワークフローを実行します。

gcloud workflows execute workflow

数秒後に、ワークフローの実行を確認して結果を確認できます。

gcloud workflows executions describe <your-execution-id> --workflow workflow

出力には、整数 resultstate が含まれます。

result: '{"body":"5","code":200 ... } 

...
state: SUCCEEDED

10. 完了

以上で、この Codelab は完了です。

学習した内容

  • Workflows の基礎」へようこそ。
  • パブリック Cloud Functions の関数を Workflows に接続する方法。
  • 限定公開の Cloud Run サービスを Workflows に接続する方法。
  • Workflows で外部 HTTP API を接続する方法。