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


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

ターゲット

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

前提条件

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

ECRリポジトリの作成

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

マネジメントコンソールのホームから、「ecr」を検索してリポジトリを開きます。

「private」タブを選択して、「リポジトリを作成」を押下します。

一般設定で可視性設定の「プライベート」を選択します。
リポジトリ名は任意で指定します。

「リポジトリを作成」を押下します。

作成されたことを確認します。

後ほど使用するため、URIを保存します。

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

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

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

Lambda関数スクリプト作成

lambda-test
│
└─api
        script.py

Pythonを使用してLambda関数に使用するスクリプトを作成します。
実行されると、{"A": "hoge", "B": "fuga", "C": "piyo"}のようにJSON形式のデータを返すようにしています。
各項目は、プロキシ統合のための Lambda 関数の出力形式に沿って設定しています。

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依存パッケージ管理

lambda-test
│
└─api
        requirements.txt

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

requirements.txt

コンテナイメージの設計

lambda-test
│
└─api
        Dockerfile

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

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"]

環境変数を定義

lambda-test
│  .env

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

.env
IMAGE_TAG=<ECRリポジトリのイメージURI>

コンテナイメージをECRリポジトリにプッシュ

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

リポジトリ名を押下してください。

「プッシュコマンドを表示」を押下します。

コマンドの1.をコピーします。

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

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

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

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

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

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

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

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

マネジメントコンソールに戻り、ECRリポジトリにコンテナイメージがあることを確認します。

Lambdaを作成

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

マネジメントコンソールで、「lambda」と検索して、サービスの「Lambda」を押下します。

左のナビゲーションペインから「関数」を選択して、右上の「関数の作成」を押下します。

オプションで「コンテナイメージ」を選択します。
関数名に任意の値を指定します。

「イメージを参照」を押下します。

対象のECRリポジトリからイメージタグがlatestのイメージを選択します。
「イメージを選択」を押下します。

「関数の作成」を押下してLambdaを作成します。

以下の画面に遷移すれば作成完了です。

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

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

Lambdaの詳細画面の「トリガーを追加」を押下します。

トリガーの設定で「API Gateway」を選択します。

新規でAPIを作成するので、「APIを作成する」を押下します。

「REST API」を選択して、セキュリティは「APIキー」を選択します。
「作成」を押下します。

トリガーに「API Gateway」が追加されます。

動作確認

APIキーの確認

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

Lambdaの詳細画面の「設定」タブを押下します。

「トリガー」を選択して、API Gatewayのリンクを押下します。

API Gatewayの詳細画面で、左のAPIキーを押下します。

<Lambda名>-Keyを押下します。

APIキーの「表示」を押下すると、APIキーが確認できますので保管します。

APIのUrlを確認

APIのURLを確認します。
「ステージ」を選択します。

「default」を選択して、URLを確認します。

curlでAPIの動作確認

APIキーを設定した場合は、ヘッダーのx-api-keyに指定する必要があります。

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

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

APIの動作確認
$ curl -H 'x-api-key:<API_KEY>' <API_URL>
実行結果
{"A": "hoge", "B": "fuga", "C": "piyo"}

おわりに

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

参考

API Gateway で Lambda プロキシ統合を設定する