この記事は?
AWSのエミュレーションツールLocalStackを使って仮想のAWS環境を立ち上げ、その中にLambdaとS3をデプロイして動かしたときの備忘録
LocalStackとは?
Dockerコンテナ内でAWSのエミュレートし、その環境内にデプロイしたりできるツール
無料版では利用できるAWSサービスに制限があるものの、メジャーどころは大体抑えられている
無料版で利用できるのはこのページにあるサービスのうち(Pro)
がついていないもの
Lambda, S3, DynamoDBなど主要なサービスはだいたい無料で使える
メリット
- (一部対象外のサービスはあるけど)無料でAWSを使える
- 実際のクラウド上の環境を汚さずに動作確認できる
- CIで統合テスト回したいときとかに便利
デメリット
- 一部のAWSサービスは有料
- (極端な表現をすると)Lambdaとかのコード上にテスト環境でのみ実行する処理を置かないといけない場合がある
- こちらを参照
今回デプロイするものは?
↓のようなファイルを保存しておくS3バケットとそれにPutするためのLambda関数
LocalStack内のLambdaからLocalStack内のAWSリソースへ触りに行くケースの実験をするためにこれを用意した
LocalStackのインストールと準備
実装に取り掛かる前にLocalStackとその前提となる環境の用意をする
前提
LocalStackを使うのに必要なのが↓
- Python(3.7~3.10)
- Docker
今回はCDKやAWS CLIも使うのでそれらも必要
- AWS CLI
- Node.js
- AWS CDK
LocalStackのインストール
pip install localstack
LocalStackのドキュメントにある通り、rootユーザーでインストールしないように注意!
Note: Please do not use sudo or the root user - LocalStack should be installed and started entirely under a local non-root user. If you have problems with permissions in macOS High Sierra, install with pip install --user localstack
インストールしたら↓のコマンドで起動&ステータス確認してみよう
localstack start -d
localstack status services
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃ Service ┃ Status ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
│ acm │ ✔ available │
│ apigateway │ ✔ available │
│ cloudformation │ ✔ available │
│ cloudwatch │ ✔ available │
│ config │ ✔ available │
│ dynamodb │ ✔ available │
...
AWS Localのインストール
続いてAWS Localをインストール
AWS Localとは?
AWS CLIをLocalStack向けにラップしたツール
通常であればAPIのエンドポイントがAWSクラウトを向いているところ、LocalStackのDockerコンテナに向けてくれる
pip install awscli-local
awslocal --version
デフォルトリージョンも指定しておく(ACCESS_KEYなどは不要)
export DEFAULT_REGION=ap-northeast-1
※ このドキュメントを読んだ感じだとawslocal configure
で設定できそうだけど自分の環境ではコマンドが見つからないと言われてしまった
CDK Localのインストール
最後にCDK Localをインストール
CDK Localとは?
こちらもAWS Localと同じようにAWS CDKのエンドポイントをLocalStackのコンテナに向けたツール
npm install -g aws-cdk-local aws-cdk
cdklocal --version
こちらは特に設定する必要はなし
普通にCDKでinitしたプロジェクト以下で、CDKの代わりにcdklocal
でコマンド実行するだけでOK
コードを実装
↓にサンプルを用意したので適当にClone
実装のポイント
基本的には普通にAWSを使うときの感覚で実装すればOK
ただし、AWS SDKのエンドポイント指定に注意!
触りに行くリソース(今回の場合はS3バケット)がAWSクラウド上ではなくLocalStackにあるので、エンドポイントをそちらに向けてあげる必要がある
さらに、S3の場合はURIの指定方法が2通りある関係上、forcePathStyle
というプロパティをtrueにしないといけない(デフォルトではvirtual-hosted-styleで処理されるが、LocalStackで使うときはpath-styleじゃないとダメ)
とりあえずこうやって書けば動く
function initS3Client() {
const localStackHostName = process.env["LOCALSTACK_HOSTNAME"];
const edgePort = process.env["EDGE_PORT"];
// LocalStack環境じゃない時のハンドリングはよしなに
const endpoint = `https://${localStackHostName}:${edgePort}`;
return new S3Client({
endpoint,
forcePathStyle: true,
});
}
なお、DynamoDBなどの場合はforcePathStyle
がどうのこうの関係なく、↓のように設定してあげればOK
const dynamoDBClient = new DynamoDBClient({endpoint});
LocalStackへデプロイ
bootstrap
CDKを初めて使う場合はbootstrapコマンドでcdk-xxx-assets-{ACCOUNT}-{REGION}
というバケットを作る必要があるので実行する
cdklocal bootstrap
awslocal s3 ls
2022-09-10 17:50:37 cdk-hnb659fds-assets-000000000000-ap-northeast-1
localstackのコンテナを停止するとデプロイしたリソースもすべて揮発する(=再度bootstrapからやり直しになる)ので注意!
デプロイ
cdklocal deploy LocalstackStack
awslocal cloudformation describe-stacks \
--stack-name LocalstackStack
テスト
Lambdaを実行して期待通りバケットにファイルが置かれているか確認する
# Lambda実行
awslocal lambda invoke \
--function-name test-function \
--payload '{ "file": "test", "key": "test.txt" }' \
--cli-binary-format raw-in-base64-out \
response.json
# バケットにファイル置かれてるか見に行く
awslocal s3api get-object \
--bucket test-bucket
--key test.txt \
response.json