【AWS】Dockerイメージを使用したLambda + APIGatewayでREST APIを構築

2020年12月から、LambdaはDcokerイメージを使用して作成できるようになりました。
そこで、DcokerイメージからLambdaを作成します。
また、作成したLambdaをもとにAPIGatewayをトリガーとしたREST APIの構築方法を説明します。

![docker_lambda_apigateway_qiita.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/312689bb-ed6a-6934-bfcb-f93ccc01ad50.png)

# ターゲット

- AWSのコンテナ関連サービスについて学びたい
- サーバーレスでAPIを構築したい
- Docker,Lambda,APIGatewayについて学びたい

# 前提条件

- Dcokerを利用する環境がある(Docker Desktopがインストールされている)
- AWSマネジメントコンソール、AWS CLIの両方が利用できる環境がある

# ECRリポジトリの作成

Lambdaを作成するコンテナイメージはECRリポジトリから取得します。
そのため、API用のコンテナイメージを格納するECRリポジトリを作成します。

マネジメントコンソールのホームから、「ecr」を検索してリポジトリを開きます。
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/8a010b87-f295-367e-a54b-7b812ef4856b.png)

「private」タブを選択して、「リポジトリを作成」を押下します。
![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/c12b3937-ff8c-bfc8-9cae-76df6b8e97c7.png)


一般設定で可視性設定の「プライベート」を選択します。
リポジトリ名は任意で指定します。
![図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/9c64e149-7ea0-e6a8-cb13-86e245fc3f1d.png)


「リポジトリを作成」を押下します。
![図4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/21bc1af9-f2c3-2cca-83ef-c5606f4209ba.png)

作成されたことを確認します。
![図5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/65a54816-9980-1987-1558-71d9c40e62cf.png)

後ほど使用するため、URIを保存します。
![図6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/b441f170-a1c7-5578-361a-f65a8c049be4.png)


# Lambdaのコンテナイメージ作成

```bash:フォルダ構成
lambda-test
│  .env
│  docker-compose.yml
│
└─api
        script.py
        Dockerfile
        requirements.txt
```

LambdaのDockerイメージを作成します。

### Lambda関数スクリプト作成

```bash
lambda-test
│
└─api
        script.py
```

Pythonを使用してLambda関数に使用するスクリプトを作成します。
実行されると、`{"A": "hoge", "B": "fuga", "C": "piyo"}`のようにJSON形式のデータを返すようにしています。
各項目は、[プロキシ統合のための Lambda 関数の出力形式](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html)に沿って設定しています。

```python:script.py
import json

def lambda_handler(event, context):
    return {
        "isBase64Encoded": False,
        "statusCode": 200,
        "body": json.dumps({
            "A": "hoge",
            "B": "fuga",
            "C": "piyo"
        })
    }
```

Lambda用のイメージがAWS公式から公開されていますので、そちらを利用します。

### Python依存パッケージ管理

```bash
lambda-test
│
└─api
        requirements.txt
```

Pythonのpipというパッケージ管理ツールで、依存パッケージを管理します。
`requirements.txt`に依存パッケージを指定することで、一括インストールが可能です。
今回に関しては依存パッケージがありませんので、**空ファイル**を作成します。

```text:requirements.txt

```

### コンテナイメージの設計

```bash
lambda-test
│
└─api
        Dockerfile
```

Dockerでは、既存のコンテナイメージをpullするだけではなく、コンテナイメージをカスタマイズすることができます。
カスタマイズ内容は`Dockerfile`で定義します。今回はAWS公式から提供されているLambda - Python用コンテナイメージ`public.ecr.aws/lambda/python:3.8`をベースに作成します。

```Dockerfile:Dockerfile
# Lambda - Python用コンテナイメージを使用
FROM public.ecr.aws/lambda/python:3.8

# Pythonのパッケージマネージャを最新化
RUN pip install --upgrade pip

# api配下をコンテナイメージにコピー
COPY . ./

# requirements.txtで記述した依存パッケージを一括インストール
RUN pip install -r requirements.txt

# コンテナ起動時の実行関数を定義
CMD ["script.lambda_handler"]
```

### 環境変数を定義

```bash
lambda-test
│  .env
```

環境依存の変数を`.env`に定義します。
このファイルに定義した変数は、`docker-compose.yml`で使用することができます。
`<ECRリポジトリのイメージURI>`はECRの作成の際に控えたURIに置き換えてください。

```config:.env
IMAGE_TAG=<ECRリポジトリのイメージURI>
```
# コンテナイメージをECRリポジトリにプッシュ

以下からログインコマンドを確認します。

リポジトリ名を押下してください。
![図7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/18a61540-af09-f0f3-dd47-5cc696ed18f1.png)

「プッシュコマンドを表示」を押下します。
![図8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/d46fba37-143b-76d5-8b80-e4956c692148.png)

コマンドの`1.`をコピーします。
![図9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/474f4050-9ff4-9849-d1ea-51f2aeefd7fb.png)

コピーしたコマンドを使用して、AWS CLIでECRにログインしてください。
**※ログインには、`$ aws configure`でAWS CLIへのアカウント設定が必要です。**

```bash:ECRリポジトリにログイン
$ aws ecr get-login-password --region <リージョン> | docker login --username AWS --password-stdin <アカウントID>.dkr.ecr.<リージョン>.amazonaws.com
```

`lambda-test`(docker-compose.ymlのあるフォルダ)まで移動しします。

```:フォルダ移動
$ cd <BASE_DIR>\lambda-test
```

コンテナイメージをビルドします。

```bash:コンテナイメージのビルド
$ docker-compose build --no-cache
```

作成したコンテナイメージの`REPOSITORY`の値がECRリポジトリのURIになっていることを確認します。

```bash:コンテナイメージの確認
$ docker images
REPOSITORY                                                      TAG       IMAGE ID       CREATED          SIZE
<アカウントID>.dkr.ecr.<リージョン>.amazonaws.com/<リポジトリ名>   latest    e694ba04a4e7   16 seconds ago   614MB
```

ECRリポジトリにプッシュします。

```bash:ECRリポジトリにプッシュ
$ docker-compose push
```

マネジメントコンソールに戻り、ECRリポジトリにコンテナイメージがあることを確認します。
![図10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/92c39820-389f-4a7b-fb37-d9bed650c871.png)

# Lambdaを作成 

ECRリポジトリのコンテナイメージから、Lambdaを作成します。

マネジメントコンソールで、「lambda」と検索して、サービスの「Lambda」を押下します。
![図11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/1bae95d7-97ca-71f1-ff5d-a25e90b581a7.png)

左のナビゲーションペインから「関数」を選択して、右上の「関数の作成」を押下します。
![図12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/36afe72e-c189-ebc8-8a6c-066809fa9e67.png)

オプションで「コンテナイメージ」を選択します。
関数名に任意の値を指定します。
![図13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/8ac0b0d8-2cea-4e6e-969e-ead7c68f283a.png)

「イメージを参照」を押下します。
![図14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/bcdca014-0532-4e96-d4b5-0d83e0b26f7b.png)

対象のECRリポジトリからイメージタグがlatestのイメージを選択します。
「イメージを選択」を押下します。
![図15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/432dba7a-8066-f175-e601-d782d85fa6c6.png)

「関数の作成」を押下してLambdaを作成します。
![図16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/b218d5de-76bc-4277-00ff-db527be3a076.png)


以下の画面に遷移すれば作成完了です。
![図17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/80b2698e-85af-a61e-f9f1-bdd3074a5f4b.png)



# LambdaのトリガーとしてAPIGatewayを作成

Lamndaの起動条件を設定します。

Lambdaの詳細画面の「トリガーを追加」を押下します。
![図18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/03b246b5-bbb5-cbb1-5f34-05f5ac958828.png)

トリガーの設定で「API Gateway」を選択します。
![図19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/54f08d14-a67f-8a3a-65a2-a202f2f7149a.png)

新規でAPIを作成するので、「APIを作成する」を押下します。
![図20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/9f4f036d-c9ef-bb09-6996-cc3ce6687a63.png)

「REST API」を選択して、セキュリティは「APIキー」を選択します。
「作成」を押下します。
![図21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/0e942f13-ebc2-a6ce-6ca0-c1d0648fe04b.png)

トリガーに「API Gateway」が追加されます。
![図22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/030a7ab3-c797-c684-d57a-85438119d75a.png)

# 動作確認

### APIキーの確認

APIの実行にAPIキーを必須に設定したため、ヘッダーに指定する必要があります。

Lambdaの詳細画面の「設定」タブを押下します。
![図23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/73d7fde0-fc05-fd6b-b924-c964c7e2e1d0.png)

「トリガー」を選択して、API Gatewayのリンクを押下します。
![図24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/a7386b45-2e1f-9e3c-2508-6766ce41ef29.png)


API Gatewayの詳細画面で、左のAPIキーを押下します。
![図25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/c119a875-2fb2-d6be-0036-00c9468adab3.png)

`<Lambda名>-Key`を押下します。
![図26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/3fd0ded4-7c2e-7214-5259-e399b0adfc51.png)

APIキーの「表示」を押下すると、APIキーが確認できますので保管します。
![図27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/23dff661-75f4-a927-24cc-292d01622a72.png)

### APIのUrlを確認

APIのURLを確認します。
「ステージ」を選択します。
![図28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/589a3b29-019b-f552-3876-c16a105233ee.png)

「default」を選択して、URLを確認します。
![図29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166959/a06c8290-e3af-5387-4daa-9da30ea067e7.png)

### curlでAPIの動作確認
APIキーを設定した場合は、ヘッダーの`x-api-key`に指定する必要があります。

以下のコマンドを実行します。

:::note info
Windowsのコマンドプロンプトで実行する場合は、ヘッダーをダブルクォーテーションで指定してください。
:::

```bash:APIの動作確認
$ curl -H 'x-api-key:<API_KEY>' <API_URL>
```

```bash:実行結果
{"A": "hoge", "B": "fuga", "C": "piyo"}
```

# おわりに

REST APIをコンテナ(Dcoker)とサーバーレス(Lambda)を使用して構築してみました。
上記の技術は、スピード重視の小規模開発や、マイクロサービスにも使用できると思いますので、参考にしていただければ幸いです。

# 参考

[API Gateway で Lambda プロキシ統合を設定する](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html)