ServerlessでLayerを作成する

こんなふうに愚直にLayer代わりを作ってたわけですよ
https://qiita.com/heat_exchange/items/a38a7a5572e0c09e3422

ただどうしてもlayerを使いたい場合というのがあって、例えば

- モジュールが大容量なとき
    - コードがインラインで見れない
- インポート先が一つ増えるごとにコマンドを一つ増やすのはだるい

と思っていると、Serverless　でLambda Layerを作成できることを知った。
[本当にLambda Layersで幸せになれるのか？ 旧式のデプロイ方式からLambda Layersを活用したデプロイ方式への移行を検討する](https://dev.classmethod.jp/server-side/serverless/migrate-to-lambda-layers/)
これは試してみるほかない。

# コード

先程の記事[^1]や公式ドキュメント[^2]を参考に書いていく。ここでは、次のようなディレクトリ構造を念頭に置く。

```
.
├── template.yml
├── layers
    └── python
        └── dynamodb.py
```

##Layer側

```yaml:template.yml
  #Cloudformation内の物理ID。ProertiesでlayerNameを設定しなければ、自動的にこれがLayerNameとなる
  PythonModulesLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      #layerの説明
      Description: python modules Layer
      
      #layerのURI。S3のURIを指定しても良いが、Cloudformationなら、相対パスを記載することもできる。
      ContentUri: ./layer/python
      
      #lmbda layerの互換性。これを設定しておくことで、レイヤーの設定が楽になる。リストで指定する。
      CompatibleRuntimes:
        - python3.7
        - python3.6
        - python2.7
     
      #Layerのライセンス情報
      LicenseInfo: string

      #Layerのバージョン保持設定。Retainにしておくことで、layerバージョンをアップデート後も古いバージョンは保持される
      RetentionPolicy: Retain(Default)|Delete
```

##Lambda側の設定
lambdaもserverless変換を使わないとlayerを使えないっぽい?要確認。よほどのことが無い限りlambdaの作成はServerless変換で良いと思う。

```yaml:template.yml
  testLambda:
    Type: AWS::Serverless::Function
    Properties:
      Handler: test.handler
      Runtime: python3.7
      CodeUri: tesLambda
      Role:
        !GetAtt testLambdaRole.Arn
      #レイヤーは!Ref(または!Sub)で値を取得し、リストで列挙していく。
      Layers:
        - !Ref PythonModulesLayer
      Timeout: 25
```
あと普通にlayerを使うつもりでlambdaを使っていけば良い。

# つまづきどころ
## Cloudformationからlambda-layerを作成するときは、pythonフォルダごとアップロードしなければならない。
やや基礎的な部分だが、~~自分が~~つまづきやすいのでメモ
例えば、layerにしたいモジュールを、次のようなディレクトリ構造で保存していたとする。

```
.
├── template.yml
├── layers
    └── module.py

```
この場合、テンプレートにはこう記載する。

```yaml:template.yml
  ctrlDynamodb:
    Type: AWS::Serverless::LayerVersion
    Properties:
        Description: python modules Layer
        ContentUri: layers
        CompatibleRuntimes:
          - python3.6
          - python3.7
          - python2.7
```
だが、このレイヤーをインポートしてもこのままでは次のようなエラーが発生する。

```text:error
No modules named modules
```

ここで、lambdaの使用を確認してみる。[^3]
>
ポイントは以下の2つです。
>
Layerのコードは /opt 以下に展開される
/opt 以下の特定のパスはあらかじめライブラリ検索パスに設定されている
例としてPython 3.7の場合を見てみます。まず sys.path を表示するだけの簡単なLambda関数を実行してみると、以下のような結果が得られます。/opt/python, /opt/python/lib/python3.7/site-packages が含まれています。
[Lambda Layerの基本的な仕組みを確認する #reinvent](https://dev.classmethod.jp/cloud/aws/lambda-layer-basics-how-it-works/)



lambdaは標準で`/opt`以下のディレクトリがインポートされるようになっているので、lambda側のディレクトリ構造を調べてみると、次のようになっている。

```
.
├── opt
    └── module.py
```

本来あるべきであるディレクトリ`python`が無いことがわかる。
そこで、次のようにしなければならない。

```
.
├── template.yml
├── layers
    └── python
        └── dynamodb.py
```

```yaml:template.yml
  ctrlDynamodb:
    Type: AWS::Serverless::LayerVersion
    Properties:
        Description: python modules Layer
        ContentUri: layers
        CompatibleRuntimes:
          - python3.6
          - python3.7
          - python2.7
   ```


[^1]: [本当にLambda Layersで幸せになれるのか？ 旧式のデプロイ方式からLambda Layersを活用したデプロイ方式への移行を検討する](https://dev.classmethod.jp/server-side/serverless/migrate-to-lambda-layers/)
[^2]: [AWS::Serverless::LayerVersion](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlesslayerversion)
[^3]: [Lambda Layerの基本的な仕組みを確認する #reinvent](https://dev.classmethod.jp/cloud/aws/lambda-layer-basics-how-it-works/)