AWS Emulatorは、LocalStackの代わりになるのか?

コードを読まないAIエンジニア
サムネイル

AWS Emulatorは、AWSが2026年11月11日に発表した公式のローカルエミュレータで、AWSアカウントなし・無料でAWSのAPIを手元に再現します。LocalStackは2026年3月から認証トークンが必須になり、仕事で使うには有料プランが要ります。その代わりになるのでしょうか?

Apple siliconのMacとDockerでv1.0.322を動かすと、S3・DynamoDB・SQS・Lambdaを使うコードはそのまま流れ、TerraformとCDKも通りました。これらのサービスの基本操作を使うテストを回す用途なら、代わりになります。IAMの権限設計を確かめる用途には向きません。

AWS Emulatorを手元のDockerで立ち上げるには?

ビルド済みのイメージは配られていない

LinuxはRPMかDebのパッケージで入りますが、macOSとWindowsは公式ドキュメントのDockerfileから自分でイメージを作ります。ビルドは33秒、イメージは1.16GBでした。

# IntelのMacは aarch64 を x86_64 に替える
cat > Dockerfile <<'EOF'
FROM public.ecr.aws/amazonlinux/amazonlinux:2023-minimal
ADD https://downloads.aws.com/aws-emulator/packages/aws-emulator/LTS/latest/aws-emulator-aarch64.rpm /tmp/aws-emulator.rpm
RUN rpm -i /tmp/aws-emulator.rpm && rm /tmp/aws-emulator.rpm \
    && mkdir -p /home/emulator && chown 1000 /home/emulator
USER 1000
ENV HOME=/home/emulator
EXPOSE 9297
ENTRYPOINT ["aws-emulator", "-b", "0.0.0.0"]
EOF
docker build -t aws-emulator .

mkdir -p ~/.aws/emulator
docker run --rm --memory=2g --memory-swap=2g -p 127.0.0.1:9297:9297 \
  -e HOME="$HOME" -v "$HOME/.aws/emulator:$HOME/.aws/emulator" \
  aws-emulator -p 9297 --telemetry off

起動は1秒足らず、テレメトリは自分で切る

エミュレータは起動のたびに、そのとき限りのアクセスキー・設定ファイル・証明書を~/.aws/emulator/9297/に作ります。-vは、コンテナが作ったこれらのファイルを手元のCLIやSDKから読めるようにする指定です。-e HOMEでコンテナの中のホームを手元と同じパスにそろえると、設定ファイルに書かれるパスが手元でもそのまま通ります。-p 127.0.0.1:9297:9297は自分のマシンにだけポートを開く指定、--memory=2gは使うメモリの上限で、超えるとコンテナが止まって作ったリソースも消えます。

何も指定しないと、匿名の利用状況とエラーのデータをAWSに送る設定(テレメトリ)が有効なまま起動するので、--telemetry offで止めました。docker runから、ヘルスチェックcurl --cacert ~/.aws/emulator/9297/ca.pem https://127.0.0.1:9297/__admin/healthがokを返すまで0.56秒です。

普段のAWS CLIとboto3のコードは、環境変数だけでAWS Emulatorに向く

AWS_ENDPOINT_URLを設定すると、同じコードの送り先が本物のAWSから手元のAWS Emulator(127.0.0.1:9297)に変わる図

宛先と鍵の置き場を、環境変数8個で手元に向ける

AWS CLIやSDKはリージョンから送り先を決めていて、それをAWS_ENDPOINT_URLで手元に書き換えるのがendpoint URLの差し替えです。エミュレータは署名のない要求を断り、通信もHTTPSなので、鍵と証明書の場所も渡します。

export AWS_ENDPOINT_URL=https://127.0.0.1:9297
export AWS_SHARED_CREDENTIALS_FILE=~/.aws/emulator/9297/emulator_credentials
export AWS_CONFIG_FILE=~/.aws/emulator/9297/config
export AWS_CA_BUNDLE=~/.aws/emulator/9297/ca.pem
export AWS_EC2_METADATA_DISABLED=true
export AWS_DISABLE_HOST_PREFIX_INJECTION=true
export AWS_REGION=us-east-1
export AWS_DEFAULT_REGION=us-east-1
# 本物の認証情報は外しておく
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_PROFILE

AWS_EC2_METADATA_DISABLEDはEC2上で本物の認証情報を拾わないため、AWS_DISABLE_HOST_PREFIX_INJECTIONはSDKが一部の操作でホスト名に接頭辞を足すのを止めるためです。AWS_CONFIG_FILEを差し替えるので、このシェルでは普段のprofileの設定は効きません。

S3とDynamoDBは、条件付き書き込みの失敗まで通った

試したのはAWS CLI 2.40.1とboto3 1.44.1です。aws sts get-caller-identityはAccount 000000000000を返し、S3はバケット作成から読み出しまでの4コマンドが1.7秒で通って、アップロードした中身hello-emulatorが返りました。

AWS Emulatorの起動とSTS、S3の操作結果(実際の出力を画像にしたもの)

DynamoDBもboto3でテーブル作成とput・getが通り、条件付き書き込みの失敗はConditionalCheckFailedExceptionで返りました。どちらも、コードにはエンドポイントも鍵も書いていません。

SQSからLambdaまで、AWS EmulatorはDockerソケットなしでつながった

Lambdaの関数は、Dockerを渡さなくても実行された

LocalStackはLambdaの実行にDockerを使うので、コンテナにDockerソケットを渡す設定が要ります。AWS Emulatorでpython3.12の関数をzipで作って呼ぶと、その設定なしで0.53秒で返り、動いていたプロセスはaws-emulatorの1つだけでした。試したランタイムはPythonです。

イベントソースマッピングは、ロールの権限不足で一度断られた

ロールにSQSの権限を付けずにSQSとLambdaをつなぐと、InvalidParameterValueException(ReceiveMessageを呼ぶ権限がない)で止まりました。エミュレータが見たのは、つなぐときにロールが必要な権限を持っているかという1か所です。公式は、IAMポリシー全般は本物どおりに評価しないと明言しているので、権限の設計を確かめるテストには使えません。

AWSLambdaSQSQueueExecutionRoleを付けて作り直すと、溜まっていた2件が1秒以内に処理され、ログに2件の中身が出ました。

権限不足で断られたイベントソースマッピングと、権限を付けたあとのLambdaのログ(実際の出力を画像にしたもの)

次は、同じ経路をTerraformとCDKで作ってみます。

TerraformとCDKは、AWS Emulatorにそのまま流せるか?

AWS provider 6系では、S3バケットだけがエラーで止まった

S3・DynamoDB・SQS・IAMロール・ポリシーの付与・Lambda・SQSとLambdaのつなぎの7個を、環境変数を設定したままterraform applyしました。AWS provider 6.73.0では6個が作れ、S3バケットだけがタグの読み出しでlookup 000000000000.127.0.0.1: no such hostと失敗しました。

タグの読み出しで呼ばれていたのは、S3 Controlという別のAPIです。S3 Controlの要求はSDKがホスト名にアカウントIDを付け、これはAWS_DISABLE_HOST_PREFIX_INJECTIONでも止められないので届かない、と公式ドキュメントにあります。

providerを5系に下げると、7個すべて作れた

terraform {
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}

# 接続先は環境変数 AWS_ENDPOINT_URL などで AWS Emulator に向けている
provider "aws" {}

5.100.0ではApply complete! Resources: 7 addedとなり、作ったキューに送るとLambdaまで届きました。6系のまま避ける方法は見つけていないので、5系に固定しています。

Terraformのprovider 6系でのエラーと、5系でのapply成功(実際の出力を画像にしたもの)

CDKはbootstrapからdeployまで通った

cdk bootstrapのあとcdk deployでS3・DynamoDB・SQS・Lambdaのスタックを作ると、9/9がCREATE_COMPLETEで7.59秒でした。普段のcdkコマンドのままで、Node.jsにはNODE_EXTRA_CA_CERTS=$HOME/.aws/emulator/9297/ca.pemで証明書を渡します。

CDKのdeployで9個のリソースが作成された出力(実際の出力を画像にしたもの)

CDKが使うCloudFormationは、本物との一致率を示す点数が19%しかないのに、です。この点数はどう読めばいいのでしょうか?

fidelity scoreで、使うサービスの再現度を先に見る

AWSは、本物のAWSで確かめた挙動を書いた短いテストを公開しています。エミュレータがそれを通った割合をサービスごとに出したものがfidelity scoreで、ステータスコードや作られたリソースまで本物と一致して、はじめて通過と数えます。

v1.0.322のリリースに添付されたscores.json.gzでは、全体の通過が56.5%でした。S3は63%、DynamoDBとSQSは68%、Lambdaは52%、CloudFormationは19%です。

自分のスタックが使うサービスは何%でしょうか?このファイルはGitHubのReleasesページで、各版のAssetsに付いています。中身はテスト1件ごとの記録なので、使う版のファイルを落として、たとえばS3なら次のように数えます。結果は1763 pass、1035 fail、16 skippedでした。

gunzip -c scores.json.gz \
  | jq -r '.units[] | select(any(.services[]; test("^com.amazonaws.s3#"))) | .outcome' \
  | sort | uniq -c
AWS Emulator v1.0.322のfidelity scoreの集計(実際の出力を画像にしたもの)

点数が低いサービスほど、自分のスタックで試して確かめる

1つのテストが複数のサービスをまたぐこともあり、点数はそのサービスが出てくるテスト全体の率です。自分が使う操作だけの率ではありません。CloudFormationが19%でも、基本的なリソースを作るだけの今回のCDKスタックは通りました。

未実装の操作はHTTP 501のNotImplementedで止まるので見つけやすい一方、実装済みの操作が本物と同じ結果を返すかは、自分のスタックを流して確かめます。

LocalStackの代わりに使う前に、確かめておくこと

LocalStackの最新版は、認証トークンなしでは起動しない

LocalStackの最新版(2026.10.7)は、トークンなしだとLicense activation failed!で止まりました。2026年3月にCommunity版のリポジトリがアーカイブされ、認証トークンが必須の1つのイメージになったためです。トークンは無料のHobbyプランでも得られますが、Hobbyは非商用向けです。AWS Emulatorは本体のライセンスがCC0(パブリックドメインに近い扱い)です。

比べる相手は、トークンなしで起動できる最後の4系の4.14です。

同じboto3スクリプトで分かれたのは、Lambdaと権限の確認

手順
AWS Emulator
LocalStack 4.14
Lambda 作成→呼び出し
OK
失敗(Docker not available)
権限のないロールでSQS→Lambdaをつなぐ(作成時の確認)
拒否
通る

S3・DynamoDB・SQSの基本操作と、失敗したときの返り方は両方とも同じでした。いまLocalStackで回しているテストがLambdaを含むなら、Dockerソケットの設定が要らなくなる分、移す手間は減ります。

4.14のLambdaは、Dockerソケットを渡さない今回の構成で失敗しました(渡した構成は試していません)。つなぐときの権限の確認は、AWS Emulatorが止め、4.14は通しました。

同じboto3スクリプトをAWS EmulatorとLocalStack 4.14に流した結果(実際の出力を画像にしたもの)

AWS Emulatorで本物と同じにならない所

  • IAMポリシーは本物どおりに評価されないので、セキュリティのテストには使わない(公式の明言)
  • 1アカウント・1リージョンの前提で使う
  • リソースはメモリにあり、止めると消える
  • エンドポイントは127.0.0.1のままにする。localhostだと一部のSDKがS3のバケット名をホスト名に入れる
  • 証明書は127.0.0.1とlocalhost向けなので、別のコンテナからホスト名で呼ぶと証明書エラーになる

最後の点は、呼ぶ側のコンテナをエミュレータのネットワークに相乗りさせると避けられました。--network container:<エミュレータ>で起動し、~/.aws/emulatorを共有すれば127.0.0.1のまま呼べます。docker composeのnetwork_mode: "service:<名前>"と同じ形です。

まずは自分のスタックが使うサービスの点数を見て、手持ちのテストを1本、AWS Emulatorに流してみてください。