gVisor vs Firecracker: Hai triết lý sandbox container
May 27, 2026

Photo by Peter Conrad on Unsplash
Mở đầu — Vì sao container truyền thống chưa đủ?
Container "kiểu runc" (namespace + cgroup + seccomp) gần như là chuẩn de facto của workload hiện đại. Nhưng có một nhược điểm không thể né: tất cả container trên cùng một host đều chia sẻ duy nhất một kernel Linux. Toàn bộ ranh giới giữa "code của tôi" và "code của khách hàng khác" nằm gọn trong khoảng 350 syscall của kernel host. Một lỗ hổng nâng quyền trong kernel (CVE-2022-0185, Dirty Pipe, CVE-2017-7308 AF_PACKET, …) là đủ để một container thoát ra và đụng tới các workload khác.
Với 95% workload nội bộ, đánh đổi này chấp nhận được. Nhưng có ba lớp use case mà nó không chấp nhận được:
- Serverless / FaaS đa khách hàng — AWS Lambda, Google Cloud Run, Fly Machines. Function của hai khách hàng có thể chạy cách nhau vài mili-giây trên cùng một host vật lý. Một CVE kernel = thảm hoạ multi-tenant.
- Code không tin tưởng — sandbox AI/code-execution (Replit, e2b, các môi trường "run my Python"), CI runners chạy PR của contributor lạ, các nền tảng học máy chạy notebook của khách hàng.
- Edge / micro-VM scale lớn — Fly.io, Koyeb, các CDN cần hàng nghìn instance trên một máy với boot time tính bằng mili-giây.
Hai cách tiếp cận đối lập nhau xuất hiện để giải bài toán này, đều ra mắt năm 2018 và đều mã nguồn mở:
gVisor (Google): thay vì để container gọi syscall trực tiếp xuống kernel host, dựng một "application kernel" trong userspace (viết bằng Go) — gọi là Sentry — đứng giữa. Sentry tự reimplement ~237 syscall Linux bằng code Go memory-safe, và chỉ gọi xuống host khoảng 68 syscall (53 nếu không có network). Attack surface co từ 350 xuống 68. (Nguồn: gVisor Security Basics Part 1)
Firecracker (AWS): thay vì tìm cách "lọc" syscall, ép mỗi workload vào một microVM riêng — một VM thực sự dùng KVM nhưng cực kỳ trimmed (~50K dòng Rust, không PCI, không USB, không BIOS, không graphics). Boot trong dưới 125ms với overhead RAM chỉ ~5MB. Mỗi function Lambda chạy trong một microVM Firecracker riêng. (Nguồn: Firecracker NSDI 2020 paper)
Cả hai đều giải cùng một bài toán — cô lập workload không tin tưởng, ở mật độ cao, với cost thấp — nhưng chọn hai triết lý kỹ thuật trái ngược: gVisor đi từ trên xuống (intercept syscall ở tầng application), Firecracker đi từ dưới lên (đưa workload vào VM riêng). Bài viết này mổ xẻ chi tiết từng cái, rồi đặt chúng cạnh nhau.
Nếu bạn đã đọc bài Container the Hard Way, section này có thể coi như phần "mở rộng deep dive" cho phần Beyond runc — chúng ta sẽ đi sâu vào từng kiến trúc, thay vì chỉ điểm qua.
gVisor — Application kernel trong userspace
Triết lý: "Eo thắt" syscall
Linux phiên bản 5.3.11 có khoảng 350 syscall. Trong một container runc thông thường, tất cả 350 syscall đó đều là attack surface — chỉ cần một lỗ hổng trong bất kỳ syscall nào là một process trong container có thể leo quyền lên host.
gVisor đặt một "application kernel" tên là Sentry đứng giữa container và host kernel. Sentry tự implement lại 237 syscall Linux bằng Go memory-safe. Khi container gọi open() hay read(), Sentry intercept syscall đó và xử lý hoàn toàn trong userspace. Bản thân Sentry — toàn bộ Sentry, phục vụ mọi syscall của container — chỉ gọi xuống host kernel 53 syscall (hoặc 68 nếu bật networking). Tỷ lệ là ~5:1: 350 syscall "tiềm năng" co lại thành 68 syscall "thực sự" reach được host. (Nguồn: gVisor Security Basics Part 1)
Sentry: Một application kernel viết bằng Go, chạy hoàn toàn trong userspace. Đảm nhiệm vai trò của một kernel mini — implement syscall, memory management, signal delivery, scheduling, page faulting, threading. Container "thấy" Sentry như là kernel Linux thật.
Gofer: Một process host riêng được spawn cùng container, đóng vai trò trusted filesystem proxy. Sentry không được phép
open()file trên host (seccomp chặn) — mọi truy cập filesystem phải đi qua Gofer. Đây là một thanh chắn quan trọng: nếu Sentry bị compromise, attacker vẫn không tựopen("/etc/shadow")được, mà phải xin Gofer.
runsc: Binary entrypoint, OCI-compliant. Đóng gói Sentry + Platform + Gofer thành một runtime mà Docker / containerd / Kubernetes có thể plug-in qua
--runtime=runschoặcRuntimeClass.
Sơ đồ kiến trúc

Layer model của gVisor. Nguồn: gvisor.dev/docs/
Diagram trên minh hoạ ý tưởng "eo thắt": container phía trên có thể gọi 350 syscall (Application Calls), nhưng Sentry chỉ chuyển một tập rất nhỏ trong số đó xuống host (Limited System Calls). Phần bên trái trong bản đầy đủ trên gvisor.dev là Gofer — proxy filesystem riêng. Cả Sentry lẫn Gofer đều chạy với seccomp filter cực hẹp và bị pivot_root vào thư mục rỗng để không thể path-traverse.
Platform — cách Sentry bắt syscall của container
Một câu hỏi cơ bản: khi container gọi getpid(), làm sao Sentry chặn được? gVisor có ba "platform" tương ứng với ba cơ chế intercept khác nhau (Nguồn: Platform Guide):
| Platform | Cơ chế bắt syscall | Trạng thái |
|---|---|---|
| ptrace | PTRACE_SYSEMU trap mỗi syscall | Deprecated (chậm) |
| KVM | Sentry vừa là guest OS vừa là VMM, dùng hardware virtualization | Tốt trên bare-metal, kém trong nested-virt |
| Systrap | seccomp SECCOMP_RET_TRAP gửi SIGSYS | Default (từ giữa 2023) |
ptrace là cách đơn giản nhất: kernel "pause" thread của container mỗi khi nó issue syscall, đẩy nó cho Sentry xử lý, rồi resume. Vấn đề: mỗi syscall = 4 context switch user/kernel/user/kernel. Workload syscall-heavy (Redis, web server nhỏ) chậm thê thảm. Đã bị deprecate.
KVM dùng hardware virtualization: Sentry tự đóng vai trò guest kernel và VMM, container chạy trong "ring 3" của một mini-VM, mỗi syscall trap qua VMEXIT. Nhanh khi chạy trên bare-metal hoặc machine có hardware virt enabled, nhưng trong VM của cloud (nested virtualization) thì chậm và nhiều khi không khả dụng.
Systrap là default mới từ giữa 2023. Kernel cài seccomp filter trên thread container với SECCOMP_RET_TRAP — khi thread gọi syscall, kernel raise SIGSYS cho thread đó, signal handler đẩy context sang Sentry. Đây là sự hợp lý giữa ptrace (universal nhưng chậm) và KVM (nhanh nhưng đòi hardware): Systrap chạy ổn trên cả bare-metal lẫn cloud VM.
Sentry — kernel mini bằng Go
Sentry implement bằng pure Go (không CGo), và phần unsafe được tách riêng vào các file được audit kỹ. Tại sao Go? Memory safety. Một lỗ hổng kiểu use-after-free hay buffer overflow trong implementation của syscall — vốn là nguồn của hầu hết kernel CVE — trở nên khó/không thể xảy ra trong Go. Một CVE kernel ảnh hưởng đến code C của Linux gần như chắc chắn không ảnh hưởng đến Sentry, vì Sentry là code Go viết lại từ đầu.
Một ví dụ thực tế: CVE-2020-14386 là một bug leo quyền trong AF_PACKET của kernel Linux. gVisor không bị ảnh hưởng vì Sentry tự implement packet socket code bằng Go — code Linux có bug, code Sentry hoàn toàn khác.
Gofer và filesystem — sự tiến hoá
Filesystem là phần phức tạp nhất của gVisor. Sentry không có quyền open() file host — vậy làm sao container mở được file?
Trả lời: Gofer. Khi runsc start một sandbox, nó cũng spawn một process Gofer riêng. Gofer có quyền truy cập rootfs của container (thông qua bind mount), Sentry thì không. Sentry và Gofer giao tiếp qua một Unix socket.
Giao thức giao tiếp đã thay đổi 3 lần:
| Thế hệ | Năm | Cơ chế | Trade-off |
|---|---|---|---|
| 9P | 2018+ | Mỗi path component = 1 RPC. stat("/a/b/c") = 3+ RPC | Đơn giản, nhưng chatty và chậm |
| LISAFS | 2022 | Linux Sandbox FS protocol, walk nhiều component trong 1 RPC | Gofer memory giảm 30-60%, cold start App Engine nhanh hơn 25% |
| Directfs | 2023 (DEFAULT) | Gofer pass raw file descriptor sang Sentry qua SCM_RIGHTS. Sentry gọi openat() trực tiếp trên FD đó | stat nhanh hơn 2x, Ruby load nhanh hơn 17% |
Directfs là cú breakthrough: Sentry vẫn không có quyền open path tuỳ ý, nhưng được Gofer cấp sẵn FD vào rootfs. Sentry dùng openat(fd, "relative/path") đi tiếp — vẫn an toàn vì bị pivot_root vào thư mục rỗng, không có cách nào "leo" ra khỏi FD đó. (Nguồn: Directfs blog, LISAFS announcement)

Resource model: sandbox bao quanh Sentry, Gofer đứng ngoài làm filesystem proxy. Nguồn: gvisor.dev/docs/architecture_guide/resources/
Networking — Netstack TCP/IP bằng Go
gVisor mặc định dùng Netstack — một stack TCP/IP đầy đủ viết bằng Go. Container gọi socket(), connect(), send() — Sentry chuyển những syscall đó cho Netstack xử lý. Netstack tự ráp packet TCP/UDP, IP header, rồi đẩy frame raw xuống host qua một AF_PACKET socket.
Hệ quả: một CVE TCP stack Linux không đụng được Netstack. Cái giá: Netstack chưa support đầy đủ các cơ chế recovery cao cấp như Linux (CUBIC variants, BBR tweaks, …) và CPU efficiency thấp hơn ~20-30% so với kernel Linux.
Có chế độ thay thế passthrough (--network=host): Sentry chuyển syscall socket xuống thẳng host kernel. Nhanh hơn nhưng đánh đổi isolation — gần như trở về như container runc về mặt network attack surface. (Nguồn: Networking guide)
Security model — ba lớp phòng thủ

Threat model. Nguồn: gvisor.dev/docs/architecture_guide/security/
Theo doc chính thức (Security Model), gVisor có ba nguyên tắc defense-in-depth:
- No syscall bypass — Mỗi syscall có một implementation Go độc lập. CVE syscall X trong Linux không tự động đụng được syscall X trong gVisor.
- Limited functionality — Chỉ implement những phần phổ biến. Phần "exotic" (xattrs, raw socket, hầu hết ioctl, io_uring đầy đủ) bị bỏ. Càng ít feature = càng ít attack surface.
- Restricted host exposure — Syscall mà Sentry được phép gọi xuống host được enumerate cứng và enforce bằng seccomp filter ngay khi Sentry start.
Khi nào gVisor chậm, khi nào nhanh
Hiểu rõ cost model là chìa khoá khi quyết định dùng gVisor (Nguồn: Performance guide).
Nhanh (gần như zero overhead):
- Code CPU-bound thuần (math, image processing). Không gọi syscall = không qua Sentry = chạy native.
- Workload genuinely I/O-bound (video transcode, large file scan). Disk cost át chi phí Sentry.
Chậm (đáng kể):
- Workload syscall-heavy. Redis-style workload có thể chậm 20-50% tuỳ benchmark, key-value store nhỏ, server xử lý nhiều request ngắn.
- VFS-heavy (Apache serve static nhỏ): Sentry serialize VFS access.
- Network throughput cao: Netstack thua kernel ~20-30%.
- Cold start: khoảng vài chục đến vài trăm ms thêm so với runc.
Sử dụng thực tế
Tại Google: App Engine standard, Cloud Functions, Cloud ML Engine, Cloud Run 1st-gen (đời sau dùng microVM kiểu Firecracker, nhưng 1st-gen vẫn dùng gVisor). Ngoài Google: nhiều CI runner cho code không tin tưởng, sandbox AI agent, OSS Fuzz.
Tích hợp Kubernetes:
# Cách 1: GKE Sandbox managed
gcloud container node-pools create sandbox-pool \
--cluster=my-cluster \
--sandbox=type=gvisor
# Trong Pod manifest:
spec:
runtimeClassName: gvisor
containers: [...]
# Cách 2: Self-managed (containerd shim)
# Install containerd-shim-runsc-v1, register RuntimeClass:
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
Docker chỉ cần sudo runsc install && docker run --runtime=runsc ubuntu. (Nguồn: gVisor Quick Start)
Firecracker — Hypervisor tối giản cho serverless
Triết lý: "Container VM"
Trong khi gVisor tránh né hardware virtualization (vì lý do cloud nested-virt), Firecracker đi ngược lại: ôm chặt KVM. Nhưng nó cắt bỏ tất cả những phần "rườm rà" của một hypervisor truyền thống. AWS đặt câu hỏi đúng: "a virtual machine would look like if it was designed for today's world of containers and functions?" (AWS announcement 2018)
Câu trả lời: một VMM chỉ 50.000 dòng Rust (so với QEMU >1.4 triệu dòng C). Không PCI, không USB, không graphics, không sound, không BIOS, không ACPI enumeration, không CPU instruction emulation. Chỉ giữ những thứ tối thiểu để guest kernel boot và phục vụ một workload kiểu container: virtio block, virtio net, virtio vsock, một virtio balloon, một serial console, và một i8042 keyboard controller bé tí — chỉ để guest có thể gửi tín hiệu reset/shutdown.
Firecracker: Một Virtual Machine Monitor (VMM) viết bằng Rust, chạy trên KVM. Mỗi process Firecracker quản lý đúng một microVM. Boot guest kernel trong dưới 125ms, overhead RAM khoảng 5MB, có thể tạo 150 microVM/giây mỗi host. Là engine đằng sau AWS Lambda và AWS Fargate.
microVM: Một VM thực sự (dùng KVM, có guest kernel riêng) nhưng được trimmed cực nhỏ — không PCI, không BIOS, chỉ có virtio device tối thiểu. Mục tiêu: chạy một container-like workload với boot time và density gần với container, mà cô lập ở tầng hardware như VM.
Jailer: Process wrapper khởi chạy Firecracker. Cài chroot, cgroup, namespace, seccomp filter, drop privilege, rồi mới
exec()vào Firecracker binary. Đây là lớp phòng thủ thứ hai — nếu guest thoát được KVM, vẫn rơi vào một jail unprivileged.
Tại sao nhỏ thế?
Firecracker được fork từ crosvm của Chromium OS (cũng là một minimal VMM Rust cho Chrome OS). Bước đầu xoá hơn nửa code — bỏ tất cả phần liên quan đến desktop, GPU, audio. Sau đó rewrite core. Rust được chọn vì memory safety: hầu hết CVE hypervisor lịch sử đến từ bug C kiểu use-after-free, buffer overflow trong device emulation. Rust loại được cả lớp lỗi đó. (Nguồn: LWN, Micah Lerner summary)
Việc bỏ PCI nghe có vẻ kinh khủng nhưng nhìn kỹ rất hợp lý: serverless function không cần plug-and-play device discovery. Guest biết trước nó có virtio-net, virtio-block ở địa chỉ cố định. Bỏ BIOS — kernel boot trực tiếp từ entry point, không cần real-mode setup. Bỏ ACPI — không có power management dynamic. Tất cả những gì còn lại là một stub guest tối giản, đủ để Linux kernel boot và chạy.
Sơ đồ kiến trúc

Firecracker host integration. Nguồn: firecracker-microvm.github.io
Threading model
Mỗi Firecracker process có ba loại thread (design doc):
+--------------------- Firecracker process ----------------------+
| Unix socket (REST API, control plane) <----+ |
| | |
| +-------------+ +--------------+ +----------------------+ |
| | API thread | | VMM thread | | vCPU thread(s) | |
| | (HTTP/UDS, | | (devices, | | (loop: | |
| | control) | | IRQ chip, | | ioctl(KVM_RUN)) | |
| | | | PIO/MMIO) | | -> guest code runs | |
| +-------------+ +--------------+ +----------------------+ |
| ^ ^ | |
| +----------------+------ ioctl KVM_* --+ |
+----------------------------------------------------------------+
Control plane (REST API qua Unix socket) tách bạch với data plane (vCPU và virtio queue). Một orchestrator gọi API để config CPU, RAM, attach drive, network, snapshot — sau đó vCPU thread tự chạy guest code qua ioctl(KVM_RUN). Có token-bucket rate limiter built-in per-device cho ops/sec và bandwidth.
Jailer — defense in depth
Firecracker tự nó đã rất nhỏ (TCB chỉ ~50K LoC Rust), nhưng AWS vẫn không tin. Threat model giả định: "vCPU thread chạy malicious code ngay từ giây thứ nhất sau boot". Vậy nếu một CVE trong virtio device cho phép guest đọc bộ nhớ host thì sao?
Jailer là câu trả lời. Trước khi exec() vào binary Firecracker, jailer làm tuần tự (Nguồn: design doc):
1. Tạo cgroup (v1/v2): giới hạn CPU, memory, PIDs
2. unshare(CLONE_NEW{PID,NET,MNT,IPC,UTS}): tách namespace
3. chroot(/firecracker/jail/<id>): khoá filesystem vào jail
4. setresuid()/setresgid(): drop về UID unprivileged
5. Load seccomp-bpf filter: whitelist ~24 syscall + ~30 ioctl
6. exec("firecracker"): chuyển sang binary chính
Sau bước 6, ngay cả nếu Firecracker bị hack từ bên trong, attacker chỉ thấy: một jail rỗng, không có UID 0, không có quyền sang namespace khác, không có syscall nào ngoài 24 cái được whitelist. Hai lớp barrier: KVM + jailer.
Snapshot/restore — bí mật của Lambda SnapStart
Một trong những feature kỹ thuật ấn tượng nhất của Firecracker là snapshot: dump toàn bộ state của microVM (memory page, device state, CPU register, vCPU state) ra disk, rồi restore lại trong vài mili-giây. (Nguồn: Marc Brooker — SnapStart blog)
AWS Lambda SnapStart dùng ý tưởng này cho cold start:
Cold start truyền thống:
boot kernel (100ms) -> init runtime (500ms-3s) -> load code -> chạy
= vài giây cho lần đầu
Cold start với SnapStart:
[build-time, offline]
boot kernel -> init runtime -> chạy customer init code -> SNAPSHOT
[request time]
RESTORE từ snapshot -> chạy handler (4-10ms)
Một số chi tiết tinh tế:
- Hierarchical snapshot tree — Lambda tạo snapshot phân tầng: post-kernel-boot, post-runtime-init, post-customer-init. Page nào không thay đổi giữa các tầng được dedupe, giảm ~90% data movement.
- Predictive page loading — thay vì demand-paging (chậm vì network), Lambda đoán trước page nào sẽ cần và prefetch.
- Uniqueness hazard — restore từ snapshot = nhiều VM có cùng PRNG state, cùng
/dev/urandominitial pool. AWS phải patch OpenSSL, Linux kernel,java.security.SecureRandomđể reseed sau restore. Bài học: snapshot-based cloning có hệ luỵ subtle về entropy/uniqueness.
SnapStart hiện GA cho Java 11+, Python 3.12, .NET 8 — AWS quảng bá giảm cold start tới 10x. (Nguồn: AWS SnapStart docs)
Performance numbers
Lấy từ paper NSDI '20 (Agache et al.) và Adrian Colyer's summary:
| Metric | Firecracker | Cloud Hypervisor | QEMU |
|---|---|---|---|
| Memory overhead / VM | ~3-5 MiB | ~13 MB | ~131 MB |
| Boot time tới userspace | ~100-125 ms | ~200 ms | seconds |
| VM creation rate | ~150 / giây | thấp hơn | nhiều thấp hơn |
| Block I/O 4KB (max) | ~13,000 IOPS / 52 MB/s | cao hơn | native 340k+ |
Đây là điểm yếu thừa nhận: Firecracker không thay thế hypervisor general-purpose. Workload I/O block performance critical (database VM) thì QEMU + virtio-blk-vhost vẫn nhanh hơn nhiều lần. Firecracker tối ưu cho density và boot time, không cho throughput.
Density trong sản xuất:
- AWS chạy production với 10x oversubscription CPU/memory, đã test tới 20x.
- Hàng nghìn microVM cùng tồn tại trên một host vật lý.
- Tháng 11/2018 khi open-source: đã chạy trillion executions/tháng trên Lambda, tens of millions container/tuần trên Fargate.
API — REST qua Unix socket
Firecracker không có CLI flag phức tạp. Toàn bộ lifecycle qua REST:
# Start daemon (jailed)
firecracker --api-sock /tmp/fc.sock
# Set kernel + rootfs
curl --unix-socket /tmp/fc.sock -X PUT 'http://localhost/boot-source' \
-d '{"kernel_image_path": "vmlinux", "boot_args": "console=ttyS0 root=/dev/vda"}'
curl --unix-socket /tmp/fc.sock -X PUT 'http://localhost/drives/rootfs' \
-d '{"drive_id": "rootfs", "path_on_host": "rootfs.ext4", "is_root_device": true}'
# CPU / RAM
curl --unix-socket /tmp/fc.sock -X PUT 'http://localhost/machine-config' \
-d '{"vcpu_count": 1, "mem_size_mib": 256}'
# Network
curl --unix-socket /tmp/fc.sock -X PUT 'http://localhost/network-interfaces/eth0' \
-d '{"iface_id": "eth0", "host_dev_name": "tap0"}'
# Start
curl --unix-socket /tmp/fc.sock -X PUT 'http://localhost/actions' \
-d '{"action_type": "InstanceStart"}'
Đơn giản, declarative. Đây là điểm mạnh khi build platform — orchestrator chỉ cần gọi HTTP, không phải parse log/output.
Production use beyond AWS
- Fly.io — Fly Machines = Firecracker microVM. Start local ~10ms, cross-region ~150ms. Mỗi customer container thực sự là một VM. (Nguồn: Fly Machines blog)
- Koyeb, Northflank, Qovery, Browserbase — serverless / sandbox platforms.
- firecracker-containerd — containerd shim cho Firecracker, cho phép chạy OCI container trong microVM. AWS Fargate's data plane chính là firecracker-containerd. (Nguồn: Fargate data plane)
- Kata Containers — Kata thực ra hỗ trợ multiple backend; chọn Firecracker thay vì QEMU mặc định khi cần boot time / density.
Liên hệ với Nitro Hypervisor
Câu hỏi hay gặp: Firecracker có phải Nitro Hypervisor không? Không. Nitro Hypervisor là một hypervisor riêng (cũng dựa trên KVM), chạy trên Nitro Card (silicon AWS thiết kế) để cô lập EC2 instance. Firecracker chạy bên trong EC2 instance (thường là Nitro-based) để cung cấp lớp isolation thứ hai cho Lambda/Fargate workload. (Nguồn: AWS Nitro)
Phân lớp: Nitro cô lập customer (instance to instance), Firecracker cô lập workload (function to function trong cùng một customer).
So sánh trực tiếp gVisor vs Firecracker
Hai công nghệ giải cùng một bài toán bằng hai triết lý ngược nhau. Section này đặt chúng cạnh nhau theo từng góc.
Bản đồ kiến trúc — vị trí của ranh giới isolation
gVisor Firecracker
+-----------------------+ +-----------------------+
| Container app | | Guest userspace (app) |
| (Linux syscalls) | | |
+-----------------------+ +-----------------------+
| |
v (syscall intercepted) v
+-----------------------+ +-----------------------+
| Sentry (Go userspace) | | Guest kernel (Linux) |
| - reimpl 237 syscalls | | (full kernel, yours) |
| - Netstack TCP/IP | +-----------------------+
+-----------------------+ |
| v (VMEXIT)
v (53-68 host syscalls) +-----------------------+
+-----------------------+ | Firecracker VMM (Rust)|
| Host Linux kernel | | + KVM ioctls |
+-----------------------+ +-----------------------+
|
v
+-----------------------+
| Host Linux kernel+KVM |
+-----------------------+
Điểm cốt lõi: ở đâu là ranh giới giữa "code không tin tưởng" và "host kernel"?
- gVisor: ranh giới là Sentry. Container code -> Sentry (Go) -> host kernel. Mọi syscall bị Sentry filter, reimplement. Không có guest kernel.
- Firecracker: ranh giới là KVM. Container code -> guest kernel -> Firecracker VMM -> host kernel. Có một guest kernel đầy đủ giữa container và host.
Bảng so sánh đầy đủ
| Chiều | gVisor | Firecracker |
|---|---|---|
| Loại isolation | Application kernel (userspace) | Hardware virtualization (KVM) |
| Ngôn ngữ | Go (memory-safe) | Rust (memory-safe) |
| Cô lập syscall qua | Reimplement trong Sentry | Guest kernel + KVM |
| Có guest kernel? | Không | Có (Linux đầy đủ) |
| Linux ABI compat | Một phần (~237/350 syscall) | 100% (chạy Linux kernel thật) |
| Boot time | Vài chục đến vài trăm ms | ~125 ms |
| Memory overhead | ~15 MB (Sentry + Gofer) | ~5 MB (chưa tính guest kernel) |
| Density / host | Hàng nghìn | Hàng nghìn |
| Syscall overhead | Đáng kể (10-30% cho syscall-heavy) | Gần native (sau khi boot) |
| Network performance | Netstack chậm hơn Linux ~20-30% | Native (qua TAP) |
| I/O storage | Qua Gofer (giờ là Directfs, gần native) | virtio-blk (≤ kernel native, OK) |
| Hardware virt yêu cầu | Không (Systrap không cần) | Có (KVM bắt buộc) |
| Nested virtualization | OK | Khả dụng nhưng ít dùng |
| GPU passthrough | NVIDIA support | Không (không có PCI) |
| OCI compatible | Có (runsc) | Không (cần firecracker-containerd) |
| Use case chính | Sandbox code không tin tưởng, GKE Sandbox | Serverless (Lambda), micro-VM platform |
| Mã nguồn | ~600K LoC Go | ~50K LoC Rust |
| Open-sourced | 2018 (Google) | 2018 (AWS) |
Attack surface — khác biệt căn bản
Đây là điểm khác biệt nền tảng nhất.
gVisor giảm attack surface bằng cách thu hẹp số syscall reach được host kernel: 350 -> 68. Container code phải xuyên qua Sentry, và Sentry chỉ "đụng" host qua 68 syscall được whitelist. Một CVE kernel trong syscall không nằm trong 68 đó không tồn tại về mặt thực tế trong môi trường gVisor.
[Container code]
v
| ~237 syscalls go through Sentry (Go reimpl)
v
[Sentry]
v
| 53-68 host syscalls (seccomp-enforced)
v
[Host kernel]
Firecracker giữ toàn bộ syscall surface — nhưng surface đó nằm ở guest kernel của container, không phải host. Code trong guest có thể exploit guest kernel — nhưng để thoát ra host, phải:
- Compromise guest kernel (350 syscall, đầy đủ Linux)
- Compromise virtio device emulation trong Firecracker VMM (~50K LoC Rust)
- Hoặc tìm bug trong KVM (host kernel module)
- Sau đó vẫn rơi vào jailer sandbox (seccomp, chroot, namespace)
Hai cách tiếp cận khác nhau triết lý: gVisor hạn chế attack surface, Firecracker xếp lớp attack surface.
Hệ quả thực tế: nếu attacker khai thác được một kernel CVE, gVisor có khả năng cao "miễn nhiễm" (vì Sentry reimplement code). Firecracker thì guest kernel của bạn cũng có CVE đó — nhưng escape ra host vẫn phải qua KVM/jailer barrier.
Compatibility — đâu mới là "Linux thật"?
Firecracker = guest kernel Linux thật. Bạn boot bất kỳ kernel Linux nào (5.10+, 6.x, custom). Mọi syscall hoạt động đúng như Linux, mọi /proc, mọi ioctl, mọi feature mới đều có. Workload chạy "as is".
gVisor = subset của Linux. ~237/350 syscall implement, một số phần:
iptableschỉ hỗ trợ một phần (đủ Docker)nftableshạn chếio_uringdisabled mặc định (chỉ basic op khi bật)- Custom hardware device files không support (trừ NVIDIA GPU, TPU)
- KVM-trong-sandbox không được (không có nested virt)
- Mount block device (ext4, fat32) từ trong sandbox: không
- Một số
ioctlexotic,xattrs, raw socket: không
Với 90% workload web (Node, Python, Go service), không vấn đề gì. Với workload "low-level" (cần perf, cần eBPF, cần io_uring cao cấp): chọn Firecracker.
Performance — khác biệt theo workload
Không có "cái nào nhanh hơn" — phụ thuộc workload:
| Workload | gVisor | Firecracker |
|---|---|---|
| CPU-bound compute | Gần native | Gần native |
| Cold start (1 lần) | 100-300 ms | ~125 ms |
| Cold start + snapshot | Không có | 4-10 ms (SnapStart) |
| Memory / instance | ~15 MB (Sentry+Gofer) | ~5 MB + guest kernel ~20 MB |
| Network throughput | Netstack: -20%; passthrough: 0% | Native (qua TAP) |
| Storage IOPS | Directfs: gần native | virtio-blk: OK nhưng có cap |
| Syscall-heavy | -10% đến -50% | Gần native |
| Memory pressure / density | Tốt (không có guest kernel) | Tốn hơn |
Nói gọn: Firecracker tốt hơn cho workload cần performance bám sát native nhưng vẫn cần strong isolation. gVisor tốt hơn cho workload mà cost của một guest kernel quá lớn (vd. hàng triệu function ngắn, mỗi cái <50ms), hoặc khi không có hardware virtualization (chạy trong cloud VM nested).
Use case "nên dùng cái nào"
Chọn gVisor khi:
- Bạn muốn drop-in thay
runccho Kubernetes (có RuntimeClass sẵn cho GKE, minikube, containerd-shim). - Workload là OCI container chuẩn, không cần guest kernel riêng.
- Cần defense in depth chống kernel CVE mà không muốn vận hành hypervisor.
- Chạy trên cloud VM không có hardware virt (Systrap platform).
- CI runner / sandbox cho user code không tin tưởng.
- Workload web tiêu chuẩn, không quá syscall-heavy.
Chọn Firecracker khi:
- Bạn xây platform (Lambda-like, Fly Machines-like). Bạn có engineering capacity để build control plane.
- Cần 100% Linux ABI — guest tự chọn kernel, tự cài module.
- Cần snapshot/restore cực nhanh (SnapStart pattern).
- Cần GPU/specialized hardware passthrough? Khó — Firecracker bỏ PCI.
- Multi-tenant function platform với SLA cold start nghiêm ngặt.
- Workload syscall-heavy mà gVisor làm chậm quá.
Cân nhắc Kata Containers nếu bạn muốn microVM nhưng cũng cần OCI compatibility ngay (Kata wrap Firecracker hoặc QEMU thành OCI runtime).
Một góc nhìn lịch sử
Hai dự án open-source gần như đồng thời (cuối 2018), từ hai đối thủ trực tiếp:
2018-05 Google open-source gVisor tại KubeCon EU
2018-11 AWS open-source Firecracker tại re:Invent
Trên thực tế, gVisor cũng từng được xem xét cho Lambda (theo lời Marc Brooker trong Seven Years of Firecracker), nhưng AWS chọn Firecracker vì lý do compatibility — họ cần chạy bất kỳ runtime nào khách hàng deploy (Java JVM, .NET, Go, Rust binary tự build), không thể chấp nhận 5% workload bị break vì subset syscall.
Ngược lại, Google chọn gVisor vì App Engine / Cloud Run / Cloud Functions phần lớn là managed runtime — Google kiểm soát interpreter, biết workload không động vào syscall exotic. Trade-off compatibility chấp nhận được.
Hai chọn lựa engineering khác nhau cho hai môi trường khách hàng khác nhau, không phải vì kỹ thuật bên này thắng bên kia.
Kết luận
gVisor và Firecracker xuất hiện cùng năm 2018 vì cùng một áp lực thị trường: chạy code không tin tưởng ở mật độ cao, với cost thấp. Nhưng triết lý giải bài toán thì gần như đối nghịch:
- gVisor: "Hãy giảm số syscall reach được host." Sentry trở thành một firewall ngữ nghĩa — container không bao giờ chạm trực tiếp host kernel.
- Firecracker: "Hãy cho workload một kernel riêng — nhưng đặt nó trong VM cực nhỏ." Hardware virtualization tách biệt rõ ràng, jailer là lớp phụ.
Hai cái không thay thế nhau, chúng bổ sung: một số platform thậm chí dùng cả hai (gVisor cho workload syscall-không-quá-exotic, Firecracker cho workload đòi 100% Linux ABI).
Vài điểm rút ra:
- Memory-safe language (Go cho Sentry, Rust cho Firecracker) là điều kiện cần khi viết code trên đường nóng của isolation boundary. Cả hai team đều tránh C có chủ đích.
- Defense in depth là chuẩn. Cả hai không tin tưởng layer chính của mình — gVisor có Sentry + seccomp host + Gofer tách quyền filesystem, Firecracker có KVM barrier + jailer + seccomp.
- Minimalism là feature. Firecracker chỉ giữ 5 virtio device. Sentry chỉ implement những syscall thật sự cần. Càng ít code = càng ít CVE.
- Trade-off là thật. gVisor nhanh hơn cho function ngắn nhưng compat thấp hơn. Firecracker compat 100% nhưng tốn RAM guest kernel. Không có free lunch.
Nếu bạn đang thiết kế hệ thống multi-tenant, câu hỏi đầu tiên không phải "gVisor hay Firecracker", mà là threat model của tôi là gì: ai chạy code không tin tưởng, ai gánh hậu quả nếu thoát container, cost của một CVE đáng giá bao nhiêu. Trả lời câu đó trước, rồi chọn công cụ phù hợp — hoặc thực dụng hơn: dùng Kubernetes RuntimeClass và để workload chọn runtime (runc, runsc, kata) qua label.
Đọc thêm trên blog này
- Container the Hard Way — mổ xẻ container từ namespace, cgroup, đến runtime OCI. gVisor và Firecracker được điểm qua ngắn trong section Beyond runc; bài hiện tại là phần deep dive mở rộng cho hai cái đó.
- eBPF Deep Dive — một con đường khác để observe và secure workload trong kernel, dùng cho phòng vệ chủ động thay vì sandbox.
- Harness Engineering Deep Dive — sandbox cho AI agent là một use case rất hiện đại của gVisor và Firecracker.
References
Tài liệu chính thức
- gVisor Documentation — root docs
- gVisor Architecture Guide — Platform, Security, Networking, Performance, Resources
- gVisor Compatibility Reference — danh sách syscall hỗ trợ
- gVisor GitHub
- Firecracker Documentation
- Firecracker Design Document
- Firecracker GitHub
- firecracker-containerd architecture
Paper và blog kỹ thuật
- Agache et al., "Firecracker: Lightweight Virtualization for Serverless Applications", NSDI 2020 — PDF
- Adrian Colyer — Firecracker paper summary (the morning paper)
- Micah Lerner — Firecracker paper summary
- Marc Brooker (AWS) — Lambda SnapStart
- Marc Brooker (AWS) — Seven Years of Firecracker (2025)
- LWN — The Firecracker VMM
gVisor specifics
- gVisor blog — Security Basics Part 1 — nguồn chính cho con số 237/68/53 syscall
- gVisor blog — Directfs filesystem (2023)
- Google Cloud — gVisor filesystem improvements (LISAFS)
- Google Cloud — How gVisor protects against CVE-2020-14386
- Google Cloud — Open-sourcing gVisor announcement
- Google Cloud — GKE Sandbox docs
Firecracker production stories
- AWS — Firecracker open-source announcement (2018)
- AWS — Under the hood: Fargate data plane
- AWS — Lambda SnapStart documentation
- AWS — Nitro System overview
- Fly.io — Fly Machines
- Fly.io — Learn Firecracker VM
So sánh & phân tích
- Northflank — Kata Containers vs Firecracker vs gVisor
- Microarchitectural security study — Springer Link
