Từ một máy ảo đến một triệu Sandbox: Chiến lược mở rộng quy mô của OpenComputer
OpenComputer đã giải quyết bài toán giới hạn tài nguyên đám mây bằng cách chuyển từ kiến trúc đơn vùng sang mô hình "Cell" phân tán. Bằng cách sử dụng Cloudflare Workers làm bộ định tuyến toàn cầu, họ hiện có thể mở rộng lên hàng triệu máy ảo trên nhiều nhà cung cấp dịch vụ đám mây khác nhau.

OpenComputer bắt đầu hành trình của mình chỉ với một máy ảo (VM) duy nhất tại một vùng (region) của Azure. Công ty phát triển nhanh chóng, nhưng chúng tôi sớm gặp phải bức tường lớn: Azure không thể tăng hạn ngạch tính toán (compute quota) tại vùng đó thêm nữa. Chúng tôi rơi vào tình trạng phải cố gắng tăng trưởng dựa trên một kho tài nguyên CPU cố định.
Bài viết này sẽ chia sẻ về cách chúng tôi vượt qua giới hạn đó để đạt được khả năng mở rộng dung lượng gần như vô hạn. Chúng tôi sẽ đi sâu vào cách chia nhỏ hệ thống thành các "cell" (ô), cách một sổ đăng ký toàn cầu tại lớp Edge quyết định vị trí của mỗi sandbox, và cách kết hợp bốn nhà cung cấp đám mây để đạt được mục tiêu một triệu CPU.
Trách nhiệm của Cell
Hạn chế của kiến trúc đơn vùng
Sandbox về bản chất là các máy ảo đầy đủ, và máy ảo cần phần cứng vật lý. Các nhà cung cấp đám mây phân bổ phần cứng đó dựa trên từng vùng dữ liệu, nơi có phần cứng hữu hạn và hàng dài khách hàng đang chờ đợi.
Vì vậy, các nhà cung cấp cấp phát dung lượng dưới dạng hạn ngạch, đo bằng tổng số CPU. Hạn ngạch ban đầu thường rất nhỏ và chỉ tăng sau khi bạn xây dựng được lịch sử sử dụng. Nếu bạn yêu cầu 10.000 CPU ngay từ ngày đầu tiên, câu trả lời chắc chắn là "Không".
Chúng tôi đã sớm chạm đến trần 300 CPU tại vùng Azure US East 2. Kế hoạch ban đầu là sử dụng hết hạn ngạch này và yêu cầu thêm, nhưng thật không may, chúng tôi đã chọn một trong những trung tâm dữ liệu bận rộn nhất thế giới, và 300 CPU là giới hạn tối đa bất chấp lịch sử sử dụng. Trong khi đó, người dùng mới vẫn tiếp tục đăng ký trong khi kho tài nguyên tính toán của chúng tôi bị cố định.
Giải pháp rõ ràng nhất là di chuyển đến một vùng ít đông đúc hơn hoặc chuyển sang một đám mây khác để có hạn ngạch lớn hơn. Tuy nhiên, vấn đề sâu xa hơn là việc di chuyển vùng hoặc nhà cung cấp đám mây chỉ đơn giản là đặt lại đồng hồ đếm ngược. Mọi vùng đều có giới hạn trên, và một kiến trúc đơn vùng phát triển đủ nhanh sẽ sớm chạm trần again.
Hơn nữa, nhu cầu về sandbox ở mức 10.000, 100.000 hoặc 1 triệu máy ảo đồng thời không thể được phục vụ vật lý từ một trung tâm dữ liệu duy nhất. Chúng tôi cần suy nghĩ lại kiến trúc sao cho việc thêm một vùng của bất kỳ đám mây nào trở thành một bước triển khai (deployment step) thay vì một dự án di chuyển.
Mô hình Cell biến dung lượng thành đơn vị triển khai
Chúng tôi bắt đầu bằng cách cắt giảm Control Plane (mặt phẳng điều khiển) xuống còn một công việc duy nhất là điều phối máy ảo, và đóng gói nó cùng với các worker mà nó quản lý thành một đơn vị triển khai vào bất kỳ vùng đám mây nào. Đơn vị này được gọi là "Cell".
Control Plane được thu gọn
Thiết kế lại này đã loại bỏ Control Plane xuống chỉ còn việc quản lý vòng đời của các máy ảo trong vùng của nó:
- Lên lịch cho một sandbox được yêu cầu lên một worker cụ thể.
- Theo dõi vị trí của mọi máy ảo và trạng thái của chúng.
- Ngủ đông (hibernate) các máy ảo nhàn rỗi vào bộ nhớ lưu trữ S3 và đánh thức chúng khi cần.
- Di chuyển máy ảo từ worker này sang worker khác khi cần cân bằng lại.
Các thành phần bên trong một cell chỉ biết về nhau và không biết gì về những thứ bên ngoài cell. Bảng điều khiển, logic thanh toán và mọi thứ liên quan đến người dùng đã được chuyển hoàn toàn ra khỏi cell.
Một cell là đơn vị cô lập lỗi nhỏ nhất, và định danh của nó mã hóa mọi thứ về vùng máy chủ mà nó cư ngụ. Mỗi cell sở hữu từ 5 đến 10 worker chạy các máy ảo QEMU.
Đặt máy ảo không chỉ là chọn worker ít tải nhất
Khi một yêu cầu tạo được gửi đi với thông tin về bộ nhớ, CPU, đĩa và mẫu (template), Control Plane có thể chọn một worker cụ thể dựa trên các tiêu chí nhất định. Một Control Plane đơn giản sẽ tìm worker ít tải nhất và tạo VM ở đó.
Tuy nhiên, một Control Plane tốt cần cân nhắc:
- Sự phù hợp của tài nguyên để tránh phân mảnh làm chết dung lượng.
- Độ "nóng" của mẫu (một worker đang giữ snapshot nóng có thể tạo sandbox trong khoảng 200ms, trong khi worker lạnh mất vài giây).
- Sự khác biệt về phần cứng như ARM so với amd64.
- Mối liên hệ mềm (soft affinity) để giữ các sandbox của một tổ chức gần bộ nhớ đệm của họ.
- Sự liên kết ngược (anti-affinity) để giữ khối lượng trả phí xa khỏi các hàng xóm miễn phí gây ồn ào.
Một cell triển khai trên bất kỳ đám mây nào
Một cell không đưa ra bất kỳ giả định nào về nhà cung cấp mà nó chạy trên đó. Hình dạng worker mà chúng tôi sử dụng có loại máy chủ tương đương trên AWS, Azure, GCP và OCI, do đó cùng một cell có thể được triển khai unchanged trên bất kỳ nền tảng nào.
Với điều này, dung lượng không còn là "hạn ngạch Azure của chúng tôi" mà trở thành "tổng số của mọi cell mà chúng tôi đã triển khai".
Sổ đăng ký toàn cầu tại Edge định tuyến mọi sandbox
Với nhiều cell độc lập, cần có một thứ gì đó để trả lời câu hỏi: Cell nào sẽ nhận sandbox này? Công việc đó nằm ngoài mọi cell, tại lớp Edge.
Cách yêu cầu tạo tìm thấy cell của mình
Lớp Edge là một tập hợp các Cloudflare Workers, với cơ sở dữ liệu D1 giữ sổ đăng ký toàn cầu và Durable Objects nhận luồng sự kiện từ các cell.
Sổ đăng ký đó biết mọi cell đang tồn tại, nơi nó chạy, và bao nhiêu dung lượng trống nó còn giữ. Chúng tôi coi nó như Control Plane của các Control Plane.
Một yêu cầu tạo sẽ hạ cánh tại vị trí Edge gần nhất, Worker sẽ chọn cell trống nhất, và từ đó Control Plane của cell đó sẽ sở hữu sandbox.
Chỉ việc tạo mới tốn thời gian tại Edge
Việc tạo sandbox vốn đã hiếm và tốn kém, nên việc dành thêm 50 đến 100ms tại Edge để xác thực, kiểm tra tín dụng và chọn cell là một sự đánh đổi hợp lý.
Mọi thao tác thường xuyên (exec, đọc/ghi file, lưu lượng PTY, hủy bỏ) đều đi thẳng đến Control Plane của cell qua một kết nối được xác thực bởi JWT đã ký, với không có tra cứu đồng bộ nào tới Cloudflare trên đường đi.
Edge quyết định nơi một sandbox được sinh ra, và sau đó cell sẽ phục vụ nó trực tiếp.
Nhịp tim giữ cho sổ đăng ký và thanh toán được cập nhật
Định tuyến là một nửa của lớp Edge. Nửa còn lại là luồng sự kiện chảy từ mọi máy ảo đang chạy quay trở lại qua Durable Objects và vào sổ đăng ký.
Thanh toán theo giây từ nhịp tim 10 giây
Sự kiện quan trọng nhất trong luồng đó là nhịp tim (heartbeat). Mọi máy ảo đang chạy đều báo hiệu "vẫn còn sống" mỗi 10 giây, và chúng tôi tổng hợp các tín hiệu này thành thanh toán, đó là cách chúng tôi tính phí cho mỗi giây sandbox thực sự chạy.
Các thay đổi trạng thái được đẩy đến sổ đăng ký thay vì chờ đợi một yêu cầu thăm dò (poll). Cùng một luồng cũng mang theo các sự kiện vòng đời. Ví dụ, một máy ảo đi ngủ đông, bị dừng hoặc di chuyển, và sổ đăng ký được cập nhật ngay lập tức bằng cách đẩy để quyết định định tuyến tiếp theo hoạt động dựa trên dung lượng hiện tại.
Bên trong một cell, các worker xuất bản sự kiện lên Redis Streams, và một bộ chuyển tiếp (forwarder) đóng gói chúng qua HTTPS tới một Cloudflare ingest Worker, xác thực mỗi lô bằng HMAC, khử trùng trên ID sự kiện trong KV, và phân phối đến sổ đăng ký và các đối tượng thanh toán.
Hiệu suất khởi động, ngủ đông và đánh thức trong thực tế
Thiết kế cell chỉ có ý nghĩa nếu các máy ảo bên trong nó đủ nhanh. Rất nhiều nỗ lực kỹ thuật và tinh chỉnh của chúng tôi đã tập trung vào độ trễ vòng đời. Hypervisor của chúng tôi là QEMU. Trong khi QEMU gốc khởi động lạnh mất khoảng 30 giây, chúng tôi đã đưa thời gian khởi động sandbox xuống dưới 1 giây ở mức p95.
Quá trình ngủ đông tạo một điểm kiểm tra máy ảo trong bộ nhớ lưu trữ S3 và hoàn thành trong khoảng 6 giây trong trường hợp thành công điển hình. Đánh thức sandbox đã ngủ đông trung bình từ 1 đến 2 giây, tốc độ phụ thuộc vào việc điểm kiểm tra vẫn còn nóng trên worker hay phải kéo lại từ S3.
Kết luận
Ban đầu chúng tôi bị mắc kẹt ở 300 CPU vì toàn bộ hệ thống phụ thuộc vào một vùng duy nhất của một nhà cung cấp đám mây. Bây giờ, đơn vị triển khai là một cell sở hữu vòng đời máy ảo của chính nó, và một vùng có thể chứa bao nhiêu cell tùy ý chúng tôi chọn.
Sổ đăng ký Edge định tuyến mọi việc tạo sandbox đến bất kỳ cell nào còn chỗ trống, và luồng nhịp tim giữ cho việc thanh toán trung thực ở độ chi tiết một giây. Lời hứa sản phẩm vẫn không thay đổi qua tất cả những điều này: Bạn nhận được một máy ảo Linux đầy đủ, được lên lịch gần bạn, và được tính phí cho từng giây nó thực sự chạy.
