트렌딩 딥다이브 · 2026-07-18 · OSSInsight Daily #42 / ★10.4k

TencentCloud/CubeSandbox 딥다이브
— AI 에이전트에게 "진짜 컴퓨터"를 60ms 만에 쥐어주는 마이크로VM

CubeSandboxAI 에이전트가 생성한 코드를 안전하게 실행하기 위한 오픈소스 샌드박스 플랫폼이다. 텐센트 클라우드가 2026년 4월 공개했고, 3개월 만에 별 1만 개를 넘겼다. 핵심 주장은 두 줄로 요약된다 — "하드웨어 격리된 가상머신을 60ms 안에 부팅하고, VM 하나당 메모리 오버헤드는 5MB 미만". 그리고 기존 E2B SDK 코드를 한 글자도 안 고치고 환경변수 URL만 바꿔 끼우면 그대로 돌아간다.

기술적으로 이 저장소가 재미있는 이유는 "남의 것을 잘 훔쳐서 잘 조립했다"는 데 있다. VMM은 Cloud Hypervisor 포크, 게스트 에이전트는 Kata Containers 포크, 코드 실행 데몬은 E2B의 envd를 그대로 컴파일해 쓴다. 대신 CubeCoW(XFS reflink 스냅샷), CubeVS(eBPF 가상 스위치), PVM(중첩 가상화 없이 KVM 흉내내기) 세 가지는 직접 만들었다. 이 "빌린 것 / 만든 것"의 경계선이 곧 이 프로젝트의 설계 사상이다.

(저장소: TencentCloud/CubeSandbox · Rust 21.7만 줄 + Go 22.5만 줄 · Apache-2.0 · x86_64/ARM64 Linux + KVM · v0.5.1)
목차
  1. 프로젝트 한 줄 요약
  2. 왜 주목받는가 — 컨테이너로는 안 되는 이유
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 — 8개 컴포넌트와 4가지 설계 패턴
  5. 디렉토리 구조 해부
  6. 학습 포인트 — 여기서 훔쳐올 설계
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한 줄 요약

이 저장소가 정확히 무엇인가
한 줄 정의

AI 에이전트에게 "일회용 리눅스 컴퓨터"를 60ms 만에 빌려주고, 다 쓰면 통째로 버리는 인프라.

AI가 짠 코드를 내 서버에서 그냥 돌리면 위험하다. 그렇다고 매번 진짜 VM을 띄우면 수 초가 걸린다. CubeSandbox는 진짜 가상머신(하드웨어 격리)이면서 컨테이너보다 빠른 중간 지점을 노린다.

비유로 이해하기

컨테이너는 같은 건물 안의 칸막이 사무실이다. 벽은 있지만 천장(커널)은 공유한다. 옆 칸 사람이 천장을 뚫으면 끝이다.

전통 VM별도의 건물이다. 안전하지만 새 건물을 짓는 데 몇 초가 걸리고 관리비(메모리)도 많이 든다.

CubeSandbox의 마이크로VM공장에서 미리 다 지어놓고 크레인으로 내려놓는 조립식 건물이다. 진짜 독립 건물(전용 커널·하드웨어 격리)인데, 미리 만든 스냅샷을 복사해 쓰기 때문에 설치가 60ms다.

용어
샌드박스 (Sandbox)
"모래놀이터". 안에서 뭘 부수든 바깥에 영향이 없는 격리 실행 환경을 뜻한다. AI 에이전트 맥락에서는 LLM이 생성한 코드/명령을 실제로 실행해보는 격리된 컴퓨터를 가리킨다.
용어
마이크로VM (microVM)
진짜 가상머신인데, 일반 VM이 흉내내는 장치(그래픽카드·사운드카드·USB…)를 다 빼고 꼭 필요한 가상 장치만 남긴 초경량 VM. 부팅이 빠르고 메모리를 적게 먹는다. AWS Firecracker가 이 개념을 대중화했고, CubeSandbox는 같은 계열인 Cloud Hypervisor를 쓴다.

2왜 주목받는가 — 컨테이너로는 안 되는 이유

트렌딩의 배경, 그리고 경쟁 제품 대비 위치

배경: "AI가 짠 코드를 어디서 돌릴 것인가" 문제

2025~2026년 코딩 에이전트가 폭발하면서 모두가 같은 벽에 부딪혔다. 에이전트에게 셸을 주려면 그 셸이 격리돼 있어야 한다. 그런데 에이전트는 사람과 달리 초당 수십 개씩 세션을 만들고 버린다. 이 두 요구는 정면으로 충돌한다.

격리 강도 ↑ │ 높음 │ 전통 VM ★ CubeSandbox │ (부팅 수 초) (부팅 <60ms) │ 중간 │ gVisor │ (syscall 가로채기) │ 낮음 │ Docker 컨테이너 │ (부팅 ~200ms, 커널 공유) └────────────────────────────────────► 시작 속도 ↑

README의 비교표를 그대로 옮기면 이렇다. (모두 저장소가 주장하는 자체 측정치이며 제3자 검증은 아니다.)

방식부팅 속도메모리 오버헤드격리 수준
Docker 컨테이너약 200ms낮음낮음 (커널 공유 + 네임스페이스)
전통 VM초 단위높음 (풀 OS)높음
CubeSandbox<60ms<5MB매우 높음 (전용 커널 + eBPF)

실측 벤치마크 — 동시성이 올라가면 어떻게 되는가

저장소의 벤치마크 문서(docs/blog/posts/2026-06-01-cubesandbox-perf-benchmark.md)는 텐센트 클라우드 BMI5 베어메탈(96코어 / 375GiB)에서 잰 수치를 공개한다. 마케팅 숫자만 보지 말고 동시성이 올라갈 때의 꼬리 지연(P95/P99)을 보는 게 핵심이다.

동시 생성 수평균 생성 시간P95처리량
147.8ms (최소 43.5ms)57.4ms
1088.7ms
2098.1ms180.9개/초 (최고점)
50276.1ms508ms하락
읽을 때 주의
"60ms"는 동시성 1일 때의 숫자다

동시성 20에서 처리량이 정점(180.9개/초)을 찍고, 50에서는 평균이 276ms로 5배 이상 늘어난다. 즉 이 시스템의 스위트스팟은 "노드당 동시 생성 20개 내외"이고, 그 이상은 노드를 늘려야 한다. 벤치마크 표를 볼 때는 항상 어떤 부하에서 잰 숫자인가를 먼저 확인하는 습관이 중요하다.

메모리 오버헤드 <5MB의 진실

같은 문서를 끝까지 읽으면 더 정직한 숫자가 나온다. 2GiB 스펙 샌드박스를 100개 띄우면 VM당 실제 소비 메모리는 약 21.5MB, 1000개를 띄우면 약 25.7MB로 수렴한다. "5MB"는 VMM 프로세스 자체의 오버헤드이고, 실제로는 게스트 커널·에이전트가 쓰는 메모리가 더해진다.

그래도 인상적인 이유
1000개를 띄워도 VM당 25.7MB에서 "수렴"한다

보통은 VM 수가 늘수록 개당 오버헤드가 커진다. 여기서 수렴하는 이유는 CoW(Copy-on-Write) + 온디맨드 메모리 할당 때문이다. 1000개 VM이 같은 템플릿 스냅샷에서 복제됐다면, 실제로 쓰기가 발생한 페이지만 물리 메모리를 차지한다. 2GiB를 "할당"받았어도 실제로 만진 건 25MB뿐인 것이다.

경쟁 제품 대비 위치

흥미롭게도 저장소는 E2B를 경쟁자로 공격하지 않고 "완전 호환"을 내세운다. 문서에는 이렇게 쓰여 있다 — "E2B Cloud에서 Cube Sandbox로 옮기는 것은 API URL 같은 환경변수를 교체하는 것만큼 간단하다."

전략 읽기

이건 흔한 "호환 계층으로 시장 진입" 전략이다. 새 API를 배우게 하는 대신, 이미 E2B를 쓰는 사람이 공짜로 갈아탈 수 있게 만든다. 대신 셀링포인트를 "셀프호스팅 가능 + 더 빠름 + 클라우드 종속 없음"으로 잡는다. Postgres 호환을 내세운 수많은 DB들이 쓴 것과 같은 수법이다.

단, 실제 코드 실행 데몬(envd)을 E2B 저장소에서 컴파일해 쓴다는 점은 양날의 검이다. 호환성은 완벽하지만, 상류 프로젝트에 대한 의존이 구조적으로 남는다.

공정하게 짚기
gVisor·Daytona·Modal·microsandbox와의 직접 비교는 저장소에 없다

저장소 전체를 grep 해도 이 이름들은 나오지 않는다. 비교 대상은 오직 Docker·"전통 VM"·E2B뿐이다. 즉 표에 있는 우위는 "레포가 주장하는 차별점"이지 "제3자가 검증한 우위"가 아니다. 특히 벤치마크는 전부 자사 하드웨어에서 잰 자사 측정치다.

3기술 스택 전체 지도

Rust 21.7만 줄 + Go 22.5만 줄이 각각 어디에 쓰였는가

이 프로젝트의 첫인상은 "언어를 왜 이렇게 섞었지?"다. 하지만 배치를 보면 규칙이 명확하다 — 커널·하드웨어에 가까울수록 Rust, 오케스트레이션·클러스터에 가까울수록 Go, 트래픽 라우팅은 Lua.

언어별 코드 규모 (실측)

언어파일 수총 라인 수주 담당 영역
Go1,041225,521클러스터 오케스트레이션, 노드 에이전트, eBPF 로더
Rust398217,259하이퍼바이저, containerd shim, 게스트 에이전트, API, CoW 엔진
Python12021,178SDK, 예제
TS/JS99Node SDK, WebUI
eBPF C5커널 내 네트워크 데이터플레인

백엔드 — Rust 진영

VMM 계층

hypervisor/ — Cloud Hypervisor 포크

Cargo.toml을 열면 정체가 바로 드러난다. 패키지명은 cube-hypervisor v28.0.0이지만 authors = ["The Cloud Hypervisor Authors"], homepage도 원본 URL 그대로다. 워크스페이스 멤버 25개(api_client, arch, devices, vmm, virtio-devices, vm-migration, tpm…)가 Cloud Hypervisor 원본 구조와 동일하다.

edition 2021 / MSRV 1.77.0 — 이 컴포넌트만 오래된 Rust에 묶여 있다.

게스트 내부

agent/ — Kata Containers 에이전트 포크 (cube-agent)

마이크로VM 안에서 PID 1로 뜨는 프로세스다. authors = ["The Kata Containers community"]. 주요 의존성이 곧 역할 설명이다:

스토리지

cubecow/ — XFS reflink 기반 CoW 엔진 (MIT 라이선스)

저장소 전체는 Apache-2.0인데 이 크레이트만 Cargo.toml에 license = "MIT"로 따로 박혀 있다. XFS의 FICLONE ioctl로 O(1) 스냅샷/클론을 구현한다. 의존성은 단출하다 — dashmap(락프리 맵), uuid, thiserror 2.

API 게이트웨이

CubeAPI/ — Axum 기반 REST 서버

백엔드 — Go 진영

컴포넌트Go 버전핵심 라이브러리역할
CubeMaster1.25.7gin-gonic, gorm, k8s client, containerd, grpc클러스터 스케줄러·상태 관리
Cubelet1.24.8cilium/ebpf, containerd, cri-api노드 로컬 에이전트 (k8s의 kubelet 대응)
network-agent1.24.8cilium/ebpf, vishvananda/netlinkeBPF 프로그램 로딩·맵 관리
cube-lifecycle-manager1.25.7go-redis, zap유휴 샌드박스 자동 일시정지/재개

인프라 / 프론트엔드

배울 점
컴포넌트별로 Rust 툴체인을 따로 고정한다

docker/Dockerfile.builder를 보면 RUST_TOOLCHAIN_HYPERVISOR=1.77.2, RUST_TOOLCHAIN_E2BAPI=1.85, RUST_TOOLCHAIN_AGENT=1.89같은 저장소 안에서 세 개의 Rust 버전을 rustup으로 병행 관리한다. 포크한 상류 코드(hypervisor)는 옛 MSRV에 묶여 있고, 새로 짠 코드는 최신 기능을 쓰고 싶을 때 쓰는 실전 기법이다. "모노레포는 언어 버전이 하나여야 한다"는 편견을 깨는 사례.

4아키텍처 심화 — 8개 컴포넌트와 4가지 설계 패턴

요청 하나가 샌드박스가 되기까지

전체 시스템 구조도

Client / SDK (E2B 호환: python · node · go) │ │ REST (E2B 경로 + /cubeapi/v1 이중 마운트) ▼ ┌──────────────────────────────────────────────────┐ │ CubeAPI (Rust/Axum) 인증 · 레이트리밋 · AgentHub │ └──────────────────────────┬───────────────────────┘ │ gRPC ▼ ┌──────────────────────────────────────┐ ┌──────────────┐ │ CubeMaster (Go) 스케줄링 · 클러스터 상태 │◄──────►│ Redis │ └──────────────────────────┬───────────┘ │ 상태/이벤트/락 │ │ gRPC └──────┬───────┘ ▼ ▲ ┌──────────────────────────────────────┐ │ 조회 │ Cubelet (Go) 노드 로컬 에이전트 │ ┌──────┴────────────┐ └──────────────────────────┬───────────┘ │ CubeProxy │◄── 클라이언트 │ containerd Shim v2 │ (OpenResty/Lua) │ 데이터 트래픽 │ (ttrpc) └──────┬────────────┘ ▼ │ ┌──────────────────────────────────────┐ │ │ CubeShim (Rust) VM 라이프사이클 │ │ └──────────────────────────┬───────────┘ │ │ KVM ioctl │ ▼ │ ┌──────────────────────────────────────┐ │ │ CubeHypervisor (Cloud Hypervisor 포크) │ │ │ vCPU 스레드마다 seccomp 필터 적용 │ │ └──────────────────────────┬───────────┘ │ ▼ │ ╔══════════════════════════════════════════════╗ │ ║ MicroVM (샌드박스 · 전용 커널) ║◄─────────┘ ║ ║ ║ cube-agent (PID 1, Kata 포크) ║ ║ └─ rustjail : namespace + cgroup + seccomp ║ ║ └─ envd (E2B 데몬) : 실제 코드 실행 ║ ╚═══════════╤══════════════════════╤═══════════╝ │ TAP 디바이스 │ FICLONE (reflink) ▼ ▼ ┌───────────────────────┐ ┌──────────────────────────┐ │ CubeVS (eBPF) │ │ CubeCoW (Rust, XFS) │ │ from_cube (TAP in) │ │ Template(읽기전용) │ │ from_world (NIC in) │ │ └→ Sandbox rootfs │ │ from_envoy (dev out) │ │ └→ Snapshot/Clone │ │ SNAT/DNAT · 정책 · DNS │ └──────────────────────────┘ └───────────┬───────────┘ ▼ ┌──────────────────────────────────────┐ │ CubeEgress (OpenResty, L7 MITM) │ │ 도메인 화이트리스트 · 자격증명 주입 · 감사 │ └───────────┬──────────────────────────┘ ▼ Internet

설계 패턴 ① — 컨트롤 플레인과 데이터 플레인의 완전 분리

다이어그램에서 가장 중요한 건 화살표가 두 갈래로 갈라진다는 점이다.

비유

호텔에 비유하면, 컨트롤 플레인은 프런트 데스크(체크인·방 배정·체크아웃)이고 데이터 플레인은 엘리베이터(투숙객이 실제로 방을 오가는 통로)다. 손님이 방에 들어간 뒤 물을 마실 때마다 프런트에 전화하지 않는다. 마찬가지로, 샌드박스가 만들어진 뒤의 코드 실행 트래픽은 CubeAPI를 거치지 않고 CubeProxy를 통해 곧장 간다.

이렇게 나누는 이유는 부하 특성이 완전히 다르기 때문이다. 컨트롤 플레인은 초당 수백 건이지만 데이터 플레인은 초당 수만 건이 될 수 있다. 섞어두면 코드 실행 트래픽이 스케줄러를 마비시킨다.

설계 패턴 ② — 이중 격리 (VM 안에 또 컨테이너)

이 프로젝트에서 가장 오해하기 쉬운 부분이다. 격리가 두 겹이다.

[호스트 커널] │ ← 1차 경계: KVM 하드웨어 가상화 │ (게스트가 여기를 뚫으려면 CPU 가상화 취약점이 필요) ┌────┴─────────────────────────────────┐ │ MicroVM : 전용 게스트 커널 6.6.119 │ │ │ │ cube-agent (PID 1) │ │ │ ← 2차 경계: rustjail │ │ │ namespace + cgroup + seccomp│ │ ┌────┴──────────────┐ │ │ │ 사용자 코드 프로세스 │ │ │ └───────────────────┘ │ └───────────────────────────────────────┘

왜 VM 안에 또 컨테이너를 넣을까? 두 경계가 막는 것이 다르기 때문이다.

경계막는 것구현 위치
KVM (1차)다른 사용자의 샌드박스·호스트로의 탈출hypervisor/, CPU 가상화 확장
rustjail (2차)같은 샌드박스 안에서 사용자 코드가 cube-agent 자체를 장악하는 것, 자원 폭주agent/rustjail/

실제 코드 위치는 이렇다:

추가 방어층
VMM 프로세스 자신도 seccomp로 묶는다

hypervisor/vmm/src/cpu.rs에서 vCPU 스레드마다 get_seccomp_filter(&self.seccomp_action, Thread::Vcpu, ...)seccompiler::apply_filter를 호출한다. 즉 게스트가 하이퍼바이저를 뚫었다 해도, 하이퍼바이저 프로세스 자체가 최소 syscall 화이트리스트에 갇혀 있다. "심층 방어(defense in depth)"의 교과서적 구현이다.

설계 패턴 ③ — CubeCoW: 파일 복사 없는 스냅샷

60ms 부팅의 진짜 비밀은 하이퍼바이저가 빨라서가 아니라 "부팅을 안 하기 때문"이다.

용어
reflink / FICLONE
XFS·Btrfs 같은 파일시스템이 지원하는 기능. 파일을 복사할 때 실제 데이터는 안 옮기고 "같은 블록을 가리키는 새 이름"만 만든다. 나중에 어느 한쪽이 수정될 때만 그 부분을 진짜로 복사한다(Copy-on-Write). 10GB 파일 복사가 밀리초 단위로 끝난다.

템플릿이 만들어지는 과정은 3단계다 (docs/guide/templates.md):

① Init OCI 이미지 + Dockerfile ──[Buildkit]──► rootfs 이미지 ② Boot & Snapshot MicroVM 콜드 부팅 ↓ Python/Node 런타임 로딩 완료 (여기가 느린 구간) ↓ ★ 이 시점의 메모리 전체를 스냅샷으로 촬영 ★ ③ Deploy rootfs + 메모리 스냅샷 = "템플릿"으로 등록 ── 이후 실제 요청이 오면 ── Template (읽기 전용) │ FICLONE ← 데이터 복사 없음, O(1) ▼ Sandbox rootfs (CoW) + 메모리 스냅샷 복원 │ ▼ 이미 Python이 로딩된 상태로 "깨어남" → 60ms
비유

노트북 절전모드(하이버네이션)와 같다. 전원을 껐다 켜면 부팅에 1분이 걸리지만, 절전모드에서 깨우면 3초다. CubeSandbox는 "Python 인터프리터까지 다 켜놓은 상태"를 절전모드로 저장해두고, 요청이 올 때마다 그 절전 상태를 복사해서 깨운다.

그리고 FICLONE 덕분에 그 "복사"조차 실제 복사가 아니다. 1000개의 샌드박스가 같은 스냅샷 파일 블록을 공유하다가, 각자 파일을 수정하는 순간에만 그 블록이 갈라진다.

CubeCoW가 내세우는 두 가지 특징:

스냅샷 메타데이터 구조체(CubeShim/shim/src/hypervisor/snapshot.rs)도 살펴볼 가치가 있다:

struct SnapshotInfo {
    kernel_version,   // 게스트 커널 버전
    image_version,    // rootfs 이미지 버전
    ch_version,       // Cloud Hypervisor 버전
    vm_res,           // VmRes { cpu, cpu_max, memory, disks, pmems }
    memory_vol_url,
    ...
}
배울 점
스냅샷에 "환경 버전"을 함께 박아둔다

커널·이미지·하이퍼바이저 버전을 메타데이터에 기록해두면, 구버전 하이퍼바이저로 찍은 스냅샷을 신버전이 복원하려 할 때 즉시 거부할 수 있다. 메모리 스냅샷은 CPU 레지스터 상태까지 담고 있어서 버전이 안 맞으면 조용히 망가진다 — 이런 건 런타임에 크래시로 발견하면 이미 늦다. 직렬화된 상태를 저장하는 모든 시스템(캐시·세션·체크포인트)에 적용되는 원칙이다.

설계 패턴 ④ — CubeVS: iptables를 버리고 eBPF로

샌드박스가 수천 개면 네트워크 규칙도 수천 개다. 전통적인 iptables는 규칙을 선형 탐색하므로 규칙이 늘수록 패킷당 지연이 커진다(이른바 "rule explosion"). CubeVS는 이걸 eBPF로 우회한다.

eBPF 프로그램 3개가 각각 다른 지점에 붙는다:

파일프로그램명부착 지점역할
mvmtap.bpf.cfrom_cubeTAP 디바이스 TC ingress샌드박스에서 나가는 패킷 (SNAT·정책 평가)
nodenic.bpf.cfrom_world호스트 NIC TC ingress외부에서 들어오는 패킷 (DNAT·세션 매칭)
localgw.bpf.cfrom_envoycube-dev TC egress프록시에서 나가는 패킷

Go 컨트롤플레인(CubeNet/cubevs/)이 BPF 맵을 관리한다 — egress_sessions, snat_iplist, allow_out_v2, dns_allow 등. iptables·리눅스 브리지·OVS를 전혀 쓰지 않는다.

용어
eBPF
리눅스 커널 안에서 안전하게 검증된 작은 프로그램을 실행하는 기술. 커널 모듈처럼 커널을 건드리지만, 로드 전에 검증기(verifier)가 무한루프·잘못된 메모리 접근이 없는지 증명한다. 네트워크 패킷 처리, 보안 정책, 추적(tracing)에 널리 쓰인다.
용어
BPF 맵 (map)
커널 안의 eBPF 프로그램과 사용자공간 프로그램이 공유하는 자료구조(해시맵·배열 등). 정책이 바뀌면 Go 컨트롤플레인이 맵의 값을 갱신하고, 커널의 eBPF 프로그램은 다음 패킷부터 새 정책으로 동작한다. 규칙 개수와 무관하게 O(1) 해시 조회라 iptables의 선형 탐색 문제가 없다.

보안 계층 총정리

docs/architecture/overview.md가 정리한 6개 방어층:

  1. KVM 마이크로VM — 전용 커널, 하드웨어 격리
  2. CubeVS — 기본 거부(사설 대역·링크로컬 차단) + 샌드박스별 정책
  3. CubeEgress — L7 도메인 화이트리스트
  4. Credential vault — 헤더 재작성으로 시크릿을 주입. 시크릿이 샌드박스 안에도, LLM 컨텍스트에도 노출되지 않는다
  5. CubeHypervisor — VMM 프로세스 최소 seccomp
  6. CubeAPI — 교체 가능한(pluggable) 인증 콜백
특히 영리한 설계
CubeEgress의 자격증명 주입

에이전트가 GitHub API를 호출해야 한다고 하자. 보통은 샌드박스 안에 토큰을 넣어준다. 그러면 LLM이 그 토큰을 읽어 로그나 응답에 흘릴 수 있다.

CubeEgress는 대신 이렇게 한다: 샌드박스는 토큰 없이 요청을 보내고, TPROXY로 가로챈 CubeEgress가 나가는 길에 Authorization 헤더를 붙인다. 샌드박스는 토큰을 본 적이 없다. 이게 가능한 이유는 CubeEgress가 발급한 루트 CA가 샌드박스 템플릿에 미리 심어져 있어 TLS 인스펙션(중간자)이 가능하기 때문이다.

5디렉토리 구조 해부

어디부터 읽어야 하는가
CubeSandbox/ │ ├── hypervisor/ ★ Rust · Cloud Hypervisor 포크 (VMM 본체) │ └── (25개 서브크레이트: vmm, arch, devices, virtio-devices, vm-migration…) │ ├── CubeShim/ ★ Rust · containerd Shim v2 구현 │ └── shim/src/hypervisor/ │ ├── cube_hypervisor.rs ← VM 생성/부팅/스냅샷/핫플러그 │ └── snapshot.rs ← SnapshotInfo / VmRes │ ├── agent/ ★ Rust · Kata 포크 게스트 에이전트 (cube-agent, PID 1) │ ├── src/namespace.rs ← unshare()로 네임스페이스 생성 │ └── rustjail/src/ │ ├── seccomp.rs ← init_seccomp() │ └── cgroups/ ← fs / systemd 드라이버 │ ├── cubecow/ ★ Rust(MIT) · XFS FICLONE 기반 CoW 스토리지 ├── cubelog/ Rust · 공용 로깅 라이브러리 │ ├── CubeAPI/ ★ Rust/Axum · E2B 호환 REST 게이트웨이 │ └── src/routes.rs ← 모든 엔드포인트 정의 │ ├── CubeMaster/ Go · 클러스터 오케스트레이터 (스케줄링/상태) ├── Cubelet/ Go · 노드 로컬 스케줄러 (containerd 연동) ├── network-agent/ Go · eBPF 프로그램 로더/컨트롤플레인 ├── cube-lifecycle-manager/ Go · 유휴 샌드박스 auto-pause/resume │ ├── CubeNet/ ★ eBPF C + Go │ ├── *.bpf.c ← mvmtap / nodenic / localgw │ └── cubevs/ ← Go 컨트롤플레인, BPF 맵 관리 │ ├── CubeProxy/ OpenResty(nginx+Lua) · E2B 호환 리버스 프록시 ├── CubeEgress/ OpenResty+Lua · L7 egress 보안 프록시 │ ├── deploy/ 설치 스크립트 │ ├── bare-metal/ pvm/ one-click/ kubernetes/ guest-image/ │ └── terraform/tencentcloud/ ← TKE+CVM 자동 배포 (v0.5.0 신규) │ ├── sdk/ go / node / python 3종 SDK ├── examples/ 17개 실습 예제 (code-sandbox, browser-sandbox, RL training…) ├── docs/ VitePress 문서 (영·중 이중언어) ├── web/ WebUI 대시보드 ├── configs/ kernel-oc9.<arch>.config 등 └── Makefile, openapi.yml, LICENSE(468줄)

읽는 순서 추천

  1. docs/architecture/overview.md — 전체 그림. 여기부터 시작 안 하면 길을 잃는다
  2. CubeAPI/src/routes.rs — 시스템이 "무엇을 할 수 있는가"의 전체 목록
  3. CubeShim/shim/src/hypervisor/cube_hypervisor.rs — VM 라이프사이클의 실제 호출 흐름
  4. cubecow/ — 크기가 작고 독립적이라 혼자 읽기 좋다. FICLONE 학습에 최적
  5. CubeNet/*.bpf.c — eBPF 실전 코드. 3개 파일뿐이라 부담 없다
주의
hypervisor/를 먼저 열지 말 것

21만 줄 Rust 중 대부분이 여기 있고, 거의 전부가 Cloud Hypervisor 원본 코드다. CubeSandbox 고유의 기여를 보려면 CubeShim/, cubecow/, CubeNet/, CubeAPI/를 봐야 한다. 상류 포크 저장소를 읽을 때는 git log로 "이 조직이 실제로 건드린 파일"부터 찾는 게 훨씬 빠르다.

6학습 포인트 — 여기서 훔쳐올 설계

기술별로 무엇을 배울 수 있는가

① 가상화 — "빠른 부팅"의 실체

이 저장소의 가장 큰 교훈은 성능 문제를 "더 빠르게 만들기"가 아니라 "안 하기"로 푼다는 것이다. 부팅이 느리면 부팅을 최적화하는 대신, 부팅 결과를 저장해두고 복원한다.

이 패턴은 도처에 있다 — JVM의 AppCDS, Android의 Zygote 프로세스, Python의 fork() 기반 프리로더, Node.js의 스냅샷. 공통 구조는 이렇다:

비싼 초기화 → 스냅샷 저장 → 요청마다 스냅샷에서 fork/clone ┌─ 한 번만 지불 ─┐ ┌────── 매 요청 (거의 공짜) ──────┐ Python 로딩 3초 → clone 60ms · clone 60ms · clone 60ms …

실습 아이디어: 무거운 초기화(대형 모델 로딩, DB 커넥션 풀)를 하는 파이썬 프로세스를 만들고, fork() 기반 프리포크 서버로 바꿔 요청당 지연이 얼마나 줄어드는지 측정해보라.

② 파일시스템 — CoW를 직접 만져보기

FICLONE은 Rust나 C가 아니어도 실험할 수 있다. 리눅스에서 XFS(reflink 활성) 또는 Btrfs 위에서 cp --reflink=always를 써보면 즉시 체감된다.

# 10GB 파일을 만들고 두 방식으로 복사해 시간 비교
dd if=/dev/zero of=big.img bs=1M count=10240
time cp big.img copy1.img                 # 수십 초
time cp --reflink=always big.img copy2.img # 밀리초
df -h .                                    # 디스크 사용량이 안 늘어난다

실습 아이디어: reflink 복사본을 만든 뒤 한쪽 파일 일부를 수정하고, filefrag -v로 extent가 어떻게 갈라지는지 관찰해보라. CoW가 "언제" 실제 복사를 하는지 눈으로 볼 수 있다.

③ eBPF — 커널 프로그래밍의 현대적 입구

CubeNet의 파일 3개는 eBPF 입문 교재로 훌륭하다. TC(Traffic Control) 훅에 붙는 eBPF는 XDP보다 다루기 쉽고 활용 범위가 넓다.

실습 아이디어: cilium/ebpf(Go) 또는 aya(Rust)로 "특정 포트로 나가는 패킷만 카운트하는" 최소 TC 프로그램을 작성해보라. 그다음 BPF 맵에서 그 카운터를 사용자공간으로 읽어오는 것까지 하면 CubeVS의 축소판이 된다.

④ 시스템 설계 — 컨트롤/데이터 플레인 분리

Kubernetes(API 서버 ↔ 워크로드 트래픽), Envoy(xDS ↔ 프록시), Kafka(주키퍼/KRaft ↔ 브로커) 모두 같은 구조다. 이 분리를 못 하면 시스템은 부하가 올라갈 때 항상 같은 방식으로 죽는다 — 제어 명령이 데이터 트래픽에 밀려 타임아웃되고, 그러면 재시도가 몰려 더 죽는다.

실습 아이디어: 자기가 만든 API 서버에서 "관리용 엔드포인트"와 "고빈도 엔드포인트"를 다른 포트/다른 프로세스로 분리해보라. 부하 테스트로 격리 효과를 측정하면 체감이 온다.

⑤ 오픈소스 전략 — 포크와 호환 계층

이 저장소는 "어디까지 빌리고 어디부터 만들 것인가"의 판단 사례집이다.

영역선택이유(추정)
VMMCloud Hypervisor 포크직접 만들면 수년 + 보안 리스크. 성숙한 것을 쓴다
게스트 에이전트Kata Containers 포크동상
코드 실행 데몬E2B의 envd 그대로SDK 호환성이 목적 자체
스토리지 CoW직접 개발 (CubeCoW)기존 것(qcow2 체인)의 구조적 한계를 피하고 싶었음
네트워크직접 개발 (CubeVS)iptables/OVS로는 규모에서 안 됨
PVM직접 개발중첩 가상화 없는 클라우드 VM 지원 = 사업적 차별점

규칙이 보인다 — "성숙하고 표준적인 것은 빌린다. 자기 제품의 차별점이 되는 부분만 만든다." README도 이를 명시한다: "made tailored modifications... original in-file copyright notices are preserved."

7하드웨어 / 시스템 요구사항

직접 돌려보려면 무엇이 필요한가
먼저 알아둘 것
맥이나 윈도우에서는 못 돌린다

x86_64 또는 aarch64 리눅스 + /dev/kvm가 필수다. 그리고 root 권한이 필요하다. 노트북에서 가볍게 시험할 수 있는 종류의 프로젝트가 아니다.

공식 스펙 (docs/guide/quickstart.md)

용도CPURAM디스크
기능 체험용 (최소)≥4코어≥8GB≥50GB (XFS 필수, /data/cubelet)
권장32코어64GB≥200GB
템플릿 다수 빌드 시200GB+
놓치기 쉬운 함정
디스크가 XFS여야 한다

CubeCoW가 FICLONE에 의존하므로 ext4에서는 핵심 기능이 동작하지 않는다. 게다가 XFS를 만들 때 reflink 옵션이 켜져 있어야 한다(xfsprogs ≥ 5.0에서는 기본값). 설치 전에 xfs_info /datareflink=1을 확인할 것.

커널

KVM이 없는 클라우드 VM이라면 — PVM

용어
PVM (Pagetable-based Virtual Machine)
대부분의 퍼블릭 클라우드 VM은 중첩 가상화(nested virtualization)를 막아둬서 그 안에서 또 VM을 띄울 수 없다. PVM은 텐센트가 논문을 기반으로 자체 개발한 기술로, 하드웨어 중첩 가상화 없이 페이지테이블 조작만으로 KVM에 준하는 능력을 제공한다. OpenCloudOS 커널에 오픈소스로 들어가 있다. 단 x86_64 전용이라 ARM64에서는 쓸 수 없다.

배포 경로 3종

방식대상비고
PVM · 클라우드 VM일반 클라우드 인스턴스공식 권장. x86_64 전용
베어메탈물리 서버최고 성능. KVM 직접 사용
dev-env (QEMU)로컬 개발README가 "비권장·저성능"이라 명시

추가 배포 자산: deploy/one-click/에 systemd 서비스 + docker-compose(webui, cubeproxy, coredns, cube-lifecycle-manager), deploy/terraform/tencentcloud/에 TKE+CVM 자동 배포(v0.5.0 신규), deploy/kubernetes/chart에 Helm 차트.

로드맵 상태
"K8s 네이티브(CRD/오퍼레이터)"는 아직 coming soon

Helm 차트는 있지만, README 로드맵에서 진짜 쿠버네티스 네이티브 통합은 미완으로 표시돼 있다. 현재 차트는 그 전 단계 지원으로 보인다(추정).

ARM64 참고사항

bare-metal-deploy.md에 따르면, 일부 구형 aarch64 호스트는 KVM이 게스트에 PMUv3(성능 카운터)를 노출하지 않는다. 마이크로VM은 정상 부팅하되 하드웨어 성능 카운터 없이 동작한다. 기능상 문제는 아니지만 프로파일링이 제한된다.

8직접 해볼 수 있는 실습 과제

난이도별 5단계
난이도 ★☆☆☆☆

1. E2B 호환성을 코드로 확인하기입문 · 30분

이 프로젝트의 핵심 주장("드롭인 교체")이 진짜인지 코드로 확인한다. 서버를 안 띄워도 된다.

examples/code-sandbox-quickstart/exec_code.py를 열어보라. 실제 내용은 이렇다:

from e2b_code_interpreter import Sandbox

with Sandbox.create(template=template_id) as sandbox:
    print(sandbox.run_code("print('hello cube')",
                           on_stdout=lambda data: print(data)))

주목할 점: import 하는 패키지가 cubesandbox가 아니라 E2B의 e2b_code_interpreter다. 바뀌는 건 E2B_API_URL 환경변수 하나뿐이다.

과제: 저장소 안 SDK 3종(sdk/python, sdk/node, sdk/go)이 자체 API를 제공하는데도 예제가 E2B SDK를 쓰는 이유를 설명해보라. 두 SDK는 각각 언제 쓰는 게 맞는가?

난이도 ★★☆☆☆

2. reflink로 CoW 직접 체험하기초급 · 1시간

CubeSandbox를 안 띄우고 그 핵심 원리만 재현한다. 리눅스(XFS reflink 또는 Btrfs)만 있으면 된다.

  1. 1GB 파일 생성 후 cpcp --reflink=always의 소요 시간·디스크 사용량 비교
  2. reflink 사본의 앞부분 100MB만 수정 (dd conv=notrunc)
  3. df로 디스크 사용량이 얼마나 늘었는지 확인 — 1GB가 아니라 100MB만 늘어야 한다
  4. filefrag -v로 두 파일의 extent 공유 상태 관찰

확장: 사본 100개를 만들고, 각각 다른 위치를 조금씩 수정한 뒤 총 디스크 사용량을 계산해보라. CubeSandbox가 "1000개 VM에서 VM당 25.7MB로 수렴"하는 이유를 수치로 이해하게 된다.

난이도 ★★★☆☆

3. 클러스터 배포하고 벤치마크 재현하기중급 · 하루

KVM 가능한 리눅스 서버(≥4코어/8GB/50GB XFS)에서 deploy/one-click/으로 설치한다.

  1. 설치 전 xfs_inforeflink=1 확인, ls /dev/kvm 확인
  2. 원클릭 설치 후 WebUI 대시보드 접속
  3. 템플릿 하나 빌드 (cubemastercli tpl create-from-image)
  4. 샌드박스 1개 생성 시간을 측정 → 문서의 47.8ms와 비교
  5. 동시 10개, 20개, 50개로 늘리며 평균·P95 측정 → 벤치마크 표 재현

핵심 질문: 내 환경 숫자가 문서(96코어 베어메탈)와 얼마나 다른가? 차이의 원인을 CPU·디스크·네트워크 중 무엇으로 설명할 수 있는가? 벤치마크를 읽을 때 "환경 의존성"을 체감하는 게 이 과제의 진짜 목적이다.

난이도 ★★★★☆

4. eBPF 미니 CubeVS 만들기고급 · 수일

CubeNet/*.bpf.c를 읽고 축소판을 직접 구현한다.

  1. mvmtap.bpf.c(from_cube)를 정독 — TC ingress 훅에서 패킷을 어떻게 파싱하고 맵을 어떻게 조회하는지
  2. cilium/ebpf(Go) 또는 aya(Rust)로 최소 TC 프로그램 작성: "허용 목록에 없는 목적지 IP는 드롭"
  3. 사용자공간에서 BPF 맵에 허용 IP를 넣고 뺄 수 있게 만들기
  4. 같은 규칙을 iptables로도 구현하고, 규칙 1개 / 100개 / 10,000개일 때 패킷 처리 지연을 비교

목표: "iptables rule explosion"이 마케팅 문구가 아니라 측정 가능한 현상임을 자기 손으로 확인하는 것.

난이도 ★★★★★

5. 스냅샷 복원 경로 추적하기전문가 · 1~2주

"60ms"가 실제로 어디서 소비되는지 코드와 프로파일로 분해한다.

  1. CubeShim/shim/src/hypervisor/cube_hypervisor.rs에서 restore_vm()resume_vm_cube_with_config()의 호출 그래프를 그린다
  2. 각 단계에 타이밍 계측을 삽입: rootfs FICLONE / 메모리 스냅샷 로드 / vCPU 상태 복원 / 네트워크 설정 / envd 준비 대기
  3. 어느 단계가 병목인지 확인 → 동시성을 올릴 때 어느 단계가 가장 먼저 무너지는지 관찰
  4. snapshot.rsSnapshotInfo 버전 체크를 일부러 실패시켜 방어 로직이 작동하는지 검증

발전 과제: 동시성 50에서 평균이 276ms로 튀는 원인을 위 분해로 설명할 수 있는가? 디스크 I/O인가, 락 경합인가, vCPU 스케줄링인가?

9관련 기술 심화 학습 로드맵

이 저장소를 제대로 읽으려면 필요한 배경지식 — 8주 코스
주차주제학습 내용산출물
1주리눅스 격리 기초namespace 7종, cgroup v2, capability, seccomp-bpf. unshare·nsenter 명령 직접 사용200줄짜리 "미니 컨테이너 런타임"(C 또는 Go)
2주OCI 스펙runtime-spec의 config.json 구조, image-spec 레이어, containerd 아키텍처agent/rustjail/이 OCI 스펙의 어느 필드를 구현하는지 매핑 표
3주가상화 원리Intel VT-x/AMD-V, EPT, KVM API(KVM_CREATE_VM, KVM_RUN), virtio 반가상화KVM ioctl만으로 "hlt만 실행하는" 최소 VM 부팅시키기
4주마이크로VM 실전Firecracker와 Cloud Hypervisor 비교, 장치 모델 축소, 스냅샷/복원 APIFirecracker로 VM 스냅샷 찍고 복원하는 스크립트
5주파일시스템 CoWXFS reflink, Btrfs subvolume, overlayfs, qcow2 백킹 체인의 문제점실습 2번 확장판 + "flat snapshot이 왜 나은가" 정리 문서
6주eBPFverifier 동작 원리, 맵 종류, XDP vs TC 훅, cilium/ebpf 또는 aya실습 4번 (미니 CubeVS)
7주Rust 시스템 프로그래밍nix/rustix로 syscall 호출, unsafe 경계 설계, tokio 비동기, ttrpc/vsockagent/src/namespace.rs를 읽고 주석 달기
8주분산 시스템 설계컨트롤/데이터 플레인 분리, 스케줄러 설계, 상태 저장소(Redis) 활용 패턴CubeMaster의 스케줄링 로직 분석 문서
현실적인 조언

8주는 이 저장소를 "읽을 수 있게" 되는 시간이지 "기여할 수 있게" 되는 시간이 아니다. 시간이 부족하다면 우선순위는 1주(격리) → 3주(KVM) → 5주(CoW)다. 이 셋만 알아도 아키텍처 문서가 이해되기 시작한다.

반대로 당장 써먹을 것만 원한다면 실습 1·2번만 하고 넘어가도 된다. 이 프로젝트의 설계 교훈(스냅샷 패턴, 플레인 분리, 빌릴 것과 만들 것의 구분)은 가상화를 몰라도 자기 코드에 적용할 수 있다.

10핵심 키워드 사전

이 문서와 저장소에서 반복되는 용어 정리
용어
microVM불필요한 가상 장치를 제거한 초경량 가상머신. 빠른 부팅과 낮은 메모리 오버헤드가 목표
KVMKernel-based Virtual Machine. 리눅스 커널에 내장된 하이퍼바이저 기능. /dev/kvm으로 접근
VMMVirtual Machine Monitor. 가상 장치를 흉내내고 게스트를 관리하는 사용자공간 프로그램. 여기선 CubeHypervisor
Cloud HypervisorRust로 작성된 오픈소스 VMM. CubeSandbox가 포크한 원본
Kata Containers컨테이너를 VM 안에서 돌리는 프로젝트. CubeSandbox의 게스트 에이전트가 이걸 포크
PVMPagetable-based VM. 중첩 가상화 지원 없이 페이지테이블만으로 KVM 능력을 제공하는 텐센트 자체 기술. x86_64 전용
CoWCopy-on-Write. 복사할 때 실제로 복사하지 않고, 수정이 일어나는 순간에만 그 부분을 복사
reflink / FICLONEXFS·Btrfs의 파일 수준 CoW 복사. 데이터 블록을 공유하는 새 파일을 O(1)에 만든다
flat snapshot스냅샷들이 사슬로 엮이지 않는 모델. 중간 스냅샷을 삭제해도 다른 스냅샷이 깨지지 않는다
dirty page마지막 저장 이후 수정된 메모리 페이지. 스냅샷 시 이것만 저장하면 증분 저장이 된다
seccomp프로세스가 호출할 수 있는 시스템콜을 화이트리스트로 제한하는 리눅스 기능
namespacePID·네트워크·마운트 등을 프로세스 그룹별로 분리해 "각자 자기만의 세상"을 보게 하는 커널 기능
cgroupCPU·메모리·I/O 사용량에 상한을 거는 커널 기능
rustjailKata Containers의 Rust 기반 컨테이너 격리 라이브러리. 게스트 VM 안에서 2차 격리를 담당
eBPF커널 안에서 검증된 작은 프로그램을 안전하게 실행하는 기술
TC (Traffic Control)리눅스 네트워크 스택의 큐 제어 계층. eBPF 프로그램을 ingress/egress에 붙일 수 있다
BPF 맵커널 eBPF 프로그램과 사용자공간이 공유하는 자료구조. 정책 갱신 통로
SNAT / DNATSource/Destination NAT. 패킷의 출발지/목적지 주소를 바꿔치기하는 것
TPROXY리눅스의 투명 프록시 기능. 클라이언트가 모르게 트래픽을 프록시로 우회
containerd Shim v2containerd가 컨테이너 런타임과 통신하는 표준 인터페이스. CubeShim이 이걸 구현해 VM을 컨테이너처럼 다루게 한다
ttrpcgRPC의 경량 버전. 임베디드·저자원 환경용. HTTP/2 대신 단순 프로토콜 사용
vsock호스트와 게스트 VM 사이의 소켓 통신 채널. 네트워크 스택을 거치지 않는다
envdE2B가 만든 샌드박스 내부 데몬. 실제 코드 실행을 담당. CubeSandbox가 소스에서 컴파일해 사용
E2BAI 에이전트용 클라우드 샌드박스 상용 서비스. CubeSandbox가 API·SDK 호환 대상으로 삼음
OpenRestynginx + Lua 스크립팅. CubeProxy·CubeEgress가 이걸로 만들어짐
BuildkitDocker의 차세대 이미지 빌더. 템플릿 rootfs 빌드에 사용
MSRVMinimum Supported Rust Version. 이 저장소는 컴포넌트별로 1.77 / 1.85 / 1.89를 따로 관리
DCODeveloper Certificate of Origin. 커밋에 Signed-off-by를 요구하는 기여 정책. CI에서 검사

11참고 링크

더 깊이 파고들 곳

이 프로젝트

포크 원본 프로젝트 (원리 학습에 필수)

배경 기술