1 điểm bởi GN⁺ 2023-10-24 | 1 bình luận | Chia sẻ qua WhatsApp
  • Tại Hackweek 22 của SUSE, tác giả đã tạo một POC unikernel chạy các mô-đun WebAssembly và chia quá trình triển khai thành nhiều phần
  • Nếu muốn port trực tiếp một ứng dụng thông thường sang unikernel thì còn phải khớp cả các dependency, nhưng nền tảng WebAssembly có ranh giới chức năng mà runtime cần cung cấp rõ ràng hơn
  • Ứng dụng Spiderlightning chỉ yêu cầu các chức năng như Key/Value, và dù host triển khai bằng Redis hay Azure Cosmos DB thì cùng một mô-đun .wasm cũng không cần biết sự khác biệt
  • Nền tảng cơ sở là unikernel Rust RustyHermit, và do Wasmtime cùng Wasmer không build được nên đã chọn wasmi, một runtime thuần Rust
  • Để khớp với Component Model và WIT, tác giả đã bổ sung hỗ trợ wasmi cho wit-bindgen, sau đó scaffold chức năng Key/Value phía host để chạy keyvalue-demo

Dự án Hackweek và mục tiêu

  • Trong thời gian diễn ra Hackweek 22, tác giả đã thực hiện dự án xây dựng một unikernel chạy WebAssembly
  • Toàn bộ quá trình triển khai quá dài để gói trong một bài viết nên được chia thành nhiều bài, và bài này là phần đầu tiên
  • Mã POC được công khai ở một nơi riêng, nhưng phần nội dung được cung cấp không có URL liên kết thực tế

Vì sao dùng unikernel cùng với WebAssembly

  • Với nhà phát triển ứng dụng, việc port sang unikernel là một gánh nặng lớn
    • Ứng dụng và toàn bộ dependency của nó đều phải hỗ trợ unikernel đích
    • Có thể cần vá trong toàn bộ stack ứng dụng
  • Người bảo trì unikernel cũng phải tốn rất nhiều công sức để giúp chạy mượt các ứng dụng tùy ý
    • Vì rất khó dự đoán ứng dụng người dùng sẽ sử dụng những primitive hệ thống nào
  • Ngược lại, nếu nhắm tới các nền tảng WebAssembly như Spin hoặc Spiderlightning, thì tập chức năng mà runtime cần cung cấp sẽ trở nên rõ ràng
  • Trong kịch bản Spiderlightning, ứng dụng có thể yêu cầu runtime cung cấp chức năng lưu trữ Key/Value
    • Dù host triển khai chức năng đó bằng Redis hay Azure Cosmos DB thì với ứng dụng điều đó vẫn hoàn toàn trong suốt
    • Cùng một mô-đun .wasm có thể chạy trên các triển khai host khác nhau

Kiến trúc mục tiêu

  • Nếu ứng dụng unikernel có thể chạy mô-đun WebAssembly và hỗ trợ tập API của Spiderlightning, thì cùng một ứng dụng Spiderlightning có thể chạy trên cả runtime slight thông thường lẫn unikernel đó
  • Nhà phát triển ứng dụng không cần làm thêm việc gì, và mô-đun Wasm cũng không cần biết nó đang chạy ở đâu
  • Độ phức tạp sẽ dồn về phía nhà phát triển unikernel, nhưng phạm vi cần triển khai rõ ràng hơn nhiều so với mục tiêu “hỗ trợ chạy mọi ứng dụng”

Triển khai dựa trên RustyHermit

  • Tác giả chọn RustyHermit làm nền tảng
    • Đây là một unikernel được viết bằng Rust
    • Nó có trong Rust nightly, nên mang lại trải nghiệm phát triển khá giống với khi viết ứng dụng Rust thông thường
  • Việc build ứng dụng RustyHermit tương đối đơn giản
    • Tài liệu hơi phân tán, nhưng chất lượng tốt và các ví dụ rất hữu ích
  • Không thể kỳ vọng mọi Rust crate đều hoạt động nguyên trạng trên RustyHermit, và ràng buộc này đã ảnh hưởng đến quá trình phát triển POC

Lựa chọn runtime WebAssembly

  • Wasmtime vốn là lựa chọn ưu tiên của tác giả nhưng không build được trên RustyHermit
    • Nhiều dependency của nó trông đợi libc hoặc các thư viện mức thấp khác
  • wasmer cũng gặp vấn đề tương tự
  • Tác giả cũng cân nhắc WebAssembly Micro Runtime, nhưng quyết định dùng một runtime viết bằng Rust để giữ trải nghiệm “RustyHermit hoàn chỉnh”
  • Cuối cùng, tác giả chọn wasmi, một runtime WebAssembly thuần Rust
    • Nó hoạt động tốt trên RustyHermit
    • Thiết kế của nó lấy cảm hứng từ Wasmtime nên có thể tái sử dụng khá nhiều kiến thức sẵn có

WebAssembly Component Model và WIT

  • Spiderlightning sử dụng đề xuất WebAssembly Component Model
    • Để cung cấp chức năng cho guest WebAssembly
    • Và để host có thể sử dụng các chức năng do guest WebAssembly cung cấp
  • Giao tiếp giữa host và guest dùng các kiểu được định nghĩa bởi Wasm Interface Type
  • Demo sử dụng Component Model theo luồng sau
    • Guest yêu cầu host khởi động HTTP server, đồng thời truyền vào các route HTTP cần đăng ký và tên hàm handler nội bộ
      • Sử dụng kiểu http-server, trong đó guest dùng chức năng do host cung cấp
    • Host xử lý các HTTP request đi vào dựa trên thông tin routing do guest cung cấp
      • HTTP handler là hàm do guest WebAssembly export ra
      • Server sử dụng chức năng do guest cung cấp và giao tiếp bằng kiểu http-handler
    • Một số HTTP handler tương tác với kho lưu trữ Key/Value
      • Trong trường hợp này, guest tiếp tục dùng chức năng do host cung cấp và được định nghĩa bằng kiểu keyvalue

Mở rộng wit-bindgen và chạy demo

  • Với mỗi kiểu WIT khác nhau, đều cần mã kiểu SDK ở phía guest và mã triển khai ở phía host
  • wit-bindgen là công cụ CLI sinh mã host/guest từ các tệp .wit
  • Trong POC này, chỉ cần triển khai giao diện phía host bên trong unikernel
  • Mã do wit-bindgen sinh ra sẽ dùng runtime WebAssembly để thực hiện các công việc mức thấp
    • Mã sinh ra phụ thuộc vào ngôn ngữ lập trình và runtime WebAssembly phía host
  • wasmi chưa được wit-bindgen hỗ trợ, tác giả đã mở rộng wit-bindgen để xử lý wasmi
  • Sau đó, tác giả scaffold mã phía host cho chức năng Key/Value và bổ sung một triển khai trait host đơn giản
    • Mã host khi đó mới chỉ ở mức in ra thông tin debug
  • Ở trạng thái này, có thể chạy keyvalue-demo của dự án Spiderlightning mà không cần sửa đổi

Phần tiếp theo

  • Có một bản ghi lại cảnh ứng dụng unikernel chạy demo Spiderlightning http-server
  • Trong phần tiếp theo, tác giả sẽ đề cập đến Rust async, Redis và một vài lỗi kỳ lạ

1 bình luận

 
GN⁺ 2023-10-24
Ý kiến trên Hacker News
  • Chẳng phải bạn sẽ nghĩ ngay đến https://www.destroyallsoftware.com/talks/the-birth-and-death... sao?

  • Nếu một người không phải hacker hệ điều hành muốn có unikernel thì cách tiếp cận trọn vẹn nhất là gì?
    Những lựa chọn tôi nghĩ đến là biến ứng dụng thành một module nhân Linux rồi đưa lên kernel thông thường và bỏ qua user space; mạnh tay cắt gọt Linux rồi gắn mã của mình vào; bắt đầu từ một dự án unikernel trên GitHub; hoặc gọt bớt một hệ điều hành khác như FreeBSD
    Tôi thích hình dung một máy x64 trong VM nối với card mạng hoạt động như một tài nguyên tính toán đa dụng, và gửi dữ liệu qua mạng để giao việc. Vì rắc rối hơn daemon trong user space nên hiện chưa có nhiều giá trị, nhưng nếu một ngày nào đó có thời gian, tôi muốn biết nên bắt đầu hack ở cấp hệ điều hành từ đâu

    • RedHat đã tìm hiểu Linux-as-unikernel từ năm 2018: https://research.redhat.com/blog/article/unikernel-linux-ukl...
      Unikernel Linux (UKL) bắt đầu như một nỗ lực tận dụng khả năng cấu hình của Linux, với mục tiêu là một kernel bao phủ từ hệ điều hành đa dụng cho đến các unikernel chuyên biệt theo ứng dụng/phần cứng. Các lĩnh vực liên quan như io_uring và eBPF cũng được nhắc đến; io_uring phân tán chi phí system call, còn eBPF là một cách khác để chạy mã trong kernel space, dù có giới hạn
      Mã nguồn: https://github.com/unikernelLinux/ukl
      UKL là một bản vá nhỏ cho Linux và glibc, cho phép build nhiều chương trình thành unikernel mà không cần chỉnh sửa. Chương trình được link với kernel Linux thành vmlinuz cuối cùng, chạy trong kernel space, có thể boot trên bare metal hoặc VM, và có thể dùng gần như mọi tính năng cùng driver của Linux
    • Nếu giả định theo họ Linux, trước hết hãy tạo một ứng dụng biên dịch tĩnh, đặt nó làm tệp duy nhất trong initramfs, đơn giản đặt tên là /init, rồi gói với kernel để boot
      Khi đó app sẽ là PID 1 và thực chất là tiến trình duy nhất; trừ vài kernel thread ra thì bạn có thể làm theo ý muốn
    • Unikraft cũng đáng xem: https://unikraft.org
      Hỗ trợ nhiều ngôn ngữ và app, x86/ARM64, QEMU/Firecracker, và cũng có thể chạy ELF build trên Linux dưới dạng unikernel: https://unikraft.org/guides/bincompat
      Discord ở https://unikraft.org/discord
    • MirageOS, một framework cho OCaml: https://mirage.io/
      Nếu bạn muốn học OCaml và cũng muốn có unikernel thì đây là một con đường khả thi
    • Về bản chất có ba cách tạo unikernel: tối giản một hệ điều hành đa dụng hiện có, đi vòng qua hệ điều hành, hoặc xây từ đầu
      Có thể xem chi tiết trong tài liệu Unikraft: https://unikraft.org/docs/concepts/design-principles#approac...
  • Dự án hay. Tôi thích WASM vì ngay từ đầu nó đã được thiết kế với sandbox và tính di động trong đầu
    Ước gì WASM đã xuất hiện thay cho JavaScript vào thập niên 90, và tôi nghĩ WASM sẽ nuốt chửng thế giới. Điều tôi mong muốn nhất là tính bền vững. Hiện có nhiều chương trình không còn chạy được nữa, các game cũ là ví dụ tiêu biểu. Một đặc tả đơn giản có khả năng sống lâu hơn, nên việc thêm tính năng mới khiến tôi hơi lo, nhưng tương lai của binary trông rất thú vị

    • Nghe khó tin, nhưng trong thập niên 90, trình duyệt web thường được xem là trình duyệt tài liệu siêu văn bản, chứ không phải thứ thay thế hệ điều hành
      Có lý do khiến JS ban đầu bị giới hạn ở scripting cơ bản như click handler hay kiểm tra form. Việc nó phình thành thứ khác không chỉ do khiếm khuyết thiết kế của JS, mà còn do các cách dùng bị nhồi nhét vào. Dùng trình duyệt làm cơ chế phân phối cho kiểu ứng dụng này khác xa những gì Tim Berners-Lee hay Marc Andreesen từng hình dung
      Vào thời đó, phe “mạng chính là máy tính” đã đưa ra các X client mỏng cho những ứng dụng phong phú hơn: https://en.wikipedia.org/wiki/Network_Computer
      Tôi có cảm xúc lẫn lộn về WASM. Hiện có một màn sương cường điệu và mới lạ phủ rất dày lên nó. Nếu chỉ coi trình duyệt web là viewport cho ảo tưởng ngôn ngữ mà UI designer và developer nhất thời ưa thích, thì sẽ có nhiều chuyện tệ xảy ra ở các mảng như accessibility và screen reader
      Xu hướng xử lý WASM bên ngoài trình duyệt như một VM phổ quát cũng là con đường đã đi qua 30 năm trước. Đó là điều JVM từng cố làm, nhưng có vẻ giờ nó không còn “ngầu” nữa
    • Một cách ngây thơ, tôi mong web sẽ tách thành các ứng dụng WASM có sandbox và nội dung tài liệu thậm chí không cần JS
      Tôi không rõ vùng trung gian nên trông như thế nào, hay vì sao ta nên muốn nó. Nhưng thực tế thì WASM có lẽ sẽ nuốt luôn cả nội dung tài liệu, và khi đó ad blocker cùng chế độ đọc rất có thể sẽ tiêu đời
    • Tôi nghĩ để chuyện này hoạt động thì cần JavaScript hoặc thứ gì đó tương tự. Nếu không, hệ sinh thái có lẽ đã bị lây nhiễm bởi một thứ kiểu Java
  • Thật sự rất thích. Trong các công nghệ được liên kết có khá nhiều cái tôi chưa từng thấy, nên đã bookmark hết
    Tiếp theo tôi muốn thử thiết lập kết nối WireGuard cho hypervisor. Việc thiết lập kết nối cũng có thể đi qua thứ gì đó như Tailscale
    Khi đó WebAssembly trên máy này sẽ nói chuyện trực tiếp với WebAssembly trên máy kia. Đây không phải kiểu một process mở kết nối TCP tới một vị trí tùy ý, mà là cấu trúc giao tiếp dựa trên cấu hình và quyền hạn đã được truyền vào

  • Hơi muộn, nhưng đã có ai nghĩ đến việc chạy Zephyr như một unikernel chưa? https://docs.zephyrproject.org/latest/boards/x86/acrn/doc/in...

  • Sẽ mất bao lâu trước khi có phần cứng WASM chuyên dụng?

    • Nói nghiêm ngặt thì có lẽ sẽ không bao giờ có. Theo nghĩa rất hẹp, vì WASM chưa được đặc tả đủ đến mức đó
      Tuy vậy, có lẽ ai đó có thể tạo ra kiểu thiết bị “bề ngoài là wasm nhưng bên dưới thực ra là RISC-V”
    • Chắc chắn sẽ có ai đó làm. Từng có Lisp machine, và cũng từng có CPU chuyên dụng cho JVM
      Nhưng tôi nghĩ loại phần cứng đó sẽ luôn chỉ nằm trong thị trường ngách. Vì chạy WASM trên phần cứng phổ thông có sẵn nhìn chung sẽ nhanh hơn. Bản thân WASM được thiết kế để chạy nhanh trên phần cứng có sẵn, và lợi thế kinh tế theo quy mô của các bộ xử lý đa dụng thì tốt hơn nhiều
      International Conference on Functional Programming ban đầu cũng là một hội nghị tên Functional Programming and Computer Architecture, nhưng về sau người ta đã tìm ra cách biên dịch hiệu quả các ngôn ngữ hàm đánh giá lười như Haskell trên phần cứng hiện có
      Lisp machine và Java machine cũng tương tự. Một trong những lý do ta không còn thấy những thứ đó nhiều nữa là vì công nghệ compiler đã bắt kịp
  • Use case của unikernel và WASM là gì?

    • Về WASM thì tôi sẽ không nói. Nếu nói thì có vẻ sẽ bật chế độ “bọn trẻ thời nay ấy mà”
      Tôi nghĩ giá trị của unikernel là 1) hiệu năng: bỏ đi những thứ không cần và kéo những thứ cần thiết vào “ring 0” để vắt thêm dù chỉ vài cycle, 2) đơn giản hóa: có khả năng giảm độ phức tạp bằng cách loại bỏ các phần không cần thiết, 3) bảo mật: cũng là khả năng thay đổi bề mặt tấn công bằng cách giảm những thứ không cần thiết
      Tuy nhiên tôi không nghĩ đây là cách phù hợp với việc viết microservice hay web app mà nhiều người trên diễn đàn này làm. Use case của nó gần với việc tạo các thành phần hạ tầng như database, load balancer hơn
    • Micro VM có thể cạnh tranh với Linux container trong một số tác vụ, và có lợi thế là không phơi bày Linux kernel cho mã ít được tin cậy hơn
      Vì vậy một số nhà cung cấp edge cloud khi chạy Docker image sẽ chuyển chúng thành micro VM
      Tuy nhiên ở edge, WASM bên trong micro VM có thể khó cạnh tranh với WASM sandbox của edge. Từ góc nhìn nhà cung cấp, phương án sau có khả năng dễ thêm các tính năng biên và tích hợp hữu ích hơn
    • Có lẽ mục đích là mở rộng những nơi WASM có thể đi tới. Sau trình duyệt và Docker container, giờ còn bao gồm cả hệ điều hành nhẹ có thể đưa lên thiết bị nhúng
  • Như đã “báo trước” từ lâu trong Birth & Death of Javascript, viễn cảnh là một ngày nào đó sẽ xuất hiện unikernel chạy một runtime garbage collection an toàn trong không gian kernel, và khi đó có thể loại bỏ hỗ trợ ánh xạ bộ nhớ ảo khỏi CPU để làm nó nhanh hơn
    Năm 2014, tác giả đã dự đoán JS và asm.js, nhưng giờ đây WASM có vẻ là con đường đó. Rất đáng mong đợi, haha
    https://www.destroyallsoftware.com/talks/the-birth-and-death...

    • Lập luận trong video là trình duyệt dù sao cũng là một tiến trình đơn, và nếu mọi thứ đều chạy trong tiến trình đó thì không cần sự tách biệt như vậy
      Nhưng sau đó chúng ta đã biết trình duyệt đơn tiến trình là ác mộng bảo mật, và trình duyệt ngày nay không còn là đơn tiến trình nữa để có sandboxing đúng nghĩa
      Dù vậy, việc video đó đã gần đúng đến mức nào vẫn rất hay, và cũng thú vị khi xem nó sai theo cách nào
    • Sự tiến hóa của JavaScript/WASM đi từ thiết kế cho ứng dụng chạy trong trình duyệt → viết ứng dụng desktop/server → viết hệ điều hành hay kernel
      Khó chỉ ra chính xác, nhưng nghe quen quen ở đâu đó. Gợi ý là thứ đó cũng bắt đầu bằng “J”
    • JavaStation là một máy tính mạng do Sun Microsystems phát triển trong giai đoạn 1996–2000, được định hướng chỉ chạy các ứng dụng Java
      https://en.wikipedia.org/wiki/JavaStation
    • Bộ nhớ ảo và paging không chỉ để bảo vệ, bảo mật và cô lập tiến trình. Chúng còn cung cấp một tập các trừu tượng cho việc sử dụng hiệu quả bộ nhớ vật lý và quản lý bộ nhớ
      Mức sử dụng ảo của một tiến trình có thể vượt quá RSS ngay cả khi không phải do swapping, và hệ điều hành cùng allocator phối hợp với nhau để xử lý khá thông minh trong các trường hợp thông thường
      Vì vậy khó có thể xem việc loại bỏ nó là tự động đem lại lợi ích hiệu năng. Đặc biệt càng đúng nếu phải đi qua một lớp WASM VM khá chậm
      Với một số ứng dụng, chẳng hạn cơ sở dữ liệu, chạy dưới dạng unikernel hoặc bám sát kernel hơn để truy cập trực tiếp MMU có thể đem lại lợi ích lớn: https://github.com/tuhhosg/exmap & https://github.com/viktorleis/vmcache & https://www.cs.cit.tum.de/fileadmin/w00cfj/dis/_my_direct_up...
      Nhưng với các ứng dụng thông thường giả định chuẩn POSIX, hoặc giả định môi trường chạy trông giống một máy tính phổ thông hiện đại, thì điều này đáng nghi ngờ. Rốt cuộc có lẽ sẽ phải viết lại trong mã người dùng rất nhiều việc mà lớp VMM từng đảm nhiệm
    • Loại bỏ hỗ trợ ánh xạ bộ nhớ ảo càng nghĩ càng thấy kém hợp lý
      Engine JS phụ thuộc vào VMM, và WASM cũng vậy theo nhiều cách. Hầu như mọi chương trình không tầm thường ngoài embedded đều ngầm giả định VMM ở mức tinh vi. Đặc biệt, một số công nghệ VM quanh micro VM cũng dùng VMM, và unikernel chỉ thực sự có ý nghĩa khi dùng như một VM