1
2

More than 1 year has passed since last update.

AWS Lambda関数のローカル動作確認をDocker内Dockerで実行する

Last updated at Posted at 2021-09-04

はじめに

やりたいことはタイトルの通りですが、以下の通りです。

  • ローカルでLambda関数の動作確認ができる
  • VS Code Remote Containerで開発環境の構築を完結させる(ローカル環境を汚さない)
  • もし可能ならデプロイも簡単に

なお、この要件マッチするツールとして、AWS Cloud9が一番手軽だと思われます。
VS Codeにこだわりがなく、Cloud9の料金に問題がなければCloud 9をお勧めします。

環境

  • 開発端末:macOS
  • 開発IDE:VS CodeのRemote Containers

前提

  • AWS IAMアカウント作成済み
  • Docker, Git, VS Code (Remote Container)は開発端末にインストール済み

補足

Lambdaのローカル実行には、AWS SAM CLIが提供されていますが、こちらのローカル実行にはコンテナ環境が使われます。
つまりコンテナからコンテナを呼べないといけません。以下の構成を目指します。

コンテナ 概要 インストール
開発端末:macOS  Docker, Git, VS Code (Remote Container) 
コンテナ① 開発端末:macOSから起動するコンテナ  Docker, AWS SAM CLI 
コンテナ② コンテナ①から起動するコンテナ  Pythonなど利用するLambda関数に応じたもの

設定していく

なお、GUIベースで操作していますが、最終的に設定ファイルへ落とし込みますので、経緯が不要な場合は、こちらからどうぞ
取り合えず動くものを見たいため手元にあったdocker-compose.ymlを暫定利用します。
OSイメージがAWS CLI対応のものであればなんでもいいと思います。
privileged: trueがないとdocker内でdockerが動きません。

docker-compose.yml
version: "3"
services:
  my_lambda:
    image: rust:latest
    privileged: true # docker内でdockerを動かすために必要

docker-compose.ymlを指定して、VS CodeのRemote Containersを使いコンテナ起動。

AWS Toolkitをインストール

スクリーンショット 2021-09-03 16.42.20.png
VS Codeの左バーにAWSのマークが表示されれば成功です。
ただし、インストールの際押下したボタンの通りですが、コンテナの方にインストールされており、コンテナをリビルドするとこのインストールはなかったことになりますので、設定ファイルへこの拡張機能を追加します。
スクリーンショット 2021-09-03 17.46.21.png
追加されました。
スクリーンショット 2021-09-03 17.48.14.png

AWS認証情報の設定

次に認証情報の設定。以下を入力します。

  • 1/3 初期プロファイルの名前:任意
  • 2/3 アクセスキー:IAMのアクセスキー
  • 3/3 シークレットキー:IAMのシークレットキー スクリーンショット 2021-09-03 16.51.59.png コンテナ内で認証情報ファイルが作成されました。
$ ls -la ~/.aws/
total 12
drwxr-xr-x 2 root root 4096 Sep  3 07:56 .
drwx------ 1 root root 4096 Sep  3 07:56 ..
-rw------- 1 root root 1244 Sep  3 07:56 credentials
-rw------- 1 root root 1244 Sep  3 07:56 config

ただ、こちらもマウント対象のディレクトリではないため、コンテナをリビルドすると消えてしまいますので対処します。
プロジェクトディレクトリ配下にキー情報をおくことでマウント対象のファイルにした後で、コンテナ起動時に~/.aws/へコピーするやり方にしました。

そのために、まずは~/.aws/からプロジェクトディレクトリはいかにキーをコピーしておきます。
順番が逆になりややこしいですが、今回はコンテナの~/.aws/に直接作ってしまったため、次回以降のためマウント配下に保持しておく準備をしています。

作成された認証情報をコピーしてマウント対象のディレクトリへ移動
$ cp -rf ~/.aws .
$ ls -la
total 16
drwxr-xr-x  9 root root  288 Sep  3 08:53 .
drwxr-xr-x  3 root root 4096 Sep  3 06:50 ..
drwxr-xr-x  3 root root   96 Sep  3 08:53 .aws
drwxr-xr-x  3 root root   96 Sep  3 06:50 .devcontainer
drwxr-xr-x 12 root root  384 Sep  3 06:32 .git
-rw-r--r--  1 root root  320 Sep  3 06:32 .gitignore
-rw-r--r--  1 root root  767 Sep  3 06:50 Dockerfile
-rw-r--r--  1 root root   15 Sep  3 06:32 README.md
drwxr-xr-x  6 root root  192 Sep  3 07:00 stock_data
devcontainer.jsonへ1行追記
"postCreateCommand": "cp -rf /workspace/.aws ~/",

なお、ローカルで保持している、キー情報を直にマウントするのが一般的かと思います。
ただし、マウントはコンテナ側の問題があった時、ローカルのマスタファイルへ反映してしまう可能性があります。
基本的に操作しないファイルですので問題ないと思いますが、VS CodeのRemote Containerの挙動はやや不安定で、以前クラッシュしてローカルのファイル含めて消えてしまったことがあるので私はマスタファイルからのマウントは行いません。

私の場合、このままだとGitHubへawsのキー情報がアップされてしまうのでgitの管理対象から外します。

.gitignoreへ1行追記
# Confidentials
credentials
config

リージョンを東京へ変更
スクリーンショット 2021-09-03 17.00.37.png
つながりました。試しにLambdaを選択してみると過去にアップした関数が見えています。
スクリーンショット 2021-09-03 17.03.14.png

AWS SAM CLIのインストール

早速開発をしようと、Lambdaを右クリックし、「Create Lambda SAM Application」を選択したところSAM CLIがないとのことでリンク先でインストール手順を確認します。
スクリーンショット 2021-09-03 17.06.58.png

Linuxはzipファイルを使うようですが、「Docker および AWS CLI が必要です。インストール手順をご確認ください」とあるため、インストール手順を確認します。
参考:

インストール手順は以下のとおりですが、1-2は済んでいるため、3(コンテナ①の中にコンテナ②を作るためにコンテナ①でDockerのインストールが必要)、4だけで良さそうです。

  1. Create an AWS account.
  2. Configure AWS Identity and Access Management (IAM) permissions and AWS credentials.
  3. Install Docker. Note: Docker is a prerequisite only for testing your application locally or using the --use-container option.
  4. Install the AWS SAM CLI.
3. Install Docker.

長いので実行したコマンドを列挙します。
基本的には、LinuxにDockerをインストールする手順通りに実施しています。
参考:

コンテナ①にて実行
$ apt-get update
$ apt-get install apt-transport-https ca-certificates curl gnupg lsb-release

$ curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

$ echo  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

$ apt-get update
$ apt-get install docker-ce docker-ce-cli containerd.io

$ apt-cache madison docker-ce
 docker-ce | 5:20.10.8~3-0~debian-bullseye | https://download.docker.com/linux/debian bullseye/stable amd64 Packages
 docker-ce | 5:20.10.7~3-0~debian-bullseye | https://download.docker.com/linux/debian bullseye/stable amd64 Packages
 docker-ce | 5:20.10.6~3-0~debian-bullseye | https://download.docker.com/linux/debian bullseye/stable amd64 Packages
// 上のコマンドで表示されたバージョンを選んで指定
$ apt-get install docker-ce=5:20.10.8~3-0~debian-bullseye docker-ce-cli=5:20.10.8~3-0~debian-bullseye containerd.io

$ service docker start
$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
4. Install the AWS SAM CLI.
コンテナ①にて実行
// DL
$ wget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip

// 解凍
$ unzip aws-sam-cli-linux-x86_64.zip -d sam-installation

// インストール(既にルートユーザだったのでsudoなしで実行)
$ ./sam-installation/install
You can now run: /usr/local/bin/sam --version

ここで問題が出ました。
3, 4共にリビルド時にも再実行されるようDocker設定ファイルに記載する必要がありますが、devcontainer.jsonpostCreateCommandは1行しか書けず可読性が良くない。
スクリーンショット 2021-09-03 19.15.59.png
docker-compose.ymlに書いたらいいのですが、開発環境で使う設定をdevcontainer.jsonにまとめておきたかったのでこちらを参考にして書き直してみます。

また、zipファイルと解凍をマウントディレクトリに置いておくには大きすぎる。zipは消せば良いですが「sam-installation」が大きいことには変わりないので設定ファイルでは、マウント対象外のディレクトリで作業するようにします。

これらの方針をもとに構成変更と設定を行なっていきます。
まずは、構成変更。

変更前構成
/workspace
 ├──.devcontainer
 │ └──devcontainer.json
 ├──aws-sam-cli-linux-x86_64.zip
 └──sam-installation
変更後構成
/workspace
 ├──.devcontainer
 │ ├──devcontainer.json
 │ └──postCreateCommand.sh // 新規作成
/tmp
 ├──aws-sam-cli-linux-x86_64.zip // コンテナ作成時DLするようにする
 └──sam-installation // コンテナ上記zipを解凍して作成するようにする

では、この構成にするために設定ファイルを変えていきます。

.devcontainer/devcontainer.json
// "postCreateCommand": "cp -rf /workspace/.aws ~/",
"postCreateCommand": "chmod 755 .devcontainer/postCreateCommand.sh && ./.devcontainer/postCreateCommand.sh",
"postStartCommand": "service docker start",

最終行はコンテナ②のプロセススタートですが、コンテナ①停止でコンテナ②が止まってしまうのでpostCreateCommandに書いてあると、手動でスタートしないといけない場面があったため、以下を参考にしてpostStartCommandに記載しました。:

.devcontainer/postCreateCommand.sh
#!/bin/bash
echo "Installing Developer Requirements"

# AWS KEY
cp -rf /workspace/.aws ~/

# Docker Install
apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io
#apt-cache madison docker-ce
apt-get install -y docker-ce=5:20.10.8~3-0~debian-bullseye docker-ce-cli=5:20.10.8~3-0~debian-bullseye containerd.io

# AWS SAM CLI Install
wget  -P /tmp https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
unzip /tmp/aws-sam-cli-linux-x86_64.zip -d /tmp/sam-installation
/tmp/sam-installation/install
rm -rf /tmp/aws-sam-cli-linux-x86_64.zip

ここで、いったんDLしたものを削除して、リビルドします。

$ rm -rf aws-sam-cli-linux-x86_64.zip
$ rm -rf sam-installation

リビルドは、VS Codeの右下の緑をクリックして[Rebuild Container]を選択。
再度立ち上がりましたので、Lambdaで「Create Lambda SAM Application」が選択できることを確認。
スクリーンショット 2021-09-03 20.11.03.png

Hello World

そのまま選択肢から選んでpythonのhelloworldアプリケーションを作成してみました。
sam initコマンドで進めた方がわかりやすいかもしれません。

少し経つとアプリケーションフォルダが作成されます。
スクリーンショット 2021-09-04 12.10.25.png
チュートリアルに従って、動作確認できるかみていきます。

Lambda 関数を直接呼び出す

$ cd lambda-python3.9
$ sam build
$ sam local invoke
(略)
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}

API をローカルでホストする

$ cd lambda-python3.9
$ sam build
$ sam local start-api
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2021-09-04 02:23:40  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

// 別のターミナルを立ち上げて実行(レスポンスが表示されるまで時間かかります)
$ curl http://127.0.0.1:3000/hello
{"message": "hello world"}

ブラウザからも表示を確認できました。
スクリーンショット 2021-09-04 12.31.18.png

デプロイ

※S3バケットが必要になりますので、ない方は作成してください。
※(イメージのデプロイの場合)ECRのリポジトリが必要になりますので、ない方は作成してください。
スクリーンショット 2021-09-04 12.38.39.png

1/5 lambda-python3.9/template.yamlを選択
2/5 既存のS3バケットからデプロイイメージをアップするS3を選択
3/5 既存のECRのリポジトリからデプロイイメージをプッシュするECRを選択
4/5 なし
5/5 Stack名を入力します。新規デプロイならここで命名となります。 例)HelloWorld-Python

デプロイエラーとその解決にIAMに必要だった権限

ここから権限エラーがたくさん出ました。
権限はAWSコンソールから利用しているIAMユーザに権限をアタッチするやり方で追加していきました。
ポリシーを直接編集した方が早い気がします。

AWSLambda_FullAccess

本開発をするにあたりLambda操作用に作成したIAMユーザに追加した権限

AmazonS3FullAccess

デプロイ時にS3バケットを選択する必要があったため

AmazonEC2ContainerRegistryFullAccess

デプロイ時の以下エラー解消のため

User: arn:aws:iam::9999999999:user/{my_user} is not authorized to perform: ecr:DescribeRepositories on resource: {my-repo}
AWSCloudFormationFullAccess

デプロイ時の以下エラー解消のため

Error: Failed to create changeset for the stack: HelloWorld-Python, An error occurred (AccessDenied) when calling the CreateChangeSet operation: User: arn:aws:iam::9999999999:user/{my_user} is not authorized to perform: cloudformation:CreateChangeSet on resource: arn:aws:cloudformation:{my-repo}:stack/HelloWorld-Python/*
IAMFullAccess

デプロイ時、CloudFormationで以下が発生

API: iam:CreateRole User: arn:aws:iam::9999999999:user/{my_user} is not authorized to perform: iam:CreateRole on resource: role arn:aws:iam::9999999999:role/HelloWorld-Python-HelloWorld-PythonFunctionRole-1XBRI8W1QTJFN

SAMのログからは詳細不明
Error: Failed to create/update the stack: HelloWorld-Python, Waiter StackCreateComplete failed: Waiter encountered a terminal failure state: For expression "Stacks[].StackStatus" we matched expected path: "ROLLBACK_COMPLETE" at least once

スクリーンショット 2021-09-04 19.26.11.png

AmazonAPIGatewayAdministrator

デプロイ時、CloudFormationで以下が発生

User: arn:aws:iam::9999999999:user/{my_user} is not authorized to perform: apigateway:POST on resource: arn:aws:apigateway:{my-repo}::/restapis (Service: AmazonApiGateway; Status Code: 403; Error Code: AccessDeniedException; Request ID: 02e82b5c-3703-49d0-bb6a-7ffc84a2420e; Proxy: null)

(IAMFullAccessと同様のエラーの出方のためログは略)

--

開発とデプロイで分けたり、そもそもユーザではなくグループにアタッチしたいですね。
もっというと既存ポリシーは不要な権限(本来許容したくない権限もセットで含まれている)が多いため、必要なポリシーを手打ちして作成すべきかと思います。
今回は、とりあえずhelloworldを動かすためと思いコンソールのボタン押下で済む進め方をしました、ものすごく時間がかかってしまいました。

デプロイ成功

ラムダ関数が追加されました。
スクリーンショット 2021-09-04 20.36.02.png
ログに表示されたURLにアクセスして、無事イベント発行と関数実行ができていることを確認しました。
スクリーンショット 2021-09-04 20.44.49.png

デプロイの情報をsamconfig.tomlとしてルートに置くと次回以降のデプロイが簡略化するようなので、ログに表示されていた内容をもとに作成します。

成果物

色々とごちゃごちゃしたので最終的な構成と設定を整理します。

変更後構成
/workspace
 ├──.aws
 │   ├──config
 │   └──credentials
 ├──.devcontainer // VS Code用設定ファイル
 │   ├──devcontainer.json
 │   ├──docker-compose.yml
 │   └──postCreateCommand.sh // コンテナ①の各種インストールコマンド
 ├──lambda-python3.9 // sam initで作成したアプリ(コンテナ②のDockerfileはここ)
 │   └──samconfig.toml
 └──docker-compose.yml // コンテナ①
/root
 └──.aws // コンテナ①のsamのインストーラ(コンテナ起動時に/workspace/.awsをコピーして作成)
/tmp
 └──sam-installation // コンテナ①のsamのインストーラ(コンテナ起動時にDL)
.aws/credentials
[default]
aws_access_key_id = xxxxxxxxxxx
aws_secret_access_key = xxxxxxxxxxx
.aws/config
[default]
region=ap-northeast-1
output=json
.devcontainer/devcontainer.json
{
    "name": "Existing Docker Compose (Extend)",
    "dockerComposeFile": [
        "../docker-compose.yml",
        "docker-compose.yml"
    ],
    "service": "my_lambda",
    "workspaceFolder": "/workspace",
    "settings": {},
    "extensions": [
        "amazonwebservices.aws-toolkit-vscode"
    ], 
    "postCreateCommand": "chmod 755 .devcontainer/postCreateCommand.sh && ./.devcontainer/postCreateCommand.sh",
    "postStartCommand": "service docker start",
}
.devcontainer/docker-compose.yml
version: '3'
services:
  my_lambda:
    volumes:
      - .:/workspace:cached
      -     command: /bin/sh -c "while sleep 1000; do :; done"
.devcontainer/postCreateCommand.sh
#!/bin/bash
echo "Installing Developer Requirements"

# AWS KEY
cp -rf /workspace/.aws ~/

# Docker Install
apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io
#apt-cache madison docker-ce
apt-get install -y docker-ce=5:20.10.8~3-0~debian-bullseye docker-ce-cli=5:20.10.8~3-0~debian-bullseye containerd.io

# AWS SAM CLI Install
wget  -P /tmp https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
unzip /tmp/aws-sam-cli-linux-x86_64.zip -d /tmp/sam-installation
/tmp/sam-installation/install
rm -rf /tmp/aws-sam-cli-linux-x86_64.zip
lambda-python3.9/samconfig.toml
version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "HelloWorld-Python"
s3_bucket = "xxxxxxxxxxxxxxxxxxxxxx"
image_repository = "xxxxxxxxxxxxxxxx"
region = "ap-northeast-1"
confirm_changeset = true
capabilities = ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"]
docker-compose.yml
version: "3"
services:
  my_lambda:
    image: rust:latest
    privileged: true # docker内でdockerを動かすために必要

おわりに

本当は、カスタムランタイムでRustのLambda関数を作りたかったのですが、Dockerにsamを入れて使っている開発環境の情報が見つけられなかったことと、サーバレス関連のリソースをほとんど触ったことがないのにエラー祭りだったため、Pythonのhello Worldを一気通貫するところをまず目指しました。
これから、少しずつアレンジしていければと思います。
ありがとうございました。

追記

動作が不安定だったのでDockerではなくMacでも直接動かせるようにしました。。

rust系
$ brew install rustup rust-analyzer
$ rustup-init
$ rustup update
$ export PATH="$HOME/.cargo/bin:$PATH"
$ cargo install cargo-edit
$ rustup target add x86_64-unknown-linux-musl
sam&aws系
$ brew tap aws/tap
$ brew install aws-sam-cli
$ cp -prf ~/work/docker/my_rust/my_stock_data/.aws ~/.
リンカー
$ brew install filosottile/musl-cross/musl-cross
$ cd my_lambda_function
$ mkdir .cargo
$ echo '[target.x86_64-unknown-linux-musl]
linker = "x86_64-linux-musl-gcc"' > .cargo/config
$ ln -s /usr/local/bin/x86_64-linux-musl-gcc /usr/local/bin/musl-gcc
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2