Rebuild toàn bộ hạ tầng Linux MicroVM trên Apple Silicon: Câu chuyện từ Encore
Encore, nền tảng triển khai backend, đã xây dựng 'crackling' — một API microVM thống nhất giúp chạy cùng một hệ thống build dựa trên Firecracker trên cả Linux lẫn macOS (Apple Silicon) mà không cần máy ảo lồng nhau. Bài viết chia sẻ chi tiết về quá trình thay thế quy trình SSH cồng kềnh, những thách thức kỹ thuật với Apple Virtualization.framework và cách họ tối ưu hóa toàn bộ image pipeline để chạy native trên Mac.

Encore tái xây dựng toàn bộ hạ tầng Linux MicroVM để chạy native trên Apple Silicon
Encore, nền tảng tự động hóa backend, đã chính thức thay thế quy trình build từ xa đầy bất tiện bằng một hệ thống microVM thống nhất mang tên "crackling". Hệ thống này cho phép các kỹ sư chạy cùng một công cụ build dựa trên Firecracker — loại microVM từng chỉ hoạt động trên Linux — ngay trên chiếc MacBook Apple Silicon của họ, thông qua Apple Virtualization.framework. Với crackling, Encore không chỉ loại bỏ nhu cầu SSH vào máy chủ dùng chung mà còn tối ưu toàn bộ quy trình chuyển đổi Docker image thành rootfs có thể boot, mở ra trải nghiệm phát triển mượt mà như đúng cam kết "backend nên dễ dàng" của họ.
Vấn đề: Bốn năm build system "ở đâu đó"
Encore đã sử dụng Firecracker để cô lập mọi build sau khi triển khai từ giữa năm 2022. Firecracker loại bỏ phần cứng giả lập thừa thãi, chỉ giữ lại những gì kernel Linux cần, mang lại độ khởi động nhanh như container nhưng độ cô lập mạnh như máy ảo.
Tuy nhiên, có một vấn đề lớn: Firecracker dựa trên KVM, và KVM không tồn tại trên macOS. Trong khi đó, hầu hết kỹ sư của Encore đều phát triển trên Mac. Ngay cả một bản proof-of-concept dùng Apple's Virtualization.framework cũng bị đội ngũ upstream từ chối, vì họ không có kế hoạch hỗ trợ macOS.
Kết quả là trong suốt bốn năm, làm việc trên build system đồng nghĩa với việc bạn phải làm việc trên một máy chủ Linux ở data center, thông qua SSH.
Quy trình cũ: Một loạt script và SSH "không thể chạm vào breakpoint"
Quy trình cũ của Encore được mô tả là cồng kềnh và đầy thủ công:
- Onboarding: Mỗi kỹ sư chạy một script để SSH vào máy build dùng chung với quyền root, tự tạo user, thêm vào nhóm
kvmvàdocker, rồi copy VM images và hard-link binary firecracker vào thư mục cá nhân. - Đồng bộ mã: Họ cross-compile với
GOOS=linux GOARCH=amd64, rồirsynckết quả lên máy chủ. - Xử lý image: Đây là phần khó nhất. Họ phải tự viết công cụ để chuyển Docker layers thành block device mà Firecracker có thể boot. Script này dùng
findđể xử lý các whiteout marker, hardcode/etc/resolv.conf, rồi gọimksquashfs— tất cả đều trên máy chủ từ xa. - Restart: Cần thêm một lệnh SSH thứ ba để kill container cũ và start container mới. Firecracker chạy trong Docker, nên họ phải dựng một bridge ảo tên
docker0bên trong container để gắn tap devices.
Vòng lặp này khiến các kỹ sư phải suy nghĩ kỹ trước khi thử nghiệm bất cứ điều gì mang tính khám phá. Đặt breakpoint cục bộ là điều không tưởng, còn đọc log thì phải tail file qua SSH. Họ muốn một thứ gì đó chạy ngay trên chiếc Mac họ đang ngồi.
Giải pháp: "crackling" — Một API, hai nền tảng
Encore đã xem xét các giải pháp có sẵn như Lima, Tart, podman với libkrun, hay thậm chí chạy KVM lồng nhau trên M3 (macOS 15). Nhưng tất cả đều dẫn đến việc tạo ra "một hệ thống build thứ hai, chỉ tồn tại trên laptop" — điều họ không muốn.
Vì vậy, họ xây dựng crackling: một daemon và CLI có khả năng boot OCI images thành microVM Linux trên cả hai nền tảng. Cụ thể:
- Trên Linux: Vẫn dùng Firecracker làm backend.
- Trên macOS: Dùng Apple Virtualization.framework (VZ).
Cả hai backend cùng tuân theo một trait MachineBackend với các phương thức start, shutdown, pause, resume, snapshot, wait, dispose, và connect_vsock. Việc lựa chọn backend được quyết định tĩnh tại thời điểm compile vì mỗi hệ điều hành chỉ có một backend khả dụng.
Xử lý rào cản threading của Apple VZ
Điểm kỹ thuật đau đầu nhất là VZVirtualMachine là !Send + !Sync, trong khi phần còn lại của daemon chạy trên tokio (vốn di chuyển future giữa các thread). Giải pháp của Encore là tạo một serial DispatchQueue toàn cục cho mọi hoạt động liên quan đến VM. Các closure dispatch lên queue này chỉ chứa dữ liệu Send, còn bản thân đối tượng VM không bao giờ rời khỏi queue. Nhờ vậy, compiler có thể chặn mọi capture không hợp lệ.
Họ cũng phải tách quá trình "lowering" từ MachineSpec thành VZVirtualMachineConfiguration thành hai pha: một pha tạo cấu trúc dữ liệu Send trên tokio, pha còn lại chuyển nó thành cấu trúc VZ trên queue.
Booting kernel và rootfs trên macOS
Không có KVM, không có quyền root, không thể loop mount — Encore phải tự viết lại toàn bộ image pipeline:
- Giải nén kernel: Kernel arm64 thường là file EFI zboot (vmlinuz). Họ tự viết code để nhận diện chữ ký MZ và zimg, đọc header để lấy offset, kích thước và thuật toán nén. Việc đưa kernel nén vào Virtualization.framework sẽ gây lỗi "internal error" vô cùng khó hiểu — họ thậm chí đã mất cả buổi chiều để tái ký binary trước khi nhận ra vấn đề.
- Áp dụng OCI layers: Họ xử lý hoàn toàn trong userspace, tôn trọng các whiteout entry
.whmột cách "in-process", không cần pass cleanup. Họ cũng phải viết custom platform resolver vì resolver mặc định không bao giờ khớp với imagelinux/arm64khi request từ Mac. - Tạo initramfs: Cần tạo archive cpio định dạng newc, nén bằng gzip, và bơm vào RAM. Họ dùng pure Rust để tạo cả hai lớp này. Cache được clone bằng
clonefiletrên APFS hoặcFICLONEreflink trên Linux, giúp tăng tốc đáng kể.
Agent trong guest và giao thức vsock
Mỗi VM chạy một agent tĩnh (static musl binary) lắng nghe trên AF_VSOCK. Agent này hỗ trợ:
execvới stdout/stderr được stream- Shell tương tác qua PTY
cphai chiềuforward— biến một connection thành tunnel tới một port bên trong guest
"Agent dùng vsock cho control, để guest không cần cấu hình mạng thủ công hay SSH daemon. Outbound networking là một tùy chọn riêng, còn inbound chỉ có thể truy cập qua control-plane forward với token xác thực sinh ra tại thời điểm boot."
Một phát hiện đáng chú ý về Apple Virtualization.framework
Khi triển khai tính năng snapshot, Encore phát hiện một điều khá "bất công":
VZVirtualMachine.saveMachineStateToURLvàvalidateSaveRestoreSupporttồn tại công khai.- Validator trả về thành công, nhưng lệnh save lại fail với
VZErrorInternal. - Lý do: Để save state, bạn cần entitlement
com.apple.private.virtualization— thứ Apple không cấp cho bên thứ ba. Entitlement công khaicom.apple.security.virtualizationchỉ đủ để chạy VM, không đủ để save.
Họ gọi đây là một hành vi "lừa đảo" vì validator không kiểm tra entitlement thứ hai, khiến lập trình viên mất thời gian truy tìm nguyên nhân.
Kết quả và sau đó
Giờ đây, một kỹ sư Encore chỉ cần clone build system về máy, chạy nó trên laptop, và boot chính những OCI image dùng cho build và deploy của Encore qua cùng một agent và vsock path. Máy chủ dùng chung đã biến mất — cùng với script docker save pipe qua rsync và cây cầu docker0 dựng thủ công trong container đặc quyền.
Bạn có thể đặt breakpoint trong build system và chạm vào nó. Điều đó đã không thể thực hiện trong suốt bốn năm qua.
Với việc crackling trở thành một phần trong nỗ lực cải thiện trải nghiệm backend, Encore hy vọng sẽ tiếp tục mở rộng hạ tầng này, đưa toàn bộ vòng lặp phát triển — từ local dev đến production — trở nên mượt mà và tự động hơn cho cả con người lẫn agent.
Sơ đồ kiến trúc crackling
Sơ đồ minh họa quá trình crackling điều phối giữa Firecracker (Linux) và Apple Virtualization.framework (macOS).
So sánh quy trình cũ và mới
So sánh trực quan giữa quy trình SSH từ xa nhiều bước và quy trình native trên máy tính cá nhân.
Agent vsock bên trong guest
*Minh họa agent chạy trong guest VM, giao tiếp với daemon qua giao thức vsock. *