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