概要
前回はローカルでデバッグ実行をできるようにした。
今回はS3をローカルで実行する。
リファクタリングは次回。
環境
- windows 10
- Docker version 20.10.5, build 55c4c88
- aws-cdk 1.95.1
- aws-cdk-local 1.65.4
- aws-cli/2.1.29 Python/3.8.8 Windows/10 exe/AMD64 prompt/off
- node v14.16.0
ローカルでS3を動かす
検討: localstack
ローカルでS3を実行する方法は、Minio(S3クローン)とlocalstackがある模様。
localStackについて調べてみると、test/mock環境をローカルに構築してくれるものの模様。
サポートしているものと他の手段があるものを、今調べて分かる範囲で表にしてみた。
localstack | その他の選択肢 |
---|---|
ACM | 未調査 |
API Gateway | sam local start-api |
CloudFormation | 未調査 |
CloudWatch | 未調査 |
CloudWatch Logs | 未調査 |
DynamoDB | Dynamo DB local |
DynamoDB Streams | 未調査 |
EC2 | 未調査 |
Elasticsearch Service | 未調査 |
EventBridge (CloudWatch Events) | 未調査 |
Firehose | 未調査 |
IAM | 未調査 |
Kinesis | 未調査 |
KMS | 未調査 |
Lambda | sam local invoke |
Redshift | 未調査 |
Route53 | 未調査 |
S3 | minio |
SecretsManager | 未調査 |
SES | 未調査 |
SNS | 未調査 |
SQS | ElasticMQ |
SSM | 未調査 |
StepFunctions | 未調査 |
STS | 未調査 |
localstackでs3をデプロイする
-
aws cdk
を使ってデプロイする手順。
localstack用のprofileを作成
-
aws configure
でprofileを作る
aws configure --profile localstack
入力した内容は以下。
AWS Access Key ID [None]: dummy
AWS Secret Access Key [None]: dummy
Default region name [None]: us-east-1
Default output format [None]: json
localstack用のDockerを作成
- aws-cdkを使ってS3をデプロイするため、aws-cdkの前提条件であるcloudformationもSERVICEに含めている。
version: '3.5'
networks:
localstack:
driver: bridge
services:
localstack:
image: localstack/localstack
ports:
- 4566:4566
environment:
- SERVICES=cloudformation,s3
networks:
- localstack
起動スクリプト。何度も使うのでshにしておく。
#!/bin/bash
bin_dir=$(cd $(dirname $0) && pwd)
composeFile=${1:-"localstack-docker-compose.yml"}
cd $bin_dir/../docker && docker-compose -f $composeFile up
./bin/localstack.sh
を実行し、localstackを起動しておく。
cdkのファイルを作成
- シンプルにS3を作成するだけのスタック
export class S3Stack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const bucket = new Bucket(this, 'my-bucket', {
versioned: true,
bucketName: 'my-sample-bucket'
});
}
}
#!/usr/bin/env node
import { App } from '@aws-cdk/core';
import { S3Stack } from '../lib/s3-stack';
const app = new App();
new S3Stack(app, 'S3Stack2021');
Localstack上の S3 に Bucket/Object を作成する
- cdk.jsonにはlambdaのstackを入れておきたいので、
--app
オプションでlambdaのスタックを指定している。- ※公式ガイドの
--app "npx ts-node"
を使おうとすると、Command Not Exist
のエラーが出たので、npm i -g ts-node
でts-nodeをグローバルインストールしている※注
- ※公式ガイドの
※注
- package.jsonの中でなら、ローカルインストールでもいけるみたい。何故?
"s3-localdeploy": "cdklocal deploy --app \"npx ts-node --prefer-ts-exts bin/s3-index.ts\" --endpoint-url=http://localhost:4566 --profile=localstack"
stackが問題ないことを確認。
npx cdk synth --app "ts-node --prefer-ts-exts bin/s3-index.ts"
デプロイ
npx cdklocal deploy --app "ts-node --prefer-ts-exts bin/s3-index.ts" --endpoint-url=http://localhost:4566 --profile=localstack
ファイルアップロード確認
aws s3 --endpoint-url=http://localhost:4566 cp package.json s3://my-sample-bucket --profile=localstack
S3にアクセスするLambdaの作成
実装
- イベントでKeyを受け取り、愚直にS3にアクセスする
import { S3 } from 'aws-sdk';
import { Handler } from 'aws-lambda';
const config = {
endpoint: (process.env.IS_LOCAL_STACK === "true" ? "http://localstack:4566" : undefined),
s3ForcePathStyle: process.env.IS_LOCAL_STACK === "true",
}
const s3 = new S3(config);
const { S3_BUCKET } = process.env;
if (!S3_BUCKET) throw Error('env S3_BUCKET is empy')
export const lambdaHandler: Handler = async (event, context, callback) => {
try {
const params = {
Bucket: S3_BUCKET,
Key: event.Key
};
const ret = await s3.getObject(params).promise();
if (!ret.Body) throw Error(`s3 object is empty. Bucket: ${S3_BUCKET}, Key: ${event.Key}`)
const message = ret.Body.toString();
console.log(message);
callback(null, `access ${event.Key}`)
} catch (err) {
console.log(err);
return err;
}
};
S3にアクセスするLambda用のStackの作成
#!/usr/bin/env node
import { App } from '@aws-cdk/core';
import { S3LambdaStack } from '../lib/s3-lambda-stack';
import { createLaunch } from '../lib/process/after';
const app = new App();
const stack = new S3LambdaStack(app, 'S3LambdaStack2021');
createLaunch(stack);
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs';
import { entryHandlerDir } from './constants';
export class S3LambdaStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new NodejsFunction(this, 's3-sample', {
runtime: lambda.Runtime.NODEJS_14_X,
entry: `${entryHandlerDir}/s3-sample.ts`,
handler: 'lambdaHandler',
bundling: {
sourceMap: true,
},
environment: {
IS_LOCAL_STACK: process.env.IS_LOCAL_STACK || '',
S3_BUCKET: process.env.S3_BUCKET || ''
}
});
}
}
export const entryHandlerDir = '../src/lambda/handlers/';
動作確認
実行準備
- 環境変数の準備
- 実行用のスクリプトをpackage.jsonに用意
{
"s3sampleA17B5BBA": {
"IS_LOCAL_STACK": "true",
"S3_BUCKET": "my-sample-bucket"
}
}
- docker-composeで立ち上げているlocalstackのネットワークの名前は
docker_localstack
となる- --docker-networkにそれを指定。
"scripts": {
+ "s3-localdeploy": "cdklocal deploy --app \"npx ts-node --prefer-ts-exts bin/s3-index.ts\" --endpoint-url=http://localhost:4566 --profile=localstack",
+ "s3-bundle": "cdk synth --app \"npx ts-node --prefer-ts-exts bin/s3-lambda-index.ts\" > cdk.out/s3-template.yaml",
+ "s3-invoke": "cd cdk.out && echo {\"Key\": \"package.json\"} | sam.cmd local invoke s3sampleA17B5BBA --docker-network docker_localstack --profile=localstack -t s3-template.yaml -n ../env.json --event -",
+ "debug-s3-invoke": "cd cdk.out && echo {\"Key\": \"package.json\"} | sam.cmd local invoke -d 5858 s3sampleA17B5BBA --docker-network docker_localstack --profile=localstack -t s3-template.yaml -n ../env.json --event -"
実行
cd cdk
npm run s3-bundle
npm run s3-invoke
実行できた。
失敗:ネットワークエラー
- 指定ポートを間違えて
4577
にしているのが原因。
{"message":"Inaccessible host: `localstack'. This service may not be available in the `us-east-1' region.","code":"UnknownEndpoint","region":"us-east-1","hostname":"localstack","retryable":true,"originalError":{"message":"connect ECONNREFUSED 172.21.0.2:4572","errno":-111,"code":"NetworkingError","syscall":"connect","address":"172.21.0.2","port":4572,"region":"us-east-1","hostname":"localstack","retryable":true,"time":"2021-03-27T10:32:44.322Z"},"time":"2021-03-27T10:32:44.322Z"
参考
Testable - lambda : 和田卓人
aws-sam-localstack-example
aws cdk s3
ローカル環境でLambda+S3のテストをする
github - localstack
github - minio
github - localstack-aws-cdk
github - awscli-local
Localstack + CDK = local AWS development
localstackのローカル環境にAWS CDKでS3バケットを作成してみる
LocalStackに向けてTerraformを実行する
LocalStackをつかってローカルでLambdaを実行してみた
LocalStackでAWSサービスを試してみた
Local Development with LocalStack
AWS SAM CLI + DynamoDB localを使ってローカル上で完結するAPI開発
elastcisq - sws local
docker elasticsq
Lambda Function の開発について悩んだところをまとめてみた
LocalStackをGitHub Actions上で動かしてテストする
LaravelをElasticMQ(Amazon SQS互換)と連携してみる
開発のためにローカルにもS3が欲しいというわがまま、MINIOが叶えてくれました
【AWS S3】S3 Presigned URLの仕組みを調べてみた
local invoke option`
localstack有料版をちゃんと使ってみる