今天在 GitHub Trending 上看到一个有意思的项目:Floci,一个轻量级开源的 AWS 本地模拟器,主打快速、免费、无门槛,作为 LocalStack 社区版停止维护后的完美替代方案。

一、项目概述

Floci 是一个免费、开源的 AWS 本地模拟器,专为开发、测试和 CI 环境设计。它让你可以在本地运行 AWS 兼容的服务,无需云账户、无需认证令牌、无需付费功能限制。只需 docker compose up,即可启动。

核心特性

  • 零门槛启动:无账户、无认证令牌、无功能限制
  • 快速轻量:启动时间约 24 毫秒,空闲内存仅 13MB,Docker 镜像仅 90MB
  • 广泛支持:支持 69 种 AWS 服务
  • 真实容器支持:Lambda、RDS、Neptune、ElastiCache、MSK、ECS、EC2、EKS、OpenSearch、CodeBuild 等使用真实 Docker 容器而非浅层模拟
  • MIT 许可证:完全免费、开源

二、技术原理

架构设计

Floci 采用 Quarkus 框架构建,基于 JAX-RS 和 Vert.x 实现高性能 HTTP 路由。整体架构分为三层:

flowchart LR
    Client["AWS SDK / CLI"]

    subgraph Floci ["Floci, port 4566"]
        Router["HTTP Router\nJAX-RS / Vert.x"]

        subgraph Stateless ["无状态服务"]
            A["SSM · SQS · SNS\nIAM · STS · KMS\nSecrets Manager · SES\nCognito · Kinesis\nEventBridge · Scheduler"]
        end

        subgraph Stateful ["有状态服务"]
            B["S3 · DynamoDB\nDynamoDB Streams"]
        end

        subgraph Containers ["容器服务"]
            C["Lambda\nElastiCache\nRDS\nNeptune\nECS\nEC2\nMSK\nEKS"]
        end

        Router --> Stateless
        Router --> Stateful
        Router --> Containers
        Stateless & Stateful --> Store[("StorageBackend\nmemory · hybrid · persistent · wal")]
    end

    Docker["Docker Engine"]
    Client -->|"HTTP :4566"| Router
    Containers -->|"Docker API"| Docker

核心技术栈

从 pom.xml 可以看出技术选型:

<quarkus.platform.version>3.36.3</quarkus.platform.version>
<jackson.version>2.21.4</jackson.version>
  • Quarkus:云原生 Java 框架,支持原生镜像编译,启动快、内存小
  • Vert.x:高性能异步事件驱动框架,处理大量并发连接
  • Jackson:JSON 序列化与反序列化
  • Docker Java Client:管理容器生命周期(Lambda、RDS 等)

存储后端设计

Floci 支持四种存储模式,适应不同场景:

模式行为适用场景持久性
memory全内存,停止即丢失CI 和临时测试无
persistent启动加载,写即刷盘简单本地状态保存中
hybrid内存性能 + 5秒异步刷盘本地开发良好
wal预写日志,最高可靠性最高持久性要求最高

三、安装与快速开始

环境要求

  • Docker(用于容器服务)
  • 可选:AWS CLI 或 SDK

安装方式

方式一:CLI 工具(推荐)

# 安装 Floci CLI
pip install floci-cli

# 启动
floci start

# 配置环境变量
eval $(floci env)

方式二:Docker Compose

services:
  floci:
    image: floci/floci:latest
    ports:
      - "4566:4566"
    # 如需容器服务,挂载 Docker socket
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
docker compose up

最简运行示例

# 配置 AWS 环境变量
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_DEFAULT_REGION=us-east-1
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test

# 创建 S3 存储桶
aws s3 mb s3://my-bucket

# 创建 DynamoDB 表
aws dynamodb create-table \
  --table-name demo-table \
  --attribute-definitions AttributeName=pk,AttributeType=S \
  --key-schema AttributeName=pk,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

# 列出表
aws dynamodb list-tables

四、使用方法与实战

SDK 集成

Python (boto3)

import boto3

client = boto3.client(
    "ssm",
    endpoint_url="http://localhost:4566",
    region_name="us-east-1",
    aws_access_key_id="test",
    aws_secret_access_key="test",
)

client.put_parameter(
    Name="/demo/app/message",
    Value="hello from floci",
    Type="String",
    Overwrite=True,
)

response = client.get_parameter(Name="/demo/app/message")
print(response["Parameter"]["Value"])

Node.js (AWS SDK v3)

import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";

const client = new SQSClient({
  endpoint: "http://localhost:4566",
  region: "us-east-1",
  credentials: { accessKeyId: "test", secretAccessKey: "test" },
});

await client.send(
  new SendMessageCommand({
    QueueUrl: "http://localhost:4566/000000000000/demo-queue",
    MessageBody: "hello from floci",
  }),
);

Java (AWS SDK v2)

var client = DynamoDbClient.builder()
    .endpointOverride(URI.create("http://localhost:4566"))
    .region(Region.US_EAST_1)
    .credentialsProvider(StaticCredentialsProvider.create(
        AwsBasicCredentials.create("test", "test")))
    .build();

client.createTable(b -> b
    .tableName("demo-table")
    .billingMode(BillingMode.PAY_PER_REQUEST)
    .attributeDefinitions(
        AttributeDefinition.builder().attributeName("pk").attributeType(ScalarAttributeType.S).build())
    .keySchema(
        KeySchemaElement.builder().attributeName("pk").keyType(KeyType.HASH).build()));

System.out.println(client.listTables().tableNames());

Testcontainers 集成

Floci 提供多语言 Testcontainers 模块,适合隔离测试:

Java

<dependency>
    <groupId>io.floci</groupId>
    <artifactId>testcontainers-floci</artifactId>
    <version>1.4.0</version>
    <scope>test</scope>
</dependency>
@Testcontainers
class S3IntegrationTest {
    @Container
    static FlociContainer floci = new FlociContainer();

    @Test
    void shouldCreateBucket() {
        S3Client s3 = S3Client.builder()
                .endpointOverride(URI.create(floci.getEndpoint()))
                .region(Region.of(floci.getRegion()))
                .credentialsProvider(StaticCredentialsProvider.create(
                        AwsBasicCredentials.create(floci.getAccessKey(), floci.getSecretKey())))
                .forcePathStyle(true)
                .build();

        s3.createBucket(b -> b.bucket("my-bucket"));
    }
}

Python

pip install testcontainers-floci
import boto3
from testcontainers_floci import FlociContainer

def test_s3_create_bucket():
    with FlociContainer() as floci:
        s3 = boto3.client(
            "s3",
            endpoint_url=floci.get_endpoint(),
            region_name=floci.get_region(),
            aws_access_key_id=floci.get_access_key(),
            aws_secret_access_key=floci.get_secret_key(),
        )
        s3.create_bucket(Bucket="my-bucket")

从 LocalStack 迁移

Floci 是 LocalStack Community 的直接替代品,端口、凭证、SDK 配置完全兼容:

# 替换镜像即可
# 之前
image: localstack/localstack

# 之后
image: floci/floci:latest

环境变量自动转换:

LocalStackFloci 等价
LOCALSTACK_HOSTFLOCI_HOSTNAME
PERSISTENCE=1FLOCI_STORAGE_MODE=persistent
LAMBDA_DOCKER_NETWORKFLOCI_SERVICES_LAMBDA_DOCKER_NETWORK
DEBUG=1QUARKUS_LOG_LEVEL=DEBUG

五、常见问题与解决方案

端口冲突

问题:4566 端口被占用

解决:通过环境变量指定其他端口

export FLOCI_PORT=14566

容器服务无法启动

问题:Lambda、RDS 等容器服务报错

解决:确保挂载了 Docker socket

services:
  floci:
    image: floci/floci:latest
    ports:
      - "4566:4566"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    user: root  # 某些系统需要

多容器环境下 URL 解析错误

问题:应用容器访问 Floci 返回的 URL(如 SQS QueueUrl)无法解析

解决:设置 FLOCI_HOSTNAME 为服务名

services:
  floci:
    image: floci/floci:latest
    ports:
      - "4566:4566"
    environment:
      - FLOCI_HOSTNAME=floci

  my-app:
    environment:
      - AWS_ENDPOINT_URL=http://floci:4566
    depends_on:
      - floci

数据持久化

问题:重启后数据丢失

解决:使用持久化存储模式

environment:
  - FLOCI_STORAGE_MODE=persistent
  - FLOCI_STORAGE_PERSISTENT_PATH=/data
volumes:
  - ./data:/data

六、总结

Floci 作为 LocalStack 社区版停止维护后的开源替代方案,凭借其轻量、快速、无门槛的特点,非常适合本地开发和 CI 环境使用。相比 LocalStack,Floci 具有以下优势:

  • 无需认证令牌:开箱即用
  • 安全更新持续:LocalStack 社区版已冻结安全更新
  • 启动快 100 倍:24ms vs 3.3s
  • 内存占用小 10 倍:13MB vs 143MB
  • 镜像体积小 10 倍:90MB vs 1GB
  • MIT 许可证:无使用限制

如果你的项目依赖 AWS 服务进行本地开发或测试,Floci 值得一试。