1차 공개 · 8월 22일에 1차 강의가 모두 공개됩니다.
TECH 으로 돌아가기
TECH HACKER NEWS 오늘 10분 읽기 20 READS

파이어크래커를 넘어: Encore가 애플 실리콘에서 마이크로VM 스택을 다시 만든 이유

파이어크래커를 넘어: Encore가 애플 실리콘에서 마이크로VM 스택을 다시 만든 이유
SOURCE IMAGE · HACKER NEWS

백엔드 애플리케이션을 빌드하고 배포하는 Encore는 2022년 중반부터 모든 빌드를 파이어크래커(Firecracker) 마이크로VM 안에서 실행해 왔다. 파이어크래커는 리눅스 커널이 실제로 필요로 하는 최소한의 하드웨어만 남기고 나머지 에뮬레이션을 걷어낸다. 그 결과 각 빌드는 가상머신 수준의 격리를 얻으면서도 기동 비용은 컨테이너에 가깝다. 문제는 파이어크래커가 KVM을 직접 다룬다는 점이다. 즉 /dev/kvm이 있는 리눅스 호스트가 반드시 필요한데, 정작 Encore 엔지니어 대부분은 맥에서 개발한다. 파이어크래커 메인테이너들은 애플의 Virtualization.framework 기반 개념 증명(PoC)이 실제로 동작했음에도 이를 받아들이지 않았고, 당분간 macOS를 지원할 계획이 없다고 밝혔다. 결국 빌드 시스템을 손보려면 언제나 '다른 곳'에서 작업해야 했다.

4년을 버틴 원격 개발의 대가

Encore가 택한 우회로는 공용 빌드 머신이었다. 신규 엔지니어는 한 번 실행하는 스크립트로 온보딩됐다. 스크립트는 공용 머신에 root로 접속해 깃허브 공개 키를 끌어와 사용자를 만들고, kvm과 docker 그룹에 넣어 하이퍼바이저와 컨테이너에 접근하게 했다. VM 이미지는 각자의 홈 디렉터리로 복사되고 파이어크래커 바이너리는 하드링크로 걸렸다. 한 대의 장비 안에 데이터센터급 개인 환경이 만들어지고, Tailscale로 접근하며, 다른 사람들의 환경과 나란히 놓였다. 변경 사항을 반영하려면 CUE 설정에서 사용자명과 포트를 읽는 또 다른 스크립트가 필요했다. 한 호스트를 공유하니 서로 충돌하지 않기로 '약속'해야 했던 것이다.

바이너리는 쉬운 쪽이었다. GOOS=linux GOARCH=amd64로 크로스 컴파일해 rsync로 옮기고, 전송된 파일 수를 세어 재시작이 필요한지 판단했다. 어려운 쪽은 이미지였다. 파이어크래커는 블록 디바이스를 부팅하는데 도커는 레이어를 만들어내기 때문이다. 도커 레이어를 파이어크래커가 부팅할 블록 디바이스로 바꿔주는 기존 도구가 없어 직접 만들었고, 그 과정은 절반은 노트북에서 절반은 SSH 너머에서 돌아갔다. 레이어를 매니페스트 순서대로 다시 풀고, tar가 처리하지 않는 whiteout 마커를 find로 지우고, DNS가 없는 VM을 위해 resolv.conf를 하드코딩하고, jq로 환경변수를 꺼낸 뒤 mksquashfs로 부팅 가능한 이미지를 만들었다. 게다가 파이어크래커는 도커 컨테이너 안에서 돌았기에 --privileged와 /dev/kvm, /dev/net/tun을 통과시켜야 했고, 컨테이너 안에 존재하지 않는 호스트 브리지를 셸 스크립트로 직접 만들어 넣어야 했다.

스무스함을 파는 회사의 역설

이 방식은 2022년부터 교체 직전까지 실제로 동작했다. 하지만 비용은 정당화하기 어려웠다. 백엔드 개발이 매끄러워야 한다고, 코드를 짜고 로컬에서 돌리면 인프라가 코드를 따라와야 한다고 주장하는 회사에서, 정작 git push를 실행 중인 애플리케이션으로 바꿔주는 핵심 부분만은 개발자가 앉아 있는 바로 그 장비에서 돌릴 수 없었다. 빌드 시스템이 원격이라 로컬 중단점은 걸리지 않았고 로그는 SSH로 파일을 tail해야 했다. 프로파일러를 붙이려면 먼저 장비에 복사해야 했고, 게스트 쪽을 한 번 고칠 때마다 docker save와 rsync, 그리고 남들이 빌드를 돌리는 공용 호스트에서의 추출과 mksquashfs가 이어졌다. 루프가 길어 무언가를 시험 삼아 해보기 전에 한 번 더 망설이게 됐다.

crackling: 두 하이퍼바이저를 하나의 API로

Encore는 기존 대안을 먼저 검토했다. 맥에서 리눅스 VM을 돌리는 구현은 여럿 있다. 애플의 container는 올해 6월 1.0에 도달했고 Lima와 Tart는 수년째 그 일을 하고 있으며, podman도 libkrun을 통해 가능하다. M3 이상에 macOS 15 조합이라면 리눅스 VM 안에서 /dev/kvm을 얻어 파이어크래커를 그대로 돌릴 수도 있다. 그러나 어느 것도 두 호스트를 모두 아우르지 못했고, 중첩 방식은 일부 노트북에서만 되는 리눅스 VM 안에 갇히는 결과를 남긴다. 어떤 것을 채택하든 노트북에만 존재하고 다르게 동작하는 '두 번째 빌드 방식'이 생기는 셈이었다.

그래서 만든 것이 crackling이다. 리눅스에서는 파이어크래커를, macOS에서는 애플의 Virtualization.framework(VZ)를 구동하는 단일 마이크로VM API로, 양쪽에서 같은 OCI 이미지를 부팅한다. CLI가 gRPC로 데몬을 몰고, 데몬은 컴파일 시점에 두 하이퍼바이저 백엔드 중 하나를 고르는 파사드를 거쳐 VM을 부팅하며, 양쪽 모두 vsock으로 게스트 내부의 동일한 에이전트와 대화한다. 핵심 크레이트는 어느 하이퍼바이저에도 종속되지 않게 유지했다. 머신은 vcpus, mem, kernel, rootfs 등을 담은 MachineSpec으로 기술되고, 두 백엔드는 start·shutdown·pause·resume·snapshot 같은 공통 계약을 MachineBackend 트레잇으로 구현한다. 다만 기능은 다르다. 파이어크래커는 호스트 tap 디바이스와 MMDS 메타데이터 서비스를 갖고, 애플 쪽은 내장 NAT와 virtiofs 디렉터리 공유, x86 바이너리를 arm64 게스트에서 돌리는 로제타 변환을 제공한다. 각 백엔드는 지원하지 못하는 기능을 이름과 함께 API 경계에서 오류로 되돌려준다.

tokio와 애플 프레임워크의 스레드 전쟁

VZ 백엔드 구현에는 까다로운 스레딩 제약이 따랐다. VZVirtualMachine을 비롯한 VZ 객체들은 !Send + !Sync이며, 모든 VM 호출과 완료 핸들러는 그 VM을 만든 직렬 디스패치 큐에서 실행돼야 한다. 반면 데몬의 나머지는 워커 스레드 사이로 퓨처를 마음대로 옮기는 tokio 위에 있다. 두 제약을 동시에 만족하며 await 지점을 넘어 VM 객체를 붙들 방법은 없다. Encore는 프로세스 전역의 단일 직렬 DispatchQueue에서만 모든 VM을 생성·접근하고, VM 레지스트리는 그 큐에 디스패치된 클로저에만 노출해 객체가 tokio 스레드에 닿지 않게 했다. 비동기 쪽은 Send 데이터만 실어 나르는 평범한 핸들로 남겼다. MachineSpec을 Send 데이터만 담은 구조로 먼저 낮춘 뒤 큐 위에서 VZVirtualMachineConfiguration으로 바꾸는 2단계 로우어링도 이 제약에서 나왔다.

이미지 파이프라인도 리눅스 전용이라 통째로 다시 써야 했다. OCI 이미지를 부팅 가능한 루트 파일시스템으로 바꾸는 일은 보통 root 권한과 loop 마운트를 요구하지만 macOS에는 둘 다 없다. 그래서 레이어를 전부 사용자 공간에서, 프로세스 내부에서 적용하고 whiteout 항목을 존중했다. arm64 배포판이 싣는 vmlinuz는 대개 압축 페이로드를 감싼 EFI zboot 파일인데, 여기엔 펌웨어가 없으니 MZ·zimg 시그니처로 형식을 식별해 직접 풀었다. 압축 커널을 그대로 넘기면 VZ는 세부 정보 없는 일반 내부 오류로 실패했기에, 이제는 부팅 전에 오프셋 0x38의 ARMd 시그니처로 raw arm64 Image인지 확인한다. initramfs는 순수 러스트로 생성하고, 캐시된 rootfs는 APFS의 clonefile이나 리눅스의 reflink로 복제한다. 4년간 버틴 원격 우회로를 걷어내고 개발자가 앉은 바로 그 맥에서 같은 이미지를 네이티브로 부팅하겠다는 목표가, 결국 리눅스 이미지 툴체인의 상당 부분을 macOS 위에서 다시 만드는 작업으로 이어진 셈이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://encore.dev/blog/firecracker-apple-silicon
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...