1P by GN⁺ | ★ favorite | 댓글 1개
  • SUSE Hackweek 22에서 WebAssembly 모듈을 실행하는 유니커널 POC를 만들었고, 구현 과정을 여러 파트로 나눠 정리함
  • 일반 애플리케이션을 유니커널에 직접 포팅하려면 의존성까지 맞춰야 하지만, WebAssembly 플랫폼은 런타임이 제공할 기능 경계가 더 뚜렷함
  • Spiderlightning 애플리케이션은 Key/Value 같은 기능만 요구하고, 호스트가 Redis나 Azure Cosmos DB로 구현해도 같은 .wasm 모듈은 차이를 알 필요가 없음
  • 기반은 Rust 유니커널 RustyHermit이고, Wasmtime과 Wasmer가 빌드되지 않아 순수 Rust 런타임인 wasmi를 선택함
  • Component Model과 WIT를 맞추기 위해 wit-bindgen에 wasmi 지원을 추가한 뒤, 호스트 측 Key/Value 기능을 스캐폴딩해 keyvalue-demo를 실행함

Hackweek 프로젝트와 목표

  • SUSE의 Hackweek 22 기간에 WebAssembly를 실행하는 유니커널 구축 프로젝트를 진행함
  • 전체 구현 과정을 한 글에 담기에는 길어 여러 글로 나눴고, 이번 글은 첫 번째 파트임
  • POC 코드는 별도 위치에 공개되어 있지만, 제공된 본문에는 실제 링크 URL이 없음

유니커널과 WebAssembly를 함께 쓰는 이유

  • 애플리케이션 개발자에게 유니커널 포팅은 부담이 큼
    • 애플리케이션과 모든 의존성이 대상 유니커널을 지원해야 함
    • 전체 애플리케이션 스택 안에서 패치가 필요할 수 있음
  • 유니커널 유지보수자도 임의의 애플리케이션을 매끄럽게 실행하도록 만드는 데 많은 에너지를 써야 함
    • 사용자 애플리케이션이 어떤 시스템 프리미티브를 사용할지 예측하기 어렵기 때문임
  • 반대로 Spin이나 Spiderlightning 같은 WebAssembly 플랫폼을 대상으로 하면, 런타임이 제공해야 할 기능 집합이 명확해짐
  • Spiderlightning 시나리오에서 애플리케이션은 런타임에 Key/Value 저장소 기능을 요구할 수 있음
    • 호스트가 해당 기능을 Redis로 구현하든 Azure Cosmos DB로 구현하든 애플리케이션에는 투명함
    • 같은 .wasm 모듈을 서로 다른 호스트 구현 위에서 실행할 수 있음

목표 아키텍처

  • 유니커널 애플리케이션이 WebAssembly 모듈을 실행하고 Spiderlightning API 집합을 지원하면, 같은 Spiderlightning 애플리케이션을 일반 slight 런타임과 해당 유니커널 양쪽에서 실행할 수 있음
  • 애플리케이션 개발자는 추가 작업을 하지 않아도 되고, Wasm 모듈도 자신이 어디에서 실행되는지 알 필요가 없음
  • 복잡성은 유니커널 개발자에게 집중되지만, 구현해야 할 범위는 “모든 애플리케이션 실행 지원”보다 훨씬 분명함

RustyHermit 기반 구현

  • 기반으로 RustyHermit을 선택함
    • Rust로 작성된 유니커널임
    • Rust nightly에 포함되어 있어 일반 Rust 애플리케이션을 작성하는 것과 비슷한 개발 경험을 제공함
  • RustyHermit 애플리케이션 빌드는 비교적 직관적임
    • 문서는 다소 흩어져 있지만 품질이 좋고, 예제가 많은 도움을 줌
  • 모든 Rust crate가 RustyHermit에서 그대로 동작한다고 기대할 수는 없으며, 이 제약이 POC 개발에 영향을 줌

WebAssembly 런타임 선택

  • 선호하던 Wasmtime은 RustyHermit 위에서 빌드되지 않음
    • 많은 의존성이 libc나 다른 저수준 라이브러리를 기대함
  • wasmer도 같은 문제가 있음
  • WebAssembly Micro Runtime도 고려했지만, Rust로 작성된 런타임을 써서 “완전한 RustyHermit 경험”을 유지하기로 함
  • 최종적으로 순수 Rust WebAssembly 런타임인 wasmi를 선택함
    • RustyHermit 위에서 잘 동작함
    • 설계가 Wasmtime에서 영감을 받아 기존 지식을 많이 재사용할 수 있었음

WebAssembly Component Model과 WIT

  • Spiderlightning은 WebAssembly Component Model 제안을 사용함
    • WebAssembly 게스트에 기능을 제공함
    • 호스트가 WebAssembly 게스트가 제공하는 기능을 소비할 수 있게 함
  • 호스트와 게스트의 통신은 Wasm Interface Type으로 정의된 타입을 사용함
  • 데모는 Component Model을 다음 흐름으로 사용함
    • 게스트가 호스트에 HTTP 서버 시작을 요청하고, 등록할 HTTP 라우트와 내부 핸들러 함수 이름을 전달함
      • http-server 타입을 사용하며, 게스트가 호스트 제공 기능을 사용함
    • 호스트는 게스트가 제공한 라우팅 정보로 들어오는 HTTP 요청을 처리함
      • HTTP 핸들러는 WebAssembly 게스트가 노출한 함수임
      • 서버는 게스트 제공 기능을 소비하며 http-handler 타입으로 통신함
    • 일부 HTTP 핸들러는 Key/Value 저장소와 상호작용함
      • 이 경우도 게스트가 호스트 제공 기능을 사용하며 keyvalue 타입으로 정의됨

wit-bindgen 확장과 데모 실행

  • 여러 WIT 타입마다 게스트 측 SDK 성격의 코드와 호스트 측 구현 코드가 필요함
  • wit-bindgen.wit 파일에서 호스트/게스트 코드를 생성하는 CLI 도구임
  • 이번 POC에서는 유니커널 안에 호스트 측 인터페이스만 구현하면 됨
  • wit-bindgen이 생성하는 코드는 WebAssembly 런타임을 사용해 저수준 작업을 수행함
    • 생성 코드는 프로그래밍 언어와 호스트 측 WebAssembly 런타임에 의존함
  • wasmiwit-bindgen에서 지원되지 않았기 때문에, wit-bindgen을 확장해 wasmi를 처리하도록 만듦
  • 이후 Key/Value 기능의 호스트 측 코드를 스캐폴딩하고 간단한 호스트 trait 구현을 추가함
    • 호스트 코드는 디버그 정보를 출력하는 수준이었음
  • 이 상태에서 Spiderlightning 프로젝트의 keyvalue-demo를 수정 없이 실행할 수 있었음

다음 파트 예고

  • 유니커널 애플리케이션이 Spiderlightning http-server 데모를 실행하는 녹화가 있음
  • 다음 파트에서는 Rust async, Redis, 일부 이상한 오류를 다룰 예정임

댓글과 토론

Hacker News 의견들
  • 바로 https://www.destroyallsoftware.com/talks/the-birth-and-death...가 떠오르지 않나?

  • 운영체제 해커가 아닌 사람이 유니커널을 원한다면 가장 온전한 접근은 뭘까?
    떠오르는 선택지는 애플리케이션을 Linux 커널 모듈로 만들어 일반 커널에 올리고 사용자 공간은 무시하기, Linux를 과감히 줄여서 자기 코드를 붙이기, GitHub의 유니커널 프로젝트에서 시작하기, FreeBSD 같은 다른 운영체제를 깎아내기 정도임
    네트워크 카드에 연결된 VM에서 x64 머신이 범용 계산 자원처럼 동작하고, 네트워크로 데이터를 보내 작업을 할당하는 그림이 마음에 듦. 사용자 공간 데몬보다 번거로워 아직 가치가 크진 않았지만, 언젠가 시간이 나면 운영체제 수준 해킹을 어디서 시작하면 좋을지 궁금함

    • RedHat은 2018년부터 Linux-as-unikernel을 살펴봐 왔음: https://research.redhat.com/blog/article/unikernel-linux-ukl...
      Unikernel Linux(UKL)는 Linux의 구성 가능성을 활용하려는 시도에서 시작했고, 범용 운영체제부터 애플리케이션·하드웨어 특화 유니커널까지 아우르는 커널을 목표로 함. 관련 영역으로 io_uring과 eBPF도 언급되는데, io_uring은 시스템 호출 비용을 분산시키고 eBPF는 제한적이나마 커널 공간에서 코드를 실행하는 또 다른 방식임
      코드: https://github.com/unikernelLinux/ukl
      UKL은 Linux와 glibc에 대한 작은 패치로, 많은 프로그램을 수정 없이 유니커널로 빌드하게 해줌. 프로그램이 Linux 커널과 최종 vmlinuz로 링크되어 커널 공간에서 실행되고, 베어메탈이나 VM에서 부팅 가능하며 Linux의 거의 모든 기능과 드라이버를 쓸 수 있음
    • Linux 계열을 가정하면, 먼저 정적 컴파일 애플리케이션을 만들고 initramfs에 유일한 파일로 넣은 뒤 간단히 /init으로 이름 붙이고 커널에 묶어 부팅하면 됨
      그러면 앱이 PID 1이자 사실상 유일한 프로세스가 되고, 커널 스레드 몇 개를 제외하면 원하는 대로 할 수 있음
    • Unikraft도 확인해볼 만함: https://unikraft.org
      여러 언어와 앱, x86/ARM64, QEMU/Firecracker를 지원하고, Linux에서 빌드한 ELF를 유니커널로 실행할 수도 있음: https://unikraft.org/guides/bincompat
      Discord는 https://unikraft.org/discord에 있음
    • OCaml용 프레임워크로 MirageOS가 있음: https://mirage.io/
      OCaml을 배우고 싶고 유니커널도 원한다면 가능한 경로임
    • 유니커널을 만드는 방식은 본질적으로 세 가지임: 기존 범용 운영체제를 최소화하기, 운영체제를 우회하기, 처음부터 만들기
      자세한 내용은 Unikraft 문서에서 볼 수 있음: https://unikraft.org/docs/concepts/design-principles#approac...
  • 좋은 프로젝트임. WASM은 처음부터 샌드박스와 이식성을 염두에 두고 설계돼서 마음에 듦
    90년대에 JavaScript 대신 WASM이 나왔으면 좋았겠고, WASM이 세상을 먹을 것 같음. 가장 바라는 건 지속성임. 지금은 더 이상 실행할 수 없는 프로그램이 많고, 오래된 게임이 대표적임. 단순한 명세가 오래 살아남을 가능성이 높아서 새 기능 추가는 조금 불안하지만, 바이너리의 미래는 흥미로워 보임

    • 믿기 어렵겠지만 90년대에는 웹 브라우저가 대체로 하이퍼텍스트 문서 탐색기라고 생각했지, 운영체제 대체재라고 보진 않았음
      JS가 처음에 클릭 핸들러나 폼 검증 같은 기본 스크립팅으로 제한됐던 데는 이유가 있음. 다른 무언가로 커진 건 JS의 설계 결함만이 아니라 억지로 끼워 넣어진 사용처 때문임. 브라우저를 이런 앱의 전달 메커니즘으로 쓰는 건 Tim Berners-Lee나 Marc Andreesen이 상상한 것과는 거리가 있음
      당시 “네트워크가 컴퓨터다” 진영은 더 풍부한 앱을 위해 얇은 X 클라이언트를 내놓았음: https://en.wikipedia.org/wiki/Network_Computer
      WASM에는 감정이 복잡함. 지금은 과대광고와 신기함의 장막이 크게 드리워져 있음. 웹 브라우저를 UI 디자이너와 개발자가 그때그때 선호하는 언어 환상의 뷰포트로만 취급하면 접근성, 스크린 리더 같은 영역에서 나쁜 일이 많아짐
      브라우저 밖에서 WASM을 보편 VM으로 다루는 흐름도 30년 전에 이미 가본 길임. 그게 JVM이 하려던 일이었는데, 지금은 “쿨”하지 않은 듯함
    • 순진하게는 웹이 샌드박스 WASM 앱과 JS조차 필요 없는 문서 콘텐츠로 갈라지길 바람
      중간 지대가 어떤 모습이어야 하는지, 왜 원해야 하는지는 잘 모르겠음. 다만 현실적으로는 WASM이 문서 콘텐츠까지 먹어 치울 테고, 그러면 광고 차단기와 읽기 모드는 끝장날 가능성이 큼
    • 이게 작동하려면 JavaScript나 비슷한 무언가가 필요했다고 봄. 아니었다면 생태계가 Java 같은 것에 감염됐을 가능성이 큼
  • 정말 마음에 듦. 링크된 기술 중 못 봤던 것도 여럿 있어서 전부 북마크함
    다음으로는 하이퍼바이저에 WireGuard 연결을 설정해보고 싶음. 연결 수립은 Tailscale 같은 걸 거칠 수도 있음
    그러면 이 머신의 WebAssembly가 저 머신의 WebAssembly와 직접 이야기하게 됨. 프로세스가 임의의 위치로 TCP 연결을 여는 방식이 아니라, 전달된 구성과 권한을 기반으로 통신하는 구조임

  • 늦었지만, Zephyr를 유니커널로 실행하는 걸 생각해본 사람이 있을까? https://docs.zephyrproject.org/latest/boards/x86/acrn/doc/in...

  • 전용 WASM 하드웨어가 나오기까지 얼마나 걸릴까?

    • 엄밀히 말하면 절대 안 나올 듯함. 아주 좁은 의미에서는 WASM이 그 정도로 충분히 명세화되어 있지 않기 때문임
      다만 “겉은 wasm인데 실제로는 아래에 RISC-V가 있는” 식의 물건은 누군가 만들 수 있을 듯함
    • 누군가는 분명 만들 것 같음. Lisp 머신도 있었고 JVM 전용 CPU도 있었으니까
      하지만 그런 하드웨어는 늘 틈새에 머물 거라고 봄. 일반 기성 하드웨어에서 WASM을 돌리는 편이 대체로 더 빠를 것이기 때문임. WASM 자체가 기성 하드웨어에서 빠르게 돌도록 설계됐고, 범용 프로세서의 규모의 경제가 훨씬 좋음
      International Conference on Functional Programming도 처음에는 Functional Programming and Computer Architecture라는 학회였지만, 이후 Haskell 같은 지연 평가 함수형 언어를 기존 하드웨어에서 효율적으로 컴파일하는 방법을 찾아냈음
      Lisp와 Java 머신도 비슷함. 더 이상 그런 물건이 잘 보이지 않는 이유 중 하나는 컴파일러 기술이 따라잡았기 때문임
  • 유니커널과 WASM의 사용 사례가 뭘까?

    • WASM에 대해서는 말하지 않겠음. 그러면 “요즘 애들은 말이야” 모드가 될 것 같음
      유니커널의 가치는 1) 성능: 필요 없는 것을 버리고 필요한 것을 “ring 0”으로 당겨 몇 사이클이라도 더 짜내기, 2) 단순화: 필요 없는 부분을 버려 복잡도를 줄일 가능성, 3) 보안: 역시 불필요한 것을 줄여 공격 표면을 바꿀 가능성이라고 봄
      다만 이 포럼의 많은 사람이 하는 마이크로서비스나 웹앱 작성에 맞는 방식은 아니라고 생각함. 사용처는 데이터베이스, 로드 밸런서 같은 인프라 구성요소를 만드는 쪽에 더 가까움
    • 마이크로 VM은 일부 작업에서 Linux 컨테이너와 경쟁할 수 있고, 덜 신뢰하는 코드에 Linux 커널을 노출하지 않는 장점이 있음
      그래서 일부 엣지 클라우드 제공자는 Docker 이미지를 실행할 때 마이크로 VM으로 변환함
      다만 엣지에서 마이크로 VM 안의 WASM은 엣지의 샌드박스 WASM과 경쟁하기 어려울 수 있음. 제공자 입장에서는 후자가 유용한 경계 기능과 통합을 추가하기 더 쉬울 가능성이 큼
    • WASM이 갈 수 있는 곳을 넓히려는 용도일 듯함. 브라우저, Docker 컨테이너에 이어 이제는 임베디드 장치에 올릴 수 있는 가벼운 운영체제까지 포함됨
  • 오래전 Birth & Death of Javascript에서 “예고”했듯이, 언젠가는 커널 공간에서 안전한 가비지 컬렉션 런타임을 돌리는 유니커널이 나올 것이고, 그러면 CPU에서 가상 메모리 매핑 지원을 제거해 더 빠르게 만들 수 있다는 그림이었음
    2014년에는 저자가 JS와 asm.js를 예상했지만, 지금은 WASM이 그 길처럼 보임. 기대됨, 하하
    https://www.destroyallsoftware.com/talks/the-birth-and-death...

    • 영상의 논리는 브라우저가 어차피 단일 프로세스이고 모든 것이 그 프로세스에서 돈다면 그런 분리가 필요 없다는 것이었음
      하지만 이후 단일 프로세스 브라우저가 보안 악몽이라는 걸 배웠고, 요즘 브라우저는 제대로 된 샌드박싱을 위해 더 이상 단일 프로세스가 아님
      그래도 그 영상이 얼마나 정답에 가까웠는지는 좋고, 어떤 방식으로 틀렸는지 보는 것도 흥미로움
    • JavaScript/WASM의 진화가 브라우저에서 실행되는 앱용 설계 → 데스크톱·서버 앱 작성 → 운영체제나 커널 작성으로 흘러감
      정확히 짚긴 어렵지만 어딘가 익숙함. 힌트는 그것도 “J”로 시작한다는 것임
    • JavaStation은 Sun Microsystems가 1996년부터 2000년 사이 개발한 네트워크 컴퓨터로, Java 애플리케이션만 실행하도록 의도됐음
      https://en.wikipedia.org/wiki/JavaStation
    • 가상 메모리와 페이징은 보호, 보안, 프로세스 격리만을 위한 게 아님. 물리 메모리의 효율적 사용과 메모리 관리를 위한 추상화 집합도 제공함
      프로세스의 가상 사용량은 스와핑 때문이 아니더라도 RSS를 초과할 수 있고, 운영체제와 할당자가 함께 동작하면서 일반적인 경우에 꽤 영리하게 처리함
      그래서 이것을 없앤다고 자동으로 성능 이득이 생긴다고 보긴 어려움. 특히 꽤 느린 WASM VM 계층을 거친다면 더 그렇다
      일부 애플리케이션, 예를 들어 데이터베이스에서는 유니커널로 돌리거나 커널에 더 가깝게 붙어 MMU에 직접 접근하는 게 큰 이득일 수 있음: https://github.com/tuhhosg/exmap & https://github.com/viktorleis/vmcache & https://www.cs.cit.tum.de/fileadmin/w00cfj/dis/_my_direct_up...
      하지만 POSIX 표준을 가정하거나 실행 환경이 현대적인 일반 컴퓨터처럼 보인다고 전제하는 일반 애플리케이션에는 의문임. 결국 VMM 계층이 하던 많은 일을 사용자 코드로 다시 쓰게 될 것 같음
    • 가상 메모리 매핑 지원 제거는 생각할수록 말이 덜 됨
      JS 엔진은 VMM에 의존하고 WASM도 여러 방식으로 그렇다. 임베디드가 아닌 거의 모든 비사소한 프로그램은 미묘하게 VMM을 전제로 삼고 있음. 특히 마이크로 VM 주변의 일부 VM 기술도 VMM을 쓰며, 유니커널은 VM으로 쓸 때에야 진짜 의미가 있음