리눅스 서버에서 macOS용 명령줄 바이너리를 그대로 실행한다는 발상은 오랫동안 무모한 도전으로 여겨졌다. 두 운영체제는 커널 인터페이스, 시스템 라이브러리, 실행 파일 형식이 근본적으로 다르기 때문이다. 최근 GitHub에 공개된 카케하시(Kakehashi)는 이 벽을 정면으로 다루는 실험적 프로젝트다. Linux aarch64 위에서 macOS ARM64용 Mach-O 실행 파일을 구동하는 유저스페이스 변환 계층으로, GUI가 아닌 CLI 도구를 우선 대상으로 삼는다. 지금까지 Docker/Colima 환경과 UTM 상의 Linux aarch64에서 동작이 확인됐다.
명령어 변환이 아니라 시스템콜 번역
카케하시의 핵심 설계는 흔히 떠올리는 명령어 에뮬레이터와 다르다. 이미 Apple 실리콘용으로 컴파일된 macOS 바이너리는 ARM64 명령어를 담고 있고, 리눅스 서버의 CPU 역시 같은 aarch64 아키텍처다. 따라서 명령어 자체를 통역할 필요가 없다. 카케하시는 Darwin Mach-O 바이너리를 리눅스 메모리에 적재한 뒤, 독립형(freestanding) libSystem을 매핑하고, 게스트가 호출하는 BSD 계열 시스템콜을 리눅스 커널이 이해하는 형태로 번역한다. JIT 없이 게스트 코드를 CPU에서 네이티브로 실행하므로, 성능 비용은 명령어 해석이 아니라 시스템콜 경계에서 발생한다. TLS 전환, 대체 스택 설정, NEON 레지스터 저장·복원, Rust 디스패치 같은 작업이 시스템콜마다 쌓이고, 게스트가 얼마나 자주 커널을 호출하느냐에 비례해 오버헤드가 늘어난다.
이 구조는 벤치마크 결과에 그대로 드러난다. UTM 상의 우분투 aarch64 베어메탈에서 7-Zip의 7zz로 약 8천 개 파일, 240MiB 규모의 트리를 압축하면 네이티브 대비 격차가 크게 벌어진다. 반면 파일 수가 적고 압축 연산이 지배적인 샘플에서는 격차가 약 1.1~1.2배 수준으로 좁혀진다. 프로젝트 측은 이 차이가 LZMA 압축 자체의 문제가 아니라 경로 탐색과 파일마다 반복되는 시스템콜 경계 비용에서 비롯된다고 설명한다. 파일 입출력이 잦은 워크로드일수록 불리하고, 연산 중심 워크로드에서는 상대적으로 실용적인 성능을 낸다는 뜻이다.
왜 느려도 쓸 만한가: CI 비용의 경제학
성능이 네이티브에 못 미치는데도 이 프로젝트가 겨냥하는 지점은 명확하다. 바로 CI 비용이다. macOS용 CLI 도구를 검증하려면 보통 값비싸고 물량이 제한된 macOS 러너를 써야 한다. 카케하시의 목표는 이런 워크로드를 저렴한 리눅스 aarch64 러너로 옮기는 것이다. 공개된 계산을 보면 GitHub Actions에서 macOS 표준 러너의 분당 요금은 리눅스 arm64의 약 10~12배에 달한다. 설령 같은 작업이 카케하시 위에서 5배 느리게 돈다 해도, macOS 러너의 분당 단가가 워낙 높아 실제 청구 비용은 더 낮을 수 있다. 원문이 든 예시는 리눅스에서 5분에 약 0.025달러, macOS에서 1분에 약 0.062달러다. 공개 저장소의 무료 분이나 자체 호스팅 리눅스를 쓰면 격차는 더 벌어지고, macOS 호스팅 용량은 대기열이 길어지는 경향까지 있다.
다만 이 논리가 통하는 범위는 순수 CLI Darwin 바이너리로 한정된다. GUI, 코드사이닝과 공증(notarization), Xcode UI 테스트처럼 독립형 libSystem만으로 감당되지 않는 작업에서는 여전히 macOS 러너가 답이다. 프로젝트도 이 경계를 분명히 그으며, CI에서 추구하는 것은 네이티브 macOS만큼 빠른 속도가 아니라고 못박는다. 검증 기준 역시 벽시계 시간이 아니라 cargo test, 스모크 테스트, 멀티스레드 7zz 실행 같은 정합성 게이트다. 성능 개선은 별도 로드맵으로 관리된다.
배포 구조와 라이선스
실무자 입장에서 눈여겨볼 부분은 배포 방식이다. 게스트용 libSystem.B.dylib은 런타임 크레이트 안에 include_bytes!로 컴파일되어 들어간다. kh-runtime를 배포하면 이 dylib이 함께 실리므로 최종 사용자가 별도로 내려받을 필요가 없다. 데이터 루트는 기본적으로 ~/.local/share/kakehashi/bottle/ 아래에 잡히고 환경 변수로 재정의할 수 있다. 리눅스 파일시스템은 게스트에게 /Volumes/linux/… 경로로 노출되어 호스트 루트와 다리를 놓으며, 상대 경로는 kh 프로세스의 현재 작업 디렉터리를 기준으로 해석된다. 하이퍼콜은 모든 게스트 스레드에서 기본 활성화되어 있고, 잔여 트랩 신호를 디버깅할 때만 끄도록 안내한다.
프로젝트는 이미 clang 프로브, curl의 전체 기능 집합, 실제 Apple Security.framework 연동을 확인했다고 밝히며, 다음 단계로 kh install xcode-tools를 통한 git 지원을 예고했다. 라이선스는 Apache 2.0이고, Darling에서 파생되지 않았으며 독점 Apple SDK나 바이너리 블롭을 벤더링하지 않는다는 점을 명시한다. 아직 실험 단계인 만큼 프로덕션 도입을 서두를 대상은 아니지만, macOS CLI 파이프라인을 리눅스 ARM 인프라로 옮겨 비용을 낮추려는 팀이라면 그 기술적 접근과 손익 구조를 미리 살펴볼 가치가 있다.