前回はローカル環境を動かしました。
今回はServerlessFrameworkを使って、AWS Lambdaにデプロイしてみます。
TypeScript + Lambdaの組み合わせは情報が少ないのでうまくいくかどうか・・(うまく行きました)
※実際の確認手順で書いていますので、情報としてはまとまっていません。逆に、知らないアーキテクチャにどうやって立ち向かってくか、みたいな観点で見ていただけると、参考になるかと思います。
コマンド確認
npm run deploy:dev
ちゃんと設定すれば、1コマンドでデプロイできるみたいですね。
deploy:dev
の中身は、
npm run build && serverless create_domain --stage dev && serverless deploy --stage dev
3つのコマンドが連結されているので、それぞれ確認します。
build
build
は
nest build
のaliasです。
nestjsのマニュアルを確認すると、
wrapper on top of the standard
tsc
compiler (for standard projects) or the webpack compiler (for monorepos).
webpackの設定ファイルはなくて tsconfig.json
ファイルがあるので、多分standard projectsなんだと思います。
というわけで、TypeScriptのコンパイルコマンドのtscを実行してるだけみたいです。ふむふむ。
serverless create_domain –stage dev
serverlessのドキュメントを検索しても、create_domain
なんてコマンドないぞ〜と思ったのですが、google検索したらヒットしました。
serverless-domain-manager
というpluginのコマンドなんですね。(そもそもserverlessってplugin形式で拡張するんですね)
何もしない場合、AWSの API Gateway
で割り振られるドメインはランダム文字列ぽいものになりますが、これを使うとCloudFront
経由で、指定のドメインでアクセスできるようになるようです。
ただし、httpsアクセスに必要なCertificationは自動生成してくれないので、手動でやる必要がある模様。
serverless.yaml
ファイルではここらへんの記述が該当。(関係ありそうなところを抜粋)
plugins:
- serverless-domain-manager
custom:
customDomain:
domainName: ${self:custom.SUBDOMAIN.${self:provider.stage}}.${self:custom.DOMAIN}
DOMAIN: ${ssm:/singleton/domain_name}
SUBDOMAIN:
dev: api-dev
stg: api-stg
demo: api-demo
DOMAIN
は ${ssm:/singleton/domain_name}
と変数で指定されていますね、、、ssmとはなんぞや、ですが。
AWSのSystemsManagerの事みたいですね。
key/value形式で、AWSの各サービスで使うパラメータを環境変数的にセットして使えるサービスです。
というわけで、この手順通り動かすためには、
- DomainをAWSに登録する(手動)
- CertificationをAWSに登録する(手動)
- ssmに必要なパラメータを登録する
という手順を踏まないと、このコマンドは動かなさそうです。
オプションの --stage dev
設定ファイルの serverless.yml
にパラメータを渡せして変数として扱えるようです。
provider:
stage: ${opt:stage, 'dev'}
となっていて、
${ssm:/envvars/${self:provider.stage}/FIREBASE_DATABASE_URL~true}
だと、${ssm:/envvars/dev/FIREBASE_DATABASE_URL~true}
になるって事ですね。
最後の ~true
は、ssmから取得する時に暗号化された値を使う時に必要らしいです。
serverless deploy –stage dev
実際にdeployするコマンド。CloudFormation形式に変換して実行する作戦らしい。
- serverless.yml -> CloudFormationのtemplateに変換
- ソースコードをzip化
- zipを(lambdaが読み込む)s3にupload
- hashのチェックをして差分があった時だけ実行される
- CloudFormationのtemplate実行
という流れ。CloudFormation遅いのでdeployコマンドも遅いよ、deploy function
コマンドを使うと、CloudFormation実行しないので早いよ、というtipsも。
serverless.yml確認
設定ファイルも一通り確認します。
基本的にはここで解説されている。
plugin確認
serverless-deployment-bucket
デプロイする時に使うs3のbucket周りを良い感じにしてくれるプラグイン。
serverless-domain-manager
APIGatewayのドメインを指定できるplugin(前述)
serverless-layers
LambdaのLayerを良い感じにしてくれるplugin。
なのだが、そもそもLambda Layerつかってたっけ??? serverless.yml
には特に設定は無い。
うーん??
serverless-offline
ローカルでLamdaを実行するエミュレーター機能を提供してくれる。
package.json
にsls-offline
というコマンドがある。
今回の構成だと、普段は直接Nestjsを叩いて動くので必要ないけど、エンドポイントのコードは別だったりするので、そこら辺を確認したい時に使うと良さそう。
serverless-prune-plugin

古いソースを消してくれるplugin。
いざデプロイ
調査は終わったので、実際に動かしてみます。
事前準備
いくつか手動で設定が必要な内容があるので設定していきます。
AWSの認証設定
Serverless FrameworkがAWSにアクセスできるように設定してあげる必要があります。
基本マニュアル通りなのですが、こちらで詳しく日本語で解説してくれていました。
私は元々aws-cli
の設定をしてあるので、特に何もしなくて良い(はず)。
ドメインの設定
${self:custom.SUBDOMAIN.${self:provider.stage}}.${self:custom.DOMAIN}
に該当するドメインをRoute53に設定して、Credientialまで設定する必要があります。
私は、moyashidaisuke.com
というドメインを持っていて、Route53に設定済なので、これを流用する事にします。
stageがdev
の場合のドメインは、
SUBDOMAIN:
dev: api-dev
stg: api-stg
demo: api-demo
なので、api-dev.moyashidaisuke.com
になるはず。
これを設定していきます。
証明書を登録。us-east-1で設定する事を忘れずに。
ssmに登録
AWS SystemManagerのパラメータストアに環境変数を登録する必要があるので、登録していきます。必要なものは以下の通り。
- /envvars/dev/FIREBASE_DATABASE_URL
- /envvars/dev/FIREBASE_STORAGE_BUCKET
- /envvars/dev/FIREBASE_type
- /envvars/dev/FIREBASE_project_id
- /envvars/dev/FIREBASE_private_key_id
- /envvars/dev/FIREBASE_private_key
- /envvars/dev/FIREBASE_client_email
- /envvars/dev/FIREBASE_client_id
- /envvars/dev/FIREBASE_auth_uri
- /envvars/dev/FIREBASE_token_uri
- /envvars/dev/FIREBASE_auth_provider_x509_cert_url
- /envvars/dev/FIREBASE_client_x509_cert_url
- /envvars/dev/FIREBASE_WEB_API_KEY
- /singleton/domain_name
ほとんどがFirebaseの設定です。前回作成したローカル環境用の設定をそのまま流用する事にします。
domain_name
以外は認証有りなので、タイプを「安全な文字列」にする事をわすれずに(domain_name
は違うので注意。一回間違えた)。リージョンはap-northeast-1
。
done(めんどくさかった🤮
いざ実行
いざ実行します。
サブコマンドを一つずつ実行します。
$ npm run build
> mamori-i-japan-api@0.1.0 prebuild /Library/WebServer/mamori-i-japan-api
> rimraf dist
> mamori-i-japan-api@0.1.0 build /Library/WebServer/mamori-i-japan-api
> nest build
無事成功。dist
フォルダにjsファイルが生成されています。
$ npx serverless create_domain --stage dev
Serverless: Custom domain api-dev.moyashidaisuke.com was created.
New domains may take up to 40 minutes to be initialized.
成功っぽい。40分、、、🤮
Route53見たら何かできてました。

$ npx serverless deploy --stage dev
Serverless: [ LayersPlugin ]: => default
... ○ Downloading package.json from bucket...
... ○ package.json does not exists at bucket...
... ○ Changes identified ! Re-installing...
... ∅ [warning] "yarn.lock" file does not exists!
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated buffer@4.9.1: This version of 'buffer' is out-of-date. You must update to v4.9.2 or newer
npm WARN deprecated chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.
npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
npm WARN deprecated @types/chalk@2.2.0: This is a stub types definition for chalk (https://github.com/chalk/chalk). chalk provides its own type definitions, so you don't need @types/chalk installed!
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN ws@7.3.0 requires a peer of bufferutil@^4.0.1 but none is installed. You must install peer dependencies yourself.
npm WARN ws@7.3.0 requires a peer of utf-8-validate@^5.0.2 but none is installed. You must install peer dependencies yourself.
npm WARN jsdom@15.2.1 requires a peer of canvas@^2.5.0 but none is installed. You must install peer dependencies yourself.
npm WARN mamori-i-japan-api@0.1.0 No repository field.
> protobufjs@6.9.0 postinstall /Library/WebServer/mamori-i-japan-api/.serverless/layers/nodejs/node_modules/protobufjs
> node scripts/postinstall
> @nestjs/core@7.1.1 postinstall /Library/WebServer/mamori-i-japan-api/.serverless/layers/nodejs/node_modules/@nestjs/core
> opencollective || exit 0
added 742 packages from 426 contributors and audited 2461 packages in 26.872s
found 2 low severity vulnerabilities
run `npm audit fix` to fix them, or `npm audit` for details
... ○ Created layer package /Library/WebServer/mamori-i-japan-api/.serverless/contact-tracing-api-dev-nodejs-default.zip (29.3 MB)
... ○ Uploading layer package...
Access Denied
uploadの前後で失敗するので、s3を見てみるとdeploymentBucket
に指定されているcontact-tracing-api-serverless
が作成されていない。
serverless-deployment-bucket
がうまく動いていないかな〜と思いつつ、bucketを手動で作成しよう、、、と思ったところで、bucket前は全世界で一意である必要があるので、本家さんが作成したbucket名とかぶっていて、bucketの作成に失敗しているのだろう、と。
そこで、設定を少し変えます。
deploymentBucket:
- name: contact-tracing-api-serverless
+ name: contact-tracing-api-serverless-daisuke-fukuda
エラーメッセージが変わった。
... ○ Uploading layer package...
The specified bucket does not exist
やっぱり自動でbucketは作成してくれないようなので、手動で作成します。
再チャレンジ。
けっこう時間がかかりますが、進捗状況はCloudFormationで確認できます。

Serverless: [ LayersPlugin ]: => default
... ○ Downloading package.json from bucket...
... ○ package.json does not exists at bucket...
... ○ Changes identified ! Re-installing...
... ∅ [warning] "yarn.lock" file does not exists!
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated buffer@4.9.1: This version of 'buffer' is out-of-date. You must update to v4.9.2 or newer
npm WARN deprecated chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.
npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
npm WARN deprecated @types/chalk@2.2.0: This is a stub types definition for chalk (https://github.com/chalk/chalk). chalk provides its own type definitions, so you don't need @types/chalk installed!
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN ws@7.3.0 requires a peer of bufferutil@^4.0.1 but none is installed. You must install peer dependencies yourself.
npm WARN ws@7.3.0 requires a peer of utf-8-validate@^5.0.2 but none is installed. You must install peer dependencies yourself.
npm WARN jsdom@15.2.1 requires a peer of canvas@^2.5.0 but none is installed. You must install peer dependencies yourself.
npm WARN mamori-i-japan-api@0.1.0 No repository field.
> protobufjs@6.9.0 postinstall /Library/WebServer/mamori-i-japan-api/.serverless/layers/nodejs/node_modules/protobufjs
> node scripts/postinstall
> @nestjs/core@7.1.1 postinstall /Library/WebServer/mamori-i-japan-api/.serverless/layers/nodejs/node_modules/@nestjs/core
> opencollective || exit 0
added 742 packages from 426 contributors and audited 2461 packages in 31.271s
found 2 low severity vulnerabilities
run `npm audit fix` to fix them, or `npm audit` for details
... ○ Created layer package /Library/WebServer/mamori-i-japan-api/.serverless/contact-tracing-api-dev-nodejs-default.zip (29.3 MB)
... ○ Uploading layer package...
... ○ OK...
... ○ New layer version published...
... ○ Uploading remote /Library/WebServer/mamori-i-japan-api/package.json...
... ○ OK...
... ○ Adding layers...
... ✓ function.lambda-main - arn:aws:lambda:ap-northeast-1:*********:contact-tracing-api-dev-nodejs-default:1
... ✓ function.lambda-swagger - arn:aws:lambda:ap-northeast-1:*********:contact-tracing-api-dev-nodejs-default:1
... ✓ function.lambda-schedule - arn:aws:lambda:ap-northeast-1:*********:contact-tracing-api-dev-nodejs-default:1
Serverless: Using deployment bucket 'contact-tracing-api-serverless-daisuke-fukuda'
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Installing dependencies for custom CloudFormation resources...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service contact-tracing-api.zip file to S3 (186.39 KB)...
Serverless: Uploading custom CloudFormation resources...
Serverless: Validating template...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
...............................................................................................
Serverless: Stack create finished...
Service Information
service: contact-tracing-api
stage: dev
region: ap-northeast-1
stack: contact-tracing-api-dev
resources: 31
api keys:
None
endpoints:
ANY - https://5boc93w30c.execute-api.ap-northeast-1.amazonaws.com/dev/
ANY - https://5boc93w30c.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+}
ANY - https://5boc93w30c.execute-api.ap-northeast-1.amazonaws.com/dev/swagger
ANY - https://5boc93w30c.execute-api.ap-northeast-1.amazonaws.com/dev/swagger/{proxy+}
functions:
lambda-main: contact-tracing-api-dev-lambda-main
lambda-swagger: contact-tracing-api-dev-lambda-swagger
lambda-schedule: contact-tracing-api-dev-lambda-schedule
layers:
None
Serverless: [ LayersPlugin ]: => Layers Info
... ○ function.lambda-main = layers.arn:aws:lambda:ap-northeast-1:*********:contact-tracing-api-dev-nodejs-default:1
... ○ function.lambda-swagger = layers.arn:aws:lambda:ap-northeast-1:*********:contact-tracing-api-dev-nodejs-default:1
... ○ function.lambda-schedule = layers.arn:aws:lambda:ap-northeast-1:*********:contact-tracing-api-dev-nodejs-default:1
Serverless: Created basepath mapping.
Serverless Domain Manager Summary
Domain Name
api-dev.moyashidaisuke.com
Distribution Domain Name
Target Domain: d13ggeqz72u24d.cloudfront.net
Hosted Zone Id: Z2FDTNDATAQYW2
Serverless: Prune: Running post-deployment pruning
Serverless: Prune: Querying for deployed function versions
Serverless: Prune: Querying for deployed layer versions
Serverless: Prune: contact-tracing-api-dev-lambda-main has 1 additional version published and 0 aliases, 0 versions selected for deletion
Serverless: Prune: contact-tracing-api-dev-lambda-swagger has 1 additional version published and 0 aliases, 0 versions selected for deletion
Serverless: Prune: contact-tracing-api-dev-lambda-schedule has 1 additional version published and 0 aliases, 0 versions selected for deletion
Serverless: Prune: Pruning complete.
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
正常終了!
作成したドメインにアクセスしてみると、

動いています!
ローカルで動かした管理画面からもつながりましたので、Firebaseへの接続も大丈夫そうです。

デプロイされたもの確認
Lambda Layer
設定ファイルをみた限りでは、LambdaLayer使ってないのではと思ったのですが、デプロイされたものを見ると使われているようです。
Lambdaのメインのところには、dist
以下のファイルしか含まれていないので、node_modules
配下のファイルがLayerに含まれていそうです。(デプロイログ的にもそんな雰囲気)
これがどこで行われているのか、改めて調べてみます。
logを眺めた感じserverless-layers
がやっているようだったので、こちらをコメントアウトして実行してみた結果、確かにlayerが作成されなかったので、このpluginが実行してるようです。
改めてpluginのドキュメントを読んでみると、
It attaches automatically layers for each function
という事で、plugin突っ込むだけで色々やってくれるようです。素晴らしい。
改めて調べたら、こちらのやり方と一緒でしたね。
ちなみに、yarn.lock
のwarningが出ているのは、最新だと修正されていました。
deploymentBucketが自動生成されない
Bucketがある場合のlogも出力されていないので、そもそもpluginが動いていないと判断。
試しに他のpluginをコメントアウトして実行したら、何か動きました。plugin同士の噛み合わせが悪い模様。
Serverless: Creating deployment bucket 'contact-tracing-api-serverless-daisuke-fukuda'...
Serverless: Applied SSE (AES256) to deployment bucket
色々組み合わせを試した結果、serverless-layers
と serverless-deployment-bucket
を同時に使用する発生しました。
新規でs3のbuckeを作成する時だけ発生するので、serverless-layers
を一回コメントアウトしてdeployしてあげれば、以降は問題なく使えます。
コード読んだのですが、ちょっと良くわからなかったです。pluguin同士の噛み合わせの話なので、どちらにissueを出せば良いんだ、、、
まとめ
微妙にはまりつつも、Serverless Framework
+ AWS Lambda
+ TypeScript
+ Firestore
でデプロイが実行できるようになりました。
LambdaLayerまで簡単に設定してくれるのは超良いですね。初めてだったので設定ファイル理解したりで時間とりましたが、一度わかっちゃえばコピペで使い回せますね。
というか割と最近目のpluginも使ってくれていて、これがOSSで公開されてるとかめっちゃ素晴らしいです。改めて感謝です。
次回はFirebaseにデプロイ(rulesとかが対象)の予定。
コメント