SAM CLIを用いたLambda Layer 検証方法メモ


## Lambda Layerとは

* 複数のLambda関数で外部ライブラリやビジネスロジックを共有できる仕組み

* メリット

  * ライブラリなどの共通コンテンツをレイヤーとして作成することで、パッケージにライブラリを含める必要がなくなる。

  * アップロードされたデプロイアーカイブのサイズを縮小し、デプロイスピードを高速化できる。

## 検証

* DynamoDBアクセス共通処理をLayer化し、SAMで検証用APIを作成する。

### 事前準備

* DynamoDB環境構築

  * `docker-compose.yml`

    ```yaml
    version: '3'
    
    services:
      dynamodb-local:
        container_name: dynamodb-local
        image: amazon/dynamodb-local:latest
        user: root
        command: -jar DynamoDBLocal.jar -sharedDb -dbPath /data
        volumes:
          - dynamodb-data:/data
        ports:
          - 8000:8000
        networks:
          - dynamodb-network
    
      dynamodb-admin:
        container_name: dynamodb-admin
        image: aaronshaf/dynamodb-admin:latest
        environment:
          - DYNAMO_ENDPOINT=dynamodb-local:8000
        ports:
          - 8001:8001
        depends_on:
          - dynamodb-local
        networks:
          - dynamodb-network
    
    volumes:
      dynamodb-data:
    
    networks:
      dynamodb-network:
        external: true
    ```

  * 起動

    ```
    docker-compose up -d
    ```

    ※localhost:8000でDynamoDBが起動する。

  * `test_user`という名前でテーブル作成しておく。

    

    

### コード

* `sam init`でHello World templateを利用して作成。

* 構成

  ```tree
  ├── src
  │   ├── func
  │   │   ├── __init__.py
  │   │   └── app.py
  │   └── layer
  │       └── python
  │           ├── __init__.py
  │           ├── dynamo_util.py
  │           └── requirements.txt
  ├── template.yaml
  ```

* `template.yaml`

  ```yaml
  AWSTemplateFormatVersion: '2010-09-09'
  Transform: AWS::Serverless-2016-10-31
  Description: >
    sam-app
  
    Sample SAM Template for sam-app
  
  # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
  Globals:
    Function:
      Timeout: 3
  
  Resources:
    MyLayer:
      Type: AWS::Serverless::LayerVersion
      Properties:
        ContentUri: src/layer/python
        CompatibleRuntimes:
          - python3.8
      Metadata:
        BuildMethod: python3.8
      LayerName: MyLayer
    HelloWorldFunction:
      Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
      Properties:
        CodeUri: src/func
        Handler: app.lambda_handler
        Runtime: python3.8
        Architectures:
          - x86_64
        Environment:
          Variables:
            aws_access_key_id: dummy
            aws_secret_access_key: dummy
            region_name: ap-northeast-1
            dynamo_endpoint: http://dynamodb-local:8000
        Events:
          HelloWorld:
            Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
            Properties:
              Path: /item
              Method: post
        Layers:
          - !Ref MyLayer
  ```

* `dynamo_util.py`  : DynamoDB共通処理

  ```python
  from boto3.session import Session 
  import os
  # DynamoDB
  session = Session(
      aws_access_key_id=os.environ['aws_access_key_id'],
      aws_secret_access_key=os.environ['aws_secret_access_key'],
      region_name=os.environ['region_name']
  )
  dynamodb = session.resource(
      service_name='dynamodb', 
      endpoint_url=os.environ['dynamo_endpoint']
  )
  
  def put_item(item):
      
      table = dynamodb.Table('test_user')
      table.put_item(
          Item=item
      )
      
  ```

* `app.py` : DynamoDB共通処理を呼び出し、リクエストで受け付けた内容をテーブルに登録する。

  ```python
  import json
  import dynamo_util as dynamo_util
  
  def lambda_handler(event, context):
      dynamo_util.put_item(json.loads(event['body']))
      return {
          "statusCode": 200,
          "body": json.dumps({
              "message": "item_put succeeded",
          }),
      }
  
  ```

* `requirements.txt` : 依存ライブラリ

  ```
  requests
  boto3
  ```

  

### 動作確認

* ビルド

  ```shell
  sam build
  ```

* 起動※Docker 起動しているDynamoDBローカルと接続するためにオプション指定している。

  ```shell
  sam local start-api --docker-network dynamodb-network
  ```

* リクエスト

  ```http
  POST /item HTTP/1.1
  Host: localhost:3000
  Content-Type: application/json
  Content-Length: 58
  
  {
      "id":"12345",
      "email":"example@example.com"
  }
  ```

* レスポンス

  ```json
  {
      "message": "item_put succeeded"
  }
  ```

* DynamoDB Local登録結果

  ![dynamo_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/cb3538c2-b674-9eee-5122-1e074aa00891.png)


## 参考情報

* [Lambda レイヤーの作成と共有](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-layers.html)