AWS Amplify フレームワークの使い方Part14〜Lambda レイヤー編〜

# 2021/7/20追記
先日、AmplifyCLIのv5で新しくプロジェクトを作成しましたが、この記事に書いている注意点は、ほぼほぼ改善されていて気にしなくて良くなっています。
ただし、v4で作成したプロジェクトをv5に上げた場合、どの程度まで恩恵を受けられるかは不明なので引き続き注意が必要です。

# 2021/7/7追記
先日、AmplifyCLIのv5.0.0がリリースされました。
こちらのメインの変更点が、Layerの仕様変更です。

現状、v4からv5への移行を色々と試していますが、様々なトラブルと向き合っています。

v5は、これまで苦しんだLayerのバグの多くが解消されているようですので、これからLayerを始める方は、最初からv5で作成することをおすすめします。

# はじめに
Lambda関数を複数作成していると、このライブラリや関数を共通化したいな、と思うことがよく有りました。そんな時、気づいたら追加されていたLambdaのレイヤー機能を使えば、一発解決です。

# バージョン
amplify-cli 4.32.1

# 実装方法
## 新規作成
まずは、関数を追加します。

```console
$ amplify add function
```
レイヤーを選択。

```console
? Select which capability you want to add:
> Lambda layer (shared code & resource used across functions)
```
名前を決めて、実行環境は今回はNodeJSを選択します。

```console
? Provide a name for your Lambda layer: testLayer
? Select up to 2 compatible runtimes: NodeJS
```

同一のアカウントor同一の組織(Organizationsで設定したAWSアカウント)or全体公開の3つからアクセス権限を選びます。同一アカウントだけでなく同一の組織とも共通化できるなんて素晴らしいですね。

```console
? The current AWS account will always have access to this layer.
Optionally, configure who else can access this layer. (Hit <Enter> to skip) (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ Specific AWS accounts
 ◯ Specific AWS organization
 ◯ Public (Anyone on AWS can use this layer)

```
これで準備完了です。ライブラリの追加は、自動生成された関数フォルダ内の`/lib/nodejs`直下に移動して、必要なライブラリをインストールしてください。

```console
$ cd amplify/backend/function/testLayer/lib/nodejs/
```

関数の共通化をしたい場合は、`/opt`直下に`testLayer.js`を作成し、共通化したい関数を記載すれば、各Lambda関数から呼び出すことができます。(ファイル名は何でもいいですが、後で各関数からこのファイル名を呼び出すので、レイヤー名と同じがわかりやすいと思います。)

```console
$ cd amplify/backend/function/testLayer/opt/
$ touch testLayer.js 
```

```javascript:/opt/testLayer.js
module.exports = {
  // 現時刻の取得
  testFunction: () => {
   console.log('test!!!')
  }
}

```

## 紐付け
紐付けたいLambda関数の作成時または更新時に紐付けの設定を行います。

```console
$ amplify function add
or
$ amplify function update
```
設定を進めていくと以下のようにレイヤーん設定をどうするか聞かれますので、Yesを選択し、紐付けしたいレイヤーとそのバージョンを選択します。

```console
? Do you want to configure Lambda layers for this function?
> Yes
? Provide existing layers or select layers in this project to access from this function (pick up to 5):
> testLayer
? Select a version for testLayer:
> 1
```
レイヤーは5つまで選択することができます。複数のレイヤーを紐付けた場合はその順番を並び替えることで、競合する箇所があれば前のレイヤーを上書きします。


```console
? Modify the layer order (Layers with conflicting files will overwrite contents of layers earlier in the list):
testLayer
testLayer2
```

また、共通化した関数を使用したい場合は、紐付けるLambda関数の`index.js`に記載します。

```javascript:index.js
const TEST_LAYER = require('/opt/testLayer')
exports.handler = () => {
  TEST_LAYER.testFunction()
}
```

あとはいつものコマンドでローカルの更新情報をアップしたらお終いです。たったこれだけの設定なんてすばらしい。

```console
$ ampify push
```


## 更新
コードやライブラリを更新して `$ amplify push`すると以下の質問ができてきます。最新のレイヤーと同じ権限かこのアカウントのみの権限にするかを選択すると、新しいバージョンのレイヤーが作成されます。

```console
What permissions do you want to grant to this new layer version? 
❯ The same permission as the latest layer version 
  Only accessible by the current account. You can always edit this later with: amplif
y update function 
```

レイヤーはそんなに更新するものではないかもしれませんが、ここでひとつ問題が。

基本的に既存のバージョンを更新するのはではなく、新しいバージョンを追加する形のため、その更新した新しいレイヤーに関しては、再度各関数に紐付けが必要です。

これって地味に大変ですよね、、、。何かいい方法があれば教えていただきたい、、、。

## 削除
レイヤーの削除については、わかっていないことが多く、苦しめられる可能性があるので、注意が必要です。

```console
$ amplify function remove
```

このコマンドで消したらこれまでの様々な関数のとの関係を解消してすべてがなかったことに、なってくれたらなんて幸せなことか、、、。

紐付けの解消が、まぁー何故かうまく行かない、、、。(わたしだけ？)
`amplify function update`で紐付けている関数を更新してちゃんと紐付けを解消し、pushし、その上でremoveをしても、紐付けをちゃんと解除してから削除しろ、と怒られ、現状スマートな方法が見つかりませんでした。(紐付け解消後に、`backend.config`や紐付けた関数のtemplate内に残っているlayer情報を削除したりしましたがエラー、、、。)

結局現段階では、紐付けた関数を一旦削除してから、layer関数も削除するという力技なんとかしています。

この削除のスマートな方法も誰か知っていたら教えていただきたい、、、。


# 注意点
## 更新の注意点(2020/11/28追記)
### 基本はレイヤーのみを更新するのがベター
これはおそらくバグだと思いますが、レイヤー以外のリソースを一緒に `$ amplify push`で更新しようとすると、レイヤーの新しいバージョンがローカル上でしか更新されず、実際のLambdaには作成されないため、非常にややこしい状況が生まれます。(レイヤーの権限選択の質問がスキップされる)

いずれ解消されるとは思いますが、2020/11/28時点では、レイヤーのみリソースを更新するようにしたほうが幸せになれます。

## うっかり一緒にampify pushを実行してしまった場合の対処法
このまま紐付けを行ってamplify pushを行うと、新しいバージョンのレイヤーが存在しません、というエラーで一生amplify pushができなくなります。

そんなときは、以下の方法で修正を行います。

1. LambdaのGUIを開き、存在しないレイヤーのバージョンを作成する(zipファイルをUP、zipファイルは何でも良いですが、レイヤー関数のdistにある前バージョンのzipファイルが間違いないと思います。)
2. レイヤーを一旦更新前の状態に戻す(amplify statusでNo changeの状態)
3. レイヤー以外をamplify pushで更新
4. レイヤーのコードを更新して、amplify push
5. amplifyが認識しているバージョン数と実際のLambdaに作成されたレイヤーのバージョン(LambdaのGUIから確認)にズレが生じるため、`amplify/team-provider-info.json`のバージョン数を実際に作成されたレイヤーのバージョンに変更する
6. amplify function updateで各関数に紐付けを行う(5で変更したバージョンを選択)
7. amplify push

## 削除の注意点(2020/11/26追記)
amplifyを通して色々設定を行える中で、レイヤーだけ少し特殊(`ampify delete`しても消えない等)なので、注意が必要です。

### AmplifyCLIからではレイヤーの完全削除されない
`amplify function remove`や`amplify delete`で削除されたレイヤーは、プロジェクトからは削除されますが、Lambda上にレイヤーとしては存在し続けます。
完全に削除したい場合は、LambdaのGUIから削除してください。(or別途 CLIのコマンド)

ただ色々トラブルを生むので、基本消さないほうが無難です。

### 完全削除からの復活は要注意
完全削除した後に、また同じ名前のレイヤーを作成した場合、バージョンの番号は前回の続きから始まります。(バージョン2で完全削除していたら、バージョン3から始まります。)

しかしながら、amplifyはそんなことは知らないので、バージョン1として自動作成をします。その結果、Lambda上にはバージョン3が作られているのに、amplifyはバージョン1と勘違いするというカオスが生まれます。

その場合は、以下のファイルを修正し、レイヤーを更新すれば解決します。

```json:amplify/team-provider-info.json
{
  "dev": {
    "nonCFNdata": {
      "function": {
        "testLayer": {
          "layerVersionMap": {
            // この数字が1になっているので、対応すべきバージョン数に変更する
            "3": { ... }
          }
        }
      }
    }
  }
}
```

# おわりに
と、最後はやや愚痴気味になってしまいましたが、そんなトラブルがあってもメリットのほうが大きいので、大いに役に立ってはくれそうです。
シンプルな更新と削除の情報がありましたら、教えていただけると嬉しいです。

よきAmplifyライフを！！

# 参考
[How to use Lambda layers with the Amplify CLI(公式コラム)](https://aws.amazon.com/jp/blogs/mobile/how-to-use-lambda-layers-with-the-amplify-cli/)

# 関連記事
[AWS amplify フレームワークの使い方Part1〜Auth設定編〜](https://qiita.com/too/items/fc961283dcbef3aafdeb)
[AWS Amplify フレームワークの使い方Part2〜Auth実践編〜]
(https://qiita.com/drafts/54992bb871fc1a2ab101/edit)
[AWS Amplify フレームワークの使い方Part3〜API設定編〜](https://qiita.com/too/items/c0ce14d16772b9f71223)
[AWS Amplify フレームワークの使い方Part4〜API実践編〜](https://qiita.com/too/items/f9078803ea394c35df7d)
[AWS Amplify フレームワークの使い方Part5〜GraphQL Transform @model編〜](https://qiita.com/too/items/ab6b313da9f9e34be4a7)
[AWS Amplify フレームワークの使い方Part6〜GraphQL Transform @auth編〜]
(https://qiita.com/too/items/fae2879ea36f00c3ae10)
[AWS Amplify フレームワークの使い方Part7〜GraphQL Transform @key編〜]
(https://qiita.com/too/items/cb1dfb4f44536a3e9855)
[AWS Amplify フレームワークの使い方Part8〜GraphQL Transform @connection編〜](https://qiita.com/too/items/c77f1e5487d2497b5061)
[AWS Amplify フレームワークの使い方Part9〜Function 基礎編〜](https://qiita.com/too/items/d2732b3ec625011f4567)
[AWS Amplify フレームワークの使い方Part10〜Storage編〜](https://qiita.com/too/items/361bc10b6d27f133e9f8)
[AWS Amplify フレームワークの使い方Part11〜Function 権限管理編〜](https://qiita.com/too/items/b34cbfd7e96a01e33f23)
[AWS Amplify フレームワークの使い方Part12〜ENV編〜](https://qiita.com/too/items/5c1e28cf95fc6229547e)
[AWS Amplify フレームワークの使い方Part13〜Auth 設定更新編〜]
(https://qiita.com/too/items/52f35860bcb5bdf5e667)