emahiro/b.log

Drastically Repeat Yourself !!!!

Serverless Framework でモノレポっぽい構成にする

Overview

Deprecated

Deprecation code: NESTED_CUSTOM_CONFIGURATION_PATH

Note: Applies only to eventual programmatic usage of the Framework

Service configuration in all cases should be put at root folder of a service. All paths in this configuration are resolved against service directory, and it's also the case if configuration is nested in sub directory.

To avoid confusing behavior starting with v3.0.0 Framework will no longer permit to rely on configurations placed in sub directories

ref https://www.serverless.com/framework/docs/deprecations

Serverless Framework V3 で sub directory の configuration ファイルに依存することは非推奨になってデプロイ時に警告が出るようになりました。
これにより、このモノレポの構成が使えるのは V2 までになります。なので今後この構成を選択することできなくなります。
今後は ディレクトリの TOP に配置した serverless.yaml のみに依存し、sub directory に配置していた FaaS たちは Handler で直指定する(設定はすべてサービスで共通)形になりそうです。V3 でも環境変数の値を FaaS ごとに変更するケースがほしい場合があると思うので、別途調べてエントリにしようと思います。


Serverless Framework を使う時に異なるディレクトリに serverless.yml を配置してモノレポっぽい構成の Lambda で作られたサービスを考えます。

※ 今回は Go で Lambda を作ったケースを考えていますが、他のランタイムでも同様です。

考えたきっかけ

通常は以下のような Serverless.yml の設定を 1 つにして Lambda を複数定義する(handlerが複数存在する)のが、Lambda を 1 サービスと見たててマイクロサービスっぽく振る舞わせます。

# 構成

- serverless.yml
- app
    - hogeFunc
        - main.go
    - fugaFunc
        - main.go
  ... 略

この場合 serverless.yml には以下のような設定をすることになります、

functions:
 hogeFunc:
    handler: ...
 fugaFunc:
    handler: ...
 ... 略

これ自体はそんなに難しいことではないですが、マイクロサービスや、あるメインシステムに対してのサブシステムとして Lambda を採用する場合に、いくつもの Lambda を作っていくと serverless.yaml が肥大化することになります。そして何より functions 配下で色んなコンテキストを持つ Lambda が混在することになることに課題を感じてました。これに対して、もちろんリポジトリを分ければ良かったりもするのですが、それだとリポジトリが無尽蔵に増えていくことになり、運用のことを考えるとある程度まとめておきたいなーという気持ちもあって、ものレポっぽい構成にしたいなと考えてました。
serverless.yml は変数を外部ファイルから読み込むこともできる(*)ので分けて整理することもできますが、それでも読み込めない値の定義も存在します。
リポジトリが増えることは抑制してソフトウェアとしては1つにまとめるためにも、ある程度の粒度で個別の serverless.yml として管理したいと考えてました。

*Serverless Frameworkで環境変数を外部ファイルから読み込み、環境毎に自動で切り替えてみる

モノレポ構成で考える

前述したような構成(hogeFunc, fugaFunc..) で複数の serverless.yml を持つ以下のような定義を考えます。

- hogeSvc
    - hogeHandler
        - main.go
    - fugaHanlder
        - main.go
    - serverless.yml
- fugaSvc
    - main.go
    - serverless.yml

各サービスの serverless.yml には以下のような設定が追加されます。

functions:
 hogeHanlder:
    handler: ....
 fugaHandler:
    handler: ...

これだとサービスごと関係する Lambda を追加してサービスごとに設定ファイルをわけ流ことができます。(serverless.yml 1つあたりのの肥大化の抑制)

ハマったところ

デプロイ周り

Lambda のデプロイには以下の公式の GitHub Actions を使います。 (普通にCIの中で serverless framework をインストールして sls コマンドでデプロイすることも可能です。)

github.com

この Actions の設定を使ったときにちょっとデプロイ周りでハマりました。この Actions の設定でそのまま sls deploy を実行した場合、実行するディレクトリと同じ深さに serverless.yml がないとデプロイできない、というエラーが出てしまいます。
このエラーが発生する理由は、今回使用する serverless 公式の Actions で sls deploy を実行する場合、ディレクトリ移動といった設定を Actions の設定ファイルに記述することができないからです。

実際に遭遇したエラーは以下です。

This command can only be run in a Serverless service directory. Make sure to reference a valid config file in the current working directory if you're using a custom config file

このエラーが発生する場合(今回のような複数の serverless.yml を Lambda ごとに定義した場合) はエラーメッセージの後半に記載してある カスタム config ファイルの指定をする必要があります。

config ファイルをカスタムで指定する場合、sls deploy で指定する config ファイル(serverless.yaml) の指定方法は --config の設定を使います。

www.serverless.com

この設定を使うと、以下のようなディレクトリ構成の場合

root
- hogeSvc
- fugaSvc
- ...

以下のように特定サービスの serverless.yml を指定してデプロイします。

$ sls deploy --config ./hogeSvc/serverless.yml

Actions の設定は以下のようになります(抜粋)

- name: serverless deploy stage
    uses: serverless/github-action@master
  with:
    args: deploy --config ./hogeSvc/serverless.yml --verbose --stage=stage
  env:
    AWS_ACCESS_KEY_ID: $ACCESS_KEY
    AWS_SECRET_ACCESS_KEY: $SECRET_ACCESS_KEY

デプロイコマンドを実行する位置からの相対 path を指定する

またこのデプロイするときに注意したいのは serverless.yml に記載している path は sls deploy を実行する path するからの相対 path にしないと正しく読み込まれない、ということです。

例えばdev/prod で参照する変数を分けてる場合、hogeSvc を actions からデプロイする上記のコマンドを実行する際には以下のように変数の読み込みファイルの読み込み先指定を hogeSvc から指定しないといけません。

# hogeSvc/serverless.yml
custom:
  defaultStage: stage
  environment:
-    stage: ${file(./conf/dev.yml)}
-    prod: ${file(./conf/prod.yml)}
+    stage: ${file(./hogeSvc/conf/dev.yml)} // リポジトリのルートからみると conf ファイルは hogeSvc 配下にあることになる。
+    prod: ${file(./hogeSvc/conf/prod.yml)}

また実行バイナリの path も早退パスになりいます。例えば hogeSvc 配下で以下のように実行バイナリを bin ディレクトリに格納していた場合を考えます。

- hogeSvc
- bin
    - hoge

このケースでは serverless.yml の handler の設定を sls deploy を実行する path からの相対 path にしないと以下のような Lambda のエラーが出ます。

{
    "errorMessage": "fork/exec /var/task/bin/factReviewer: no such file or directory",
    "errorType": "PathError"
}

serveless.yml の設定は以下

functions:
    hogeHandler:
        handler: ./hogeSvc/bin/hoge

まとめ

Lambda の構成をマイクロサービスっぽくするときにうまく管理をわけることができないか?というところが発想の起点でしたが、やりようはある、ということがわかりました。
とはいえ、結構相対 path にすることを忘れたりするのでこれも構成の一つと捉えてみると良さそうです。