ServerlessFrameworkで既存S3バケットへのイベント追加機能がサポートされてました!

2019.11.07

こんにちは、平野です。

ServerlessFrameworkでS3から発火するLambdaを配置する場合、 S3バケットを準備しておかなくてもLambda発火のeventsとしてS3を指定すれば、 バケットの作成までやってくれてスーパー便利です!! しかし逆に、既にファイルがいくつか存在するバケットに対してLambda発火のイベントを追加する場合、 Serverlessでは設定するのがちょっと大変でした。

これを実現するためのプラグイン serverless-external-s3-eventというものがあることは小耳に挟んでいたので、 いざこのプラグインの使い方を調べようとしたのですが、 このプラグインのREADMEを見ると、

This functionality is now native to serverless.

なんとServerless公式で対応がされているようでした! ということで、プラグインの使い方記事の予定でしたが、こちらの使い方を調べました。

検証バージョン

$ sls --version
Framework Core: 1.57.0
Plugin: 3.2.2
SDK: 2.2.1
Components Core: 1.1.2
Components CLI: 1.4.0

この機能は1.47.0で実装されたようなので、結構前からあったようです。

検証YAML

functionsの書き方の部分だけ抜粋ですが、以下のように書きます。

functions:
  SampleFunction:
    handler: handler/sample_function.lambda_handler
    events:
      - s3:
          bucket: "cm-hirano-bucket-event-setting-01"
          events:
            - "s3:ObjectCreated:*"
          rules:
            - prefix: images/
            - suffix: .jpg
          existing: true

追加された部分は最終行の

existing: true

だけです。 バケットを新規作成する場合の設定にこの1行を加えるだけとなっています。 簡単!!

動作検証

existing指定をしない場合

まず比較のため、existingを指定しないでデプロイしようとして失敗してみます。

An error occurred: S3BucketCmhiranobucketeventsetting01 - cm-hirano-bucket-event-setting-01 already exists.

スタック外にすでにバケットが存在するのでデプロイは失敗します。

existingを指定してみる

existing: trueを設定してデプロイすると、以下のような感じで特に問題なくデプロイできました。

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 S3BucketTest.zip file to S3 (105.46 KB)...
Serverless: Uploading custom CloudFormation resources...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
.............
Serverless: Stack update finished...
Service Information
service: S3BucketTest
stage: dev
region: ap-northeast-1
stack: S3BucketTest-dev
resources: 9
api keys:
  None
endpoints:
  None
functions:
  SampleFunction: S3BucketTest-dev-SampleFunction
layers:
  None
Serverless: Removing old service artifacts from S3...
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.

イベントがすでに設定されている場合

次に、対象の既存バケットに既にイベントが設定されているケースで試してみます。 マネジメントコンソールから手動でイベントを追加します。

この状態でデプロイを行うと、以下のようにイベントが 追加 されます。 既存イベントの置き換えではありません のでご注意ください (多分置き換えられるより安全かとは思いますが)。

CloudFormationスタックを確認

このServerlessから作成されるCloudFormationスタックを確認してみます。 「リソース」の部分は以下のようになっていました。

タイプがCustom::S3というものがあり、 また、「CustomDashresourceDashexistingDashs3LambdaFunction」という、 特に作った覚えのないLambda関数も含まれています。

これはカスタムリソースというもので、 スタック外のリソースである当該S3バケットに対しての「変更処理」をスタックの一部に含めているようです。 このスタックが更新された際には、Lambda関数が実行されてS3バケットへのイベント追加処理が実行されるようです。
この辺はまだよくわかっていないので、勉強します...!

こんな機能を使って結構頑張って設定をしてるんだなぁ、って感じです。

まとめ

ServerlessFrameworkでLambdaイベントにS3を指定する場合、 既存のS3バケットに対してイベントを追加したくても標準ではできませんでした。 serverless-external-s3-eventというプラグインがあり、 これを使うことで実現はできていたのですが、 ServerlessFrameworkの公式機能としてexistingという設定ができるようになっていたので試してみました。

検証の結果、もちろん期待した通りの結果が得られたのですが、 対象のバケットはあくまでも Serverlessで作られるスタック外のもの なので、 CloudFormationスタックの、一つの「かたまり」のように直感的に更新や削除が行える便利さは少し失われてしまいます。
ということで、この機能が実装されたとは言え、 新規バケットを作って一つのスタックとして扱えないかという点は必ず検討を行い、 データ移行が可能であれば新しいバケットを作ってしまった方が後々は幸せになれるかな、と感じました。

この辺はServerlessFrameworkなどというより、 CloudFormationでリソースを上手に扱うためのロジックによる制約なので、 出来るだけそのロジックに沿った構成にできると良さそうです。

以上、誰かの参考になれば幸いです。

参照リンク

Using existing buckets - ServerlessFramework