Lambda関数でEmbulkコマンドを実行してみる(カスタムランタイム使用)

# はじめに
この記事は [BeeX Advent Calendar 2020](https://qiita.com/advent-calendar/2020/beex) の12/20の記事です。

==
12/16にLambdaでEmbulkコマンドを実行する記事を書きました。

[Lambda関数でEmbulkコマンドを実行してみる](https://qiita.com/handy-dd18/items/c7d3e02b69ca5ebbbe83)

この時はpythonのベースイメージを使用して実行しましたが、よくよく考えるとBashで実行できるのであればそっちのほうが余計な実装が不要なので、同じことをLambdaのカスタムランタイムを使用してやってみました。

以下を参考にさせていただいています。
Embulk以外の詳細はこちらをご確認ください。
* [AWS Lambdaのコンテナイメージ上のkubectlコマンド実行してEKSを管理してみる](https://yomon.hatenablog.com/entry/2020/12/lambda_container_kubectl)

# 環境
PC：Windows 10
Docker：Docker version 19.03.13, build 4484c46d9d

# 前提
Dockerの初期設定は完了済み
AWSアカウントは作成済み
Amazon ECRリポジトリは作成済み

# 実際の操作
## bootstrap.shの作成
handler.shを呼び出すbootstrap.shを作成します。

```bootstrap.sh
#!/bin/sh

export _HANDLER=/aws/handler.sh
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
  exec /usr/local/bin/aws-lambda-rie $_HANDLER
else
  exec $_HANDLER
fi
```

# handler.shの作成
実際の処理を行うhandler.shを作成します。
handler関数にはembulkコマンドを記述します。

```handler.sh
#!/bin/sh

handler(){
  echo "start function"
  echo "run emulk"
  /usr/bin/embulk help
  echo "end function"
}

while true
do
  HEADERS="$(mktemp)"
  # Get an event. The HTTP request will block until one is received
  EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")

  # Extract request ID by scraping response headers received above
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

  # Run the handler
  handler ${EVENT_DATA}

  # Send the response
  curl -s -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response"  -d "OK"
done
```

## Dockerfileの作成
イメージを作成するためのDockerfileを作成します。
correttoをインストールしているのはEmbulkコマンドにjavaが必要なためです。

```Dockerfile
FROM amazon/aws-cli

RUN curl -L -o /usr/local/bin/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie
RUN chmod 755 /usr/local/bin/aws-lambda-rie

RUN amazon-linux-extras enable corretto8
RUN yum install java-1.8.0-amazon-corretto-devel -y
COPY embulk /usr/bin/embulk
RUN chmod +x /usr/bin/embulk

ADD ./bootstrap.sh /aws/bootstrap
ADD ./handler.sh /aws/handler.sh
RUN chmod +x /aws/handler.sh
RUN chmod +x /aws/bootstrap
ENTRYPOINT ["/aws/bootstrap"]
```

## イメージをビルドする
特定のフォルダに作成した以下のファイルを格納します。

```
$ ls
Dockerfile  bootstrap.sh  embulk  handler.sh
```

ビルドコマンドを実行し、イメージを作成します。

```
$ docker build -t lambda_embulk_shell .
~build実行中~
$ docker images
REPOSITORY           TAG       IMAGE ID       CREATED            SIZE
lambda_embulk_shell latest    8b25a817b50c   15 seconds ago       428MB
```

## テスト
ローカルで実行できるか以下のコマンドでテストをしてみます。
※curlコマンドは別ターミナル

```
$ docker run -p 9000:8080 lambda_shell:latest
$ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d'{}'
```

問題なさそうです。

```
$ docker run -p 9000:8080 lambda_embulk_shell:latest
time="2020-12-20T03:32:24.907" level=info msg="exec '/aws/handler.sh' (cwd=/aws, handler=)"
time="2020-12-20T03:32:29.614" level=info msg="extensionsDisabledByLayer(/opt/disable-extensions-jwigqn8j) -> stat /opt/disable-extensions-jwigqn8j: no such file or directory"
time="2020-12-20T03:32:29.614" level=warning msg="Cannot list external agents" error="open /opt/extensions: no such file or directory"
START RequestId: da60f400-e93c-4341-accc-52c680834281 Version: $LATEST
start function
run emulk
Embulk v0.9.23
Usage: embulk [-vm-options] <command> [--options]
Commands:
   mkbundle   <directory>                             # create a new plugin bundle environment.
   bundle     [directory]                             # update a plugin bundle environment.
   run        <config.yml>                            # run a bulk load transaction.
   cleanup    <config.yml>                            # cleanup resume state.
   preview    <config.yml>                            # dry-run the bulk load without output and show preview.
   guess      <partial-config.yml> -o <output.yml>    # guess missing parameters to create a complete configuration file.
   gem        <install | list | help>                 # install a plugin or show installed plugins.
   new        <category> <name>                       # generates new plugin template
   migrate    <path>                                  # modify plugin code to use the latest Embulk plugin API
   example    [path]                                  # creates an example config file and csv file to try embulk.
   selfupdate [version]                               # upgrades embulk to the latest released version or to the specified version.
```

## ECRリポジトリへのPush
リポジトリを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/0044dae2-a3f4-904d-00a9-54aa233084ac.png)

以下のコマンドでECRにPushします。

```
$ docker tag lambda_embulk_shell:latest XXXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-embulk-shell
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin XXXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com
$ docker push XXXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-embulk-shell:latest
```

ECRにイメージがPushされました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/d064a36f-0e88-3375-05bd-5b47d36255a6.png)

## Lambda作成・実行
Lambdaは[前回](https://qiita.com/handy-dd18/items/c7d3e02b69ca5ebbbe83)使用したものを流用します。

やることはLambdaで指定しているイメージを変更するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/ffda689d-f626-e782-9dc4-7cb2b00e9583.png)


少し時間がかかりましたが、正常に実行できているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/54f281b9-4f8d-fd74-aaa5-f4b057e12cb7.png)

# おわりに
普段Lambdaではpythonを使用するので前回は使ってましたが、コマンドを実行したいだけならpythonである必要性もないので、カスタムランタイムを使用してみました。

実は当初はpythonで実装しようと進めていたのですが、pythonのsubprocessだとデバッグがしにくく、それならshellで実行できたほうが楽だなと思って途中で内容を修正した、という背景もあったりします。

この流れで次回はEmbulkのサンプルファイルを使用した実際の処理の実行を検証してみたいと思っています。