GCPでサーバレスアーキテクチャを採用する場合、FaaSであるCloud Functionsの利用を検討するかと思います。FaaSといえばNode.jsというイメージ(個人の感想です)もありますし、Node.jsを使うならTypeScriptを使いたいですよね。
一方、サーバレスアーキテクチャはクラウドサービス依存が強く、ローカルに開発環境を作るのに苦労しがちです。そこで、Googleが提供している__Functions Framework__を使って開発環境を整え、Cloud Functionsにデプロイするところまでをやってみます。
事前準備
2021/03/22時点で、Cloud FunctionsにおけるNode.jsの安定バージョンは12系なので、ローカルにもNode.jsの12系をインストールしてください。
また、Cloud Funcionsにデプロイするためにgcloudコマンドが必要です。Macを使っている方はHomebrew-caskでインストールできます。
$ brew cask install google-cloud-sdk
Functions Framework + TypeScript の開発環境を作る
リポジトリにFunctions FrameworkでTypeScriptを使う場合のドキュメントがあるので、説明に従って開発環境を作っていきます。
$ cd /path/to/your/workspace
$ npx gts init
package.json
を作るかどうか聞かれるので、そのままEnterで作ってもらいましょう。
次に、必要なパッケージをインストールし、
$ npm install @google-cloud/functions-framework
$ npm install @types/express concurrently nodemon --save-dev
npm-scriptsにローカルでFunctions Frameworkを動かすためのコマンドを追記します。
"scripts": {
"start": "functions-framework --source=build/src/ --target=helloWorld",
"watch": "concurrently \"tsc -w\" \"nodemon --watch ./build/ --exec npm run start\"",
...
}
自動生成された src/index.ts
を以下のサンプルの内容で上書きし、
import type { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
export const helloWorld: HttpFunction = (req, res) => {
res.send('Hello, World');
};
起動すると、ローカルのURLにアクセスできるようになります。
$ npm run watch
$ curl http://localhost:8080
Hello, World
たったこれだけで、Cloud Functions + TypeScript の開発環境を構築できました
Cloud Functionsにデプロイする
gcloudコマンドでデプロイしてみましょう。
$ gcloud functions deploy sample \
--project YOUT_PROJECT_ID \
--allow-unauthenticated \
--runtime nodejs12 \
--trigger-http \
--entry-point helloWorld
…
はい、失敗しましたね
以下のようなエラーが出ているはずです。
ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Build failed: > @0.0.0 prepare /workspace
> npm run compile
> @0.0.0 compile /workspace
> tsc
sh: 1: tsc: not found
Cloud Functionsでは、package.json
と package-lock.json
を含めてデプロイすると、 npm ci
が使用され、prepare
スクリプトが常に実行されます。
package.json
を見ると、prepare
スクリプトとしてtscコマンドが実行されるようになっていますが、npm ci
ではdevDependenciesがインストールされないため、tscコマンドが見つからずエラーになってしまいます。
"scripts": {
...
"compile": "tsc",
"fix": "gts fix",
"prepare": "npm run compile",
...
}
typescript npmをdependenciesとしてインストールすれば解決しそうですが、合わせて他のnpmもdependenciesとしてインストールしなければならず、アプリケーションの実行と関係ないファイルが大量に含まれてしまいます。
そこで、prepare
スクリプトを削除し、デプロイする前にコンパイルしておくことで、余計なファイルが含まれないようにします。
"scripts": {
...
"compile": "tsc",
"fix": "gts fix",
- "prepare": "npm run compile",
...
}
$ npm run compile
コンパイル結果は build
ディレクトリに出力されます。
デプロイする際 .gcloudignore
というファイルが自動生成されますが、そのままだと余計なファイルが含まれてしまうため、以下の内容で上書きします。
*
!build/**
!package*.json
!.
コンパイル結果と package.json
package-lock.json
があればアプリケーションを実行するには十分、ということです。
デプロイに再チャレンジ
それでは、再度gcloudコマンドでデプロイしてみましょう。
$ gcloud functions deploy sample \
--project YOUT_PROJECT_ID \
--allow-unauthenticated \
--runtime nodejs12 \
--trigger-http \
--entry-point helloWorld
entryPoint: helloWorld
httpsTrigger:
securityLevel: SECURE_OPTIONAL
url: https://us-central1-{YOUT_PROJECT_ID}.cloudfunctions.net/sample
ingressSettings: ALLOW_ALL
labels:
deployment-tool: cli-gcloud
name: projects/{YOUR_PROJECT_ID}/locations/us-central1/functions/sample
runtime: nodejs12
無事にデプロイできました
$ curl https://us-central1-{YOUT_PROJECT_ID}.cloudfunctions.net/sample
Hello, World
Cloud FunctionsのURLにもアクセスできましたね
作成したFunctionをGCPの管理コンソールで見ると、 以下のように余計なファイルが含まれていないことが分かります。
最後に
今回はトリガーとしてHTTPを試しましたが、Cloud StorageやCloud Pub/Sub等のGCPで発生する各種イベントをトリガーとして実行させることもできます。また、Cloud FunctionsだけではなくCloud RunやKnativeで実行させることもできるため、用途に応じて使い分けられるのも良いですね。
なお、Functions FrameworkはNode.js以外の言語でも提供されているので、他の言語で使いたい方はぜひリポジトリを探してみてください。