はじめに
やりたいことはタイトルの通りですが、以下の通りです。
- ローカルで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が動きません。
version: "3"
services:
my_lambda:
image: rust:latest
privileged: true # docker内でdockerを動かすために必要
docker-compose.yml
を指定して、VS CodeのRemote Containersを使いコンテナ起動。
AWS Toolkitをインストール
VS Codeの左バーにAWSのマークが表示されれば成功です。
ただし、インストールの際押下したボタンの通りですが、コンテナの方にインストールされており、コンテナをリビルドするとこのインストールはなかったことになりますので、設定ファイルへこの拡張機能を追加します。
追加されました。
AWS認証情報の設定
次に認証情報の設定。以下を入力します。
$ 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
"postCreateCommand": "cp -rf /workspace/.aws ~/",
なお、ローカルで保持している、キー情報を直にマウントするのが一般的かと思います。
ただし、マウントはコンテナ側の問題があった時、ローカルのマスタファイルへ反映してしまう可能性があります。
基本的に操作しないファイルですので問題ないと思いますが、VS CodeのRemote Containerの挙動はやや不安定で、以前クラッシュしてローカルのファイル含めて消えてしまったことがあるので私はマスタファイルからのマウントは行いません。
私の場合、このままだとGitHubへawsのキー情報がアップされてしまうのでgitの管理対象から外します。
# Confidentials
credentials
config
リージョンを東京へ変更
つながりました。試しにLambdaを選択してみると過去にアップした関数が見えています。
AWS SAM CLIのインストール
早速開発をしようと、Lambdaを右クリックし、「Create Lambda SAM Application」を選択したところSAM CLIがないとのことでリンク先でインストール手順を確認します。
Linuxはzipファイルを使うようですが、「Docker および AWS CLI が必要です。インストール手順をご確認ください」とあるため、インストール手順を確認します。
参考:
インストール手順は以下のとおりですが、1-2は済んでいるため、3(コンテナ①の中にコンテナ②を作るためにコンテナ①でDockerのインストールが必要)、4だけで良さそうです。
- Create an AWS account.
- Configure AWS Identity and Access Management (IAM) permissions and AWS credentials.
- Install Docker. Note: Docker is a prerequisite only for testing your application locally or using the --use-container option.
- 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.json
のpostCreateCommand
は1行しか書けず可読性が良くない。
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を解凍して作成するようにする
では、この構成にするために設定ファイルを変えていきます。
// "postCreateCommand": "cp -rf /workspace/.aws ~/",
"postCreateCommand": "chmod 755 .devcontainer/postCreateCommand.sh && ./.devcontainer/postCreateCommand.sh",
"postStartCommand": "service docker start",
最終行はコンテナ②のプロセススタートですが、コンテナ①停止でコンテナ②が止まってしまうのでpostCreateCommandに書いてあると、手動でスタートしないといけない場面があったため、以下を参考にしてpostStartCommandに記載しました。:
#!/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」が選択できることを確認。
Hello World
そのまま選択肢から選んでpythonのhelloworldアプリケーションを作成してみました。
sam init
コマンドで進めた方がわかりやすいかもしれません。
少し経つとアプリケーションフォルダが作成されます。
チュートリアルに従って、動作確認できるかみていきます。
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"}
デプロイ
※S3バケットが必要になりますので、ない方は作成してください。
※(イメージのデプロイの場合)ECRのリポジトリが必要になりますので、ない方は作成してください。
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
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
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を動かすためと思いコンソールのボタン押下で済む進め方をしました、ものすごく時間がかかってしまいました。
デプロイ成功
ラムダ関数が追加されました。
ログに表示されたURLにアクセスして、無事イベント発行と関数実行ができていることを確認しました。
デプロイの情報を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)
[default]
aws_access_key_id = xxxxxxxxxxx
aws_secret_access_key = xxxxxxxxxxx
[default]
region=ap-northeast-1
output=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",
}
version: '3'
services:
my_lambda:
volumes:
- .:/workspace:cached
- command: /bin/sh -c "while sleep 1000; do :; done"
#!/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
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"]
version: "3"
services:
my_lambda:
image: rust:latest
privileged: true # docker内でdockerを動かすために必要
おわりに
本当は、カスタムランタイムでRustのLambda関数を作りたかったのですが、Dockerにsamを入れて使っている開発環境の情報が見つけられなかったことと、サーバレス関連のリソースをほとんど触ったことがないのにエラー祭りだったため、Pythonのhello Worldを一気通貫するところをまず目指しました。
これから、少しずつアレンジしていければと思います。
ありがとうございました。
追記
動作が不安定だったのでDockerではなくMacでも直接動かせるようにしました。。
$ 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
$ 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