【AWS Lambda】独自のコンテナイメージを超シンプルに使用する方法

## 概要
AWS LambdaでContainer Image Supportが発表されました。
[AWS Lambda の新機能 – コンテナイメージのサポート](https://aws.amazon.com/jp/blogs/news/new-for-aws-lambda-container-image-support/)

しかし、**『AWSに用意してあるコンテナイメージではなく、独自コンテナを使いたい』**時の方法が、
AWS公式ドキュメントだと癖があって少しわかりづらい内容です。


なので、**独自コンテナイメージを使用したlambda**を**シンプル**にデプロイ・実行する方法
について書いていきます。

## 対象読者

- Dockerやコンテナの知識がある。
- AWSのLambdaのざっくりとした知識がある。
- aws-cliをインストール・セットアップしている。

## 重要なポイント
以下のポイントさえ押さえればOKです！

```sh
1. ２つのライブラリを'Dockerfile内'でインストールする。
    - 'awslambdaric'
    - 'runtime interface emulator'
2. 上記２つ用いたスクリプト'entry.sh'を、Dokcerのentrypointにする。
```

詳しくはこの後の記事を呼んでください。

## ハンズオン
以下の流れで行います。
1. ファイルの準備
2. Image作成・ローカルでの実行
3. Imageのデプロイ
4. Lambdaの設定・実行

今回のディレクトリ構成です。
応用したいときは、`Dockerfile`や`app.py`を改造してください。

```sh
# ディレクトリ構成
├── Dockerfile # 依存ライブラリのインストールをする。
├── entry.sh #  entrypointに指定するファイル
└── app
    └── app.py # 実行したいpythonスクリプト。
```

### (1) ファイルの準備

#### Dockerfile
 
```Dockerfile:Dockerfile

# 今回は、buster(LinuxディストリビューションのOS)を使用。
FROM python:3.9-buster

# runtime interface consoleのインストール
RUN pip install awslambdaric

# localで実行するために、runtime interface emulatorのinstall
ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/bin/aws-lambda-rie
RUN chmod 755 /usr/bin/aws-lambda-rie
COPY entry.sh "/entry.sh"
RUN chmod 755 /entry.sh

# 実行ファイルをコンテナ内に配置。
ARG APP_DIR="/home/app/"
WORKDIR ${APP_DIR}
COPY app ${APP_DIR}

ENTRYPOINT [ "/bin/bash", "/entry.sh" ]
CMD [ "app.handler" ]
```

#### entry.sh

ローカルであるか、AWS Lambda上のコンテナであるかを判断して、
`aws-lambda-rie`や`awslambdaric`を使用します。
[`公式そのままのスクリプト`](https://aws.amazon.com/jp/blogs/aws/new-for-aws-lambda-container-image-support/#:~:text=I%20create%20the,Runtime%20Interface%20Emulator.)です。

```sh:app/entry.sh
# localの場合
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
    exec /usr/bin/aws-lambda-rie /usr/local/bin/python -m awslambdaric $1
# AWS Lambda上の場合
else
    exec /usr/local/bin/python -m awslambdaric $1
fi
```

> ※**注意1**
> `/usr/local/bin/python`とか`/entry.sh`にしてるのは、lambdaの仕様で、コマンドは絶対バスで書く必要あるらしいです。
> 絶対パスでない場合、ローカルでは動きますが、Lambdaで実行すると以下のようなエラーが生じます。
> `IMAGE	Launch error: Couldn't find valid bootstrap(s): [python]	Entrypoint: []`

　
> ※**注意2**
> pythonのpathである、`/usr/local/bin/python`は、
使用するDockerImageやpythonのインストールの方法で変わります。
> Container内で`which python`をすることで、
pythonまでの絶対パスを調べてください。

#### app/app.py

実行したいソースコードです。

```python:app/app.py
def handler(event, context):
    return "Hello world!!"
```

### (2) イメージの作成・ローカルでの実行

まずはイメージを作成します。

```shell
# イメージの作成
docker build -t container_lambda .

# localでコンテナを立ち上げる
docker run -it --rm -p 9000:8080 container_lambda
> NFO[0000] exec '/usr/local/bin/python' (cwd=/home/app, handler=app.handler) 

# localのコンテナにリクエストを投げる
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
> "Hello world!!"% 
```

すると`Hello World!!`が返ってきました！
ローカルでの実行成功です。
実行するファイルや関数を変えたいときは、CMDで指定するhandlerを変更しましょう。

### (3) イメージのデプロイ

実際にLambdaで動かすために、AWSのElastic Container Registory(ECR)にイメージを登録します。
まずは、ECRの画面にうつりましょう。

`リポジトリ作成`を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182163/4b004740-5053-5b5b-bea3-bcdaef1d20cc.png)

今回は、`container_lambda`という名前でリポジトリを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182163/0d31c557-e586-c136-5bf8-92c8dfa1b1ef.png)
名前を決めたら、一番下の`リポジトリ作成`を押してください

次に、このリポジトリにlocalのimageのpushします。

```shell
# imageの名前を指定。
IMAGENAME=container_lambda

# ECRのURLを指定。
REGISTRYURL=xxxxxxxxx.ecr.ap-northeast-1.amazonaws.com

# AWS ECR　にログイン。
aws ecr get-login-password | docker login --username AWS --password-stdin $REGISTRYURL

# imageの作成、およびAWS ECRへのデプロイ。
docker build -t ${IMAGENAME} .
docker tag ${IMAGENAME} 386617633989.dkr.ecr.ap-northeast-1.amazonaws.com/${IMAGENAME}
docker push 386617633989.dkr.ecr.ap-northeast-1.amazonaws.com/${IMAGENAME}
```
これでimageをデプロイすることができました。

### (4) Lambdaの設定・実行

最後にLambdaで実行しましょう！
まずはAWSコンソールから、Lambdaの画面に行き、関数の作成を押します。

1. `コンテナイメージ`を選択
2. 関数名をつける(今回は`container_lambda`)
3. 先ほどデプロイしたDocker Imageを指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182163/1c1321a4-cfaa-a0be-e594-95869c9a7584.png)
そして`関数の作成`を押すと、画面が遷移します。
しばらくすると上の帯が緑になって、lambdaのセットアップが終わります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182163/eb318e78-9e48-e401-9579-a14bb74053d5.png)

右上のテストを定義し、実行してみると...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182163/21529daa-9f92-f351-3ae5-aa8e32e705be.png)
`Hello world!!`が返ってきました！

## まとめ

ということで、オリジナルランタイムでの実行をまとめてみました。

公式ドキュメントではわざとalpine系を使ったり、Dockerのmulti stage buiidしたりしてややこしそうですが、実際はもっと簡単にできることが分かったと思います。

Lambdaは、最大コア数6、メモリ10GBまでの対応も発表されり激アツです。簡単な機械学習APIもより作りやすくなりました。
[AWS Lambda now supports up to 10 GB of memory and 6 vCPU cores for Lambda Functions](https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-lambda-supports-10gb-memory-6-vcpu-cores-lambda-functions/#:~:text=AWS%20Lambda%20customers%20can%20now,memory%20intensive%20operations%20at%20scale.)

非常に便利なので是非みなさんトライしてみてください！

次は、Detectron2という画像認識モデルをAWS Lambdaで動かしてみたいと思います！