結論:LocalStackで遅いと感じたらまずkumo v0.12を試す
v0.9〜v0.12のわずか2週間で対応サービスは74→76に拡大し、DynamoDB GSI/LSI/TTL、EventBridge API Destinations、SQS DLQ(RedrivePolicy)、KUMO_INIT_DIRによる初期化スクリプト実行など、本番ワークロードに近い機能が一気に揃った。LocalStackで動いていたテストはdocker-compose.ymlのimageを差し替えるだけ(:4566ポート互換)で乗り換え可能で、CI起動時間を秒単位に短縮できる。Go単一バイナリ・認証不要・Helmチャート提供で、CIから本格的なローカル統合テストまで幅広く活躍する。

kumoとは|Go製の軽量AWSエミュレータ・LocalStackポート互換

kumoは、Go言語で実装されたAWSサービスエミュレータだ。LocalStackと同じポート :4566 を使い、AWS SDK v2と完全互換。76のAWSサービスをローカルで再現でき、認証不要・単一バイナリで起動できる軽量さが特徴だ。CI/CDパイプライン高速化と、ローカル開発でのAWS依存を切る用途で活躍する。

「LocalStackは便利だがDockerが重く起動が遅い」「Vercel EmulateはDocker不要で良いがAWSカバレッジが浅い」——この2つの課題を同時に解決するのがkumoの立ち位置だ。LocalStackと同じポート(:4566)を採用しているため、既存のLocalStackテストコードのエンドポイント設定をそのまま流用できる。乗り換えコストが極めて低い。

kumo logo

GitHubで1,067スター(2026年4月時点、3月公開当初の676から約58%増)、活発に更新されているOSSで、ライセンスはMIT。商用・社内利用ともに自由だ。本記事ではkumo v0.12時点の起動方法、Go AWS SDK v2との連携実装、DynamoDB拡張・EventBridge API Destinations・SQS DLQなどの新機能、CI/CDへの組み込みまで実例コード付きで解説する。

graph LR App["Goアプリケーション
(AWS SDK v2)"] App -->|"BaseEndpoint:
http://localhost:4566"| K["kumo v0.12
(:4566)"] K --> S3["S3 / DynamoDB
(GSI/LSI/TTL/Transact)"] K --> Q["SQS / SNS / EventBridge
(DLQ / API Destinations)"] K --> Sec["IAM / KMS / Cognito
Secrets Manager"] K --> Mon["CloudWatch / X-Ray"] K --> Net["API Gateway / Route 53"] K --> Mail["SES v2 / Pinpoint SMS
(送信内容を/kumo/で確認)"] K -.->|"任意"| D["KUMO_DATA_DIR
永続化"] K -.->|"任意"| I["KUMO_INIT_DIR
初期化スクリプト"] style K fill:#d4edda,stroke:#28a745 style D fill:#fff3cd,stroke:#ffc107 style I fill:#fff3cd,stroke:#ffc107

kumoの最大の特徴は「LocalStack互換ポート」と「Go単一バイナリ」の組み合わせ。LocalStackで動いていたテストをそのまま走らせつつ、CI環境ではDockerを排除して起動時間を大幅短縮できる。

この章のポイント
kumoはGo製・単一バイナリ・LocalStack互換ポート(:4566)のAWSエミュレータ
v0.12で76サービス対応・AWS SDK v2と完全互換・認証不要
LocalStackからの乗り換え・CI高速化・ローカル開発の3用途で強い
v0.9〜v0.12でDynamoDB拡張・EventBridge強化・SQS DLQ・初期化スクリプト対応

v0.9〜v0.12アップデート|DynamoDB拡張・EventBridge強化・初期化スクリプト

2026年4月、kumoは2週間でv0.9.0からv0.12.0まで4つのマイナーリリースを重ね、本番に近いAWS挙動が大量に追加された。リライト前の記事は3月時点(v0.7前後)の内容だったため、ここでは差分を整理する。

リリース別の主要機能

バージョン リリース日 主な追加機能
v0.9.0 2026-04-16 Neptune・DocumentDB・Redshift追加、SQS タグ操作、Helmチャート公開、SSM SecureString マスキング
v0.10.0 2026-04-21 Route 53 GetChange、SES v2の送信メール取得用 /kumo/ エンドポイント、Pinpoint SMS Voice v2
v0.11.0 2026-04-22 DynamoDB ConditionExpression / GSI / BatchWriteItem / TransactWriteItems、EventBridge イベントパターンマッチング
v0.12.0 2026-04-23 KUMO_INIT_DIR、DynamoDB LSI / TTL / UpdateItem ADD・DELETE、EventBridge API Destinations(実HTTP配信)、SQS RedrivePolicy(DLQ)、SES v2 Raw email、S3 CORS / Notification stub
v0.9〜v0.12の意味
これまでのkumoは「APIシグネチャを返すだけ」のスタブ実装が多かった。v0.11以降はDynamoDBインデックス検索・条件式・トランザクションEventBridgeのパターンマッチと外部HTTP配信SQSのDLQ移送といった「振る舞いまで再現する」フェーズに入った。本番ロジックの統合テストにそのまま使える領域が一気に広がった。

数字で見るアップデート

  • 対応サービス数: 74 → 76(Redshift・DocumentDB追加)
  • カテゴリ数: 8 → 13(Application Integration・Analytics & ML・Developer Tools・Other Servicesが新カテゴリ化)
  • GitHubスター: 676 → 1,067(+58%)
  • 配布形態: Docker / バイナリ → Docker / バイナリ / Helmチャート

互換性に関する注意

KUMO_HOST KUMO_PORT KUMO_LOG_LEVEL KUMO_DATA_DIR の4つの環境変数は変更なし。新たに KUMO_INIT_DIR が加わったが任意指定なので既存のCI設定は壊れない。v0.7→v0.12のアップグレードはDockerイメージのタグを差し替えるだけで完了する。


kumoの使い方|Docker・バイナリ・Helm・Docker Composeで起動

kumoの起動方法はv0.9でHelmチャートが追加され、4パターンになった。用途に応じて選ぶ。

Dockerで起動(最も簡単)

公式イメージが GitHub Container Registry で配布されている。Docker一発で全サービスが動く。

docker run -p 4566:4566 ghcr.io/sivchari/kumo:latest

データを永続化したい場合は KUMO_DATA_DIR とボリュームを指定する。

docker run -p 4566:4566 \
  -e KUMO_DATA_DIR=/data \
  -v kumo-data:/data \
  ghcr.io/sivchari/kumo:latest

v0.8以降、Dockerイメージは非rootユーザーで起動する設定(PR #398)に変更されている。Kubernetes環境のPodSecurityPolicyやrestrictedプロファイルでもそのまま動く。

バイナリで起動(Docker不要)

Goがあればソースからビルドして単一バイナリで起動できる。Docker依存を完全に排除したいCI/CD環境ではこちらが速い。

git clone https://github.com/sivchari/kumo.git
cd kumo
make build
./bin/kumo

# データ永続化付き
KUMO_DATA_DIR=./data ./bin/kumo

# 起動時に初期化スクリプトを実行(v0.12新機能)
KUMO_INIT_DIR=./init ./bin/kumo

ビルド済みリリースもGitHubのReleasesから配布されているため、CIでは curl で取得するだけでも動かせる。

Helmチャートで起動(v0.9以降・Kubernetes向け)

v0.9.0でHelmチャート(PR #399)が追加された。Kubernetes上で統合テスト環境を組む場合に便利だ。

git clone https://github.com/sivchari/kumo.git
cd kumo
helm install kumo ./charts/kumo --namespace kumo --create-namespace
kubectl port-forward svc/kumo 4566:4566 -n kumo

charts/kumo/values.yaml でレプリカ数・リソース制限・サービスタイプを調整できる。CIではephemeralな環境でテスト後に helm uninstall で破棄するのが定石。

Docker Composeで他サービスと連携

アプリケーションコンテナと一緒に立ち上げるパターン。テスト用Compose定義の典型例だ。

services:
  kumo:
    image: ghcr.io/sivchari/kumo:latest
    ports:
      - "4566:4566"
    environment:
      - KUMO_DATA_DIR=/data
      - KUMO_INIT_DIR=/init
    volumes:
      - kumo-data:/data
      - ./init:/init:ro

  app:
    build: .
    depends_on:
      - kumo
    environment:
      - AWS_ENDPOINT_URL=http://kumo:4566
      - AWS_ACCESS_KEY_ID=test
      - AWS_SECRET_ACCESS_KEY=test

volumes:
  kumo-data:

設定可能な環境変数は以下の5つ。設定項目が極端に少ないため学習コストがほぼゼロ。

環境変数 デフォルト 説明
KUMO_HOST 0.0.0.0 バインドアドレス
KUMO_PORT 4566 ポート(LocalStack互換)
KUMO_LOG_LEVEL info ログレベル(debug/info/warn/error)
KUMO_DATA_DIR unset 永続化ディレクトリ。未設定ならin-memory
KUMO_INIT_DIR unset 起動時に実行するシェルスクリプトのディレクトリ(v0.12新機能)

kumo vs LocalStack vs Vercel Emulate|AWSエミュレータ3製品比較

AWSエミュレータの選択肢は実質3つに集約されている。ユースケース別の使い分けを横並びで整理する。

比較表:kumo / LocalStack / Vercel Emulate

項目 kumo v0.12 LocalStack Vercel Emulate
実装言語 Go Python TypeScript (Node.js)
配布形態 単一バイナリ + Docker + Helm Dockerイメージ npxパッケージ
Docker依存 ❌ なし(任意) ✅ 必須 ❌ なし
メモリ使用量 軽量(数十MB) 重量(数百MB〜1GB+) 軽量(数十MB)
対応サービス AWS 76サービス AWS 80+サービス AWS基本+Vercel/GitHub/Slack等7マルチ
ポート :4566(LocalStack互換) :4566 サービスごと(4000-4006)
AWS SDK連携 Go SDK v2と完全互換 全言語SDK対応 Node SDK中心
DynamoDB詳細 GSI/LSI/TTL/Transact対応 同等 限定的
EventBridge パターンマッチ + API Destinations 同等 非対応
SQS DLQ RedrivePolicy対応 対応 非対応
データ永続化 KUMO_DATA_DIRで対応 ボリュームで対応 reset()でメモリリセット
初期化スクリプト KUMO_INIT_DIR initハンドラ テストコード内
マルチクラウド ❌ AWS専業 ❌ AWS専業 ✅ Slack/GitHub等もサポート
料金 OSS完全無料(MIT) OSS無料 + Pro有料 OSS完全無料
graph TD A["何をテストしたい?"] A -->|"AWSのみ・Go中心
LocalStackから移行したい"| K["kumo v0.12
(Go単一バイナリ・76サービス)"] A -->|"AWS本格インフラ・IaC検証
Lambda/RDS/ECS実行が必要"| L["LocalStack
(Docker・80+サービス)"] A -->|"AWS基本 + Slack/GitHub
マルチサービスを軽量に"| V["Vercel Emulate
(Node.js・マルチクラウド)"] K --> K2["LocalStack互換ポート
endpoint変更不要で乗り換え可"] L --> L2["LocalStack Pro検討
(高度AWSサービス)"] V --> V2["npx emulate で
Slack/GitHub同時起動"] style K fill:#d4edda,stroke:#28a745

kumoを選ぶべきケース

  • LocalStackから乗り換えたい:4566 ポート互換のためアプリ側のエンドポイント設定を変更せずに済む
  • CI/CD起動時間を秒単位に短縮したい:Goバイナリは起動が極めて高速、Dockerイメージpullも不要にできる
  • GoでAWS連携アプリを開発している:AWS SDK v2との互換性が手厚く実装されている
  • DynamoDBインデックスやイベント駆動を本気でテストしたい:v0.11/v0.12でGSI・LSI・EventBridge API Destinationsが揃った
  • ローカル開発で永続化したいKUMO_DATA_DIR で再起動後も状態維持できる

LocalStackを継続すべきケース

  • Lambda・ECS・API Gateway等の実行環境が必要:kumoは「APIエミュレート」だが、関数実行エンジン自体は提供しない
  • Terraform/CDK/CloudFormationでIaC検証:LocalStackはインフラコード実行環境としての実績が厚い
  • エンタープライズ商用サポートが必須:LocalStack Proの保証が必要な場面

Vercel Emulateを選ぶべきケース

  • AWS+Slack/GitHubの統合テスト:1ツールでマルチサービスを同時エミュレート
  • TypeScript/Node.js中心のプロジェクトcreateEmulator() でテストフレームワークから細かく制御
  • AWSは基本機能(S3/SQS/IAM/STS)だけでよい:高度なAWSサービスを使わないならEmulateの軽さが活きる

詳細はVercel Emulate入門で解説している。kumoが「AWS深掘り型」、Emulateが「マルチクラウド広く浅く型」と覚えると選びやすい。

第4の選択肢:MiniStack(実コンテナ起動が強み)

2026年4月時点で、もう一つの有力な選択肢が登場した。MiniStack(MIT・Python製のAWSローカルエミュレータ・LocalStack有償化への対抗馬) だ。kumoと同じくLocalStack互換ポート(:4566)を採用しているが、設計思想が大きく違う。

観点 kumo MiniStack
言語 Go Python
配布 単一バイナリ + Docker pip / Docker
起動速度 <1秒 <1秒
対応サービス 76 40+
RDS/ElastiCache モック 実Postgres/Redisコンテナを起動
EKS/ECS モック 実k3s/Dockerコンテナを起動
データ永続化 KUMO_DATA_DIR PERSIST_STATE=1
主戦場 CI高速化・Goプロジェクト 本番に近い実DB統合テスト

「実Postgresに対してSQLが通るか確認したい」「実k3sクラスタでHelmチャートをテストしたい」といった本番並みの統合テストを求めるなら MiniStack、「とにかく軽くて速いCI/CDテスト」を求めるなら kumo、と棲み分けられる。

実は 3者併用 も現実的だ。

  • PRごとのCI高速テスト → kumo(Go単一バイナリ・最速起動)
  • 統合テスト(実DB必要) → MiniStack(実Postgresコンテナ)
  • Slack/GitHub連携テスト → Vercel Emulate(AWS以外のSaaSも対応)

これで LocalStack Pro($35〜/月)に頼らずに本番並みの網羅性 が実現する。LocalStack有償化に伴うコスト増を、3つのMITライセンスOSSの組み合わせで回避できる。


Go AWS SDK v2との連携実装|S3・SQS・DynamoDB・Secrets Manager

kumoの真価は Go AWS SDK v2 との完全互換性にある。BaseEndpointhttp://localhost:4566 に指定するだけで、本番AWSと同じコードが動く。

S3:バケット作成とオブジェクト操作

package main

import (
    "context"
    "strings"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/credentials"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
    cfg, _ := config.LoadDefaultConfig(context.TODO(),
        config.WithRegion("us-east-1"),
        config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("test", "test", "")),
    )

    client := s3.NewFromConfig(cfg, func(o *s3.Options) {
        o.BaseEndpoint = aws.String("http://localhost:4566")
        o.UsePathStyle = true   // ローカルエミュレータでは必須
    })

    // バケット作成
    client.CreateBucket(context.TODO(), &s3.CreateBucketInput{
        Bucket: aws.String("my-bucket"),
    })

    // オブジェクト書き込み
    client.PutObject(context.TODO(), &s3.PutObjectInput{
        Bucket: aws.String("my-bucket"),
        Key:    aws.String("hello.txt"),
        Body:   strings.NewReader("Hello, World!"),
    })
}

ポイントは UsePathStyle = true(path-style URL)を指定すること。LocalStackやVercel Emulateも同様の制約があり、ローカルエミュレータ共通の作法だ。v0.12からはS3にCORS応答ヘッダとOPTIONSプリフライト対応が追加され、ブラウザ直叩きの統合テストもしやすくなった。

SQS:キュー作成と送受信

client := sqs.NewFromConfig(cfg, func(o *sqs.Options) {
    o.BaseEndpoint = aws.String("http://localhost:4566")
})

// キュー作成
result, _ := client.CreateQueue(context.TODO(), &sqs.CreateQueueInput{
    QueueName: aws.String("my-queue"),
})

// メッセージ送信
client.SendMessage(context.TODO(), &sqs.SendMessageInput{
    QueueUrl:    result.QueueUrl,
    MessageBody: aws.String("Hello from SQS!"),
})

// メッセージ受信
messages, _ := client.ReceiveMessage(context.TODO(), &sqs.ReceiveMessageInput{
    QueueUrl: result.QueueUrl,
})

for _, msg := range messages.Messages {
    fmt.Println(*msg.Body)
}

DynamoDB:テーブル作成とアイテム書き込み

client := dynamodb.NewFromConfig(cfg, func(o *dynamodb.Options) {
    o.BaseEndpoint = aws.String("http://localhost:4566")
})

// テーブル作成
client.CreateTable(context.TODO(), &dynamodb.CreateTableInput{
    TableName: aws.String("users"),
    KeySchema: []types.KeySchemaElement{
        {AttributeName: aws.String("id"), KeyType: types.KeyTypeHash},
    },
    AttributeDefinitions: []types.AttributeDefinition{
        {AttributeName: aws.String("id"), AttributeType: types.ScalarAttributeTypeS},
    },
    BillingMode: types.BillingModePayPerRequest,
})

// アイテム書き込み
client.PutItem(context.TODO(), &dynamodb.PutItemInput{
    TableName: aws.String("users"),
    Item: map[string]types.AttributeValue{
        "id":   &types.AttributeValueMemberS{Value: "user-1"},
        "name": &types.AttributeValueMemberS{Value: "Alice"},
    },
})

Secrets Manager:認証情報のローカル管理

CIテストでもSecrets Managerをローカル再現できる。本番のシークレットを使わずに同じインターフェースで動かせるのは大きな利点だ。

client := secretsmanager.NewFromConfig(cfg, func(o *secretsmanager.Options) {
    o.BaseEndpoint = aws.String("http://localhost:4566")
})

// シークレット作成
client.CreateSecret(context.TODO(), &secretsmanager.CreateSecretInput{
    Name:         aws.String("my-secret"),
    SecretString: aws.String(`{"username":"admin","password":"secret123"}`),
})

// シークレット取得
result, _ := client.GetSecretValue(context.TODO(), &secretsmanager.GetSecretValueInput{
    SecretId: aws.String("my-secret"),
})

fmt.Println(*result.SecretString) // {"username":"admin","password":"secret123"}

DynamoDB拡張機能|GSI・LSI・トランザクション・TTL(v0.11/v0.12)

v0.11〜v0.12で、DynamoDBは「テーブル作成と単純なPut/Getだけ」のスタブ実装から、本番に近い検索・更新・トランザクション機能を備えた実装に進化した。

Global Secondary Index(GSI)でセカンダリ検索

CreateTable 時に GlobalSecondaryIndexes を指定すると、GSIが作られる。Query API でインデックスキーから引ける。

client.CreateTable(context.TODO(), &dynamodb.CreateTableInput{
    TableName: aws.String("orders"),
    KeySchema: []types.KeySchemaElement{
        {AttributeName: aws.String("order_id"), KeyType: types.KeyTypeHash},
    },
    AttributeDefinitions: []types.AttributeDefinition{
        {AttributeName: aws.String("order_id"), AttributeType: types.ScalarAttributeTypeS},
        {AttributeName: aws.String("user_id"), AttributeType: types.ScalarAttributeTypeS},
    },
    GlobalSecondaryIndexes: []types.GlobalSecondaryIndex{
        {
            IndexName: aws.String("UserIdIndex"),
            KeySchema: []types.KeySchemaElement{
                {AttributeName: aws.String("user_id"), KeyType: types.KeyTypeHash},
            },
            Projection: &types.Projection{ProjectionType: types.ProjectionTypeAll},
        },
    },
    BillingMode: types.BillingModePayPerRequest,
})

// GSI経由でクエリ
out, _ := client.Query(context.TODO(), &dynamodb.QueryInput{
    TableName:              aws.String("orders"),
    IndexName:              aws.String("UserIdIndex"),
    KeyConditionExpression: aws.String("user_id = :uid"),
    ExpressionAttributeValues: map[string]types.AttributeValue{
        ":uid": &types.AttributeValueMemberS{Value: "user-42"},
    },
})

TransactWriteItems で複数テーブル横断のアトミック更新

v0.11で TransactWriteItems / TransactGetItems がサポートされた。決済・在庫引き当てなどの整合性が要求されるテストをそのまま走らせられる。

client.TransactWriteItems(context.TODO(), &dynamodb.TransactWriteItemsInput{
    TransactItems: []types.TransactWriteItem{
        {
            Put: &types.Put{
                TableName: aws.String("orders"),
                Item: map[string]types.AttributeValue{
                    "order_id": &types.AttributeValueMemberS{Value: "o-1"},
                    "amount":   &types.AttributeValueMemberN{Value: "1200"},
                },
            },
        },
        {
            Update: &types.Update{
                TableName: aws.String("inventory"),
                Key: map[string]types.AttributeValue{
                    "sku": &types.AttributeValueMemberS{Value: "SKU-A"},
                },
                UpdateExpression: aws.String("SET stock = stock - :n"),
                ExpressionAttributeValues: map[string]types.AttributeValue{
                    ":n": &types.AttributeValueMemberN{Value: "1"},
                },
                ConditionExpression: aws.String("stock >= :n"),
            },
        },
    },
})

TTL自動削除でクリーンアップロジックをテスト

v0.12で UpdateTimeToLive が機能するようになり、TTL属性に基づいた自動削除がkumo内部でも発動する。期限切れキャッシュやセッション削除のロジックをローカルで検証できる。

client.UpdateTimeToLive(context.TODO(), &dynamodb.UpdateTimeToLiveInput{
    TableName: aws.String("sessions"),
    TimeToLiveSpecification: &types.TimeToLiveSpecification{
        AttributeName: aws.String("expires_at"),
        Enabled:       aws.Bool(true),
    },
})

expires_at にUnix秒(過去日時)が入ったアイテムは順次削除される。

注意:本番DynamoDBとの差異
TTLの削除タイミングは本番AWSが「最大48時間遅延」なのに対し、kumoは即座に近いタイミングで削除する。テストの期待値はこの差を踏まえて書く必要がある。GSIの結果整合性も本番より厳密(強整合に近い)。厳密な遅延挙動を再現したい場合は、テストコード側でラグを挟むラッパを用意する。

EventBridge API DestinationsとSQS DLQ|本番に近いイベント駆動テスト

v0.11/v0.12でイベント駆動アーキテクチャの主要パーツがほぼ揃った。ローカルだけで「イベント発火→ルーティング→外部HTTP配信/DLQ移送」の完全なフローを検証できる。

EventBridge:パターンマッチング + API Destinations

PutEvents で投げたイベントは、ルールの EventPattern にマッチするとSQSや外部HTTPエンドポイントへ配信される。kumoは v0.12 で API Destinations(CreateConnection / CreateApiDestination)に対応し、実際にHTTPリクエストを発行するようになった(PR #465)。

// 1. Connection作成
ebClient.CreateConnection(context.TODO(), &eventbridge.CreateConnectionInput{
    Name:               aws.String("webhook-conn"),
    AuthorizationType:  types.ConnectionAuthorizationTypeApiKey,
    AuthParameters: &types.CreateConnectionAuthRequestParameters{
        ApiKeyAuthParameters: &types.CreateConnectionApiKeyAuthRequestParameters{
            ApiKeyName:  aws.String("X-API-Key"),
            ApiKeyValue: aws.String("secret"),
        },
    },
})

// 2. API Destination作成
ebClient.CreateApiDestination(context.TODO(), &eventbridge.CreateApiDestinationInput{
    Name:                aws.String("webhook-dest"),
    ConnectionArn:       aws.String("arn:aws:events:us-east-1:000000000000:connection/webhook-conn/..."),
    InvocationEndpoint:  aws.String("http://host.docker.internal:8080/webhook"),
    HttpMethod:          types.ApiDestinationHttpMethodPost,
})

// 3. ルールに紐づけ → PutEventsでHTTPに飛ぶ

テスト側では httptest.NewServer を立てて API Destination の宛先に指定し、配信内容(ヘッダ・ボディ・パラメータ)をassertするのが定石。

SQS:RedrivePolicyでDead Letter Queue

v0.12で CreateQueue / SetQueueAttributesRedrivePolicy 属性に対応した(PR #466)。maxReceiveCount を超えたメッセージは指定したDLQへ移送される。

// DLQを作成
dlq, _ := client.CreateQueue(context.TODO(), &sqs.CreateQueueInput{
    QueueName: aws.String("orders-dlq"),
})

// メインキューにRedrivePolicyを付与
client.CreateQueue(context.TODO(), &sqs.CreateQueueInput{
    QueueName: aws.String("orders"),
    Attributes: map[string]string{
        "RedrivePolicy": fmt.Sprintf(
            `{"deadLetterTargetArn":"%s","maxReceiveCount":"3"}`,
            "arn:aws:sqs:us-east-1:000000000000:orders-dlq",
        ),
    },
})

ReceiveMessageDeleteMessage 失敗を3回繰り返すと、メッセージはDLQ側で ReceiveMessage できる状態になる。リトライ上限・毒メッセージのアラートといった「本番では起こすのが難しい」テストケースをローカルで安全に再現できる。


データ永続化と初期化スクリプト|開発・CI両モードを使い分ける

kumoはin-memoryモード永続化モードを切り替えられる。CIではin-memoryで毎回クリーン、ローカル開発では永続化で再起動後も状態維持、という使い分けが定石だ。v0.12からはさらに起動時の初期化スクリプト実行もサポートした。

in-memoryモード(CI向け・デフォルト)

KUMO_DATA_DIR を指定しなければ全データはプロセス内メモリに保持される。プロセス停止で消失するため、CI/CDではテスト間の汚染を心配する必要がない。

./bin/kumo  # in-memoryモードで起動

永続化モード(ローカル開発向け)

KUMO_DATA_DIR=./data を指定すると、SIGTERM/SIGINT時に各サービスの状態が ./data/{service}.json に保存される。次回起動時に自動復元されるため、開発中のデータを手動で再作成する手間が消える。

KUMO_DATA_DIR=./data ./bin/kumo
./data/
  s3.json
  sqs.json
  dynamodb.json
  iam.json
  secretsmanager.json
  ...

書き込みは tmp file + rename によるアトミック処理で行われるため、クラッシュ時のデータ破損も防止される。一方、SQSのin-flightメッセージやS3のmultipart uploadなど一時的な状態は永続化対象外なので注意。

初期化スクリプト(KUMO_INIT_DIR・v0.12新機能)

PR #461で追加された KUMO_INIT_DIR を指定すると、kumoはサーバ起動直後に指定ディレクトリ内のシェルスクリプトをアルファベット順で順次実行する。テストの前提となるバケット・キュー・テーブルをコードで毎回作る代わりに、初期データを一発で投入できる。

mkdir -p ./init
cat > ./init/01-create-bucket.sh <<'EOF'
#!/bin/bash
aws --endpoint-url=http://localhost:4566 s3 mb s3://my-bucket
aws --endpoint-url=http://localhost:4566 s3 cp /init/seed.json s3://my-bucket/seed.json
EOF
chmod +x ./init/01-create-bucket.sh

cat > ./init/02-create-table.sh <<'EOF'
#!/bin/bash
aws --endpoint-url=http://localhost:4566 dynamodb create-table \
  --table-name users \
  --attribute-definitions AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST
EOF
chmod +x ./init/02-create-table.sh

KUMO_INIT_DIR=./init ./bin/kumo

スクリプトは1本ずつ実行され、1本失敗しても後続はスキップされない。LocalStack Proの init-aws.sh 相当の挙動で、「テストの初期データはコードでなくスクリプトで管理したい」というニーズにぴったりはまる。

ログレベル:デバッグ時はDEBUGモードへ

問題発生時はログレベルを debug に上げると、リクエストボディまで出力される。

KUMO_LOG_LEVEL=debug ./bin/kumo
level=INFO  msg=request method=POST path=/ status=200 duration=61µs target=secretsmanager.CreateSecret
level=DEBUG msg="request body" body={"Name":"my-secret","SecretString":"..."}

target フィールドはJSON/Queryプロトコルサービス(Secrets Manager・DynamoDB・SQS等)で表示され、どのAPIアクションが呼ばれたかすぐ判別できる。CIで失敗した時の原因追跡が高速化する。


kumo独自エンドポイント|送信メール・SMSの内容をテストで確認

v0.10で追加された /kumo/ プレフィックス配下の独自エンドポイントは、AWS APIには存在しないがテスト検証用に重宝する。SES v2 で送ったメール・Pinpoint SMS Voice v2 で送ったSMSの中身を、HTTP GETで取得できる。

メソッド パス 用途
GET /kumo/ses/v2/sent-emails SES v2 SendEmail で送信したメール一覧
GET /kumo/pinpointsmsvoicev2/sent-messages Pinpoint SMS Voice v2 SendTextMessage で送信したSMS一覧

送信メールの確認例

# kumo起動中に
curl http://localhost:4566/kumo/ses/v2/sent-emails
{
  "SentEmails": [
    {
      "MessageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "FromEmailAddress": "[email protected]",
      "Destination": { "ToAddresses": ["[email protected]"] },
      "Subject": "Hello",
      "Body": "Hello, World!",
      "SentAt": "2026-04-26T00:00:00Z"
    }
  ]
}

これによって「ユーザー登録時に正しい件名・本文のメールが飛んだか」を、本番のメール配信を切らずにE2Eテストで検証できる。Pinpoint SMSも同じ要領で /kumo/pinpointsmsvoicev2/sent-messages から本文・宛先番号を確認できる。

テストでよくあるパターン
Goのテストではhttp.Get("http://localhost:4566/kumo/ses/v2/sent-emails")でJSONを取得し、件名・宛先・本文をassertするのが定番。本番のSES/Pinpointを切らずに送信内容まで検証できるのはローカルエミュレータならではの強み。

CI/CDパイプラインへの組み込み(GitHub Actions/Docker Compose)

kumoの真骨頂はCI/CD組み込みだ。LocalStackと違いDocker-in-Docker設定が要らないため、どんなCI環境にも素直に入る。

GitHub Actionsでのkumo起動

services: セクションに登録するとジョブ開始時にコンテナが立ち上がる。

# .github/workflows/test.yml
name: Test
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      kumo:
        image: ghcr.io/sivchari/kumo:latest
        ports:
          - 4566:4566
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with: { go-version: '1.22' }
      - name: Wait for kumo
        run: |
          for i in {1..30}; do
            curl -sf http://localhost:4566/ > /dev/null && break
            sleep 1
          done
      - name: Run tests
        env:
          AWS_ENDPOINT_URL: http://localhost:4566
          AWS_ACCESS_KEY_ID: test
          AWS_SECRET_ACCESS_KEY: test
        run: go test ./...

GitHub Actionsの services: セクションを使えばDocker-in-Dockerの複雑な設定が不要。ジョブ開始時に自動でコンテナが立ち上がり、終了時に破棄される。

LocalStackからの乗り換え:1行差分で完了

LocalStackで動いていたテストを kumo に切り替える場合、エンドポイントポートが同じ :4566 なのでほぼ変更不要。docker-compose.yml の image だけを差し替える。

 services:
-  localstack:
-    image: localstack/localstack
+  kumo:
+    image: ghcr.io/sivchari/kumo:latest
     ports:
       - "4566:4566"

この互換性こそがkumoの最大のセールスポイント。テストコード本体は1行も変えず、CIの速度だけが上がる。LocalStackで遅いと感じていたら試す価値がある。

AWS DevOps AgentのようにAWSを多用するエージェントを開発する際も、ローカル検証層としてkumoを噛ませると本番AWS課金を防ぎつつ高速イテレーションできる。コンテナサンドボックス層との組み合わせはAIO SandboxのようなフルDockerスタックと相互補完できる。


よくある質問(FAQ)

kumoとLocalStackの違いは?

kumoはGo製の単一バイナリで軽量・高速起動。LocalStackと同じポート(:4566)を使うため、endpoint 設定をそのまま流用できる。一方、対応サービス数はkumo 76、LocalStack 80+。Lambda・API Gateway・ECSなど高度なサービス実行が必要ならLocalStack(特にPro)が依然として有力。

v0.12で追加された主な機能は?

DynamoDBはGSI/LSI/TTL自動削除/TransactWrite/BatchWrite/ConditionExpression、EventBridgeはイベントパターンマッチングと API Destinations による実HTTP配信、SQSはRedrivePolicy(DLQ)、SES v2はEmailContent.Raw、KUMO_INIT_DIR による起動時シェルスクリプト実行、S3のCORS / Notification設定の受け入れなど。本番に近い挙動が一気に揃った。

kumoはAWS SDK v2と互換性がありますか?

完全に互換。BaseEndpointhttp://localhost:4566 を指定するだけで、Go AWS SDK v2のS3・SQS・DynamoDB・Secrets Managerなど全クライアントが動作する。認証は不要で、ダミー credentials(test/test)を渡せばOK。

kumoはDockerなしでも動きますか?

動く。Goでビルドした単一バイナリ ./bin/kumo で起動できるためDocker不要。GitHub Actionsの軽量ランナーや権限制限の厳しいCI環境でも使いやすい点がLocalStackとの大きな差別化。Docker版(ghcr.io/sivchari/kumo)とHelmチャートも用意されており用途に応じて選べる。

kumoでデータを永続化できますか?

可能。環境変数 KUMO_DATA_DIR=./data を指定するとSIGTERM/SIGINT時に各サービスの状態が {service}.json で保存され、再起動時に復元される。CIではin-memoryモード、ローカル開発では永続化モードと使い分けるのが定石。

kumoが対応するAWSサービスは?

現在76サービスに対応。Storage 8、Compute 4、Container 3、Database 3(RDS/Neptune/Redshift)、Messaging 8、Security 8、Monitoring 4、Networking 8、Application Integration 6(Step Functions/AppSync/SES v2/Pinpoint SMS Voice v2/Scheduler/Amplify)、Management 7、Analytics & ML 8、Developer Tools 2、Other 7。


まとめ:DynamoDB拡張・イベント駆動・初期化まで揃った2026年版AWSエミュレータ

kumoは「LocalStack互換ポート」「Go単一バイナリ」「76サービス対応」の3拍子に加え、v0.11/v0.12でDynamoDBインデックス・トランザクション・TTLEventBridge API Destinations による実HTTP配信SQS Dead Letter QueueKUMO_INIT_DIR による初期化スクリプトまで揃い、本番に近いAWS挙動をローカルで検証できる土台ができた。

Vercel Emulateが「マルチクラウドを浅く広く」、LocalStackが「AWSを深く・Lambda実行まで」という棲み分けの中で、kumoは「AWSだけを軽く・速く・年単位で急速にカバレッジ拡大」のポジションを担う。GoでAWS連携アプリを書いている開発者・LocalStackで起動時間に悩んでいるDevOpsエンジニアにとって、最初に試すべき選択肢と言える。

参照ソース