gcp

Google Cloud Functions(Beta)の基本的な仕組み、使い方を学ぶ

概要

Google Cloud Functions(Beta)の基本的な仕様や使い方を、オフィシャルドキュメントの入門ガイド、チュートリアルをもとに学習したまとめの記事です。
下記に引用した通りGoogle Cloud Functionsはベータ版(2018年4月現在)です。将来のリリースでこの記事の内容が役に立たなくなるかもしれませんのでご注意ください。

★ Beta
これは、Google Cloud Functionsのベータ版リリースです。このAPIは、下位互換性のない方法で変更される可能性があり、SLAサポート終了予定ポリシーの対象ではありません。

参考

Google Cloud Functionsの概要

関数の実行環境

Google Cloud Functionsで利用しているNode.jsのバージョンは下記に引用したとおりv6.11.5です。

The Cloud Functions Node.js execution environment follows the Node "LTS" releases, starting with the v6
LTS release published on 2016-10-18. The current Node.js version running in Cloud Functions is Node v6.11.5.

Node.js Foundation Release Working Group

実行時サービスアカウント

Cloud Functions > Documentation > Access Control

関数は次のサービスアカウントの権限で実行されます。(関数を作成すると自動的にサービスアカウントが作成されます。)
このサービスアカウントはデフォルトではプロジェクトの編集者というロールを持っていますが、最適な権限を割り当てることもできます。

PROJECT_ID@appspot.gserviceaccount.com

Google APIサービスアカウント

実行時サービスアカウントの他に、関数の作成、更新、削除を行うためのサービスアカウントが作成されます。
このサービスアカウントの権限は基本的に編集しません。

PROJECT_NUMBER@cloudservices.gserviceaccount.com

関数の種類

Google Cloud Functionsの関数には、HTTP関数とバックグラウンド関数という2つの種類があります。
それぞれの関数には以下のような特徴があります。

  • HTTP関数
    • HTTPリクエストをトリガーとします
    • 関数の実行は同期的でレスポンス=関数の実行終了です
  • バックグラウンド関数
    • Cloud Pub/SubやCloud Storageなどのインフライベントをトリガーとします
    • 関数の実行は非同期的で処理終了の通知はコールバック関数を使用します

HTTP関数

HTTP関数はトリガーとなるURLにHTTPリクエストが起きたときに呼び出されます。

関数はrequestとresponseの2つのパラメータを取ります。

exports.helloHttp = (request, response) => {

  // 処理

  response.status(200).end();
};

HTTPリクエスト (HTTP Triggers)

デプロイコマンド
gcloud beta functions deploy [FUNCTION_NAME] --trigger-http

--trigger-http

関数にエンドポイント(HTTPトリガーURL)が割り当てられます。

HTTPトリガーURL

デプロイ後にdescribeコマンドで確認できます。

> gcloud beta functions describe [FUNCTION_NAME]
関数呼び出しの例

HTTPトリガーURLに対してHTTPリクエスト(GET,POST,PUT,DELETE,OPTIONS)を発行します。
この例はcurlを使ってHTTPトリガーURLへPOSTリクエストを行います。

curl -X POST -H "Content-Type:application/json" -d "{\"message\": \"hello world!\"}" "HTTP_TRIGGER_URL"

バックグラウンド関数

バックグラウンド関数は、Cloud Pub/Subのトピックへメッセージをパブリッシュしたときや、Cloud Storageのバケットにファイルをアップロードしたときに呼び出されます。

関数はeventとオプションのcallbackの2つのパラメータを取ります。

exports.helloBackground = (event, callback) => {

  // 処理

  callback();
};

eventオブジェクトは次の2つのプロパティを持っています。

property description 保持するデータ
event.data イベントデータ Pub/Subではメッセージ、Storageではオブジェクトのデータ
event.context イベントコンテキスト イベントIDやイベントタイプなどのメタデータ

Cloud Pub/Subのトピック (Cloud Pub/Sub Triggers)

デプロイコマンド(1)
> gcloud beta functions deploy [FUNCTION_NAME] --trigger-topic=[TRIGGER_TOPIC]

--trigger-topic=[TRIGGER_TOPIC]

実行トリガーとするCloud Pub/Subトピックの名を指定します。 このトピックへパブリッシュされたメッセージは、関数のパラメータに渡されます。

デプロイコマンド(2)
> gcloud beta functions deploy [FUNCTION_NAME] --trigger-resource [TRIGGER_TOPIC] --trigger-event [EVENT_TYPE]

--trigger-resource [TRIGGER_TOPIC]

実行トリガーとするCloud Pub/Subのトピック名を指定します。

--trigger-event [EVENT_TYPE]

[EVENT_TYPE]に指定できるのは(いまのところ)次の1種類だけなので、上記のデプロイコマンド(1)とデプロイコマンド(2)は同じ結果になります。

event_type イベント
google.pubsub.topic.publish トピックにメッセージがパブリッシュされたとき
関数呼び出しの例

関数デプロイ時に実行トリガーとして指定したトピックへパブリッシュすることで関数を呼び出します。

> gcloud beta pubsub topics publish [TOPIC_NAME] --message "なんらかのメッセージ"

Cloud Storageのオブジェクト (Cloud Storage Triggers)

デプロイコマンド(1)
> gcloud beta functions deploy [FUNCTION_NAME] --trigger-bucket=[TRIGGER_BUCKET]

--trigger-bucket=[TRIGGER_BUCKET]

実行トリガーとするCloud Storageのバケット名を指定します。このバケット内のファイルが変更されるたびに関数が実行されます。

デプロイコマンド(2)
> gcloud beta functions deploy [FUNCTION_NAME] --trigger-resource [TRIGGER_BUCKET] --trigger-event [EVENT_TYPE]

--trigger-resource [TRIGGER_BUCKET]

実行トリガーとするCloud Storageのバケット名を指定します。

--trigger-event [EVENT_TYPE]

[EVENT_TYPE]に指定できるイベントは次の通りです。

event_type イベント
google.storage.object.finalize バケットに新しいオブジェクトが作成されたとき
google.storage.object.delete バケットのオブジェクトが削除されたとき
google.storage.object.archive バケットのオブジェクトがアーカイブされたとき
google.storage.object.metadataUpdate バケットのオブジェクトのメタデータが更新されたとき
  • オブジェクトのバージョニングの設定によって挙動が変わります。
関数呼び出しの例

関数デプロイ時に実行トリガーとして指定したバケットのオブジェクトを操作(コピー、削除など)することで関数を呼び出します。
この例はローカルPCからgsutilツールを使ってファイルをバケットへコピーしています。

> gsutil cp test.txt gs://[BUCKET_NAME]

Direct Triggers

callコマンドでHTTP関数やバックグラウンド関数をイベントを介さずに直接呼び出すことができます。
このコマンドは開発時の動作確認やデバッグ用途に使うためのものです。

関数の呼び出し

> gcloud beta functions call [FUNCTION_NAME] --data "{\"message\": \"hello world!\"}"

--data

JSON文字列を指定します。
HTTP関数の場合はrequestオブジェクトのbodyプロパティにバインドされます。
バックグラウンド関数の場合はeventオブジェクトのdataプロパティにバインドされます。

実行環境のセットアップ

ローカルPC側のセットアップ

Cloud SDKをアップデートします。

> gcloud components update

> gcloud components install beta

Node.jsの開発環境を準備します。

ドキュメント > Node.js > Node.js 開発環境のセットアップ

GCP側のNode.jsのバージョンは6.11.5のようなので、ローカルPC側もバージョンを揃えました。
私の環境では既にnodistをインストールしていたのでnodistを利用していますが、ドキュメントではnvm(Windowsはnvm-windows)を利用する方法が紹介されています。

> nodist + 6.11.5

> nodist 6.11.5
> npm install --save express

GCP側のセットアップ

Cloud Functions APIを有効にします。

cf0.png

HTTP関数を実行するチュートリアル

Cloud Functions > Documentation > HTTP Tutorial

Cloud Functions APIを有効にしたら、続けてHTTP関数を作成しローカルから呼び出すまでのチュートリアルを行います。

cf1.png

下の図は「関数を作成」ボタンをクリックした後の画面です。

  • 関数の名前を”helloWorld”、使用するメモリを最小の128MBに変更しました。
  • トリガーは”HTTPトリガー”を指定します。
  • 画面に表示されているURLはこの関数のエンドポイント(HTTPトリガーURL)で、このエンドポイントにHTTPリクエストを発行する関数が実行されます。

表示されているソースコードはデフォルトのもので、この画面でソースコードを変更できますがこのチュートリアルではそのまま使用しました。

cf2b.png

関数が作成されると一覧に表示されます。これで関数を実行する準備ができたのでローカルPCから呼び出してみます。

cf4.png

関数を実行

curlでhttpトリガーにアクセスすると関数が実行され"Success: hello world!"というレスポンスが表示されます。
下記の結果はローカルPCより実行したものです。

> curl -X POST -H "Content-Type:application/json" -d "{\"message\": \"hello world!\"}" "https://us-central1-project-********.cloudfunctions.net/helloWorld"
Success: hello world!

gcloudツールで関数を直接呼び出した場合です。

> gcloud beta functions call helloWorld --data="{\"message\": \"hello world\"}"
executionId: sa2mbsm8xv0c
result: 'Success: hello world'

関数の詳細

GCP Consoleの「関数の詳細」画面で関数の実行状況を確認することができます。
また「ソース」タブの画面からは関数のソースコードをzipファイルでダウンロードすることができます。

cf5.png

gcloud beta functionsコマンドで操作する

gcloud beta functions

ソースコードをダウンロードし、少し修正してからgcloudツールでデプロイをしてみます。
下記はダウンロードしたzipファイルをhello_worldというディレクトリに展開した結果です。

/hello_world
  |
  +--- index.js
  |
  +--- package.json

index.jsを下記のように修正してGETでもレスポンスを返せるようにしました。

function handle(message, res) {
  if (message === undefined) {
    res.status(400).send('No message defined!');
  } else {
    console.log(message);
    res.status(200).send('Success: ' + message);
  }
}

/**
 * Responds to any HTTP request that can provide a "message" field in the body.
 *
 * @param {!Object} req Cloud Function request context.
 * @param {!Object} res Cloud Function response context.
 */
exports.helloWorld = (req, res) => {
  switch (req.method) {
    case 'GET':
      handle(req.query.message, res);
      break;
    case 'POST':
      handle(req.body.message, res);
      break;
    default:
      res.status(500).send({ error: 'Something blew up!' });
      break;
  }
};

functionをデプロイする

hello_worldディレクトリで次のコマンドを実行しデプロイを行います。
デプロイ時に引数を指定して ―たとえば使用メモリ量(e.g. --memory=256MB)やタイムアウト時間(e.g. --timeout=300s)など― 関数の設定を変更することも可能です。

> gcloud beta functions deploy helloWorld --verbosity debug
DEBUG: Running [gcloud.beta.functions.deploy] with arguments: [--verbosity: "debug", NAME: "helloWorld"]
INFO: Refreshing access_token
INFO: Not using a .gcloudignore file.
INFO: Not using a .gcloudignore file.
Deploying function (may take a while - up to 2 minutes)...done.
INFO: Display format: "default"
availableMemoryMb: 128
entryPoint: helloWorld
httpsTrigger:
  url: https://us-central1-project-********.cloudfunctions.net/helloWorld
labels:
  deployment-tool: cli-gcloud
name: projects/project-********/locations/us-central1/functions/helloWorld
serviceAccountEmail: project-********@appspot.gserviceaccount.com
sourceUploadUrl: https://storage.googleapis.com/<省略...>
status: ACTIVE
timeout: 60s
updateTime: '2018-03-30T12:27:11Z'
versionId: '2'

関数の情報を確認する

functions listコマンドで関数の一覧を確認

> gcloud beta functions list
NAME        STATUS  TRIGGER       REGION
helloWorld  ACTIVE  HTTP Trigger  us-central1

functions describeコマンドで関数の詳細を確認

> gcloud beta functions describe helloWorld
availableMemoryMb: 128
entryPoint: helloWorld
httpsTrigger:
  url: https://us-central1-project-********.cloudfunctions.net/helloWorld
labels:
  deployment-tool: cli-gcloud
name: projects/project-********/locations/us-central1/functions/helloWorld
serviceAccountEmail: project-********@appspot.gserviceaccount.com
sourceUploadUrl: https://storage.googleapis.com/<省略...>
status: ACTIVE
timeout: 60s
updateTime: '2018-03-30T12:27:11Z'
versionId: '2'

動作確認

修正した通りにGETでもレスポンスが返るか確認します。

> curl "https://us-central1-project-********.cloudfunctions.net/helloWorld?message=HELLO%20WORLD!"
Success: HELLO WORLD!

関数を削除する

functions deleteコマンドで関数を削除できます。

> gcloud beta functions delete helloWorld

バックグラウンド関数を実行するチュートリアル

Google Cloud Storageを利用するチュートリアル

Cloud Functions > Documentation > Cloud Storage Tutorial

このチュートリアルではGoogle Cloud Storageを利用します。

Google Cloud Storage JSON APIの有効化

このAPIはデフォルトで有効になっています。

バケットの作成

イベントのトリガーになるバケットを用意します。

バックグラウンド関数の作成

  • 関数の名前を”processFile”、使用するメモリを最小の128MBに変更しました。
  • トリガーは”Cloud Storage バケット”を指定します。
  • 画面にイベントタイプとバケットを選択するリストが表示されます。
    • イベントタイプはファイルが作成されたときのイベントの”ファイナライズ / 作成”を選択しました。
    • バケットには作成したバケットを選択しました。

表示されているソースコードはデフォルトのものです。関数の最後に必ずコールバック関数(この例ではcallback();)を実行する必要があります。

cf7.png

関数が作成されると一覧に表示されます。これで関数を実行する準備ができたのでバケットにファイルをアップロードします。

cf8.png

ファイルのアップロード後にログを確認します。
ログにアップロードしたファイルが出力されていることが確認できました。

cf9.png

関数の処理が失敗したことを通知するには、callback関数の第1引数にErrorオブジェクトを渡します。

callback(new Error("processing failed!"));

cf11.png

Google Cloud Pub/Subを利用するチュートリアル

Cloud Functions > Documentation > Cloud Pub/Sub Tutorial

このチュートリアルではGoogle Cloud Pub/Sub APIを利用します。

Google Cloud Pub/Subはフルマネージドのリアルタイム メッセージング サービスで、個別のアプリケーション間でメッセージを送受信できます。

Cloud Pub/Sub APIの有効化

Cloud Pub/Sub APIを有効化します。

トピックの作成

イベントのトリガーになるトピックを用意します。

cf12.png

cf13.png

バックグラウンド関数の作成

  • 関数の名前を”subscribe”、使用するメモリを最小の128MBに変更しました。
  • トリガーは”Cloud Pub / Sub トピック”を指定します。
  • 画面にトピックを選択するリストが表示されます。

表示されているソースコードはデフォルトのものです。関数の最後に必ずコールバック関数(この例ではcallback();)を実行する必要があります。

cf14.png

関数が作成されると一覧に表示されます。これで関数を実行する準備ができたのでトピックにメッセージをパブリッシュします。

cf15.png

メッセージのパブリッシュ後にログを確認します。
ログにメッセージが出力されていることが確認できました。

cf16.png

gcloudツールで関数を直接呼び出した場合です。
メッセージはbase64エンコードされている必要があります。下記のメッセージは"hello world"をエンコードしたものです。

> gcloud beta functions call subscribe --data="{\"data\": \"aGVsbG8gd29ybGQ=\"}"
executionId: 7fjpocvlean9

cf17.png