はじめに
Serverless Frameworkはサーバレスなアプリケーションを簡単にデプロイできるOSSのフレームワークです。
Serverless Frameworkを使うときには、複数のパッケージを一つのリポジトリで管理する、いわゆるモノレポな構成にしたいことがあると思います。モノレポなプロジェクトの管理方法にはyarn workspaceやlernaなどがあります。
ここでは、yarn workspaceを使ったServerless Frameworkのモノレポを構築する方法を紹介したいと思います。サンプルには、AWSのAPIGatewayとLambdaを使います。
構築中にいろいろと問題が発生したので、誰かの参考になればと思います。
初心者なので、おかしいところがあれば是非指摘お願いします。
githubにもサンプルを残したので、よければ参考にしてください。
serverless-webpackを使う方法: https://github.com/allJokin/serverless-webpack-service
serverless-bundleを使う方法: https://github.com/allJokin/serverless-bundle-service
環境
serverless@2.16.0
yarn workspaceの設定
以下のようなディレクトリ構成でプロジェクトを作成します。
Serverless Frameworkではサービスという単位で環境を作っていきます。
├── packages # serviceで使うパッケージ
| ├── package-1
| └── ...
├── services # lambdaなど
| ├── package-1
| └── ...
├── package.json
└── yarn.lock
yarn workspaceを有効にするためにrootのpackage.jsonを以下のように記載します。
{
"name": "root",
"private": true,
"workspaces": [
"packages/*",
"services/*"
]
}
Serviceの作成
公式サイトの手順を参考にAPIGatewayとLamdaのServiceを作っていきます。
aws-nodejs-typescriptのテンプレを使って、servicesの配下にapi(名前は何でもいい)を作成します。
serverless create --template aws-nodejs-typescript --path services/api
コマンドを実行するとapiの配下に以下のようなファイルが作成されていると思います。
├── .gitignore
├── handler.ts
├── package.json
├── serverless.ts
├── tsconfig.ts
└── webpack.config.js
handler.tsにはLambdaの中身、serverless.tsにはインフラの設定が書いてあります。
Serviceを用意したので、rootでyarn install
しましょう。yarn workspaceを有効にしているので、apiにもモジュールがインストールされます。
モジュールをインストールしたら、services/apiにcdしてデプロイしてみましょう!
sls deploy
deployできない
deployしようとすると、以下のようなエラーが出ました…。
ERROR in ./handler.ts 1:0-37
Module not found: Error: Can't resolve 'source-map-support/register' in '/workspaces/services/serverless-webpack-service'
どうやら、serverless-webpackがyarn workspaceに対応していないようです。(2020年12月時点)
以下のissueを参考にwebpack.config.ts修正します。nodeExternalsにrootのnode_moduleを指定してあげると良いようです。
externals: [nodeExternals({
modulesDir: path.resolve(__dirname, '../../node_modules'),
})],
これでデプロイできるようになりました!
なぜこれでできるようになるのか、原理はわかりません…。
自作のpackageを追加
自作のpackageをpackagesフォルダに追加します。文字列を返すだけの簡単なサンプルです。
export const mypackage = () => {
return 'This is my pakcage.';
}
service/apiに自作パッケージを追加します。rootで以下のコマンドを実行します。(yarn addのworkspace版)
yarn workspace api add my-package
handler.tsでimportして、
import { APIGatewayProxyHandler } from 'aws-lambda';
import 'source-map-support/register';
import { mypackage } from 'my-package';
export const hello: APIGatewayProxyHandler = async (_event, _context) => {
return {
statusCode: 200,
body: JSON.stringify({
message: mypackage()
}, null, 2),
};
}
deployします!
deployできない Part2
今度は以下のようなエラー。
Error: npm install failed with code 1
at ChildProcess.<anonymous> (/workspaces/node_modules/serverless-webpack/lib/utils.js:91:16)
at ChildProcess.emit (events.js:314:20)
at ChildProcess.EventEmitter.emit (domain.js:483:12)
at maybeClose (internal/child_process.js:1021:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:286:5)
今度は、以下のissueの最後にある修正をします。allowlistにpackage名を入れてあげる。
...
externals: [
nodeExternals({
modulesDir: path.resolve(__dirname, '../../node_modules'),
allowlist: ['my-package'],
})
],
...
デプロイできた!URLにアクセスすると、ちゃんと表示されているので動いてそう。なぜこれでできるようになるのか、これも原理はわかりません…。
もっと簡単な対処法
webpack.config.tsをいろいろ修正して、なんとかデプロイできました。実はもっと簡単な方法がありました。
それは、serverless-bundleを使う方法です。
これはwebpackのconfigを書かなくてもいい感じにやってくれるプラグインのようです。
導入はとても簡単で、
serverless create --template aws-nodejs-typescript --path services/api
をした後に、不要なファイル(webpack.config.ts)を削除して、以下のファイルを変更するだけです。
"devDependencies": {
"@serverless/typescript": "^2.12.0",
"@types/aws-lambda": "^8.10.64",
"@types/node": "^14.14.6",
"serverless-bundle": "^4.1.0",
"ts-loader": "^8.0.10",
"ts-node": "^9.0.0",
"typescript": "^4.0.5"
}
custom: {
bundle: {
packager: 'yarn',
tsConfig: './tsconfig.json',
caching: false
}
},
{
"compilerOptions": {
"baseUrl": ".", // 追加
"lib": ["es2017"],
これでデプロイできるようになると思います!
webnpackのconfigを細かく調整する必要がないならこれが楽でいいかもしれません。
結論
Serverless Frameworkでyarn workspaceを使う方法について紹介しました。serverless-webpackでいろいろエラーが出ましたが、なんとか動くようになりました。issueでも議論されているので、いつか対応されるかもしれません。issue通りに修正してなぜ動くのかは今後勉強したいと思います。
serverless-bundleは導入するだけでいい感じにbuildしてくれるので、それまではserverless-webpackではなく、こっちを使うのがいいかもしれません。