Typescript × Cloud FunctionsでServerless Frameworkに入門する
Typescript × Cloud Functions を利用して Serverless Framework に入門した際の作業ログをまとめています。
また、完成形のサンプルコードは下記の Repository にまとめています。
セットアップ
この記事では、下記のバージョンで検証を進めていきます。
node : v14.17.0
npm : v7.5.3
typescript : v4.3.2
最初に、Serverless の npm package を install します。
また、Plugin として利用する下記の npm package も install しておきます。
$ npm install -g serverless
$ serverless --version
Framework Core: 2.46.0
Plugin: 5.3.0
SDK: 4.2.3
Components: 3.12.0
$ npm install --save serverless-google-cloudfunctions
$ npm install --save serverless-plugin-scripts
$ npm install --save serverless-plugin-typescript
Credentialの作成
Deploy 前に、Serverless Framework 側で GCP Resource の作成・管理ができるように必要な権限を渡す必要があります。
下記のドキュメントに従って、Service Account(Key file) の作成 → serverless.yml
での Key file を指定します。
gcloud iam service-accounts serverless-sample \
--project <project_id> \
--display-name "Service Account for the sample of serverless framework"
Service Account を作成したら、IAM Role をバインドします。
(今回は Document の内容を踏まえて、Deploy と最低限の動作確認ができる程度の IAM Role に修正しています。)
gcloud projects add-iam-policy-binding <project_id> \
--member=serviceAccount:serverless-sample@<project_id>.iam.gserviceaccount.com \
--role=roles/storage.objectAdmin
gcloud projects add-iam-policy-binding <project_id> \
--member=serviceAccount:serverless-sample@<project_id>.iam.gserviceaccount.com \
--role=roles/deploymentmanager.editor
gcloud projects add-iam-policy-binding <project_id> \
--member=serviceAccount:serverless-sample@<project_id>.iam.gserviceaccount.com \
--role=roles/logging.logWriter
gcloud projects add-iam-policy-binding <project_id> \
--member=serviceAccount:serverless-sample@<project_id>.iam.gserviceaccount.com \
--role=roles/cloudfunctions.developer
次に、Service Account の key file を作成します。
gcloud iam service-accounts keys create ~/.gcloud/serverless-sample-key.json \
--iam-account=serverless-sample@<project_id>.iam.gserviceaccount.com
key file の作成後は、Serverless.yml
内の provider.credentials
でパスを指定するので、忘れずに指定しておきましょう。
provider:
name: google
stage: dev
runtime: nodejs10
region: us-central1
project: <project_id>
# The GCF credentials can be a little tricky to set up. Luckily we've documented this for you here:
# https://serverless.com/framework/docs/providers/google/guide/credentials/
#
# the path to the credentials file needs to be absolute
credentials: ~/.gcloud/serverless-sample-key.json
サンプルコードの実装
次に Cloud Functions 側で動作させるサンプルコードを実装します。
今回は、動作確認目的なので、既存のテンプレートからコードを持ってきます。
(この時点では Typescript ではなく単純な Node.js 用の template です)
$ serverless create --template google-nodejs --path serverless-framework-ts-sample --name serverless-framework-ts-sample
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/xxx/..."
_______ __
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
| | | The Serverless Application Framework
| | serverless.com, v2.46.0
-------'
Serverless: Successfully generated boilerplate for template: "google-nodejs"
テンプレートでは、下記のような code が生成されるので、こちらを利用します
'use strict';
exports.http = (request, response) => {
response.status(200).send('Hello World!');
};
exports.event = (event, callback) => {
callback();
};
Cloud Functionsのデプロイ
Credential も含めた準備が完了したら、serverless deploy
コマンドを実行します。
serverless invoke --function <function_name>
で作成した Function を実行して、正常に response が返ってくることを確認します。
$ serverless deploy
Serverless: Deprecation warning: Support for "package.include" and "package.exclude" will be removed with next major release. Please use "package.patterns" instead
More Info: https://www.serverless.com/framework/docs/deprecations/#NEW_PACKAGE_PATTERNS
Serverless: Packaging service...
...
Deployed functions
first
https://us-central1-<project_id>.cloudfunctions.net/sfw-sample-dev-first
**************************************************************************************************************************************
Serverless: Announcing Metrics, CI/CD, Secrets and more built into Serverless Framework. Run "serverless login" to activate for free..
**************************************************************************************************************************************
$ serverless invoke --function first
Serverless: Deprecation warning: Support for "package.include" and "package.exclude" will be removed with next major release. Please use "package.patterns" instead
More Info: https://www.serverless.com/framework/docs/deprecations/#NEW_PACKAGE_PATTERNS
Serverless: 8l5l1vr48v2t Hello World!
serverless invoke
で指定した Function name は serverless.yml
の下記の部分から持ってきます。
functions:
first: // function_name
handler: helloWorld
events:
- http: path
一点注意点として、--allow-unauthenticated
(=Public からのアクセスを許可する) の状態で Function を Deploy するためには、追加で IAM 周りの調整が必要になります。
(Deploy した Functions に Cloud function invoker
の Role を allUsers
の Role をアタッチする必要があります。)
デフォルトの状態では、curl でリクエストを投げると 403 が返ってきます。
curl https://us-central1-<project_id>.cloudfunctions.net/sfw-sample-dev-first
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>403 Forbidden</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Forbidden</h1>
<h2>Your client does not have permission to get URL <code>/sfw-sample-dev-first</code> from this server.</h2>
<h2></h2>
</body></html>
毎回、手動で実行するのは手間なので、Scripts の Plugin を利用して、Deploy 後に hook して IAM Role を Deploy された Function に bind します。
Plugin の README.md を参照しつつ、custom.scripts
以下で hooks と commands の定義を追加します。
plugins:
- serverless-google-cloudfunctions
- serverless-plugin-scripts
...
custom:
# this comes from https://www.npmjs.com/package/serverless-plugin-scripts
scripts:
commands:
make-public: gcloud functions add-iam-policy-binding ${self:service}-${self:provider.stage}-${env:FUNC_NAME, "first"} --member="allUsers" --role="roles/cloudfunctions.invoker" --project=${self:provider.project} --region=${self:provider.region} | xargs echo
hooks:
'after:deploy:deploy': FUNC_NAME=first npx serverless make-public
再度、serverless deploy
コマンドを実行します。
$ serverless deploy
Serverless: Deprecation warning: Support for "package.include" and "package.exclude" will be removed with next major release. Please use "package.patterns" instead
More Info: https://www.serverless.com/framework/docs/deprecations/#NEW_PACKAGE_PATTERNS
Serverless: Packaging service...
...
Deployed functions
first
https://us-central1-<project_id>.cloudfunctions.net/sfw-sample-dev-first
Serverless: Removing old artifacts...
Serverless: Deprecation warning: Detected unrecognized CLI options: "--function".
Starting with the next major, Serverless Framework will report them with a thrown error
More Info: https://www.serverless.com/framework/docs/deprecations/#UNSUPPORTED_CLI_OPTIONS
Serverless: Deprecation warning: Support for "package.include" and "package.exclude" will be removed with next major release. Please use "package.patterns" instead
More Info: https://www.serverless.com/framework/docs/deprecations/#NEW_PACKAGE_PATTERNS
bindings: - members: - allUsers role: roles/cloudfunctions.invoker etag: BwXEjVhKUP4= version: 1
curl でリクエストを投げて、Functions から Response が返ってきたことを確認しました。
curl https://us-central1-<project_id>.cloudfunctions.net/sfw-sample-dev-first
Hello World!%
Deploy 時の設定に、いくつか duprecated な設定が含まれているので、下記を参考にこちらも修正します。
Typescriptでの再実装
次に、Typescript での実装方法についてまとめていきます。
Deploy 前に事前にコンパイルして、serverless deploy
を実行する方法もありますが、今回は Typescript 用の Plugin を利用します。
Document に従い、serverless.yml
での Plugin の指定と tsconfig.json
の設定を行います。
plugins:
- serverless-plugin-typescript
また、ここでは、下記のようにディレクトリ構造を変更しています。
(Function 上で動かすコードは src/
配下に格納して、 package.json
で entrypoint を指定します。)
|- src
| |- index.ts
|
|- package.json
|- tsconfig.json
package.json
では、entrypoint となるコンパイル後の js ファイルを指定します。
{
// ...
"main": "src/index.js",
// ...
}
コンパイル自体は Serverless Framework 側で実行されて、outDir で指定された .build
配下にコンパイル済みのコードを吐き出します。
And this plugin will automatically compile your typescript correctly. Note that the field must refer to the compiled file name, namely, ending with a .js extension.
この状態で再度 serverless deploy
を実行します。
(entrypoint などの設定に不備がなければ、正常に Deploy されます)
$ serverless deploy
Serverless: Compiling with Typescript...
Serverless: Using local tsconfig.json
Serverless: Typescript compiled.
Deployed functions
first
https://us-central1-<project_id>.cloudfunctions.net/sfw-sample-dev-first
Serverless: Removing old artifacts...
bindings: - members: - allUsers role: roles/cloudfunctions.invoker etag: BwXEkgzdWow= version: 1
終わりに
この記事では、Serverless Framework の基本的な利用方法についてまとめました。
GCP は AWS と比較すると、S3 などの連携して利用する resources を local で emulate するための Plugin がありません。
また、CLI では invoke local
(=local での Function 実行)のコマンドなど一部コマンドが実装されていません。
それでも、TS の Plugin など利用できるものもあるので、個人開発などであれば利用できるような印象を持ちました。
Discussion