これは、なにをしたくて書いたもの?
AWS Lambda関数を試す時にLocalStackをよく使うのですが、LocalStack内で動作するAWS Lambda関数内から、同じLocalStack上の
リソースにアクセスする際の方法をよく忘れるのでメモしておくことにしました。
localhost
でアクセスするわけじゃないんですよね。
結論を言うと、LOCALSTACK_HOSTNAME
という環境変数を使います。
LOCALSTACK_HOSTNAME環境変数
LOCALSTACK_HOSTNAME
環境変数については以下に説明があり、AWS Lambda関数内から他のサービスにアクセスするための
エンドポイントとしてのホスト名が格納された環境変数とされています。
LOCALSTACK_HOSTNAME
。
Deprecated. Name of the host where LocalStack services are available. Use this hostname as endpoint in order to access the services from within your Lambda functions (e.g., to store an item to DynamoDB or S3 from a Lambda). This option is read-only. Use LOCALSTACK_HOST instead.
一方でこの環境変数は非推奨扱いになっていて、LOCALSTACK_HOST
環境変数を代わりに使うこと、となっています。
LOCALSTACK_HOST
。
This is interpolated into URLs and addresses that are returned by LocalStack. It has the form
: .
なのですが、実際に使ってみたらLocalStack 2.1.0の時点ではLOCALSTACK_HOST
環境変数には値が入っていませんでした…。
今回のエントリーではLOCALSTACK_HOSTNAME
環境変数を使うことにします。
ちなみに、LOCALSTACK_HOSTNAME
環境変数に入っているのはホスト名ですが、ポートについてはEDGE_PORT
環境変数を
参照すればよさそうです。
EDGE_PORT
。
Deprecated. Port number for the edge service, the main entry point for all API invocations.
まあ、EDGE_PORT
環境変数も非推奨なのですが…。
非推奨なのはさておき、現時点ではLOCALSTACK_HOSTNAME
環境変数とEDGE_PORT
環境変数を使っていってみましょう。
お題
今回のお題は、以下とします。
- Amazon SQSキューをサブスクライブするAWS Lambda関数を作成し、LocalStack内で動作させる
- 作成したAWS Lambda関数からは、Amazon S3バケットにデータをアップロードする
- テストでの確認のために、LocalStackにアクセスしてテストするものと、AWS SDK v3 Client mockを使ってテストするものの2種類を用意する
環境
今回の環境は、こちら。
$ python3 -V Python 3.10.6 $ localstack --version 2.1.0
LocalStackを起動。
$ LAMBDA_EXECUTOR=docker-reuse localstack start
$ awslocal --version aws-cli/2.12.6 Python/3.11.4 Linux/5.15.0-76-generic exe/x86_64.ubuntu.22 prompt/off
LocalStack向けのものですが。
AWS Lambda関数を作成する
まずはAWS Lambda関数を作成します。AWS Lambda関数はTypeScript+Serverless Frameworkを使って作成することにします。
Node.jsプロジェクトの作成と、TypeScript、Serverless Frameworkのインストール。
$ npm init -y $ npm i -D typescript $ npm i -D prettier $ npm i -D serverless
Serverless Frameworkでaws-nodejs
をテンプレートにサービス作成。
$ npx serverless create --template aws-nodejs
Node.jsとAWS Lambda関数の型宣言のインストール。
$ npm i -D @types/node@v18 $ npm i -D @types/aws-lambda
Serverless Frameworkのプラグインをインストール。
$ npm i -D serverless-esbuild esbuild $ npm i -D serverless-localstack
Amazon S3にアクセスするためのクライアントライブラリをインストール。
$ npm i @aws-sdk/client-s3
テストライブラリをインストール。
$ npm i -D jest @types/jest $ npm i -D esbuild-jest $ npm i -D aws-sdk-client-mock aws-sdk-client-mock-jest
依存関係は、このようになりました。
"devDependencies": { "@types/aws-lambda": "^8.10.119", "@types/jest": "^29.5.2", "@types/node": "^18.16.19", "aws-sdk-client-mock": "^3.0.0", "aws-sdk-client-mock-jest": "^3.0.0", "esbuild": "^0.17.19", "esbuild-jest": "^0.5.0", "jest": "^29.5.0", "prettier": "^2.8.8", "serverless": "^3.33.0", "serverless-esbuild": "^1.45.1", "serverless-localstack": "^1.1.1", "typescript": "^5.1.6" }, "dependencies": { "@aws-sdk/client-s3": "^3.363.0" }
scripts
。
"scripts": { "build:watch": "tsc --project . --watch", "format": "prettier --write **/*.ts", "test": "jest" },
設定ファイル。
tsconfig.json
{ "compilerOptions": { "target": "esnext", "module": "commonjs", "moduleResolution": "node", "lib": ["esnext"], "baseUrl": "./", "noEmit": true, "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true, "esModuleInterop": true }, "include": ["./**/*.ts"], "exclude": [ "node_modules/**/*", ".serverless/**/*" ] }
.prettierrc.json
{ "singleQuote": true, "printWidth": 120 }
jest.config.js
module.exports = { testEnvironment: 'node', transform: { '^.+\\.tsx?$': 'esbuild-jest', }, };
生成されたソースコードは削除しておきます。
$ rm handler.js
作成したAWS Lambda関数は、こちら。Amazon SQSキューをサブスクライブするものとして作成しています。
subscribe-sqs/handler.ts
import { SQSEvent } from 'aws-lambda'; import { s3Client } from './clients'; import { CreateBucketCommand, HeadBucketCommand, NotFound, PutObjectCommand } from '@aws-sdk/client-s3'; export const handler = async (event: SQSEvent): Promise<void> => { console.log(`env[LOCALSTACK_HOST] => ${process.env['LOCALSTACK_HOST']}`); console.log(`env[LOCALSTACK_HOSTNAME] => ${process.env['LOCALSTACK_HOSTNAME']}`); console.log(`env[S3_ENDPOINT] => ${process.env['S3_ENDPOINT']}`); const bucketName = 'my-bucket'; try { await s3Client.send(new HeadBucketCommand({ Bucket: bucketName })); } catch (e) { if (e instanceof NotFound) { await s3Client.send(new CreateBucketCommand({ Bucket: bucketName })); } } for (const record of event.Records) { const messageId = record.messageId; const message = record.body; await s3Client.send(new PutObjectCommand({ Bucket: bucketName, Key: messageId, Body: message })); console.log(`put object => ${JSON.stringify({ Bucket: bucketName, Key: messageId, Body: message })}`); } };
Amazon S3バケットが存在していなかったら、まず作成。
const bucketName = 'my-bucket'; try { await s3Client.send(new HeadBucketCommand({ Bucket: bucketName })); } catch (e) { if (e instanceof NotFound) { await s3Client.send(new CreateBucketCommand({ Bucket: bucketName })); } }
その後、Amazon SQSから受け取ったメッセージの内容をAmazon S3バケットにオブジェクトとしてアップロードする流れにしています。
for (const record of event.Records) { const messageId = record.messageId; const message = record.body; await s3Client.send(new PutObjectCommand({ Bucket: bucketName, Key: messageId, Body: message })); console.log(`put object => ${JSON.stringify({ Bucket: bucketName, Key: messageId, Body: message })}`); }
Amazon S3へアクセスするためのクライアントですが、以下のようにしました。
subscribe-sqs/clients.ts
import { S3Client } from '@aws-sdk/client-s3'; const s3Endpoint = [ process.env['LOCALSTACK_HOST'] ? `http://${process.env['LOCALSTACK_HOST']}:${process.env['EDGE_PORT']}` : undefined, process.env['LOCALSTACK_HOSTNAME'] ? `http://${process.env['LOCALSTACK_HOSTNAME']}:${process.env['EDGE_PORT']}` : undefined, process.env['S3_ENDPOINT']!, ].find((v) => v !== undefined); export const s3Client = new S3Client({ endpoint: s3Endpoint, });
環境変数LOCALSTACK_HOST
、LOCALSTACK_HOSTNAME
のどちらかがあったら、EDGE_PORT
と合わせてエンドポイント
とします。
S3_ENDPOINT
というのは、自前で用意するJestでのテストで使う環境変数です。
実際の実行時に、これらがどのような値になるかはAWS Lambda関数内でログ出力するようにしています。
console.log(`env[LOCALSTACK_HOST] => ${process.env['LOCALSTACK_HOST']}`); console.log(`env[LOCALSTACK_HOSTNAME] => ${process.env['LOCALSTACK_HOSTNAME']}`); console.log(`env[S3_ENDPOINT] => ${process.env['S3_ENDPOINT']}`);
Serverless Frameworkの設定ファイルは、こちら。
serverless.yml
service: access-aws-resource-in-localstack-lambda frameworkVersion: "3" provider: name: aws runtime: nodejs18.x stage: dev region: us-east-1 package: individually: true functions: subscribeSqs: handler: subscribe-sqs/handler.handler events: - sqs: arn: !GetAtt MyQueue.Arn resources: Resources: MyQueue: Type: AWS::SQS::Queue Properties: QueueName: my-queue custom: esbuild: bundle: true target: node18 platform: node plugins: - serverless-esbuild - serverless-localstack
Amazon SQSキューは、この中で作成します。
デプロイ。
$ npx serverless deploy
作成されたAmazon SQSキューに対して、メッセージを送信。
$ QUEUE_URL=$(awslocal sqs list-queues --query 'QueueUrls[-1]' --output text) $ awslocal sqs send-message --queue-url $QUEUE_URL --message-body 'Hello LocalStack!!'
Amazon CloudWatch Logsを確認してみます。
$ awslocal logs tail /aws/lambda/access-aws-resource-in-localstack-lambda-dev-subscribeSqs 2023-07-02T13:05:38.177000+00:00 2023/07/02/[$LATEST]bca1008fdc670ed62f6482ac15e3eb88 START RequestId: d70805e7-e2b7-43fc-bf98-7c695e73d35c Version: $LATEST 2023-07-02T13:05:38.181000+00:00 2023/07/02/[$LATEST]bca1008fdc670ed62f6482ac15e3eb88 2023-07-02T13:05:37.991Z d70805e7-e2b7-43fc-bf98-7c695e73d35c INFO env[LOCALSTACK_HOST] => undefined 2023-07-02T13:05:38.185000+00:00 2023/07/02/[$LATEST]bca1008fdc670ed62f6482ac15e3eb88 2023-07-02T13:05:37.992Z d70805e7-e2b7-43fc-bf98-7c695e73d35c INFO env[LOCALSTACK_HOSTNAME] => 172.17.0.2 2023-07-02T13:05:38.189000+00:00 2023/07/02/[$LATEST]bca1008fdc670ed62f6482ac15e3eb88 2023-07-02T13:05:37.992Z d70805e7-e2b7-43fc-bf98-7c695e73d35c INFO env[S3_ENDPOINT] => undefined 2023-07-02T13:05:38.193000+00:00 2023/07/02/[$LATEST]bca1008fdc670ed62f6482ac15e3eb88 2023-07-02T13:05:38.150Z d70805e7-e2b7-43fc-bf98-7c695e73d35c INFO put object => {"Bucket":"my-bucket","Key":"56b7b34a-0b4a-4c2b-9e9c-2d93c158e89e","Body":"Hello LocalStack!!"} 2023-07-02T13:05:38.197000+00:00 2023/07/02/[$LATEST]bca1008fdc670ed62f6482ac15e3eb88 END RequestId: d70805e7-e2b7-43fc-bf98-7c695e73d35c 2023-07-02T13:05:38.201000+00:00 2023/07/02/[$LATEST]bca1008fdc670ed62f6482ac15e3eb88 REPORT RequestId: d70805e7-e2b7-43fc-bf98-7c695e73d35c Duration: 163.37 ms Billed Duration: 164 msMemory Size: 1024 MB Max Memory Used: 1024 MB
よく見ると、LOCALSTACK_HOST
環境変数には値が入っていません…。LOCALSTACK_HOSTNAME
環境変数には値が入っています。
ドキュメント上はLOCALSTACK_HOSTNAME
環境変数は非推奨なのですが…どうなっているのでしょう?
43fc-bf98-7c695e73d35c INFO env[LOCALSTACK_HOST] => undefined 2023-07-02T13:05:38.185000+00:00 2023/07/02/[$LATEST]bca1008fdc670ed62f6482ac15e3eb88 2023-07-02T13:05:37.992Z d70805e7-e2b7-43fc-bf98-7c695e73d35c INFO env[LOCALSTACK_HOSTNAME] => 172.17.0.2
なお、取得できている値はコンテナのIPアドレスなのです。LocalStackだからlocalhost:4566
でアクセスできるという想定で
値をハードコードしてしまうと、ここでハマることになります。
それはそうと、受信したメッセージの内容がAmazon S3にアップロードされたはずなので、そちらも確認します。
$ awslocal s3 ls my-bucket/ 2023-07-02 22:05:38 18 56b7b34a-0b4a-4c2b-9e9c-2d93c158e89e $ awslocal s3 cp s3://my-bucket/56b7b34a-0b4a-4c2b-9e9c-2d93c158e89e - Hello LocalStack!!
OKですね。これで、LocalStack内で動作するAWS Lambda関数内から、別のリソースにアクセスする際には
LOCALSTACK_HOSTNAME
環境変数を活用するとよさそうなことがわかりました。
テストを書く
続いては、テストを書いていきたいと思います。
まずは、LocalStackにアクセスする前提でテストを書いてみます。
subscribe-sqs/handler-with-localstack.test.ts
import { SQSEvent } from 'aws-lambda'; import { handler } from './handler'; import { s3Client } from './clients'; import { GetObjectCommand } from '@aws-sdk/client-s3'; test('lambda test with LocalStack', async () => { const payload: SQSEvent = JSON.parse(` { "Records": [ { "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", "body": "{\\"message\\": \\"Hello\\"}", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1545082649183", "SenderId": "AIDAIENQZJOLO23YVJ4VO", "ApproximateFirstReceiveTimestamp": "1545082649185" }, "messageAttributes": {}, "md5OfBody": "098f6bcd4621d373cade4e832627b4f6", "eventSource": "aws:sqs", "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue", "awsRegion": "us-east-2" } ] } `); await handler(payload); const s3Object = await s3Client.send( new GetObjectCommand({ Bucket: 'my-bucket', Key: '059f36b4-87a3-44ab-83d2-661975830a7d' }) ); expect(await s3Object.Body?.transformToString('utf-8')).toBe('{"message": "Hello"}'); });
AWS Lambda関数として受け取るイベントデータを渡して、関数を呼び出します。最後にAmazon S3バケットにアップロードされた
オブジェクトを取得しています。
イベントのデータは、こちらで作成して少し加工しました。
$ npx serverless generate-event -t aws:sqs -b '{"message": "Hello"}' | jq { "Records": [ { "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", "body": "{\"message\": \"Hello\"}", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1545082649183", "SenderId": "AIDAIENQZJOLO23YVJ4VO", "ApproximateFirstReceiveTimestamp": "1545082649185" }, "messageAttributes": {}, "md5OfBody": "098f6bcd4621d373cade4e832627b4f6", "eventSource": "aws:sqs", "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue", "awsRegion": "us-east-2" } ] }
ところで、ソースコードとしてこう書いただけだとクレデンシャルがないので動作しません。
今回はscripts
内に環境変数を定義することにしました。
"scripts": { "build:watch": "tsc --project . --watch", "format": "prettier --write **/*.ts", "test": "AWS_ACCESS_KEY_ID=mock_access_key_id AWS_SECRET_ACCESS_KEY=mock_secret_access_key AWS_REGION=us-east-1 S3_ENDPOINT=http://127.0.0.1:4566 jest" },
LocalStackのエンドポイントは、S3_ENDPOINT=http://127.0.0.1:4566
として指定してあります。ちなみに、http://localhost:4566
だと
以下のようなエラーになってうまく動きませんでした…。
AWS SDK error wrapper for Error: connect ECONNREFUSED ::1:4566
今回は直接記述しましたが、場合によってはdotenvを使ったりするのでしょう。
もうひとつは、AWS SDK v3 Client mockを使った場合。
subscribe-sqs/handler-with-mock.test.ts
import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; import { HeadBucketCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; import { SQSEvent } from 'aws-lambda'; import { handler } from './handler'; const s3Mock = mockClient(S3Client); beforeEach(() => { s3Mock.reset(); }); test('lambda test with AWS SDK v3 Client mock', async () => { s3Mock.on(HeadBucketCommand).resolves({}); s3Mock.on(PutObjectCommand).resolves({}); const payload: SQSEvent = JSON.parse(` { "Records": [ { "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", "receiptHandle": "AQEBwJnKynrHigUMZj6rYigCgxlaS3SLy0a...", "body": "{\\"message\\": \\"Hello\\"}", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1545082649183", "SenderId": "AIDAIENQZJOLO23YVJ4VO", "ApproximateFirstReceiveTimestamp": "1545082649185" }, "messageAttributes": {}, "md5OfBody": "098f6bcd4621d373cade4e832627b4f6", "eventSource": "aws:sqs", "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue", "awsRegion": "us-east-2" } ] } `); await handler(payload); expect(s3Mock).toReceiveNthCommandWith(1, HeadBucketCommand, { Bucket: 'my-bucket' }); expect(s3Mock).toReceiveNthCommandWith(2, PutObjectCommand, { Bucket: 'my-bucket', Key: '059f36b4-87a3-44ab-83d2-661975830a7d', Body: '{"message": "Hello"}', }); });
こちらは、AWS SDK for JavaScript v3のクライアントをモックにするので、LocalStackがなくても動作させられます。
確認。
$ npm run test > access-aws-resource-in-localstack-lambda@1.0.0 test > AWS_ACCESS_KEY_ID=mock_access_key_id AWS_SECRET_ACCESS_KEY=mock_secret_access_key AWS_REGION=us-east-1 S3_ENDPOINT=http://127.0.0.1:4566 jest PASS subscribe-sqs/handler-with-localstack.test.ts ● Console console.log env[LOCALSTACK_HOST] => undefined at handler (subscribe-sqs/handler.ts:26:11) console.log env[LOCALSTACK_HOSTNAME] => undefined at handler (subscribe-sqs/handler.ts:27:11) console.log env[S3_ENDPOINT] => http://127.0.0.1:4566 at handler (subscribe-sqs/handler.ts:28:11) console.log put object => {"Bucket":"my-bucket","Key":"059f36b4-87a3-44ab-83d2-661975830a7d","Body":"{\"message\": \"Hello\"}"} at handler (subscribe-sqs/handler.ts:41:13) PASS subscribe-sqs/handler-with-mock.test.ts ● Console console.log env[LOCALSTACK_HOST] => undefined at handler (subscribe-sqs/handler.ts:26:11) console.log env[LOCALSTACK_HOSTNAME] => undefined at handler (subscribe-sqs/handler.ts:27:11) console.log env[S3_ENDPOINT] => http://127.0.0.1:4566 at handler (subscribe-sqs/handler.ts:28:11) console.log put object => {"Bucket":"my-bucket","Key":"059f36b4-87a3-44ab-83d2-661975830a7d","Body":"{\"message\": \"Hello\"}"} at handler (subscribe-sqs/handler.ts:41:13) Test Suites: 2 passed, 2 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 1.275 s, estimated 2 s Ran all test suites.
OKですね。
AWS SDK v3 Client mockを使っている方は、LocalStackに依存していないことを確認するために単独で実行しておきましょう。
$ npx jest subscribe-sqs/handler-with-mock.test.ts console.log env[LOCALSTACK_HOST] => undefined at handler (subscribe-sqs/handler.ts:26:11) console.log env[LOCALSTACK_HOSTNAME] => undefined at handler (subscribe-sqs/handler.ts:27:11) console.log env[S3_ENDPOINT] => undefined at handler (subscribe-sqs/handler.ts:28:11) console.log put object => {"Bucket":"my-bucket","Key":"059f36b4-87a3-44ab-83d2-661975830a7d","Body":"{\"message\": \"Hello\"}"} at handler (subscribe-sqs/handler.ts:41:13) PASS subscribe-sqs/handler-with-mock.test.ts ✓ lambda test with AWS SDK v3 Client mock (27 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 0.769 s, estimated 2 s Ran all test suites matching /subscribe-sqs\/handler-with-mock.test.ts/i.
大丈夫そうですね。
オマケ
AWS Lambda関数が動作しているコンテナ内で参照できる環境変数は、どこで定義されているのかな?と思って見てみたのですが、
以下のようですね。
env_vars = { # 1) Public AWS defined runtime environment variables (in same order): # https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html # a) Reserved environment variables # _HANDLER conditionally added below # TODO: _X_AMZN_TRACE_ID "AWS_DEFAULT_REGION": self.function_version.id.region, "AWS_REGION": self.function_version.id.region, # AWS_EXECUTION_ENV conditionally added below "AWS_LAMBDA_FUNCTION_NAME": self.function_version.id.function_name, "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": self.function_version.config.memory_size, "AWS_LAMBDA_FUNCTION_VERSION": self.function_version.id.qualifier, "AWS_LAMBDA_INITIALIZATION_TYPE": self.initialization_type, "AWS_LAMBDA_LOG_GROUP_NAME": self.get_log_group_name(), "AWS_LAMBDA_LOG_STREAM_NAME": self.get_log_stream_name(), # Access IDs for role "AWS_ACCESS_KEY_ID": credentials["AccessKeyId"], "AWS_SECRET_ACCESS_KEY": credentials["SecretAccessKey"], "AWS_SESSION_TOKEN": credentials["SessionToken"], # AWS_LAMBDA_RUNTIME_API is set in the runtime interface emulator (RIE) "LAMBDA_TASK_ROOT": "/var/task", "LAMBDA_RUNTIME_DIR": "/var/runtime", # b) Unreserved environment variables # LANG # LD_LIBRARY_PATH # NODE_PATH # PYTHONPATH # GEM_PATH "AWS_XRAY_CONTEXT_MISSING": "LOG_ERROR", # TODO: allow configuration of xray address "AWS_XRAY_DAEMON_ADDRESS": "127.0.0.1:2000", # not 100% sure who sets these two # extensions are not supposed to have them in their envs => TODO: test if init removes them "_AWS_XRAY_DAEMON_PORT": "2000", "_AWS_XRAY_DAEMON_ADDRESS": "127.0.0.1", # AWS_LAMBDA_DOTNET_PREJIT "TZ": ":UTC", # 2) Public AWS RIE interface: https://github.com/aws/aws-lambda-runtime-interface-emulator "AWS_LAMBDA_FUNCTION_TIMEOUT": self.function_version.config.timeout, # 3) Public LocalStack endpoint "LOCALSTACK_HOSTNAME": self.runtime_executor.get_endpoint_from_executor(), "EDGE_PORT": str(config.EDGE_PORT), "AWS_ENDPOINT_URL": f"http://{self.runtime_executor.get_endpoint_from_executor()}:{config.EDGE_PORT}", # 4) Internal LocalStack runtime API "LOCALSTACK_RUNTIME_ID": self.id, "LOCALSTACK_RUNTIME_ENDPOINT": self.runtime_executor.get_runtime_endpoint(), # used by the init to spawn the x-ray daemon # LOCALSTACK_USER conditionally added below }
LOCALSTACK_HOST
は入っていないのですが…。
まとめ
LocalStack内で動作しているAWS Lambda関数から、LocalStackの別のリソースにアクセスするための情報をLOCALSTACK_HOSTNAME
環境変数を使うことで取得できることを確認しました。
最初はLOCALSTACK_HOST
環境変数を見るべきだとドキュメントを読んでハマったり、テストのことも考えた構成にしたら
いろいろハマりましたが、最終的にはやりたかったことは全部確認できたので良かったかなと。
今後、環境を使い分けていく時のやり方のメモとして。