Tại Sao Tôi Đổi LocalStack Sang Floci — Và Không Quay Đầu Lại
LocalStack bắt đầu chặn CI bằng auth token. Đây là cách tôi dời daily AWS dev loop sang Floci — khởi động nhanh hơn, MIT license, không cần đăng ký.
Mục lục
CI của tôi vỡ vào một sáng thứ Ba. Xanh suốt một năm, đột nhiên đỏ chót không rõ lý do. Tôi kéo log xuống — LOCALSTACK_AUTH_TOKEN missing. À. Cái deadline 23 tháng 3 đã đến và tôi đã quên mất.
Ba tiếng sau, tôi đã dời cả stack khỏi LocalStack và chuyển sang Floci. Cùng endpoint, cùng boto3 client, không cần tài khoản vendor, ít YAML hơn. Đây là báo cáo thực địa từ một engineer vừa migrate một dự án AWS cỡ vừa và sống sót để kể lại.
Câu chuyện “đám mây bỏng ngô”
Floci là một AWS local emulator. Cùng phân khúc với LocalStack — chạy S3, SQS, DynamoDB, Lambda, RDS ngay trên laptop thay vì một AWS account thật. Nó bind vào port 4566, đúng port mà LocalStack dùng, nên phần lớn code hiện có của bạn chạy được mà không phải sửa.
Tên Floci lấy từ cirrocumulus floccus — những đám mây nhỏ hình bỏng ngô lúc hoàng hôn. Triết lý thiết kế đi theo đúng cái tên: nhỏ, nhanh, không rườm rà. Nó là một Quarkus Native binary được compile bằng GraalVM, nghĩa là nó khởi động trong đúng khoảnh khắc bạn chớp mắt.
# One container, no account, no auth token, no sign-up wall
docker run -d --name floci \
-p 4566:4566 \
-v /var/run/docker.sock:/var/run/docker.sock \
-e FLOCI_DEFAULT_REGION=us-east-1 \
hectorvent/floci:latest
Cái mount docker.sock đó là thứ cho phép Floci spawn Lambda runtime container thật. Bỏ nó ra và lambda.invoke() sẽ lịch sự từ chối làm bất cứ điều gì hữu ích.
Floci đối đầu LocalStack, nhìn một phát là thấy
Tôi không giả vờ đây là trận đấu công bằng về coverage — LocalStack Ultimate hỗ trợ 110+ service còn Floci ship với 35. Nhưng ở mọi chiều khác mà tôi quan tâm hàng ngày, các con số kể một câu chuyện rất thẳng thắn.
| Tiêu chí | Floci | LocalStack Community (2026) |
|---|---|---|
| Cold start | ~24 ms | ~3.3 s |
| Idle memory | ~13 MiB | ~143 MiB |
| Image size | ~90 MB | ~1.0 GB |
| Số service | 35 | Hobby hạn chế; bản trả tiền 110+ |
| Auth token cho CI | Không bao giờ | Bắt buộc từ 23/3/2026 |
| License | MIT, miễn phí vĩnh viễn | Hobby miễn phí phi thương mại; Starter $39/tháng |
| Số SDK test pass | 1.850+ công khai | Nội bộ, không công bố |
Hobby tier của LocalStack vẫn miễn phí cho cá nhân, nhưng cấm dùng thương mại và bắt tạo account. Đó mới là phần khó chịu nhất: CI runner giờ cần token thật, gắn với một con người thật, và token đó hành xử như mật khẩu mà bạn không xoay tua được nếu không làm vỡ build.
Dev loop hàng ngày của tôi, gói gọn trong một docker-compose
Đây là file tôi drop vào mỗi dự án mới. Không có gì lạ — chỉ là cái hình dạng tôi hạ cánh sau khi mất quá nhiều buổi tối vật lộn với container networking.
# docker-compose.yml — local AWS for the whole team
services:
floci:
image: hectorvent/floci:latest
ports:
- "4566:4566"
volumes:
- /var/run/docker.sock:/var/run/docker.sock # Lambda needs this
- ./.floci-data:/app/data # Persist S3/Dynamo between runs
environment:
FLOCI_DEFAULT_REGION: us-east-1
FLOCI_STORAGE_MODE: hybrid
FLOCI_SERVICES_LAMBDA_DOCKER_NETWORK: bridge
Phần SDK call nhàm chán vẫn y hệt như trước:
import boto3
# Dummy creds — Floci validates SigV4 but doesn't care about the secret itself
s3 = boto3.client(
"s3",
endpoint_url="http://localhost:4566",
region_name="us-east-1",
aws_access_key_id="test",
aws_secret_access_key="test",
)
s3.create_bucket(Bucket="my-local-bucket")
Cái bẫy tôi dính hai lần trước khi học được: quên .gitignore thư mục .floci-data/. DynamoDB ghi nhanh đến chóng mặt và PR tiếp theo của bạn biến thành 400 file binary nhiễu loạn.
Lambda thực sự chạy code của bạn
Lambda của LocalStack Community trước đây thực thi handler trong một process rút gọn — đủ cho unit test nhẹ nhàng, nhưng không đủ để trả lời câu “wheel Python 3.13 của tôi có load được không.” Lambda của Floci spawn một Docker container thật cho mỗi invocation, đó chính là lý do bạn phải mount docker.sock.
# Package and deploy locally, same shape as real AWS
zip -r function.zip handler.py requirements/
aws --endpoint-url=http://localhost:4566 lambda create-function \
--function-name hello \
--runtime python3.13 \
--role arn:aws:iam::000000000000:role/lambda-exec \
--handler handler.lambda_handler \
--zip-file fileb://function.zip
Vì đó là container thật, cold start cũng thật. Phản ứng đầu tiên của tôi là khó chịu; phản ứng thứ hai là “ồ, tôi vừa tóm được một bug mà container không import được lxml do glibc lệch version.” Cái bug đó chắc chắn đã ship lên prod nếu không bắt được tại chỗ.
Lỗi kinh điển: chạy Floci bên trong Docker network riêng của nó rồi kỳ vọng Lambda container kết nối được. Hãy set FLOCI_SERVICES_LAMBDA_DOCKER_NETWORK=bridge hoặc đặt tên compose network rõ ràng. Không thì handler của bạn sẽ nhận một ConnectionRefusedError rất là bối rối.
IAM thật sự kiểm tra, không đóng dấu cho qua
Đây là phần tôi không ngờ mình thích. Floci validate chữ ký SigV4 đầy đủ và tôn trọng IAM role assumption trên Lambda, RDS, ElastiCache. LocalStack Community chưa bao giờ enforce IAM nghiêm túc — bạn có thể chạy toàn bộ test suite với quyền root-equivalent và test vẫn xanh trên các policy sẽ nổ tung ngoài production.
# Assume a readonly role — actually validated, not rubber-stamped
sts = boto3.client("sts", endpoint_url="http://localhost:4566", ...)
creds = sts.assume_role(
RoleArn="arn:aws:iam::000000000000:role/readonly",
RoleSessionName="local-test",
)["Credentials"]
# Now exercise your code against scoped creds, not admin
s3_readonly = boto3.client(
"s3",
aws_access_key_id=creds["AccessKeyId"],
aws_secret_access_key=creds["SecretAccessKey"],
aws_session_token=creds["SessionToken"],
endpoint_url="http://localhost:4566",
)
Tôi đã bắt được hai policy over-privileged theo cách này ngay trong tuần đầu. Một trong số đó là của tôi, và tôi đã đổ lỗi cho team infra suốt nhiều tháng.
Storage mode: chọn lời nói dối phù hợp
FLOCI_STORAGE_MODE có bốn giá trị và đúng mode phụ thuộc hoàn toàn vào loại test bạn đang chạy. Mặc định cố gắng hợp lý, nhưng “hợp lý” trong unit test và trong manual dev lại là hai chuyện khác nhau.
| Mode | Hành vi | Dùng cho |
|---|---|---|
memory | Mọi thứ biến mất sau restart | Unit test, CI |
hybrid | Các service stateful (S3, Dynamo) được lưu, phần còn lại in-memory | Local dev — mặc định |
persistent | Tất cả được lưu xuống disk | Manual QA, fixture dài hạn |
wal | Write-ahead log cho độ bền cao nhất | Reproduce bug corruption hiếm gặp |
Tôi để mặc định hybrid cho dev và memory cho CI. Lần duy nhất bạn muốn wal là khi một flaky test chỉ fail ở lần chạy thứ 400 và bạn cần chụp lại trạng thái đúng tích tắc nó đi sai.
Cái bẫy: set persistent toàn cục trong CI. Pipeline run tiếp theo kế thừa bảng DynamoDB hỏng từ hôm qua và bạn săn một con ma suốt hai tiếng trước khi nhớ kiểm tra cờ storage.
Nơi Floci không phải câu trả lời
Floci không phải siêu tập của LocalStack. Nếu stack của bạn phụ thuộc vào Glue, Athena, SageMaker, hoặc cái đuôi dài của AWS service, bạn vẫn ở lại với LocalStack Ultimate — và thật lòng, với workload kiểu đó, $89/tháng có lẽ tự trả được cho chính nó. Không emulator nào đạt parity 100% với AWS thật, nhưng LocalStack có một thập kỷ edge case đã được vá mà Floci đơn giản là chưa gặp.
Những chỗ khác tôi sẽ cân nhắc hai lần:
- Bạn đã trả tiền LocalStack rồi. Nếu ngân sách CI hấp thụ được và test đang pass, migrate vì triết lý thôi là việc vô ích.
- CloudFormation với cross-service wiring nặng. Coverage CFN của Floci đang lớn dần, nhưng intrinsic function và stack lồng sâu vẫn còn khoảng trống.
- Team phụ thuộc vào Cloud Pods, snapshot, hay dashboard LocalStack. Đó là các tính năng thương mại của LocalStack; Floci không giả vờ có chúng.
Còn lại, cho mọi thứ trong “S3 + SQS + Dynamo + Lambda + RDS” ở giữa — đúng là đa phần công việc AWS thực tế mà tôi làm — Floci giờ là mặc định trung thực.
Một hành động cụ thể: spin Floci lên trên một branch phụ trong tuần này. Trỏ endpoint_url hiện có vào port 4566 trên cùng container image và xem có bao nhiêu test không thèm để ý. Những test có để ý đang nói cho bạn điều gì đó thú vị về stack của bạn, và học điều đó bây giờ rẻ hơn rất nhiều so với học lúc auth token sập vào 9 giờ sáng thứ Ba.
Emulate vui vẻ. Và lúc nào đó hãy ngắm hoàng hôn — mấy đám mây hình bỏng ngô đúng là xứng đáng với cái tên.
Nguồn tham khảo
- Floci — Fast, Free AWS Emulator
- floci-io/floci trên GitHub
- Cập nhật quan trọng về pricing và packaging của LocalStack
- LocalStack for AWS chuyển sang single image — các bước tiếp theo