0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

floci + Colima で組む無料のローカルAWS開発環境 ― local / dev / prod を1つの Terraform モジュールで

0
Last updated at Posted at 2026-04-30

こんにちは。
トライベック株式会社の岡山です。

「Next.js + Cognito + API Gateway v2 + RDS」というよくある AWS 構成を、Mac の手元でひととおり動かせるローカル環境としてとりあえず組んでみました。
LocalStack Community では Cognito と API Gateway v2 が動かず詰んでいたのですが、floci という GraalVM 製のエミュレータに乗せ換えたら無料のまま要件を満たせたので、その構成と詰まりどころをまとめます。

TL;DR

  • Docker ランタイムは Colimabrew install 一発で入り、make setup から自動起動できるのでチーム導入の摩擦が少ない
  • AWS API エミュレータは flocihectorvent/floci)。Cognito User Pool / API Gateway v2 / Secrets Manager が無料で動くのが採用の決め手
  • Terraform は endpoints を差し替えるだけで local / dev / prod同じモジュールで運用。ローカル専用 IaC は書かない
  • エミュレートできない CloudFront / WAF / Route53 / Bedrock は enabled=false で素通りさせ、ローカルでは作らない方針に倒した
  • 検証環境は MacBook Pro 14(M4 / 32GB)+ macOS Tahoe 26.2。Colima には --cpu 4 --memory 8 --disk 60 を割り当てています

はじめに

想定した構成は「Next.js 14(static export)を S3 + CloudFront で配信、Cognito + API Gateway HTTP API v2 + Lambda で API、データは RDS MySQL / S3 / SQS / Secrets Manager」というよくあるパターンです。
特定のプロジェクトに紐づけたわけではなく、この一式が手元で完結する箱を一度ちゃんと作っておきたいというのが今回のモチベーションでした。クラウドに当てずに動作確認だけしたい場面が地味に多いので、検証用の素振り環境として組んでおく価値はあるはず、という感覚です。

最初は LocalStack Community で組もうとしたのですが、構成の中核となる Cognito User Pool / API Gateway v2 / Secrets Manager がいずれも Pro 限定で詰みました。
そこで無料のまま同じ要件を満たせる floci に切り替え、Colima と Terraform を組み合わせて local / dev / prod を 1 セットのモジュールで動かす形に落ち着いたので、その記録です。
特に「Cognito からトークンを取って API Gateway v2 の JWT Authorizer を素通しさせる」までの一連の流れを手元で再現したい方の参考になれば、という想定で書いています。

検証環境

区分 内容
マシン MacBook Pro 14インチ(2024年11月モデル)
チップ Apple M4 / メモリ 32 GB
OS macOS Tahoe 26.2
Colima colima start --cpu 4 --memory 8 --disk 60
Docker CLI / Compose Homebrew 版
Terraform 1.6.0 以上 / AWS Provider ~> 5.50
AWS CLI v2 系(AWS_ENDPOINT_URL 対応版)
floci hectorvent/floci:latest
MySQL mysql:8.0(RDS 代替として docker-compose で起動)
Next.js / Amplify 14.2 / aws-amplify@^6.6.0

floci + MySQL + Next.js + Terraform を同時に動かしてもピーク 5〜6GB 程度で、Colima に 8GB 割り当てておけば普段の IDE と並行しても余裕がありました。

1. Colima を選んだ理由

Docker Desktop ではなく Colima にした理由は、技術というより導入のしやすさの面が大きいです。

✅ ライセンスを気にせず brew install colima docker docker-compose で入れられる
make setupcolima status を見て未起動なら colima start を呼ぶだけで済む
--cpu 4 --memory 8 --disk 60 を宣言的に揃えられるので、別マシンで再現しても挙動が揃いやすい

Colima 自体は「lima(macOS 用 Linux VM ランナ)に Docker / containerd / Kubernetes を載せるラッパ」ですが、今回は素の Docker ランタイムとしてだけ使っています。

brew install colima docker docker-compose
colima start --cpu 4 --memory 8 --disk 60

make setup の中身は、必要ツールの存在確認と Colima 自動起動だけです。

.PHONY: setup
setup: ## 必要ツールの存在確認と colima 起動
	@command -v colima    >/dev/null || (echo "✗ colima not found"; exit 1)
	@command -v docker    >/dev/null || (echo "✗ docker not found"; exit 1)
	@command -v terraform >/dev/null || (echo "✗ terraform not found"; exit 1)
	@command -v aws       >/dev/null || (echo "✗ aws cli not found"; exit 1)
	@if ! colima status >/dev/null 2>&1; then \
		echo "colima is not running. Starting..."; \
		colima start --cpu 4 --memory 8 --disk 60; \
	else \
		echo "✓ colima is running."; \
	fi
	@[ -f .env ] || cp .env.example .env

この一発で起動まで持っていけるようにしておくと、後で他のマシンに移したときも make setup だけで再現できて気が楽です。

2. floci を採用した理由 ― LocalStack Community との比較

floci は GraalVM ネイティブイメージで動く AWS API エミュレータで、LocalStack 互換のエンドポイント(既定で http://localhost:4566)を提供してくれます。
今回触りたかったサービスを LocalStack Community / Pro と並べてみると、無料で組みたい前提だと選択肢がほぼ floci 一択になりました。

観点 floci(無料) LocalStack Community LocalStack Pro
Cognito User Pool
API Gateway v2 (HTTP API)
Secrets Manager
KMS
S3 / SQS
起動時間 〜24ms 数〜十数秒 同左
常駐メモリ 〜13MiB 数百MB〜 同左
商用利用 無料 無料 有償

GraalVM ネイティブイメージなのでとにかく軽く、make up から数秒でローカル AWS が立ち上がるのは試行錯誤の回転を速くしてくれます。
docker-compose.yml の中心はこれだけです。

services:
  floci:
    image: hectorvent/floci:${FLOCI_VERSION:-latest}
    container_name: aws-local-floci
    restart: unless-stopped
    ports:
      - "4566:4566"
      - "4510-4559:4510-4559"
    environment:
      - FLOCI_STORAGE_MODE=hybrid
      - FLOCI_DEFAULT_REGION=ap-northeast-1
      - FLOCI_SERVICES=s3,sqs,apigateway,apigatewayv2,lambda,iam,kms,secretsmanager,acm,cloudformation,rds,dynamodb,sts,logs,cloudwatch,events
      - FLOCI_DATA_DIR=/var/lib/floci
    volumes:
      - floci-data:/var/lib/floci
      - /var/run/docker.sock:/var/run/docker.sock
      - ./scripts/init-aws:/etc/floci/init/ready.d
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://localhost:4566/_floci/health"]
      interval: 5s
      timeout: 3s
      retries: 30

押さえておきたい挙動は3つです。

FLOCI_STORAGE_MODE=hybrid でホットデータはメモリ・コールドデータはディスク。make down してもデータが残る
init/ready.d に shell スクリプトを置けば起動後に自動実行される。CI でも同じパスで初期データを投入できる
/var/run/docker.sock をマウントしているのは、floci の RDS が内部で MySQL/Postgres コンテナを立てるため

ヘルスチェックは LocalStack 互換とは別の独自パス /_floci/health です。
ここを ready 判定に使わないと、STS が応答していても S3/SQS の初期化が終わっていないタイミングを掴んでしまうことがありました。

3. Terraform を local / dev / prod で同じモジュールに揃える

ここがこの構成のいちばんの肝で、ローカル専用の IaC は書かない方針に倒しました。
やっていることは、local 環境の providers.tfendpoints を floci に向けるだけです。

provider "aws" {
  region                      = var.region
  access_key                  = "test"
  secret_key                  = "test"
  s3_use_path_style           = true
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    s3             = var.endpoint_url
    sqs            = var.endpoint_url
    apigateway     = var.endpoint_url
    apigatewayv2   = var.endpoint_url
    lambda         = var.endpoint_url
    iam            = var.endpoint_url
    sts            = var.endpoint_url
    acm            = var.endpoint_url
    rds            = var.endpoint_url
    kms            = var.endpoint_url
    secretsmanager = var.endpoint_url
    cloudwatch     = var.endpoint_url
    cloudformation = var.endpoint_url
    logs           = var.endpoint_url
    dynamodb       = var.endpoint_url
    events         = var.endpoint_url
    cognitoidp     = var.endpoint_url
  }
}

dev / prodproviders.tf は、上記の endpoints ブロックを丸ごと外したシンプル版です。
モジュール側のコードは一切触りません。

エミュレートできないサービスは、ラッパーモジュールで count = var.enabled ? 1 : 0 パターンを使って local だけ素通りさせています。

module "cloudfront" {
  source        = "../../modules/cloudfront"
  enabled       = false           # ← local では作らない
  origin_bucket = module.s3.bucket_assets_id
}

module "cognito" {
  source         = "../../modules/cognito"
  enabled        = true            # ← local でも本番でも作る
  user_pool_name = "${var.project}-users"
}
サービス local(floci) dev / prod(AWS)
S3 / SQS / DynamoDB
Cognito User Pool
API Gateway v2 + JWT Authorizer
RDS MySQL △(compose の MySQL を直接)
CloudFront / WAF / Route53 ✗(enabled=false
CodePipeline / CodeBuild
Bedrock ✗(mock を別コンテナで)

「ローカルでは作れないものは作らない、ただしモジュール構成は揃える」と割り切った瞬間に、IaC の運用がだいぶ楽になりました。

4. ハンズオン:0 から Cognito 越しに API を叩くまで

実際に手を動かす流れです。.envAWS_ENDPOINT_URL=http://localhost:4566 を流しておくのがこの構成の前提になります。

brew install colima docker docker-compose terraform awscli jq
git clone <this-repo> && cd <this-repo>
cp .env.example .env
make setup     # ツール存在チェック + colima 起動

make up        # docker compose up -d → wait-for-floci → bootstrap-aws

make tf-init  ENV=local
make tf-apply ENV=local

make cognito-login   # テストユーザを作って IdToken / AccessToken を取得

ID_TOKEN="..."
make aws ARGS="apigatewayv2 get-apis"
curl -s -H "Authorization: Bearer $ID_TOKEN" \
  http://localhost:4566/restapis/<api_id>/dev/_user_request_/protected

make awsaws --endpoint-url=http://localhost:4566 を被せた薄いラッパなので、awscli の使い勝手は本番と同じです。
フロントエンド(Next.js + Amplify v6)と繋ぐ場合も、terraform output から Cognito の User Pool ID / Client ID を .env.local に流して npm run dev するだけで http://localhost:3000 で動きます。
Amplify 側の設定は標準のまま、何も特殊なことをしていないまま手元だけで動くのが、今回組んでみていちばん気持ちよかった点でした。

5. ハマりどころ

実際に組んでいて踏んだ落とし穴を、後続の自分のために残しておきます。

  • API Gateway v2 の JWT Authorizer は accessToken ではなく idToken を渡すaudience = Client ID で検証されるので、accessToken を投げると invalid_token で 401 が返ります。1時間悩みました
  • floci の Cognito は SRP が弱いUSER_SRP_AUTHNotAuthorizedException が出ることがあるので、ローカル限定で ALLOW_USER_PASSWORD_AUTH を許可するほうが安定します
  • S3 は s3_use_path_style = true を必ず指定する。忘れると bucket.localhost:4566 を引きに行って DNS 失敗。これは2回やらかしました
  • floci の RDS はエミュレートさせず compose の mysql を直接使うほうが速くて安定。アプリは localhost:3306 を見ればよく、RDS のエンドポイントは Terraform output から差し込むのは dev/prod のときだけにしました
  • ready 判定は /_floci/health を見るsts get-caller-identity を ready 判定に使うと、S3/SQS の初期化が間に合わずに bootstrap が空振りすることがあります
  • CloudFront / WAF / Route53 / Bedrock は割り切ってエミュレートしない。やろうとして時間を溶かすより、enabled=false で素通しにして本番でだけ確認するほうが結果的に速かったです

6. 補足:Bedrock の代替

Amazon Bedrock は floci でもエミュレートされないため、aws-samples/bedrock-access-gateway を OpenAI 互換 API として別プロファイルで起動する運用にしました。

bedrock-mock:
  image: ghcr.io/aws-samples/bedrock-access-gateway:latest
  profiles: ["bedrock"]
  ports: ["8000:8000"]
  environment:
    - API_KEY=${BEDROCK_MOCK_API_KEY:-local-dev-key}

make up PROFILES=bedrock のときだけ起動するようにして、普段の起動には影響しないようにしています。
profile を分けるかどうかで「とりあえず立ち上げる」ときの軽さがけっこう変わるので、最初に決めておくと後悔が少ないです。

おわりに

特定のプロジェクト用に詰めたわけではなく、「Cognito + API Gateway v2 + S3/SQS/RDS + Next.js」を手元で一通り動かせる素振り環境として組んでみた、というのが今回の位置づけでした。
それでも、Cognito のサインインから API Gateway v2 の JWT Authorizer 通過までを何度でもタダで叩けるようになっただけで、想像以上に検証のハードルが下がったという印象です。
今後は具体的な開発ループに乗せてみてから足りない部分を足していきたいですし、Bedrock 周りも mock の精度をどこまで詰められるか引き続き試していきたいと思います。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?