floci-io/floci

GitHub: floci-io/floci

Floci 是一个免费、开源、超轻量的 AWS 本地模拟器,通过真实 Docker 容器提供高保真服务模拟,可无缝替代 LocalStack。

Stars: 5405 | Forks: 381

Floci

Latest Release Build Status Docker Pulls Docker Image Size License: MIT GitHub Stars GitHub Contributors Join Floci on Slack

floccus 命名 —— 一种看起来极像爆米花的云状形态。

免费、开源的本地 AWS 模拟器。无需账号。没有功能限制。只需 docker compose up

加入 Slack 社区提问、分享反馈,并与其他贡献者和用户讨论 Floci。您也可以在 GitHub Discussions 中开启任何话题 —— 功能创意、兼容性问题、设计权衡、大胆提案或半成品的想法都非常欢迎。没有哪个想法会因为太小、太早或太凭一时脑热而不值得开启一场好的讨论。

## 为什么选择 Floci? | | Floci | LocalStack Community | |---|---|---| | 需要 Auth token | 否 | 是 (自 2026 年 3 月起) | | 安全更新 | 是 | 冻结 | | 启动时间 | **~24 ms** | ~3.3 s | | 空闲内存 | **~13 MiB** | ~143 MiB | | Docker 镜像大小 | **~90 MB** | ~1.0 GB | | 许可证 | **MIT** | 受限 | | API Gateway v2 / HTTP API | ✅ | ❌ | | Cognito | ✅ | ❌ | | ElastiCache (Redis + IAM auth) | ✅ | ❌ | | RDS (PostgreSQL + MySQL + IAM auth) | ✅ | ❌ | | MSK (Kafka + Redpanda) | ✅ | ❌ | | Athena (通过 DuckDB sidecar 执行真实 SQL + Glue 视图) | ✅ | ❌ | | Glue Data Catalog + Schema Registry | ✅ | ❌ | | Data Firehose (NDJSON 投递) | ✅ | ❌ | | S3 Object Lock (COMPLIANCE / GOVERNANCE) | ✅ | ⚠️ 部分 | | DynamoDB Streams | ✅ | ⚠️ 部分 | | IAM (用户、角色、策略、组) | ✅ | ⚠️ 部分 | | STS (全部 7 个操作) | ✅ | ⚠️ 部分 | | Kinesis (流、分片、fan-out) | ✅ | ⚠️ 部分 | | KMS (签名、验证、重新加密) | ✅ | ⚠️ 部分 | | ECS (集群、服务、任务) | ✅ | ❌ | | EKS (集群、模拟 + 真实 k3s) | ✅ | ❌ | | EC2 (真实 Docker 实例、IMDS、SSH、UserData) | ✅ | ❌ | | CodeBuild (真实 Docker 构建执行、S3 artifacts、CloudWatch 日志) | ✅ | ❌ | | CodeDeploy (Lambda 流量切换、生命周期钩子、自动回滚) | ✅ | ❌ | | Auto Scaling (组、启动配置、reconciler、ELB v2 集成) | ✅ | ❌ | | SSM Run Command (SendCommand + 通过 ec2messages 进行真实 agent 轮询) | ✅ | ❌ | | Transfer Family (SFTP 服务器管理、用户、SSH 密钥) | ✅ | ❌ | | 原生二进制文件 | ✅ ~40 MB | ❌ | **广泛的 AWS 覆盖范围。永久免费。** ## 从 LocalStack 迁移 Floci 是 LocalStack Community 的直接替代品。端口 (`4566`)、凭证以及所有 AWS SDK 和 CLI 调用都无需修改即可正常工作 —— 只需替换镜像即可。 ``` # 之前 image: localstack/localstack # 之后 — 无 init 脚本,或不调用 aws / boto3 的脚本 image: floci/floci:latest # 之后 — 使用 aws CLI 或 boto3 的 init 脚本 image: floci/floci:latest-compat # includes Python 3, AWS CLI, boto3 pre-configured ``` **LocalStack 环境变量会被自动转换** —— 无需重命名: | LocalStack | Floci 等效项 | |---|---| | `LOCALSTACK_HOST` | `FLOCI_HOSTNAME` | | `PERSISTENCE=1` | `FLOCI_STORAGE_MODE=persistent` | | `LAMBDA_DOCKER_NETWORK` | `FLOCI_SERVICES_LAMBDA_DOCKER_NETWORK` | | `LAMBDA_REMOVE_CONTAINERS=1` | `FLOCI_SERVICES_LAMBDA_EPHEMERAL=true` | | `DEBUG=1` | `QUARKUS_LOG_LEVEL=DEBUG` | 挂载在 `/etc/localstack/init/` 下的初始化脚本无需修改即可运行。`/_localstack/init` 和 `/_localstack/health` 端点依然提供服务。设置 `LOCALSTACK_PARITY=false` 可退出自动转换。 → [完整迁移指南](https://floci.io/floci/getting-started/migrate-from-localstack/) ## 架构概览 ``` flowchart LR Client["☁️ AWS SDK / CLI"] subgraph Floci ["Floci — port 4566"] Router["HTTP Router\n(JAX-RS / Vert.x)"] subgraph Stateless ["Stateless Services"] A["SSM · SQS · SNS\nIAM · STS · KMS\nSecrets Manager · SES\nCognito · Kinesis\nEventBridge · Scheduler · AppConfig\nCloudWatch · Step Functions\nCloudFormation · ACM\nAPI Gateway · ELB v2 · Auto Scaling\nCodeDeploy · Backup · Bedrock Runtime · Route53 · Transfer"] end subgraph Stateful ["Stateful Services"] B["S3 · DynamoDB\nDynamoDB Streams"] end subgraph Containers ["Container Services 🐳"] C["Lambda\nElastiCache\nRDS\nECS\nEC2\nMSK\nEKS\nOpenSearch\nCodeBuild"] D["Athena ➜ floci-duck\n(DuckDB sidecar)"] end Router --> Stateless Router --> Stateful Router --> Containers Stateless & Stateful --> Store[("StorageBackend\nmemory · hybrid\npersistent · wal")] end Docker["🐳 Docker Engine"] Client -->|"HTTP :4566\nAWS wire protocol"| Router Containers -->|"Docker API\n+ IAM / SigV4 auth"| Docker ``` ## 真实 Docker 集成 与仅使用模拟的模拟器不同,对于进程内模拟会影响保真度的服务(有状态数据库、高连接协议以及需要原生执行的 runtime),Floci 会运行**真实的 Docker 容器**。其结果是针对实际引擎的线路兼容行为,而非简化的近似。 | 服务 | 默认 Docker 镜像 | 真实内容 | |---|---|---| | **Lambda** | `public.ecr.aws/lambda/` | AWS runtime 环境、执行模型、热容器池 | | **ElastiCache** | `valkey/valkey:8` | 完整的 Redis/Valkey 协议,基于 ACL 的 IAM auth,SigV4 验证 | | **RDS (PostgreSQL)** | `postgres:16-alpine` | 真实的 PostgreSQL 引擎,通过 token 进行 IAM auth,兼容 JDBC | | **RDS (MySQL / Aurora)** | `mysql:8.0` | 真实的 MySQL 引擎,IAM auth,兼容 JDBC | | **RDS (MariaDB)** | `mariadb:11` | 真实的 MariaDB 引擎,IAM auth,兼容 JDBC | | **MSK** | `redpandadata/redpanda:latest` | 通过 Redpanda 实现的真实兼容 Kafka 的代理 | | **EC2** | 映射的 AMI (例如 `public.ecr.aws/amazonlinux/amazonlinux:2023`) | 真实的 Linux 容器;SSH 密钥注入;UserData 执行;支持 IMDSv1+IMDSv2 的 IMDS 及 IAM 凭证分发 | | **ECS** | 在任务定义中由用户指定 | 真实的容器生命周期 —— 启动、停止、健康检查 | | **EKS** | `rancher/k3s:latest` | 实时 Kubernetes API 服务器 (k3s),完整的 kubeconfig | | **CodeBuild** | 由用户指定的环境镜像 (例如 `public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:5.0`) | 真实的 buildspec 执行 —— 容器中的 install/pre_build/build/post_build 阶段;S3 artifact 上传;CloudWatch 日志流式传输 | | **OpenSearch** | `opensearchproject/opensearch:2` | 带有 REST API 的完整 OpenSearch 引擎 | | **ECR** | `registry:2` | 真实的兼容 OCI 的 registry —— `docker push` / `docker pull` 可原生使用 | ### Lambda runtimes Floci 将每个 Lambda runtime 解析为相应的 [AWS 公共 ECR 镜像](https://gallery.ecr.aws/lambda): | Runtime | 镜像 | |---|---| | `java25` · `java21` · `java17` · `java11` · `java8.al2` · `java8` | `public.ecr.aws/lambda/java:` | | `python3.14` · `python3.13` · `python3.12` · `python3.11` · `python3.10` · `python3.9` | `public.ecr.aws/lambda/python:` | | `nodejs24.x` · `nodejs22.x` · `nodejs20.x` · `nodejs18.x` · `nodejs16.x` | `public.ecr.aws/lambda/nodejs:` | | `ruby3.4` · `ruby3.3` · `ruby3.2` | `public.ecr.aws/lambda/ruby:` | | `dotnet10` · `dotnet9` · `dotnet8` · `dotnet6` | `public.ecr.aws/lambda/dotnet:` | | `go1.x` | `public.ecr.aws/lambda/go:1` | | `provided.al2023` · `provided.al2` · `provided` | `public.ecr.aws/lambda/provided:` | 容器镜像函数 (package type `Image`) 会直接传递 `ImageUri`,其中 ECR 存储库 URI 会自动重写为本地 Floci ECR 端点。 ### 系统要求 Docker 支持的服务需要 Docker socket 可访问: ``` docker run -d --name floci \ -p 4566:4566 \ -v /var/run/docker.sock:/var/run/docker.sock \ -u root \ floci/floci:latest ``` 在 Docker Compose 中,除了任何其他挂载之外,还要添加 socket 卷。 ### 覆盖默认镜像 所有默认镜像都可通过环境变量进行配置,适用于锁定版本或使用本地镜像源: | 变量 | 默认值 | |---|---| | `FLOCI_SERVICES_ELASTICACHE_DEFAULT_IMAGE` | `valkey/valkey:8` | | `FLOCI_SERVICES_RDS_DEFAULT_POSTGRES_IMAGE` | `postgres:16-alpine` | | `FLOCI_SERVICES_RDS_DEFAULT_MYSQL_IMAGE` | `mysql:8.0` | | `FLOCI_SERVICES_RDS_DEFAULT_MARIADB_IMAGE` | `mariadb:11` | | `FLOCI_SERVICES_MSK_DEFAULT_IMAGE` | `redpandadata/redpanda:latest` | | `FLOCI_SERVICES_OPENSEARCH_DEFAULT_IMAGE` | `opensearchproject/opensearch:2` | | `FLOCI_SERVICES_EKS_DEFAULT_IMAGE` | `rancher/k3s:latest` | | `FLOCI_SERVICES_ECR_REGISTRY_IMAGE` | `registry:2` | | `FLOCI_ECR_BASE_URI` | `public.ecr.aws` (Lambda runtime 基础路径) | ## 支持的服务 | 服务 | 工作原理 | 显著特性 | |---|---|---| | **SSM Parameter Store** | 进程内 | 版本历史、标签、SecureString、 tagging | | **SSM Run Command** | 进程内 | `SendCommand`、`GetCommandInvocation`、`ListCommands`、`CancelCommand`;`DescribeInstanceInformation`;`ec2messages` 轮询协议,以便在 EC2 容器内运行的真实 `amazon-ssm-agent` 能够注册、接收命令并报告输出 | | **SQS** | 进程内 | Standard 和 FIFO,DLQ,visibility timeout,批量操作,tagging | | **SNS** | 进程内 | 主题、订阅、SQS / Lambda / HTTP 投递、tagging | | **S3** | 进程内 | 版本控制、分段上传、预签名 URL、Object Lock、事件通知 | | **DynamoDB** | 进程内 | GSI / LSI,Query,Scan,TTL,事务,批量操作 | | **DynamoDB Streams** | 进程内 | 分片迭代器、记录、Lambda ESM 触发器 | | **Lambda** | **真实 Docker 容器** | 热池、别名、Function URLs、SQS / Kinesis / DDB Streams ESM | | **API Gateway REST** | 进程内 | 资源、方法、阶段、Lambda proxy、MOCK 集成、AWS 集成 | | **API Gateway v2 (HTTP)** | 进程内 | 路由、集成、JWT authorizers、阶段 | | **IAM** | 进程内 | 用户、角色、组、策略、实例配置文件、访问密钥 | | **STS** | 进程内 | AssumeRole、WebIdentity、SAML、GetFederationToken、GetSessionToken | | **Cognito** | 进程内 | User pools、app clients、auth flows、JWKS / OpenID well-known 端点 | | **KMS** | 进程内 | 加密/解密、签名/验证、数据密钥、别名 | | **Kinesis** | 进程内 | 流、分片、增强型 fan-out、拆分/合并 | | **Secrets Manager** | 进程内 | 版本控制、资源策略、tagging | | **Step Functions** | 进程内 | ASL 执行、任务 token、执行历史 | | **CloudFormation** | 进程内 | Stacks、change sets、资源调配 | | **EventBridge** | 进程内 | 自定义总线、规则、目标 (SQ / SNS / Lambda) | | **EventBridge Scheduler** | 进程内 | 调度组、调度计划、灵活时间窗口、重试策略、dead-letter queues | | **CloudWatch Logs** | 进程内 | 日志组、日志流、数据摄取、过滤 | | **CloudWatch Metrics** | 进程内 | 自定义指标、统计信息、告警 | | **ElastiCache** | **真实 Docker 容器** | Redis / Valkey,IAM auth,SigV4 验证 | | **RDS** | **真实 Docker 容器** | PostgreSQL 和 MySQL,IAM auth,兼容 JDBC | | **MSK** | **真实 Docker 容器** | 通过 Redpanda 编排实现兼容 Kafka | | **Athena** | 进程内 + **DuckDB sidecar** | 真实的 SQL 执行;S3 数据上由 Glue 支持的视图;根据 SerDe 推断 `read_parquet` / `read_json_auto` / `read_csv_auto` | | **Glue** | 进程内 | Data Catalog;用于 Avro / JSON Schema / Protobuf 的 Schema Registry;表在查询时被 Athena 作为 DuckDB 视图消费 | | **Data Firehose** | 进程内 | 流数据投递;记录作为 NDJSON 刷入 S3 | | **ECS** | **真实 Docker 容器** | 集群、任务定义、任务、服务、容量提供程序、任务集 | | **EC2** | **真实 Docker 容器** | `RunInstances` 启动真实的 Docker 容器;SSH 密钥注入;UserData 执行;IMDS (IMDSv1+IMDSv2, 端口 9169) 及 IAM 凭证分发;VPC、子网、安全组、AMI、密钥对、Internet 网关、路由表、Elastic IP、标签 | | **ACM** | 进程内 | 证书颁发、验证生命周期 | | **ECR** | 进程内 + **真实 OCI registry** | 存储库、通过标准 `docker` 进行镜像推/拉、基于镜像的 Lambda 函数 | | **SES** | 进程内 | 发送电子邮件/原始电子邮件、身份验证、DKIM 属性、带有 `{{var}}` 替换的电子邮件模板 | | **SES v2 (HTTP)** | 进程内 | REST JSON API、身份、DKIM、feedback 属性、账号发送、带有 `{{var}}` 替换的电子邮件模板 | | **OpenSearch** | **真实 Docker 容器** | Domain CRUD、标签、版本、实例类型、升级桩 | | **AppConfig** | 进程内 | 应用程序、环境、配置文件、托管配置版本、部署 | | **AppConfigData** | 进程内 | 配置会话、动态配置检索 | | **Bedrock Runtime** | 进程内 (桩) | 用于本地开发的虚拟 Converse 和 InvokeModel 响应;流式传输返回 501 | | **EKS** | **真实 Docker 容器** (提供模拟模式) | 集群、tagging;真实模式为每个集群启动带有实时 Kubernetes API 服务器的 k3s | | **ELB v2** | 进程内 | Application 和 Network Load Balancer、目标组、监听器、基于路径/主机的路由规则、Lambda 目标 (ALB→Lambda 事件格式)、标签 | | **CodeBuild** | 进程内 + **真实 Docker 容器** | 项目、报告组、源凭证;`StartBuild` 运行真实的 Docker 容器,将日志流式传输到 CloudWatch,通过 `docker cp` 将 artifacts 上传到 S3 (支持 Docker-in-Docker) | | **CodeDeploy** | 进程内 + **Lambda 流量切换** | 应用程序、部署组、部署配置;预置了 17 个 `CodeDeployDefault.*` 内置项;`CreateDeployment` 切换 Lambda 别名 `RoutingConfig` 权重,调用生命周期钩子,失败时自动回滚 | | **Auto Scaling** | 进程内 + **后台 reconciler** | 启动配置、具有 min/max/desired capacity 的 Auto Scaling 组;后台循环 (10 秒) 调用 `RunInstances` / `TerminateInstances` 以满足期望容量;生命周期钩子、扩展策略、ELB v2 目标组自动注册 | | **AWS Backup** | 进程内 | 备份库、带有规则的备份计划、资源选择、带有模拟生命周期 (CREATED → RUNNING → COMPLETED) 的按需作业、恢复点、tagging | | **Route53** | 进程内 | 带有自动创建的 SOA + NS 记录的托管区域、资源记录集 (CREATE/UPSERT/DELETE 附带原子验证)、变更跟踪 (始终为 INSYNC)、健康检查以及每资源 tagging | | **Transfer Family** | 进程内 | 服务器生命周期 (`CreateServer` / `DeleteServer` / `StartServer` / `StopServer` / `UpdateServer`)、用户管理、SSH 公钥导入和 tagging | | **Textract** | 进程内 (桩) | 兼容所有操作的 API 桩;具有真实结构和元数据的虚拟块数据;带有即时 SUCCEEDED 状态的异步作业模拟 | **支持 46 个 AWS 服务。** ## 持久化与存储模式 Floci 具有灵活的存储架构,旨在平衡开发者生产力、性能和数据持久性。您可以通过 `FLOCI_STORAGE_MODE` 全局配置存储模式,或者为特定服务覆盖该模式。 | 模式 | 行为 | 最适用于... | 持久性 | |:---:|---|---|:---:| | **`memory`** | **(默认)** 完全在内存中。容器停止时数据丢失。 | 速度、临时测试、CI pipelines。 | ❌ 无 | | **`persistent`** | 数据在启动时加载,在优雅关闭时刷入磁盘。 | 带有状态保留的简单本地开发。 | ⚠️ 中等 | | **`hybrid`** | 内存性能,带有定期的异步刷盘 (每 5 秒)。 | 速度与安全的完美平衡。 | ✅ 良好 | | **`wal`** | 预写式日志。每次修改在响应前都会记录到磁盘。 | 关键状态的最大持久性。 | 💎 最高 | 欲了解更多详情,请访问 [存储配置文档](https://floci.io/floci/configuration/storage/)。 ## 多账号隔离 Floci 支持完整的按账号资源隔离,且无需额外配置。如果您的 AWS access key ID 恰好是 12 位数字,Floci 将直接将其作为账号 ID —— 一个账号创建的资源对另一个账号完全不可见。 ``` # 两个账户,完全隔离 — 相同队列名,独立 namespaces AWS_ACCESS_KEY_ID=111111111111 aws sqs create-queue --queue-name orders AWS_ACCESS_KEY_ID=222222222222 aws sqs create-queue --queue-name orders ``` 任何其他密钥格式 (例如 `test`、`AKIA…`) 将回退到 `FLOCI_DEFAULT_ACCOUNT_ID` (默认为 `000000000000`) —— 标准的单账号设置。 → [多账号隔离文档](https://floci.io/floci/configuration/multi-account/) ## 快速开始 ``` # docker-compose.yml services: floci: image: floci/floci:latest ports: - "4566:4566" volumes: # Local directory bind mount (default) - ./data:/app/data # OR named volume (optional): # - floci-data:/app/data #volumes: # floci-data: ``` ``` docker compose up ``` 或者直接使用 Docker 运行 Floci: ``` docker run -d --name floci \ -p 4566:4566 \ -v /var/run/docker.sock:/var/run/docker.sock \ -e FLOCI_DEFAULT_REGION=us-east-1 \ -u root \ floci/floci:latest ``` 所有服务均可在 `http://localhost:4566` 获取。可以使用任何 AWS region —— 凭证可以是任意值。 ``` 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 # 试一试 aws s3 mb s3://my-bucket aws sqs create-queue --queue-name my-queue aws dynamodb list-tables ``` ## SDK 集成 将您现有的 AWS SDK 指向 `http://localhost:4566` —— 无需进行其他更改。 ``` // 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())); client.putItem(b -> b .tableName("demo-table") .item(Map.of("pk", AttributeValue.fromS("item-1")))); System.out.println(client.listTables().tableNames()); ``` ``` # 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"]) ``` ``` // consumer.mjs // Node.js (AWS SDK v3) import {DeleteMessageCommand, ReceiveMessageCommand, SQSClient,} from "@aws-sdk/client-sqs"; const client = new SQSClient({ endpoint: "http://localhost:4566", region: "us-east-1", credentials: {accessKeyId: "test", secretAccessKey: "test"}, }); const QUEUE_URL = "http://localhost:4566/000000000000/demo-queue"; const response = await client.send( new ReceiveMessageCommand({ QueueUrl: QUEUE_URL, MaxNumberOfMessages: 1, WaitTimeSeconds: 5, }), ); if (response.Messages) { for (const msg of response.Messages) { console.log("Message received:", msg.Body); await client.send( new DeleteMessageCommand({ QueueUrl: QUEUE_URL, ReceiptHandle: msg.ReceiptHandle, }), ); } } ``` ``` // producer.mjs // Node.js (AWS SDK v3) import {SendMessageCommand, SQSClient} from "@aws-sdk/client-sqs"; const client = new SQSClient({ endpoint: "http://localhost:4566", region: "us-east-1", credentials: {accessKeyId: "test", secretAccessKey: "test"}, }); const QUEUE_URL = "http://localhost:4566/000000000000/demo-queue"; await client.send( new SendMessageCommand({ QueueUrl: QUEUE_URL, MessageBody: "hello from producer", }), ); console.log("Message sent"); ``` ``` // Go (AWS SDK v2) package main import ( "context" "fmt" "log" "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, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-east-1"), config.WithCredentialsProvider( credentials.NewStaticCredentialsProvider("test", "test", ""), ), config.WithBaseEndpoint("http://localhost:4566"), ) if err != nil { log.Fatal(err) } client := s3.NewFromConfig(cfg, func(o *s3.Options) { o.UsePathStyle = true }) _, err = client.CreateBucket(context.TODO(), &s3.CreateBucketInput{ Bucket: aws.String("demo-bucket"), }) if err != nil { log.Fatal(err) } _, err = client.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String("demo-bucket"), Key: aws.String("demo.txt"), Body: strings.NewReader("hello from floci"), }) if err != nil { log.Fatal(err) } out, err := client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{ Bucket: aws.String("demo-bucket"), }) if err != nil { log.Fatal(err) } if len(out.Contents) > 0 { fmt.Println(*out.Contents[0].Key) } } ``` ``` // Rust (AWS SDK) use aws_sdk_secretsmanager::config::{Credentials, Region}; use aws_sdk_secretsmanager::Client; #[tokio::main] async fn main() -> Result<(), Box> { let config = aws_config::defaults(aws_config::BehaviorVersion::latest()) .region(Region::new("us-east-1")) .credentials_provider(Credentials::new("test", "test", None, None, "floci")) .endpoint_url("http://localhost:4566") .load() .await; let client = Client::new(&config); client .create_secret() .name("demo/secret") .secret_string("hello from floci") .send() .await?; let secret = client .get_secret_value() .secret_id("demo/secret") .send() .await?; println!("{}", secret.secret_string().unwrap()); Ok(()) } ``` ``` # Bash (AWS CLI) export AWS_ACCESS_KEY_ID=test export AWS_SECRET_ACCESS_KEY=test export AWS_DEFAULT_REGION=us-east-1 tmp_file="$(mktemp)" echo "hello from floci" > "$tmp_file" aws --endpoint-url http://localhost:4566 s3 mb s3://my-bucket aws --endpoint-url http://localhost:4566 s3 cp "$tmp_file" s3://my-bucket/demo.txt aws --endpoint-url http://localhost:4566 s3 ls s3://my-bucket # 清理 aws --endpoint-url http://localhost:4566 s3 rm s3://my-bucket/demo.txt rm -f "$tmp_file" ``` ## Testcontainers Floci 拥有一流的 Testcontainers 模块,因此您可以从测试中启动真实的 Floci 实例,无需任何手动设置 —— 没有运行中的 daemon,没有共享状态,也没有端口冲突。 | 语言 | 包 | 最新版 | Registry | 源码 | |---|---|---|---|---| | Java | `io.floci:testcontainers-floci` | `1.4.0` | [Maven Central](https://mvnrepository.com/artifact/io.floci/testcontainers-floci) | [GitHub](https://github.com/floci-io/testcontainers-floci) | | Node.js | `@floci/testcontainers` | `0.1.0` | [npm](https://www.npmjs.com/package/@floci/testcontainers) | [GitHub](https://github.com/floci-io/testcontainers-floci-node) | | Python | `testcontainers-floci` | `0.1.1` | [PyPI](https://pypi.org/project/testcontainers-floci/) | [GitHub](https://github.com/floci-io/testcontainers-floci-python) | | Go | — | 🚧 开发中 | — | [GitHub](https://github.com/floci-io/testcontainers-floci-go) | ### Java 添加依赖项 (Testcontainers 1.x / Spring Boot 3.x): ``` io.floci testcontainers-floci 1.4.0 test ``` 对于 Testcontainers 2.x / Spring Boot 4.x,请使用版本 `2.5.0`。 在 JUnit 5 中的基本用法: ``` @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")); assertThat(s3.listBuckets().buckets()) .anyMatch(b -> b.name().equals("my-bucket")); } } ``` **Spring Boot** —— 添加 `spring-boot-testcontainers-floci` 并使用 `@ServiceConnection` 实现零配置自动装配: ``` @SpringBootTest @Testcontainers class AppIntegrationTest { @Container @ServiceConnection static FlociContainer floci = new FlociContainer(); @Autowired S3Client s3; @Test void shouldCreateBucket() { s3.createBucket(b -> b.bucket("my-bucket")); assertThat(s3.listBuckets().buckets()) .anyMatch(b -> b.name().equals("my-bucket")); } } ``` ### Node.js / TypeScript ``` npm install --save-dev @floci/testcontainers ``` ``` import { FlociContainer } from "@floci/testcontainers"; import { S3Client, CreateBucketCommand, ListBucketsCommand } from "@aws-sdk/client-s3"; describe("S3", () => { let floci: FlociContainer; beforeAll(async () => { floci = await new FlociContainer().start(); }); afterAll(async () => { await floci.stop(); }); it("should create and list a bucket", async () => { const s3 = new S3Client({ endpoint: floci.getEndpoint(), region: floci.getRegion(), credentials: { accessKeyId: floci.getAccessKey(), secretAccessKey: floci.getSecretKey(), }, forcePathStyle: true, }); await s3.send(new CreateBucketCommand({ Bucket: "my-bucket" })); const { Buckets } = await s3.send(new ListBucketsCommand({})); expect(Buckets?.some(b => b.Name === "my-bucket")).toBe(true); }); }); ``` ### 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") buckets = s3.list_buckets()["Buckets"] assert any(b["Name"] == "my-bucket" for b in buckets) ``` Pytest fixture 风格: ``` import pytest import boto3 from testcontainers_floci import FlociContainer @pytest.fixture(scope="session") def floci(): with FlociContainer() as container: yield container def test_s3_create_bucket(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") buckets = s3.list_buckets()["Buckets"] assert any(b["Name"] == "my-bucket" for b in buckets) ``` ### Go Go 支持正在开发中。请在 [testcontainers-floci-go](https://github.com/floci-io/testcontainers-floci-go) 追踪进度。 ## 兼容性测试 此目录提供了跨多个 SDK 和工具场景的 Floci 专用兼容性测试套件,是在端到端验证集成行为时的推荐起点。 可用的兼容性测试模块: | 模块 | 语言 / 工具 | SDK / 客户端 / 版本 | 测试 | |---|---|---|---:| | `sdk-test-java` | Java 17 | AWS SDK for Java v2 | 889 | | `sdk-test-node` | Node.js | AWS SDK for JavaScript v3 | 360 | | `sdk-test-python` | Python 3 | boto3 | 264 | | `sdk-test-go` | Go | AWS SDK for Go v2 | 136 | | `sdk-test-awscli` | Bash | AWS CLI v2 | 145 | | `sdk-test-rust` | Rust | AWS SDK for Rust | 86 | | `compat-terraform` | Terraform | v1.10+ | 14 | | `compat-opentofu` | OpenTofu | v1.9+ | 14 | | `compat-cdk` | AWS CDK | v2+ | 17 | **跨 6 个 SDK 和 3 个 IaC 工具的 1,850 多个自动化兼容性测试。** ## 镜像标签 每个标签结合了两个选择:**变体** (包含的内容) 和 **渠道** (稳定程度)。 | | Standard | Compat (+ AWS CLI + boto3) | |---|---|---| | **Release (latest)** | `latest` ✅ | `latest-compat` | | **Release (pinned)** | `x.y.z` | `x.y.z-compat` | | **Nightly (浮动)** | `nightly` | `nightly-compat` | | **Nightly (日期)** | `nightly-mmddyyyy` | `nightly-mmddyyyy-compat` | - **Standard** —— GraalVM 原生二进制文件。约 24 ms 启动,约 40 MB 镜像,约 13 MiB 空闲内存。 - **Compat** —— 在 Standard 镜像基础上扩展了 Python 3、AWS CLI 和 boto3。启动速度和内存占用相同,但镜像体积更大。 - **Release** —— 在每个稳定版本标签上发布。 - **Nightly** —— 每晚 22:00 CT 从 `main` 分支构建。带有日期的标签 (例如 `nightly-05022026`) 是固定的;`nightly` 始终指向最新版本。 ``` # 推荐 image: floci/floci:latest # 使用 AWS CLI + boto3 image: floci/floci:latest-compat # 固定版本 image: floci/floci:1.5.11 # 追踪 main image: floci/floci:nightly ``` ## 配置 所有设置都可以通过环境变量 (`FLOCI_` 前缀) 进行覆盖。 | 变量 | 默认值 | 描述 | |---|---|-------------------------------------------------------------------------------------| | `FLOCI_PORT` | `4566` | Floci API 暴露的端口 | | `FLOCI_DEFAULT_REGION` | `us-east-1` | 默认 AWS region | | `FLOCI_DEFAULT_ACCOUNT_ID` | `000000000000` | 默认 AWS 账号 ID | | `FLOCI_BASE_URL` | `http://localhost:4566` | Floci 返回服务 URL 时使用的基础 URL (例如 SQS QueueUrl) | | `FLOCI_HOSTNAME` | *(未设置)* | 当 Floci 在 Docker Compose 内部运行时,在返回的 URL 中使用的 Hostname | | `FLOCI_STORAGE_MODE` | `memory` | 控制跨运行的数据存储方式:`memory` · `persistent` · `hybrid` · `wal` | | `FLOCI_STORAGE_PERSISTENT_PATH` | `./data` | 用于持久化目录 | | `FLOCI_ECR_BASE_URI` | `public.ecr.aws` | 拉取容器镜像 (例如 Lambda) 时使用的 AWS ECR 基础 URI | * 完整参考:[配置文档](https://floci.io/floci/configuration/application-yml/) * 按服务覆盖存储:[存储文档](https://floci.io/floci/configuration/storage/#per-service-storage-overrides) **多容器 Docker Compose:** 当您的应用程序在独立于 Floci 的单独容器中运行时,请将 `FLOCI_HOSTNAME` 设置为 Floci 的服务名称,以便返回的 URL (例如 SQS QueueUrl) 能够正确解析: ``` services: floci: image: floci/floci:latest ports: - "4566:4566" environment: - FLOCI_HOSTNAME=floci # URLs will use http://floci:4566/... my-app: environment: - AWS_ENDPOINT_URL=http://floci:4566 depends_on: - floci ``` 如果不进行此设置,SQS 在 QueueUrl 响应中将返回 `http://localhost:4566/...`,这将解析到错误的容器。 ## Star 历史 [![Star History Chart](https://api.star-history.com/svg?repos=floci-io/floci&type=Date)](https://star-history.com/#floci-io/floci&Date) ## 贡献者 ## 许可证 MIT —— 随意使用。
标签:AWS, AWS模拟器, Docker, Docker Compose, DPI, JS文件枚举, Lambda, LangChain, LocalStack替代品, MIT协议, NIDS, S3, 云服务测试, 云计算, 域名枚举, 安全防御评估, 容器化, 开发测试工具, 开源, 无服务器, 本地开发环境, 本地模拟, 版权保护, 规则引擎, 请求拦截, 轻量级