おうちにLambdaが欲しい！

はじめて投稿します、はるかなぎと申します。最近になり本格的にAWSを触り始めたいわばにわか勢です。

##注意！！
SAM CLIで大幅な仕様変更があったようで、最新版を用いた場合、本記事の内容では動作しないようです。
最新版で動かす方法は現在調査中です。

## AWS Lambdaってなあに？

AWS Lambdaは、サーバレス型と呼ばれるサービスのひとつで（利用者が）サーバを用意せずに、プログラムを実行できるサービスです。Faas（Function as a Service）と呼ばれる種類のサービスと説明されることもあります。

詳しい内容は、AWS公式の紹介ページを見た方が早いと思います。

[AWS Lambda（イベント発生時にコードを実行）| AWS](https://aws.amazon.com/jp/lambda/)

## ところで、ローカルで動くの？

この手のものを見ると、自宅に環境が欲しくなりますね。調べたところ、[AWS SAM CLI](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)というローカルでテストを行うツールがあるようです。

## お試し用環境を用意します

早速遊んでみようと、以下のお試し用環境を用意しました。

OS：[Amazon Linux 2（KVM用イメージ）](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/amazon-linux-2-virtual-machine.html)

ここに、SAM CLIとDockerをインストールしておきます。その他必要なツール（Git等）も一緒にインストールしておきます。（インストール方法は割愛します）

## 早速ためしてみる

SAM CLIで作成したプロジェクトは以下のコマンドでローカル実行することが可能です。実行には、Dockerがインストールされている必要があります。（プロジェクトの作成については割愛します）

```bash
$ sam local invoke
Invoking app.lambda_handler (python3.7)
Image was not found.
Building image......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-python3.7:rapid-1.22.0.

Mounting /home/izayoiwind/workspaces/sam-workspaces/sam-py/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: ac411f9d-c404-4ebf-bea9-e9ff1c6aa739 Version: $LATEST
END RequestId: ac411f9d-c404-4ebf-bea9-e9ff1c6aa739
REPORT RequestId: ac411f9d-c404-4ebf-bea9-e9ff1c6aa739  Init Duration: 0.18 ms  Duration: 93.27 ms      Billed Duration: 100 ms Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
```

sam local invokeで実行すると、上記の通り作成したLambda関数が実行されます。まぁここまでは普通の話です。SAM CLIでの実行にDockerが必要と書かれていたので、どのようなコンテナが作られるのか調べようと、意気揚々とコンテナ一覧を見てやろうとしたところ・・・

```bash
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

あれ？何もないぞ？？

試しにイメージ一覧を出してみると、それらしきイメージが存在します。

```bash
$ docker images
REPOSITORY                                     TAG                 IMAGE ID            CREATED             SIZE
amazon/aws-sam-cli-emulation-image-python3.7   rapid-1.22.0        39513e8ac96d        3 minutes ago       908MB
amazon/aws-sam-cli-emulation-image-python3.7   latest              c8f4d7381293        About an hour ago   892MB
```

まさかと思い、sam local invokeを叩いた直後にdocker psを連発してみたところ・・・

```bash
$ docker ps --no-trunc=true
CONTAINER ID                                                       IMAGE                                                       COMMAND                                         CREATED             STATUS                  PORTS                      NAMES
0ab3d3c601864b1b539dddd7ac27c0972b09ceef137ed65c1acf9053e1884cba   amazon/aws-sam-cli-emulation-image-python3.7:rapid-1.22.0   "/var/rapid/aws-lambda-rie --log-level error"   3 seconds ago       Up Less than a second   127.0.0.1:7706->8080/tcp   busy_wiles
```

お、出た出た。どうやら、SAM CLIで実行すると、Lambdaの実行環境を含んだDockerコンテナが作成され、実行後に削除するという動きをするようです。

## イメージを調べてみよう

上記の手順で表示されたイメージは、Docker Hubにあるようです。

[amazon/aws-sam-cli-build-image-python3.7](https://hub.docker.com/r/amazon/aws-sam-cli-build-image-python3.7)

とりあえず作成されているイメージを消してから、dockerコマンドでpullしてみます。

```bash
$ docker pull amazon/aws-sam-cli-emulation-image-python3.7:rapid-1.22.0
Error response from daemon: manifest for amazon/aws-sam-cli-emulation-image-python3.7:rapid-1.22.0 not found: manifest unknown: manifest unknown
```

ん？イメージが存在しない？試しにタグを指定せずにpullします。

```bash
$ docker pull amazon/aws-sam-cli-emulation-image-python3.7
Using default tag: latest
latest: Pulling from amazon/aws-sam-cli-emulation-image-python3.7
df5bcad6d15c: Pull complete 
1c6619c430f8: Pull complete 
6577ccaf68c7: Pull complete 
649bca99560c: Pull complete 
Digest: sha256:5358339925e231e94c2c4e0d917af54cadb312625a30df28cda271c5b844d9c9
Status: Downloaded newer image for amazon/aws-sam-cli-emulation-image-python3.7:latest
docker.io/amazon/aws-sam-cli-emulation-image-python3.7:latest
```

無事にpullできました。

先ほどイメージを表示したときにもlatestのものが表示されていたので、どうやらSAM CLIでの実行時にlatestのイメージから作成しているようです。

ということは、イメージの差分を見れば、どのようなイメージかが分かります。もう一度SAM CLIで実行し、イメージを作成、historyを比較してみましょう。

まずはlatest

```bash
$ docker history amazon/aws-sam-cli-emulation-image-python3.7:latest --no-trunc=true
IMAGE                                                                     CREATED             CREATED BY                                                                                                           SIZE                COMMENT
sha256:c8f4d7381293badf23098d75b891c50a6682a3146ed5c90a889f4685f0f90017   2 hours ago         CMD [ "/bin/bash" ]                                                                                                  0B                  
<missing>                                                                 2 hours ago         ENV LAMBDA_RUNTIME_DIR=/var/runtime                                                                                  0B                  
<missing>                                                                 2 hours ago         ENV LAMBDA_TASK_ROOT=/var/task                                                                                       0B                  
<missing>                                                                 2 hours ago         ENV LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib   0B                  
<missing>                                                                 2 hours ago         ENV PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin                                                        0B                  
<missing>                                                                 2 hours ago         ENV TZ=:/etc/localtime                                                                                               0B                  
<missing>                                                                 2 hours ago         ENV LANG=en_US.UTF-8                                                                                                 0B                  
<missing>                                                                 2 hours ago         WORKDIR /var/task                                                                                                    0B                  
<missing>                                                                 2 hours ago         ADD file:649bca99560c52ca2195dc8410dbee9e1634faa448acfee4696f74db0651773f /                                          66.4MB              
<missing>                                                                 2 hours ago         ADD file:6577ccaf68c7a9a42e7b1a8bbfa93959abc28b47888855eba289c032685c6770 /                                          173MB               
<missing>                                                                 2 hours ago         ADD file:1c6619c430f8d56651115043ea1cd3e6d293434dc774f21cc087e86aebf6e1d4 /                                          424kB               
<missing>                                                                 2 hours ago         ADD file:32665f54d46653b07d35bfa9cdd4baf9b1144d6824516a05218124afc01246f3 /                                          652MB               
<missing>                                                                 2 hours ago         ARCHITECTURE amd64                                                                                                   0B                               0B                
```

次にrapid-1.22.0

```bash
]$ docker history amazon/aws-sam-cli-emulation-image-python3.7:rapid-1.22.0 --no-trunc=true
IMAGE                                                                     CREATED             CREATED BY                                                                                                           SIZE                COMMENT
sha256:b42538372e40f0627b62b40313690f2561f7bba02683c066e4221ea32dd2072b   5 minutes ago       /bin/sh -c chmod +x /var/rapid/aws-lambda-rie                                                                        8.16MB              
sha256:cddd62fd80bd20c647cd6d8cc12ff1bfb822da1706e3e7eb9c44c705c559a55f   5 minutes ago       /bin/sh -c #(nop) ADD dir:bf074fbf6e9076431dc41d00943644a0df7bd2853140615db6ae0130fd5c91f8 in /var/rapid             8.16MB              
sha256:c8f4d7381293badf23098d75b891c50a6682a3146ed5c90a889f4685f0f90017   2 hours ago         CMD [ "/bin/bash" ]                                                                                                  0B                  
<missing>                                                                 2 hours ago         ENV LAMBDA_RUNTIME_DIR=/var/runtime                                                                                  0B                  
<missing>                                                                 2 hours ago         ENV LAMBDA_TASK_ROOT=/var/task                                                                                       0B                  
<missing>                                                                 2 hours ago         ENV LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib   0B                  
<missing>                                                                 2 hours ago         ENV PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin                                                        0B                  
<missing>                                                                 2 hours ago         ENV TZ=:/etc/localtime                                                                                               0B                  
<missing>                                                                 2 hours ago         ENV LANG=en_US.UTF-8                                                                                                 0B                  
<missing>                                                                 2 hours ago         WORKDIR /var/task                                                                                                    0B                  
<missing>                                                                 2 hours ago         ADD file:649bca99560c52ca2195dc8410dbee9e1634faa448acfee4696f74db0651773f /                                          66.4MB              
<missing>                                                                 2 hours ago         ADD file:6577ccaf68c7a9a42e7b1a8bbfa93959abc28b47888855eba289c032685c6770 /                                          173MB               
<missing>                                                                 2 hours ago         ADD file:1c6619c430f8d56651115043ea1cd3e6d293434dc774f21cc087e86aebf6e1d4 /                                          424kB               
<missing>                                                                 2 hours ago         ADD file:32665f54d46653b07d35bfa9cdd4baf9b1144d6824516a05218124afc01246f3 /                                          652MB               
<missing>                                                                 2 hours ago         ARCHITECTURE amd64                                                                                                   0B  
```

/var/rapid/配下にファイルを追加し、/var/rapid/aws-lambda-rieに実行権限を付与しているという違いが見つかりました。この/var/rapid/aws-lambda-rieの有無がこの2つのイメージの違いのようです。

## aws-lambda-rieってなあに？

ではこのaws-lambda-rieって何者なのか調べたところ、AWS Lambda Runtime Interface Emulatorと呼ばれるもので、名前の通りLambda実行環境のエミュレータのようです。

ここで、先ほどのコンテナ一覧の結果を見てみましょう。COMMANDの箇所を見ると「/var/rapid/aws-lambda-rie --log-level error」とあるので、まさにこいつをコンテナ作成時に実行しています。

## コンテナの中身はなんだろな

では、次にコンテナを調べていきます。

先ほどの情報だけだと足りないので、docker inspectコマンドで中身を見てみます。

しかし、実行時にコンテナが作成され、実行後に削除される関係上、まともにコマンドを叩いていては間に合わないので、簡単なスクリプトを作って取得します。なお、目的のコンテナの情報さえ取れれば良いということで複数コンテナが動いている状況はまったく考慮していませんのでご注意ください。

```bash
#!/bin/bash

CONTAINER_ID=`docker ps -q`
echo ${CONTAINER_ID}

docker inspect ${CONTAINER_ID}
```

コンテナが起動している間にこのスクリプトを実行すると以下のような情報が得られます。

```json
[
    {
        "Id": "85faef4d31c713bf1b246f7d36d1347792d419a9eef797ae1b0a804b95c57829",
        "Created": "2021-04-26T13:20:45.676396302Z",
        "Path": "/var/rapid/aws-lambda-rie",
        "Args": [
            "--log-level",
            "error"
        ],
        "State": {
            "Status": "removing",
            "Running": false,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": true,
            "Pid": 0,
            "ExitCode": 137,
            "Error": "",
            "StartedAt": "2021-04-26T13:20:47.643574462Z",
            "FinishedAt": "2021-04-26T13:20:49.361733871Z"
        },
        "Image": "sha256:b42538372e40f0627b62b40313690f2561f7bba02683c066e4221ea32dd2072b",
        "ResolvConfPath": "/var/lib/docker/containers/85faef4d31c713bf1b246f7d36d1347792d419a9eef797ae1b0a804b95c57829/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/85faef4d31c713bf1b246f7d36d1347792d419a9eef797ae1b0a804b95c57829/hostname",
        "HostsPath": "/var/lib/docker/containers/85faef4d31c713bf1b246f7d36d1347792d419a9eef797ae1b0a804b95c57829/hosts",
        "LogPath": "/var/lib/docker/containers/85faef4d31c713bf1b246f7d36d1347792d419a9eef797ae1b0a804b95c57829/85faef4d31c713bf1b246f7d36d1347792d419a9eef797ae1b0a804b95c57829-json.log",
        "Name": "/elated_archimedes",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": [
                "/home/izayoiwind/workspaces/sam-workspaces/sam-py/.aws-sam/build/HelloWorldFunction:/var/task:ro,delegated"
            ],
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {
                "8080/tcp": [
                    {
                        "HostIp": "127.0.0.1",
                        "HostPort": "5018"
                    }
                ]
            },
            "RestartPolicy": {
                "Name": "",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Capabilities": null,
            "Dns": null,
            "DnsOptions": null,
            "DnsSearch": null,
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "shareable",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 134217728,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": null,
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": null,
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 268435456,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": [
                {
                    "Name": "nofile",
                    "Hard": 4096,
                    "Soft": 1024
                }
            ],
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/af0a80a46c80ec4b4d59ce90e55969ec853bc7727d259898945277665b5dfac8-init/diff:/var/lib/docker/overlay2/3f3546128854b10663811112acc8423f84abcc1f47b3073172b55bdb3768a7af/diff:/var/lib/docker/overlay2/bcfc11b7e7c587d3a01e09c4c843a3525635ec782635bdc3e707cb56d0d5733b/diff:/var/lib/docker/overlay2/a10c8af58d6fa1f634045400e21b7f6dbbca281ba68a0ab9b5cf23ecaae7c9a7/diff:/var/lib/docker/overlay2/2cdd94e4f72dc4af42a1d5f1d25d8716ed0d7330261c5489ce847882c872d577/diff:/var/lib/docker/overlay2/a6630cafd3c07e2e8a198af012f75d3f035fc4e8ad2a69d5a2edc9d0da7c529c/diff:/var/lib/docker/overlay2/6f9c1a2e3d92bfb822c9f028fe7d98f964cd19a89b063f4082639dba4d1563ce/diff",
                "MergedDir": "/var/lib/docker/overlay2/af0a80a46c80ec4b4d59ce90e55969ec853bc7727d259898945277665b5dfac8/merged",
                "UpperDir": "/var/lib/docker/overlay2/af0a80a46c80ec4b4d59ce90e55969ec853bc7727d259898945277665b5dfac8/diff",
                "WorkDir": "/var/lib/docker/overlay2/af0a80a46c80ec4b4d59ce90e55969ec853bc7727d259898945277665b5dfac8/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/izayoiwind/workspaces/sam-workspaces/sam-py/.aws-sam/build/HelloWorldFunction",
                "Destination": "/var/task",
                "Mode": "ro,delegated",
                "RW": false,
                "Propagation": "rprivate"
            }
        ],
        "Config": {
            "Hostname": "85faef4d31c7",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": true,
            "AttachStderr": true,
            "ExposedPorts": {
                "8080/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "AWS_SAM_LOCAL=true",
                "AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128",
                "AWS_LAMBDA_FUNCTION_TIMEOUT=3",
                "AWS_LAMBDA_FUNCTION_HANDLER=app.lambda_handler",
                "AWS_LAMBDA_FUNCTION_NAME=HelloWorldFunction",
                "AWS_LAMBDA_FUNCTION_VERSION=$LATEST",
                "AWS_LAMBDA_LOG_GROUP_NAME=aws/lambda/HelloWorldFunction",
                "AWS_LAMBDA_LOG_STREAM_NAME=$LATEST",
                "AWS_REGION=us-east-1",
                "AWS_DEFAULT_REGION=us-east-1",
                "AWS_ACCESS_KEY_ID=defaultkey",
                "AWS_SECRET_ACCESS_KEY=defaultsecret",
                "AWS_ACCOUNT_ID=123456789012",
                "LANG=en_US.UTF-8",
                "TZ=:/etc/localtime",
                "PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
                "LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib",
                "LAMBDA_TASK_ROOT=/var/task",
                "LAMBDA_RUNTIME_DIR=/var/runtime"
            ],
            "Cmd": [],
            "Image": "amazon/aws-sam-cli-emulation-image-python3.7:rapid-1.22.0",
            "Volumes": {
                "/var/task": {}
            },
            "WorkingDir": "/var/task",
            "Entrypoint": [
                "/var/rapid/aws-lambda-rie",
                "--log-level",
                "error"
            ],
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "1123e52e745924dc1fce7584d2985878104283b8ea348c9cae5c6466c4dfc591",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/1123e52e7459",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "bb6cb306d6052a81de21c664211c4eafa397dca82b8b690a60d8c7e870413a69",
                    "EndpointID": "",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "",
                    "DriverOpts": null
                }
            }
        }
    }
]
```

長ったらしいJSONですが、すべてを見る必要はありません。必要なところだけ見ていきます。

### コマンド

「/var/rapid/aws-lambda-rie --log-level error」であることがここでも確認できます。

```json
"Path": "/var/rapid/aws-lambda-rie",
"Args": [
    "--log-level",
    "error"
]
```

### ポートバインディング

ホストの127.0.0.1:5018にバインドしているようです。ポートはランダムになるみたいなので、実際に動かすときは任意のポートにできると思います。IPが127.0.0.1なので、この設定では外部からアクセスできません。

```json
"PortBindings": {
    "8080/tcp": [
        {
            "HostIp": "127.0.0.1",
            "HostPort": "5018"
        }
    ]
}
```

### メモリ容量

後述の環境変数でも指定されていますが、128MBが割り当てられているようです。

```json
"Memory": 134217728
```

### 環境変数

色々設定されていますが、先ほど確認したコンテナのhistoryの情報からLANGから下はイメージ側で設定されていることがすでに判明していますので、コンテナを作成する際は「AWS」から始まるものを設定すればよさそうです。

```json
"Env": [
    "AWS_SAM_LOCAL=true",
    "AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128",
    "AWS_LAMBDA_FUNCTION_TIMEOUT=3",
    "AWS_LAMBDA_FUNCTION_HANDLER=app.lambda_handler",
    "AWS_LAMBDA_FUNCTION_NAME=HelloWorldFunction",
    "AWS_LAMBDA_FUNCTION_VERSION=$LATEST",
    "AWS_LAMBDA_LOG_GROUP_NAME=aws/lambda/HelloWorldFunction",
    "AWS_LAMBDA_LOG_STREAM_NAME=$LATEST",
    "AWS_REGION=us-east-1",
    "AWS_DEFAULT_REGION=us-east-1",
    "AWS_ACCESS_KEY_ID=defaultkey",
    "AWS_SECRET_ACCESS_KEY=defaultsecret",
    "AWS_ACCOUNT_ID=123456789012",
    "LANG=en_US.UTF-8",
    "TZ=:/etc/localtime",
    "PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
    "LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib",
    "LAMBDA_TASK_ROOT=/var/task",
    "LAMBDA_RUNTIME_DIR=/var/runtime"
]
```

| 環境変数 | 値（例） | 説明 |
| :---: | :---: | :---: |
| AWS_SAM_LOCAL | true | SAM LOCALで実行しているかを示す模様 |
| AWS_LAMBDA_FUNCTION_MEMORY_SIZE | 128 | メモリサイズ（MB） |
| AWS_LAMBDA_FUNCTION_TIMEOUT | 3 | 関数実行タイムアウト時間 |
| AWS_LAMBDA_FUNCTION_HANDLER | app.lambda_handler | ハンドラ（後述） |
| AWS_LAMBDA_FUNCTION_NAME | HelloWorldFunction | Lambda関数名 |
| AWS_LAMBDA_FUNCTION_VERSION | $LATEST | Lambda関数のバージョン |
| AWS_LAMBDA_LOG_GROUP_NAME | aws/lambda/HelloWorldFunction | ロググループ名 |
| AWS_LAMBDA_LOG_STREAM_NAME | $LATEST | ログストリーム名 |
| AWS_REGION | us-east-1 | リージョン |
| AWS_DEFAULT_REGION | us-east-1 | デフォルトリージョン |
| AWS_ACCESS_KEY_ID | defaultkey | IAMアクセスキー |
| AWS_SECRET_ACCESS_KEY | defaultsecret | IAMシークレットキー |
| AWS_ACCOUNT_ID | 123456789012 | AWSアカウントID |

### マウント情報

SourceにSAM CLIでビルドしたLambda関数のディレクトリが読み取り専用で指定されています。つまり、コンテナと作成したコードを紐づける、一番重要な部分になります。

```json
"Mounts": [
    {
        "Type": "bind",
        "Source": "/home/izayoiwind/workspaces/sam-workspaces/sam-py/.aws-sam/build/HelloWorldFunction",
        "Destination": "/var/task",
        "Mode": "ro,delegated",
        "RW": false,
        "Propagation": "rprivate"
    }
],
```

Sourceに指定されているディレクトリを確認すると、以下のような結果が得られます。

```bash
$ ls -l /home/izayoiwind/workspaces/sam-workspaces/sam-py/.aws-sam/build/HelloWorldFunction
合計 16
-rw-rw-r-- 1 izayoiwind izayoiwind    0  4月 17 08:23 __init__.py
-rw-rw-r-- 1 izayoiwind izayoiwind 1151  4月 17 08:23 app.py
drwxrwxr-x 2 izayoiwind izayoiwind   77  4月 17 08:23 certifi
drwxrwxr-x 2 izayoiwind izayoiwind   85  4月 17 08:23 certifi-2020.12.5.dist-info
drwxrwxr-x 4 izayoiwind izayoiwind 4096  4月 17 08:23 chardet
drwxrwxr-x 2 izayoiwind izayoiwind  109  4月 17 08:23 chardet-4.0.0.dist-info
drwxrwxr-x 2 izayoiwind izayoiwind  155  4月 17 08:23 idna
drwxrwxr-x 2 izayoiwind izayoiwind   89  4月 17 08:23 idna-2.10.dist-info
drwxrwxr-x 2 izayoiwind izayoiwind 4096  4月 17 08:23 requests
drwxrwxr-x 2 izayoiwind izayoiwind   85  4月 17 08:23 requests-2.25.1.dist-info
-rw-rw-r-- 1 izayoiwind izayoiwind    8  4月 17 08:23 requirements.txt
drwxrwxr-x 5 izayoiwind izayoiwind  272  4月 17 08:23 urllib3
drwxrwxr-x 2 izayoiwind izayoiwind  133  4月 17 08:23 urllib3-1.26.4.dist-info
```

app.pyの内容は以下の通りです。

```python:app.py
import json

# import requests


def lambda_handler(event, context):
    """Sample pure Lambda function

    Parameters
    ----------
    event: dict, required
        API Gateway Lambda Proxy Input Format

        Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

    context: object, required
        Lambda Context runtime methods and attributes

        Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    ------
    API Gateway Lambda Proxy Output Format: dict

        Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
    """

    # try:
    #     ip = requests.get("http://checkip.amazonaws.com/")
    # except requests.RequestException as e:
    #     # Send some context about this error to Lambda Logs
    #     print(e)

    #     raise e

    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
            # "location": ip.text.replace("\n", "")
        }),
    }
```

### 環境変数のハンドラの指定について

では、ここで環境変数のハンドラの指定を見てみましょう。以下のように指定されています。

```text
app.lambda_handler
```

これを踏まえて、上記のapp.pyのコードを見ると、以下のように指定するということが分かります。

```text
<拡張子を除いたソースファイル>.<関数名>
```

## よろしい、ならば手動で動かすまでだ

ここまで得た情報で、実際にSAM CLIを使わずに動かすことができます。

せっかくなので、SAM CLIで作ったものではなく、独自で作ってみます。以下のコードをi_want_five_quadrillion_yen.pyという名前で保存します。（エラー処理等は特に考えてません、さらに筆者はPythonに関しては初心者レベルですので悪しからず）

```python
# coding: utf-8

import json

def lambda_func(event, context):
    output = '{}兆円欲しい！'.format(event['input'])
    return {
        'output': output,
    }

```

保存先は/home/izayoiwind/i_want_five_quadrillion_yen/としています。

それでは、コンテナを起動します。

```bash
$ sudo docker run -d -m "128M" \
-e AWS_SAM_LOCAL=true \
-e AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128 \
-e AWS_LAMBDA_FUNCTION_TIMEOUT=3 \
-e AWS_LAMBDA_FUNCTION_HANDLER=i_want_five_quadrillion_yen.lambda_func \
-e AWS_LAMBDA_FUNCTION_NAME=IWantFiveQuadrillionYen \
-e AWS_LAMBDA_FUNCTION_VERSION=$LATEST \
-e AWS_LAMBDA_LOG_GROUP_NAME=aws/lambda/IWantFiveQuadrillionYen \
-e AWS_LAMBDA_LOG_STREAM_NAME=$LATEST \
-e AWS_REGION=us-east-1 \
-e AWS_DEFAULT_REGION=us-east-1 \
-e AWS_ACCESS_KEY_ID=defaultkey \
-e AWS_SECRET_ACCESS_KEY=defaultsecret \
-e AWS_ACCOUNT_ID=123456789012 \
-p 8080:8080 \
-v /home/izayoiwind/i_want_five_quadrillion_yen:/var/task:ro,delegated \
amazon/aws-sam-cli-emulation-image-python3.7:rapid-1.22.0 \
/var/rapid/aws-lambda-rie --log-level error
```

起動後、curlで以下のURLを叩くと、Lambda関数が実行されます。Pythonの場合、マルチバイト文字を入れるとUnicodeエスケープシーケンスで返されるようなので、応答結果にちょっと細工をいれています。

```bash
$ curl -XPOST "http://localhost:8080/2015-03-31/functions/function/invocations" -s -d '{"input": "5000"}' | python -c 'from sys import stdin; print stdin.readline().decode("unicode-escape")'
{"output": "5000兆円欲しい！"}
```

## 【おまけ】作成したコンテナの関数をAWS CLIから叩いてみる

ところで、先ほど出てきた以下のURLですが、このURLはAWS CLIでLambda関数を叩くときのURLと同様です。

なので、AWS CLIからも叩くことが可能です。

以下のコマンドで叩くことができます。（関係ないけどAmazon Linux 2イメージに含まれるAWS CLIは1.x系のようです）

```bash
$ aws --endpoint-url http://localhost:8080 lambda invoke --function-name function --payload '{"input":"5000"}' ./test.txt
{
    "StatusCode": 200
}
$ cat ./test.txt | python -c 'from sys import stdin; print stdin.readline().decode("unicode-escape")'
{"output": "5000兆円欲しい！"}
```

AWS CLI 2.x系の場合はpayloadにbase64エンコードした文字列を指定します。AWS CLI 2.x系を入れていた環境がPython 3系だったため、Unicodeエスケープ関連の処理を少し変えています。

```bash
$ echo {\"input\":\"5000\"} | base64
eyJpbnB1dCI6IjUwMDAifQo=
$ aws --endpoint-url http://amzn.izayoiwind.net:8080 lambda invoke --function-name function --payload eyJpbnB1dCI6IjUwMDAifQo= ./test.txt
{
    "StatusCode": 200
}
$ cat test.txt | python -c 'from sys import stdin; print(stdin.readline().encode("utf-8").decode("unicode-escape"));'
{"output": "5000兆円欲しい！"}
```

注意点として、本家AWS LambdaをCLI経由で呼び出した場合、レスポンスにX-Amz-Function-Errorヘッダが含まれるため、以下のようにFunctionErrorが通知されますが、エミュレータでの実行の場合、実行に失敗してもレスポンスにX-Amz-Function-Errorヘッダが含まれないようで、ステータスコード以外の情報は一切通知されません。

```bash
$ aws lambda invoke --function-name i_want_five_quadrillio
n_yen ./test.txt
{
    "FunctionError": "Unhandled",
    "ExecutedVersion": "$LATEST",
    "StatusCode": 200
}
```