lambda custom runtimeでbash動かす+標準にないnkfコマンドを使えるようにする

# はじめに

オンプレで動いているBashのスクリプトをlambdaで動かしたいなと思い、
AWSのチュートリアルを実践プラスアルファで、nkfコマンド使えるようにする。

# 参考にしたチュートリアル

[チュートリアル – カスタムランタイムの公開 \- AWS Lambda](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-walkthrough.html)

```
$ tree
.
├── bootstrap
├── function.sh
└── response.txt
```

こんな感じで始めていきます。

# ロールの作成

## やること

- [Roles](https://console.aws.amazon.com/iam/home#/roles)のページを開く。
- ロールの作成
    - 信頼されたエンティティ - Lambda
    - アクセス許可 - AWSLambdaBasicExecutionRole
    - ロール名 - `lambda-role`

チュートリアルでは、これをさっくり作ってくれとなっていますが、せっかくなのでawscliで作ってみます。

## AWSCLIでの手順

変数の定義

```bash
$ ROLE_NAME=lambda-role
$ echo $ROLE_NAME
lambda-role
```

信頼ポリシーの作成

```bash
$ cat << EOF > policy
> {
>             "Version": "2012-10-17",
>             "Statement": [
>                 {
>                     "Effect": "Allow",
>                     "Principal": {
>                         "Service": "lambda.amazonaws.com"
>                     },
>                     "Action": "sts:AssumeRole"
>                 }
>             ]
>         }
> EOF
```

確認。
失敗していたら`parse error`と出る

```bash
$ cat policy | jq
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

Role作成

```bash
$ aws iam create-role --role-name ${ROLE_NAME} --assume-role-policy-document file://policy
```

結果確認

```bash
$ aws iam list-roles --query  "Roles[?RoleName == '${ROLE_NAME}']"
{
    "Role": {
        "Path": "/",
        "RoleName": "lambda-role",
        "RoleId": "AAAAAAAAAAAAAAAAAAAAAA",
        "Arn": "arn:aws:iam:: 123456789012:role/lambda-role",
        "CreateDate": "2019-12-01-01",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    }
}
```

管理ポリシー`AWSLambdaBasicExecutionRole`をアタッチする

```bash
$ aws iam attach-role-policy --role-name ${ROLE_NAME} --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
```

結果確認

```bash
$  aws iam list-attached-role-policies --role-name lambda-role
{
    "AttachedPolicies": [
        {
            "PolicyName": "AWSLambdaBasicExecutionRole",
            "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        }
    ]
}
```

# bootstarpを作成する

カスタムランタイムの場合、`bootstrap`というファイルが、一番はじめに呼び出される。

```bash:bootstrap
#!/bin/sh -xv

set -euo pipefail

# ここだけ、チュートリアルから編集。tmpフォルダ内にawscliを配置するためHOMEを設定
export HOME="/tmp"

# ハンドラ関数の読み込み
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"

while true
do
HEADERS="$(mktemp)"

# イベントの取得
EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

# ハンドラ関数の実行
RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")

# 結果を返却
curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done
```


# function.sh

bootstrapを起点として、実際に呼び出される関数です。
bootstrapの`RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")`ところから呼び出されます。

```bash:function.sh
function handler () {
EVENT_DATA=$1
echo "$EVENT_DATA" 1>&2;
RESPONSE=$(nkf --help)
echo $RESPONSE
}
```

# アップロード

```bash
# Lambdaレイヤーに、bootstrapを含めてzip化
$ zip runtime.zip bootstrap 
$ aws lambda publish-layer-version --layer-name bash-runtime --zip-file fileb://runtime.zip

# Lambda作成
$ zip function_only.zip function.sh
$ aws lambda create-function --function-name bash-runtime --zip-file fileb://function-only.zip --handler function.handler --runtime provided --role  arn:aws:iam::123456789012:role/lambda-role --layers arn:aws:lambda:ap-northeast-1:123456789012:layer:bash-runtime:1

```


# 怒られます

これをアプロードし、Lambda上のテストで実行すると怒られます。

```bash:nkfコマンド自体が見つからないよって言われて、怒られる
+++ nkf --help
/var/task/function.sh: line 5: nkf: command not found
++ RESPONSE=
+ RESPONSE=
```

# というわけで、nkfコマンドをインストールして、一緒にアップロードします

Lambdaの中身はAmazon Linuxとのことなので、LinuxOSから、yum install。

最初はAL2でやろうと思ったのですが、nkfのリポジトリが無さそうだったので、とりあえずcentOSにしました（やることに対してイメージが大きい気がするけど。。）

```dockerfile
FROM centos:7

WORKDIR /usr/local/src/

# yum-config-manager のインストール
RUN yum -y install yum-utils

# yum repository の追加
RUN yum -y update
RUN yum -y install epel-release
RUN yum -y install http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
RUN yum -y install http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm

# NKF インストール
RUN yum -y install nkf
```

```yml
version: '3'
services:
  lambda-with-c-compiler:
    build: .
    volumes:
      - ./lib:/var/tmp
    command: bash -c "cp /usr/bin/nkf /var/tmp/"
```

やっていることは、`./lib`フォルダとマウントした`/var/tmp`に、yum installしたnkfをコピーしているだけです。

```bash
$ docker-compose build
$ docker-compose up -d
$ ls -l ./lib/
-rwxr-xr-x  1 nakajimaatsushi  staff  273248 Dec 24 01:37 nkf
```

また、Lambdaレイヤーとしてアップロードしたものは、`/opt`の下に配置されるそうなので、
そこにPATH通すように、bootstrapを変更します。

```bash:bootstrapに追記
export PATH="$HOME/.local/bin:/opt/lib:$PATH"
```

そこからアップし直すと、使えました。

```bash

# Lambdaレイヤーに、bootstrapとlibを含めてzip化
$ zip runtime.zip bootstrap lib/*
$ aws lambda publish-layer-version --layer-name bash-runtime --zip-file fileb://runtime.zip

# Lambda-Layerを更新
$ aws lambda update-function-configuration --function-name bash-runtime --layers arn:aws:lambda:ap-northeast-1:123456789012:layer:bash-runtime:2

# Lambdaコード更新（libだけ追加なら正直やらなくていいはず）
$ zip function_only.zip function.sh
$ aws lambda update-function-code --function-name bash-runtime --zip-file fileb://function-only.zip

```

~~正直使い所はなさそう~~

# 参考にしたもの

[チュートリアル – カスタムランタイムの公開 \- AWS Lambda](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-walkthrough.html)
[カスタム AWS Lambda ランタイム \- AWS Lambda](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-custom.html)
[AWS CLI Command Reference — AWS CLI 1\.16\.309 Command Reference](https://docs.aws.amazon.com/cli/latest/index.html)