Mooncake
개요
Mooncake는 대규모 LLM 추론 및 학습을 위한 인프라 프로젝트입니다. KV 캐시 중심의 분리형 아키텍처로 prefill과 decode 같은 컴퓨팅 역할을 분리하며, Mooncake Store는 재사용 가능한 KV 캐시와 모델 가중치를 위한 분산 객체 계층을 제공합니다. 이를 통해 서빙 프레임워크는 인스턴스 간에 캐시 상태를 공유하고, 반복되는 prefill 작업을 줄이며, 단일 GPU나 호스트를 넘어 캐시 용량을 확장할 수 있습니다.
MASS는 Mooncake Store 뒤에서 분산 영속 계층을 제공합니다. Mooncake 의 DaosAdapter는 DAOS
libdfs API를 통해 Store의 분산 offload backend를 MASS POSIX 컨테이너에 연결합니다. 따라서 메모리
캐시에서 축출되거나 비동기적으로 복사된 객체를 확장 가능한 공유 backend에 유지하고 다른 Mooncake
클라이언트에서 사용할 수 있습니다. 어댑터는 마운트된 MASS 경로를 pool과 container 확인에만 사용하며,
객체 데이터는 파일시스템 마운트가 아닌 libdfs를 통해 직접 전송됩니다.
사전 요구 사항
- 모든 테스트 호스트에 동일한 MASS client와 DAOS 지원 Mooncake 빌드를 설치합니다.
- 모든 호스트에 동일한 MASS 볼륨을 마운트하고
DFS_PATH에서 해당 POSIX container를 확인할 수 있는지 검증합니다. - 여러 호스트에서 실행하려면 launch user가 모든 호스트에 passwordless SSH 및 passwordless
sudo로 접근할 수 있어야 합니다. 또는RSH_USER=root를 설정하고 passwordless root SSH를 구성합니다.
MASS-Mooncake 통합 가이드
사전 요구 사항
Mooncake의 DAOS 백엔드를 빌드하고 실행하려면 Mooncake를 빌드하고 실행하는 호스트에 full
mass-client 패키지가 설치되어 있어야 합니다. 이 패키지는 CMake가 사용하는 DAOS 클라이언트
라이브러리, 헤더 및 mass.pc pkg-config 모듈을 제공합니다.
mass-client 설치MangoBoost 패키지 저장소를 설정하고 클라이언트를 설치하는 방법은 mass-client 설치 가이드를 참고하십시오. Ubuntu 또는 RHEL/Rocky Linux 9의 full client 패키지를 사용하십시오. RHEL/Rocky Linux 10 패키지는 thin client이므로 이 가이드에 필요한 DAOS 개발 파일을 포함하지 않습니다.
Mooncake 소스 트리를 준비하고 공통 빌드 의존성을 설치합니다.
cd /path/to/Mooncake
sudo bash dependencies.sh
빌드를 구성하기 전에 Mooncake 통합 패치를 소스 트리에 적용합니다.
MOONCAKE_SRC=/path/to/Mooncake
PATCH_FILE=/path/to/downloaded/mooncake.patch
cd "$MOONCAKE_SRC"
git apply --check "$PATCH_FILE"
git apply "$PATCH_FILE"
패치를 적용하면 mass-client의 pkg-config 메타데이터가 DAOS 헤더 및 라이브러리 경로를
자동으로 해결합니다. 별도의 PKG_CONFIG_PATH 또는 DAOS_ROOT 설정은 필요하지 않습니다.
pkg-config --exists mass
pkg-config mass --cflags --libs
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DWITH_STORE=ON \
-DUSE_DAOS=ON
cmake --build build -j"$(nproc)"
sudo cmake --install build
Mooncake에서 MASS 사용하도록 설정
Mooncake용 MASS 볼륨을 생성하거나 기존 볼륨을 선택한 후, 모든 Mooncake 호스트에 동일한
POSIX/UNS 경로로 마운트합니다. 이 경로는 DAOS pool과 container로 해석될 수 있어야 합니다
(예: /mnt/mass/mooncake). 어댑터는 이 경로로 DAOS 이름을 확인하고, 객체 I/O는 libdfs를
통해 직접 수행합니다.
Mooncake master를 offload 활성화 상태로 시작합니다.
mooncake_master --enable_offload=true
각 Mooncake client를 시작하기 전에 다음 distributed backend 환경 변수를 설정합니다.
export MOONCAKE_OFFLOAD_STORAGE_BACKEND_DESCRIPTOR=distributed
export MOONCAKE_DISTRIBUTED_FS_TYPE=daos
export MOONCAKE_DISTRIBUTED_ROOT_DIR=/mnt/mass/mooncake
각 client도 offload 활성화 옵션으로 시작합니다. 이 설정을 사용하면 Mooncake Store가 DAOS
어댑터를 통해 MASS에 offload된 객체를 저장합니다. MASS backend를 사용할 client에만
MOONCAKE_OFFLOAD_STORAGE_BACKEND_DESCRIPTOR=distributed를 설정하고, 다른 client는 기본
로컬 backend를 계속 사용할 수 있습니다.
벤치마크를 실행하기 전에 Mooncake client를 시작합니다. 환경에 맞게 host 주소, MASS 경로 및 offload 디렉터리를 조정하십시오.
MOONCAKE_DISTRIBUTED_FS_TYPE=daos \
MOONCAKE_DISTRIBUTED_ROOT_DIR=/mnt/poc-home/vllm-kv \
MOONCAKE_DISTRIBUTED_HEALTH_CHECK=true \
MOONCAKE_OFFLOAD_FILE_STORAGE_PATH=/var/tmp/mooncake-offload-stub \
MOONCAKE_OFFLOAD_HEARTBEAT_INTERVAL_SECONDS=2 \
UCX_IB_RCACHE_MAX_REGIONS=256 UCX_RCACHE_MAX_REGIONS=256 \
/opt/Mooncake/bin/mooncake_client \
--master_server_address=127.0.0.1:50051 \
--metadata_server=P2PHANDSHAKE \
--host=211.250.100.47 --port=50052 \
--protocol=rdma \
--global_segment_size="32 GB" \
--local_buffer_size="1 GB" \
--enable_offload=true
벤치마크
DAOS Adapter
daos_adapter_bench.sh는 MASS에서 Mooncake DAOS adapter의 read/write 성능을 측정하는 IOR-like
benchmark입니다.
다음 예제는 Mooncake가 /opt/Mooncake에 설치되어 있다고 가정합니다. 다른 위치에 설치했다면 prefix를
변경하세요.
벤치마크 실행
NPROCS는 호스트마다 시작할 MPI rank 수입니다. 전체 rank 수는 NPROCS와 HOSTS 항목 수를 곱한
값입니다. 각 rank가 NR개의 객체를 생성하므로 한 번의 write pass에 대한 전체 dataset 크기는
호스트 수 × NPROCS × NR × SIZE입니다.
단일 호스트 테스트를 실행하고 rank 0에서 machine-readable 결과를 기록합니다.
NPROCS=4 SUMMARY=/tmp/daos-adapter.json DFS_PATH=/mnt/mooncake-vol \
/opt/Mooncake/scripts/daos_adapter_bench.sh
여러 client의 aggregate throughput을 측정하려면 쉼표로 구분한 host list를 지정합니다. 다음 예제는 호스트마다 rank 4개, 전체 rank 8개를 실행합니다.
HOSTS=client01,client02 NPROCS=4 \
SUMMARY=/tmp/daos-adapter.json DFS_PATH=/mnt/mooncake-vol \
/opt/Mooncake/scripts/daos_adapter_bench.sh
필요한 경우 script는 sudo -E로 자신을 다시 실행합니다. MPICH가 있으면 우선 사용하고, 그렇지 않으면
Open MPI를 사용합니다. Multi-host workload를 시작하기 전에 remote access, benchmark binary, 전체 MPI
launch path를 검사합니다.
설정
NPROCS와 SUMMARY는 필수입니다. 다른 변수는 모두 선택 사항입니다.
| 변수 | 설명 | 기본값 |
|---|---|---|
NPROCS | 각 호스트에서 실행할 MPI rank 수입니다. | 없음 |
SUMMARY | Rank 0이 기록하는 JSON 결과 경로입니다. 상위 디렉터리가 rank-0 호스트에 있어야 합니다. | 없음 |
HOSTS | 쉼표로 구분한 host입니다. 모든 host에 동일한 설치와 MASS target이 있어야 합니다. | localhost |
DFS_PATH | 대상 pool과 container를 확인할 MASS dfuse/UNS 경로입니다. 데이터 I/O에는 libdfs를 사용합니다. | /mnt/mooncake-vol |
DIR | DFS_PATH 아래의 benchmark 하위 디렉터리입니다. | adapterbench |
NR | 각 rank가 생성할 객체 수입니다. 이 값을 유지하면서 rank 수를 늘리면 weak-scaling workload가 됩니다. | 512 |
SIZE | 객체 하나의 크기입니다. byte 또는 k, m, g suffix를 사용할 수 있습니다. | 16m |
XFER | 각 dfs_write의 최대 크기입니다. 0이면 write를 분할하지 않습니다. | 0 |
READ_XFER | 각 dfs_read의 최대 크기입니다. 0이면 read를 분할하지 않습니다. | 8m |
CHUNK | 파일을 생성할 때 사용할 DFS chunk 크기입니다. | 8m |
THREADS | 각 rank에서 동시에 실행할 I/O thread 수입니다. | 8 |
READ_ROUNDS | 전체 dataset을 읽는 횟수입니다. | 1 |
WR_ONLY | 1이면 write만 실행하고 이후 read-only 실행 을 위해 파일을 유지합니다. | 0 |
RD_ONLY | 1이면 일치하는 write-only 실행이 남긴 파일을 읽습니다. | 0 |
CLEANUP | 1이면 실행 후 benchmark 파일을 삭제합니다. Write-only 실행은 기본적으로 파일을 유지합니다. | 1, WR_ONLY=1이면 0 |
RSH_USER | Multi-host launch에 사용할 SSH login입니다. Passwordless SSH 및 sudo가 필요하며 root-to-root SSH에는 root를 사용합니다. | 호출한 사용자, 이미 root이면 root |
DAOS_AGENT_DRPC_DIR | daos_agent.sock이 있는 디렉터리입니다. | /run/boostx-agent 또는 활성 /tmp/boostx-agent-* 디렉터리 |
MPIRUN | MPI launcher override입니다. | 사용 가능하면 mpirun.mpich, 아니면 mpirun |
EXTRA_ARGS | --align=0 --iovcnt=4처럼 daos_adapter_bench에 그대로 전달할 추가 flag입니다. | 설정 안 함 |
DRYRUN | 1이면 실행하지 않고 생성된 command를 출력합니다. | 0 |
Write와 read 테스트를 분리하려면 write dataset을 유지하고 target, host list, rank 수, object 수, object 크기를 정확히 동일하게 사용합니다.
HOSTS=client01,client02 NPROCS=4 NR=128 SIZE=1g WR_ONLY=1 \
SUMMARY=/tmp/daos-write.json DFS_PATH=/mnt/mooncake-vol \
/opt/Mooncake/scripts/daos_adapter_bench.sh
HOSTS=client01,client02 NPROCS=4 NR=128 SIZE=1g RD_ONLY=1 \
SUMMARY=/tmp/daos-read.json DFS_PATH=/mnt/mooncake-vol \
/opt/Mooncake/scripts/daos_adapter_bench.sh
결과 해석
Console에는 rank별 line과 [AGG] line이 출력됩니다. Aggregate bandwidth는 성공한 전체 byte 수를 가장
느린 rank의 wall time으로 나누므로 빠른 rank가 client 간 불균형을 감추지 않습니다. JSON summary에는
실제 적용된 설정과 각 write/read round의 failed_ops, wall time, MiB/s, objects/s, p50/p95/p99/max
object latency 및 rank별 throughput이 기록됩니다.
Workload control은 의도적으로 IOR와 유사합니다. SIZE는 IOR block size에, XFER와 READ_XFER는
transfer size에 대응합니다. IOR에서 block은 한 client가 처리하는 연속 데이터이고 transfer는 한 번의 I/O
call로 전달하는 buffer입니다. IOR options와 MASS
IOR 가이드를 참고하세요. 결과를 비교할 때는 process 수와 data volume을 동일하게 맞추되, 이
benchmark는 Mooncake 형태의 object를 만들고 DaosAdapter를 직접 호출하므로 차이가 발생할 수 있습니다.
이 microbenchmark는 IO500을 대체하지 않습니다. IO500은 IOR bandwidth phase와 mdtest 및 parallel find phase를 결합하여 data와 metadata 동작을 모두 다룹니다. 시스템 수준의 인수 검사 또는 비교 테스트에는 MASS IO500 가이드를 사용하세요. 공식 IO500 제출에는 모든 필수 phase와 300초 write-phase stonewall 요구 사항이 그대로 적용됩니다. IO500 실행 문서와 제출 규칙을 참고하세요.
Storage KV
store_kv_bench.py는 Mooncake Store의 end-to-end 경로를 측정합니다. DAOS Adapter
microbenchmark와 달리 Store 할당 및 metadata 작업, 객체 배치, Transfer Engine 트래픽, 선택한
put 또는 get API를 모두 포함합니다. KV 작업을 검증하고 Mooncake 애플리케이션에서 관찰되는
처리량과 latency를 측정할 때 사용합니다.
스크립트는 다음 경로에 있습니다.
/opt/Mooncake/mooncake-store/benchmarks/store_kv_bench.py
Mooncake를 다른 위치에 설치했다면 경로를 조정하십시오. 실제 파일명은
storage_kv_bench.py가 아니라 store_kv_bench.py입니다. 설치한 Mooncake 버전에서 지원하는
옵션은 python3 store_kv_bench.py --help로 확인할 수 있습니다.
사전 요구 사항
- 이 페이지 앞부분의 MASS-Mooncake 통합을 완료하고 full
mass-client패키지와 DAOS 지원 Mooncake 빌드를 준비합니다. - 참여하는 모든 Mooncake 호스트에 같은 MASS 볼륨을 마운트합니다.
- offload와 metadata service가 활성화된 Mooncake master를 시작합니다.
- 각 벤치마크 프로세스에 고유하고 접근 가능한
--local-hostnameendpoint를 사용합니다. RDMA device는 자동으로 탐색됩니다.
예를 들어 MASS backend를 설정하고 embedded HTTP metadata service와 함께 master를 시작합니다.
export MOONCAKE_OFFLOAD_STORAGE_BACKEND_DESCRIPTOR=distributed
export MOONCAKE_DISTRIBUTED_FS_TYPE=daos
export MOONCAKE_DISTRIBUTED_ROOT_DIR=/mnt/mass/mooncake
mooncake_master \
--enable_offload=true \
--offload-backend distributed \
--distributed-fs-type daos \
--distributed-root-dir /mnt/mass/mooncake \
--enable_http_metadata_server=true \
--http_metadata_server_host=0.0.0.0 \
--http_metadata_server_port=8080
각 벤치마크 프로세스를 시작하는 shell에도 backend 환경 변수를 유지합니다.
설정 검증
먼저 작은 write-and-read 검증을 실행합니다. 예제의 주소를 클라이언트에서 접근 가능한 값으로 바꾸십시오.
python3 /opt/Mooncake/mooncake-store/benchmarks/store_kv_bench.py \
--scenario verify_write \
--local-hostname 10.0.0.21:50071 \
--metadata-server http://10.0.0.10:8080/metadata \
--master-server 10.0.0.10:50051 \
--protocol rdma \
--global-segment-size 1073741824 \
--local-buffer-size 268435456 \
--nr-objects 64 \
--value-size 1048576 \
--pattern 0xA5 \
--verify \
--summary-json /tmp/store-kv-verify.json
TCP만 사용해 검증하려면 --protocol tcp를 지정하고 TCP network에서 접근할 수 있는 주소를
사용합니다.
verify_write는 모든 객체를 쓴 다음 다시 읽습니 다. 프로세스가 status 0으로 종료되고 console에
실패한 KV나 verification failure가 없으며 summary JSON에 "ok": true가 있으면 성공입니다.
처리량 워크로드 실행
다음 예제는 동시 job 8개를 사용하여 16 MiB 객체 512개를 씁니다. Mooncake Store에 12 GiB memory segment를 제공하고 2 GiB local Transfer Engine buffer를 사용합니다.
python3 /opt/Mooncake/mooncake-store/benchmarks/store_kv_bench.py \
--scenario write_perf \
--local-hostname 10.0.0.21:50071 \
--metadata-server http://10.0.0.10:8080/metadata \
--master-server 10.0.0.10:50051 \
--protocol rdma \
--global-segment-size 12884901888 \
--local-buffer-size 2147483648 \
--io-api plain \
--numjobs 8 \
--iodepth 1 \
--batch-size 8 \
--nr-objects 512 \
--value-size 16777216 \
--pattern 0xA5 \
--summary-json /tmp/store-kv-write.json
read를 측정하려면 scenario를 read_perf로 바꾸고 --verify를 추가합니다. 기본적으로
read_perf는 prepare_write 단계에서 dataset을 만든 후 별도의 read_perf 단계에서 읽습니다.
python3 /opt/Mooncake/mooncake-store/benchmarks/store_kv_bench.py \
--scenario read_perf \
--local-hostname 10.0.0.21:50071 \
--metadata-server http://10.0.0.10:8080/metadata \
--master-server 10.0.0.10:50051 \
--protocol rdma \
--global-segment-size 12884901888 \
--local-buffer-size 2147483648 \
--numjobs 8 --iodepth 1 --batch-size 8 \
--nr-objects 512 --value-size 16777216 \
--pattern 0xA5 --verify \
--summary-json /tmp/store-kv-read.json
시간 기반 mixed workload에는 mixed_rw를 사용하고 0보다 큰 runtime과 read 비율을 지정합니다.
--prepare-objects는 최초 readable dataset 크기를 정하고 --write-objects는 write에 사용할 수 있는
object ID를 제한합니다.
python3 /opt/Mooncake/mooncake-store/benchmarks/store_kv_bench.py \
--scenario mixed_rw --runtime 60 --rwmixread 70 \
--prepare-objects 512 --write-objects 4096 \
--local-hostname 10.0.0.21:50071 \
--metadata-server http://10.0.0.10:8080/metadata \
--master-server 10.0.0.10:50051 \
--protocol rdma \
--global-segment-size 12884901888 \
--local-buffer-size 2147483648 \
--numjobs 8 --iodepth 1 --batch-size 8 \
--nr-objects 512 --value-size 16777216 \
--pattern 0xA5 --verify \
--summary-json /tmp/store-kv-mixed.json
설정
| 옵션 | 설명 | 기본값 |
|---|---|---|
--scenario | 검증, fill, read/write 성능, mixed I/O, metadata, 삭제 또는 replay 중 실행할 workload입니다. | 필수 |
--local-hostname | 이 Store client의 접근 가능한 주소입니다. 동시에 실행하는 각 프로세스에는 고유 endpoint가 필요합니다. | 127.0.0.1:50071 |
--metadata-server | Transfer Engine metadata service URL 또는 P2PHANDSHAKE입니다. | http://127.0.0.1:8080/metadata |
--master-server | Mooncake master 주소입니다. | 127.0.0.1:50051 |
--protocol, --device-name | 전송 protocol입니다. RDMA device는 자동으로 탐색되며 device 선택을 재정의할 때만 --device-name을 사용합니다. | tcp, 비어 있음 |
--global-segment-size | 이 프로세스가 global Store pool에 제공하는 memory 크기(byte)입니다. | 64 MiB |
--local-buffer-size | local Transfer Engine buffer용으로 예약하는 크기(byte)입니다. | 32 MiB |
--io-api | plain은 일반 Store API를, zcopy는 registered buffer를 사용합니다. | plain |
--numjobs, --iodepth | 동시성 설정입니다. worker lane 수는 numjobs × iodepth입니다. | 1, 1 |
--batch-size | 하나의 batch request로 제출하는 KV object 수입니다. | 1 |
--nr-objects | object-count 기반 단계에서 사용하는 object 수입니다. | 128 |
--runtime | 실행 시간(초)입니다. 0이면 object-count 기반으로 실행하며 mixed_rw에는 필수입니다. | 0 |
--value-size | value 크기(byte)입니다. write workload에서는 512 byte의 배수여야 합니다. | 4 KiB |
--pattern, --verify | 고정 payload를 재사용하고 필요하면 읽은 데이터를 검증합니다. --verify에는 --pattern이 필요합니다. | 비어 있음, 비활성화 |
--summary-json | phase 및 overall 결과를 저장하는 machine-readable 파일입니다. | 미설정 |
처리량을 비교할 때는 --pattern을 사용합니다. 지정하지 않으면 payload 생성이 timed path에 포함되어
Python 측 처리량을 제한할 수 있습니다. --io-api zcopy를 사용할 때는
value-size × batch-size × numjobs × iodepth가 --local-buffer-size를 넘지 않도록 하고, 설치된
Mooncake 빌드가 registered buffer allocation을 지원하는지 먼저 확인합니다.
공개 스크립트는 하나의 Store client process를 실행하며 여러 호스트에 rank를 시작하거나 결과를
집계하지 않습니다. 다중 호스트 테스트에서는 각 호스트에 하나 이상의 프로세스를 시작하고 모든
프로세스에 고유한 --local-hostname 주소나 port를 할당합니다. 서로 다른 --object-id-start 범위나
--key-prefix 값을 사용하여 key 충돌을 방지하고, 프로세스마다 별도의 summary 파일을 저장한 후 모든
프로세스가 끝나면 phase 결과를 집계합니다.
결과 해석
console과 summary JSON은 request와 KV 수, 성공한 byte, duration, requests/s, KVs/s, MiB/s,
p50/p95/p99 latency, miss, verification failure 및 error count를 보여 줍니다. read_perf와
mixed_rw에서는 overall에 dataset 준비 단계도 포함되므로 이름이 지정된 main phase를 비교합니다.
Store 처리량과 MASS persistence 처리량은 서로 다른 측정값입니다. 성공한 Store put은 MASS로의 비동기 offload가 drain되기 전에 반환될 수 있습니다. Mooncake의 offload 또는 master metric으로 queue가 비었는지 확인하고 persistence를 별도로 측정하십시오. 진행 상황을 확인하기 위해 생성된 모든 객체를 재귀적으로 나열하거나 stat하지 마십시오. 대규모 namespace scan은 workload를 왜곡하고 mounted filesystem에 불필요한 부하를 줄 수 있습니다. +
참고
- 전체 Mooncake Store 경로는 별도로 benchmark하세요. DAOS adapter throughput에는 scheduling, RPC, hashing, offload queue 및 cache hit/miss 동작이 포함되지 않습니다.
- Read-only 실행에는 일치하는 write-only 실행이 남긴 파일이 필요합니다. 두 단계 사이에서
DFS_PATH,DIR,HOSTS,NPROCS,NR,SIZE를 변경하지 마세요. - 이후 결과를 의미 있게 비교할 수 있도록 MASS client 및 Mooncake version, 참여 host, workload variable, client topology를 결과마다 기록하세요.