これは、なにをしたくて書いたもの?
- LocalStackという、クラウドアプリケーション開発のためのモック/テスト用フレームワークがあるらしい
- 現在はAWSのスタックを扱えるように開発しているらしく、これでAWSの機能をローカルで試してみよう
- とりあえず、S3を触ってみよう
そんな動機で、LocalStackというものを試してみました。
LocalStack?
最初に書きましたが、クラウドアプリケーション開発のための、モック/テスト用フレームワークだそうです。
LocalStack - A fully functional local cloud stack
エラーインジェクションができることだったり、サービスがプラガブルであること、他のフレームワーク(moto)と比べて、
プロセスが独立していることを売りにしているようです。
日本語情報も、いくつか。
LocalStackをつかってローカルでLambdaを実行してみた | DevelopersIO
AtlassianのLocalStackを使ってみてなんとなく理解するまでのお話 - Qiita
ドキュメントを読んでいると、JUnitと統合する機能もあるようです。
起動すると、各種APIが起動するようですが、それぞれが使用するポートのリストはこちら。
では、今回はS3のモックサービスとして使ってみましょう。
LocalStackを起動する
LocalStackはpipを使ってインストールするもののようですが、Dockerイメージが提供されているようなので、
こちらを使用することにします。
起動。
$ docker container run -it --rm --name localstack localstack/localstack:0.8.7
起動時に、各種APIのポートが出力されるので、見ておきましょう。
Starting mock API Gateway (http port 4567)... Starting mock DynamoDB (http port 4569)... Starting mock SES (http port 4579)... Starting mock Kinesis (http port 4568)... Starting mock Redshift (http port 4577)... Starting mock S3 (http port 4572)... Starting mock CloudWatch (http port 4582)... Starting mock CloudFormation (http port 4581)... Starting mock SSM (http port 4583)... Starting mock SQS (http port 4576)... Starting local Elasticsearch (http port 4571)... Starting mock SNS (http port 4575)... Starting mock DynamoDB Streams service (http port 4570)... Starting mock Firehose service (http port 4573)... Starting mock Route53 (http port 4580)... 2018-09-30T06:57:55:WARNING:infra.pyc: Service "elasticsearch" not yet available, retrying... Starting mock ES service (http port 4578)... Starting mock Lambda service (http port 4574)...
「Ready.」と表示されたら、起動完了です。
Ready.
あと、ダッシュボードが8080ポートで起動していたりします。
2018-09-30T06:57:43:INFO:werkzeug: * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
ホスト側のポートにはバインドしていません。コンテナのIPアドレスは、こちら。
$ docker container inspect localstack | grep IPAddress "SecondaryIPAddresses": null, "IPAddress": "172.17.0.2", "IPAddress": "172.17.0.2",
AWS CLIで接続してみる
Installing, updating, and uninstalling the AWS CLI - AWS Command Line Interface
クレデンシャルの設定。Access Key、Secret Access Keyを入力する必要があるのですが、設定さえしていれば値は
なんでも良いようです。
$ aws configure AWS Access Key ID [None]: my-access-key-id AWS Secret Access Key [None]: my-secret-access-key Default region name [None]: Default output format [None]:
「--endpoint-url」を指定しつつ、s3のコマンドを使ってみます。
## Bucket作成 $ aws --endpoint-url=http://172.17.0.2:4572 s3 mb s3://test-bucket make_bucket: test-bucket ## アップロード $ echo 'Hello LocalStack' > file.txt $ aws --endpoint-url=http://172.17.0.2:4572 s3 cp file.txt s3://test-bucket upload: ./file.txt to s3://test-bucket/file.txt ## 確認 $ aws --endpoint-url=http://172.17.0.2:4572 s3 ls test-bucket 2018-09-30 07:28:12 17 file.txt ## ダウンロード $ aws --endpoint-url=http://172.17.0.2:4572 s3 cp s3://test-bucket/file.txt download.txt download: s3://test-bucket/file.txt to ./download.txt $ cat download.txt Hello LocalStack
「--endpoint-url」を指定する必要があるものの、ふつうに動きますね。
Node.jsから使ってみる
続いて、Node.jsのAWS SDKからアクセスしてみましょう。
AWS SDK for JavaScript とは - AWS SDK for JavaScript
環境
今回の環境は、こちら。
$ node -v v8.12.0 $ npm -v 6.4.1
準備
必要なライブラリをインストールします。テストコードで実行することにして、こちらはJestで。
$ npm i --save aws-sdk $ npm i --save-dev jest
バージョンとJestの設定。
"scripts": { "test": "jest" }, "dependencies": { "aws-sdk": "^2.325.0" }, "devDependencies": { "jest": "^23.6.0" }, "jest": { "testEnvironment": "node" }
テストを書く
作成したコードは、こんな感じです。
test/localstack-s3.test.js
const AWS = require('aws-sdk'); AWS.config.update({ accessKeyId: "access-key-id", secretAccessKey: "secret-access-key", s3: { endpoint: "http://172.17.0.2:4572" }, s3ForcePathStyle: true }); const s3 = new AWS.S3(); test('using s3', done => { const bucketName = 'my-bucket'; const objectKey = 'my-object'; const message = 'Hello Node.js!!'; s3.createBucket({ Bucket: bucketName }, (err, bucketCreated) => { s3.putObject({ Bucket: bucketName, Key: objectKey, Body: message }, (err, objectPutted) => { s3.getObject({ Bucket: bucketName, Key: objectKey }, (err, data) => { expect(data.Body.toString('utf-8')).toEqual(message); done(); }); }); }); }); test('using s3 with async', async () => { const bucketName = 'my-bucket-async'; const objectKey = 'my-object'; const message = 'Hello Node.js!! with Async'; await s3.createBucket({ Bucket: bucketName }).promise(); await s3.putObject({ Bucket: bucketName, Key: objectKey, Body: message }).promise(); const data = await s3.getObject({ Bucket: bucketName, Key: objectKey }).promise(); expect(data.Body.toString('utf-8')).toEqual(message); return Promise.resolve(); });
Access Key、Secret Access Keyやエンドポイントの設定をする必要があります。
AWS.config.update({ accessKeyId: "access-key-id", secretAccessKey: "secret-access-key", s3: { endpoint: "http://172.17.0.2:4572" }, s3ForcePathStyle: true });
また、S3へのアクセスはPathStyleとなります。
あとは、ふつうに使えばOKです。
test('using s3', done => { const bucketName = 'my-bucket'; const objectKey = 'my-object'; const message = 'Hello Node.js!!'; s3.createBucket({ Bucket: bucketName }, (err, bucketCreated) => { s3.putObject({ Bucket: bucketName, Key: objectKey, Body: message }, (err, objectPutted) => { s3.getObject({ Bucket: bucketName, Key: objectKey }, (err, data) => { expect(data.Body.toString('utf-8')).toEqual(message); done(); }); }); }); });
Promiseを使って書くこともできるんですね、といろいろ調べていて気づきました。
test('using s3 with async', async () => { const bucketName = 'my-bucket-async'; const objectKey = 'my-object'; const message = 'Hello Node.js!! with Async'; await s3.createBucket({ Bucket: bucketName }).promise(); await s3.putObject({ Bucket: bucketName, Key: objectKey, Body: message }).promise(); const data = await s3.getObject({ Bucket: bucketName, Key: objectKey }).promise(); expect(data.Body.toString('utf-8')).toEqual(message); return Promise.resolve(); });
Support for Promises in the SDK | AWS Developer Tools Blog
JavaScript Promises の使用 - AWS SDK for JavaScript
実行。
$ npm test
$ aws --endpoint-url=http://172.17.0.2:4572 s3 ls 2006-02-03 16:45:09 test-bucket 2006-02-03 16:45:09 my-bucket-async 2006-02-03 16:45:09 my-bucket $ aws --endpoint-url=http://172.17.0.2:4572 s3 ls my-bucket 2018-09-30 07:40:22 15 my-object $ aws --endpoint-url=http://172.17.0.2:4572 s3 cp s3://my-bucket/my-object a.txt download: s3://my-bucket/my-object to ./a.txt $ cat a.txt Hello Node.js!! $ aws --endpoint-url=http://172.17.0.2:4572 s3 ls my-bucket-async 2018-09-30 07:40:22 26 my-object $ aws --endpoint-url=http://172.17.0.2:4572 s3 cp s3://my-bucket-async/my-object b.txt $ cat b.txt Hello Node.js!! with Async
OKそうですね。
ダッシュボード
最初に8080ポートの紹介をしましたが、LocalStackが稼働しているサーバーの8080ポートにHTTPでアクセスすると、
ダッシュボードを見ることができます。
http://[LocalStackが稼働しているサーバー]:8080/
LocalStackを使うと、どのサービスを使っているのかが確認できるようですね。
まとめ
LocalStackを使って、AWSのサービスのうち、S3のモックを使って動かしてみました。
こういう互換のサービスが、テスト目的でローカルで動かせるのは便利ですね。