Docker×Lambda環境簡単構築

# Docker×Lambda環境簡単構築
## はじめに
AWS Lambda Function を実装するときは docker-lambda を使用すると開発も容易で、デプロイまで簡単にできます。その環境構築手順、実際にデプロイするまでを簡単にご紹介します。

## 前提
以下はすでに準備されている前提となります。
- AWSアカウント
- dockerインストール
- docker-composeインストール

## 構成
最終的に以下のような構成になります。

```
.
├── deploy.sh
├── docker-compose.yml
├── lambda
│   └── SampleFunction
│       ├── Dockerfile
│       └── index.js
└── opt
    ├── Dockerfile
    └── package.json

```

## 手順
### 1. AWS Lambda関数作成
詳細手順は[こちら](https://qiita.com/A-Kira/items/293808eb44afcb367293)の記事をご参考ください。

### 2. AWSアクセスキー取得
ローカル環境からアクセスできるようにAWSのアクセスキーやシークレットキーを取得しておきます。
AWSマネジメントコンソールにログインした後、画面の右上のアカウント名のプルダウンの中にある「マイセキュリティシカク情報」を選びます。
![スクリーンショット 2020-08-21 13.20.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290840/6edbae93-eba4-0596-79b1-ce78c4be529c.png)

アクセスキーIDとシークレットアクセスキーを作成して、大切に保管ください。

### 3. Dockerを使ってローカル環境構築
ローカル環境でLamda関数の動作確認ができる環境を作ります。

#### 3.1 docker-compose.yml作成
今回はLambda関数用のコンテナとレイヤー用のコンテナの2つを作成します。

```docker-compose.yml
version: '3.5'
services:
  layer:
    image: node:12-slim
    tty: true
    volumes:
      - ./opt/:/srv:rw,delegated
    ports:
      - 3000:3000
    working_dir: /srv
    environment:
      TZ: 'Asia/Tokyo'

  SampleFunction:
    image: lambci/lambda:nodejs12.x
    tty: true
    volumes:
      - ./lambda/SampleFunction:/var/task:rw,delegated
      - ./opt:/opt:ro,delegated
    ports:
      - 9001:9001
    environment:
      DOCKER_LAMBDA_STAY_OPEN: 1
      DOCKER_LAMBDA_WATCH: 1
      DOCKER_LAMBDA_DEBUG: 1
      TZ: 'Asia/Tokyo'
    command: index.handler
```

#### 3.2 Lambda関数のサンプル作成
`lambda/SampleFunction`のディレクトリを作成します。
その中に`index.js`を作成します。
中身は手順1. で作成したLambda関数の中身をコピーして、出力文字を`Hello Lambda!`に変えたのみです。

```js:index.js
exports.handler = async (event) => {
    return  {
        statusCode: 200,
        body: JSON.stringify('Hello Lambda!'),
    };
};

```

#### 3.3 動作確認
Dockerを起動して、実際にローカルのAPIを叩いてみましょう。

```bash

# dockerを起動
% docker-compose up -d

# APIを実行
% curl -d '{}' http://localhost:9001/2015-03-31/functions/index/invocations
{"statusCode":200,"body":"\"Hello Lambda!\""}   
```

#### 3.4 レイヤーの作成
上記3.2 コードのみであればレイヤーの作成は不要なのですが、実際にはpackage.jsonを使って他のライブラリなどを利用すると思いますので、今回は初期のpackage.jsonのみセットしておきます。
すでにdockerを起動していれば`opt`ディレクトリは自動でできていると思います。

```bash
# レイヤーコンテナにインスペクション
docker-compose exec layer bash

# package.json作成
npm init
```

`opt`ディレクトリに`package.json`が作成されていたらOKです。

### 4. デプロイ環境構築
ローカル環境から手順1.で作成したLambdaへデプロイできる環境を構築します。

#### 4.1 SampleFunctionデプロイ用のDockerfile作成
`lambda/SampleFunction/Dockerfile`となるようにDockerfileを作成します。
※ function-name は 手順1. で作成した関数名と同じにしてください。

```Dockerfile
FROM lambci/lambda:build-nodejs12.x

COPY . .

RUN zip -9yr lambda.zip .

CMD aws lambda update-function-code --function-name SampleFunction --zip-file fileb://lambda.zip
```

#### 4.2 layerデプロイ用のDockerfile作成
```docker

FROM lambci/lambda:build-nodejs12.x

COPY . .

RUN npm install

RUN zip -9yr lambda.zip .

CMD aws lambda publish-layer-version \
    --layer-name SampleLayer \
    --compatible-runtimes nodejs12.x \
    --description "" \
    --zip-file fileb://lambda.zip
```

#### 4.3 環境設定ファイル .env
docker-compose.ymlと同じ階層に.envを作成します。
手順2. で取得したアクセスキーIDとシークレットアクセスキーをセットします。

```.env
AWS_ACCESS_KEY_ID=XXXXXXXXXX
AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXx
AWS_DEFAULT_REGION=ap-northeast-1
```

#### 4.4 デプロイ用のシェルスクリプト作成
layerとLambdaファンクションをそれぞれデプロイするシェルスクリプトになります。

```bash:deploy.sh
#!/bin/bash

# .env読み込み
export $(cat .env | grep -v ^# | xargs);

# LayerをLambdaにデプロイ
docker build -t layer ./opt
docker run --rm \
  -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
  -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
  -e AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION \
  layer

# SampleFunctionをLambdaにデプロイ
docker build -t sample-function ./lambda/SampleFunction
docker run --rm \
  -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
  -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
  -e AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION \
  sample-function

```

#### 4.5 デプロイ
シェルを実行すれば、Lambda関数とLayerがデプロイされます。

```bash
sh deploy.sh
```

### 5. 確認
#### 5.1 APIの実行
手順1. で確認したAPIエンドポイントを叩くと出力メッセージが`"Hello Lambda!"`に変わっていると思います。

#### 5.2 AWS Lambda関数のコード確認
手順3.2 で設定したファイルと同じになっています。

```js:index.js
exports.handler = async (event) => {
    return  {
        statusCode: 200,
        body: JSON.stringify('Hello Lambda!'),
    };
};
```

#### 5.3 Layerの確認
「レイヤーの追加」からレイヤーを追加します。
![スクリーンショット 2020-08-21 14.13.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290840/0303a17e-da55-8982-989b-aad4d52bdf3d.png)

デプロイしたレイヤーがリストに追加されているのでそれを選択し追加します。
![スクリーンショット 2020-08-21 14.14.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290840/c07808cb-8d43-fed2-06d3-f5baec25e15c.png)

## 参考
- [AWS アクセスキーの管理](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_access-keys.html)
- [lambci/docker-lambda](https://github.com/lambci/docker-lambda)