상태: 초안 (2026-10-29), 공식 문서와 논문 기준, 실측 없음. 설명은 Ceph v20.2(Tentacle) 공식 문서 (docs.ceph.com/en/tentacle)와 Ceph 원 논문을 열어 확인한 것만 적었습니다. 기본값은 모두 그 문서에 적힌 값입니다. TT Lab 실습 클러스터에서
ceph status같은 출력으로 확인하는 일은 이 시리즈의 4편(원자료가 모이면 씁니다)에서 합니다. 작성 규칙: 실측 데이터가 기본이고, 못 잰 것은 「미실측」, 문서에서 찾지 못한 것은 「미확인」으로 표시합니다.
목차
- 1. 한 문장으로: 데이터 위치를 적은 중앙 목록이 없다
- 2. RADOS 를 이루는 데몬 넷
- 3. 객체가 OSD 를 찾아가는 두 번의 계산: 풀, PG, CRUSH
- 4. 복제와 이레이저 코딩
- 5. 쓰기 경로: 클라이언트는 왜 OSD 와 직접 이야기하는가
- 6. 그 위의 세 얼굴: RBD, CephFS, RGW
- 7. 정리: 다음 편에서 볼 것
- 참고
1. 한 문장으로: 데이터 위치를 적은 중앙 목록이 없다
쿠버네티스에서 파드가 디스크를 달라고 하면(PVC) 누군가 실제 디스크 공간을 내주어야 합니다. 노드 하나의 디스크를 쓰면 그 노드가 죽을 때 데이터도 함께 묶여 버립니다. 여러 노드의 디스크를 묶어 하나의 저장소처럼 보이게 하고, 데이터를 여러 벌 두어 노드 하나가 죽어도 계속 읽고 쓸 수 있게 하는 것이 분산 스토리지입니다. Ceph 는 그 대표적인 오픈소스이고, Rook 은 Ceph 를 쿠버네티스 위에서 돌려 주는 operator 입니다. 이 시리즈는 Ceph 의 구조(1편), Rook operator 가 하는 일(2편), Helm 차트의 옵션(3편)을 차례로 읽습니다.
많은 저장 시스템은 「파일 X 의 조각은 서버 3번 디스크의 어디에 있다」 는 목록(메타데이터 서버, 할당 표)을 한곳에 둡니다. 읽고 쓸 때마다 그 목록에 물어야 하므로 목록이 병목이자 단일 장애점이 됩니다. Ceph 를 처음 소개한 논문 (Weil 등, OSDI 2006 「Ceph: A Scalable, High-Performance Distributed File System」)은 이 할당 표를 CRUSH 라는 의사 난수 분배 함수로 바꾼다고 요약합니다. 데이터가 어디 있는지는 적어 두지 않고 계산합니다. 클라이언트와 저장 데몬이 같은 지도와 같은 함수를 가지고 있으면, 누구나 같은 답을 얻습니다.
그림의 바닥이 RADOS(Reliable Autonomic Distributed Object Store, 신뢰성 있는 자율 분산 객체 저장소)입니다. Ceph 가 받는 데이터는 블록이든 파일이든 S3 객체든 결국 RADOS 객체(이름, 바이트, 이름·값 메타데이터의 묶음)가 되어 저장됩니다. 객체는 디렉터리 없이 평평한 이름 공간에 놓입니다. 그 위의 RBD·CephFS·RGW 는 RADOS 를 블록 장치, 파일시스템, S3 로 보이게 해 주는 번역층이고, 이 셋이 RADOS 에 말을 거는 공통 라이브러리가 librados 입니다 (공식 문서: Architecture).
2. RADOS 를 이루는 데몬 넷
Ceph 클러스터는 역할이 다른 데몬(백그라운드 프로세스) 네 종류로 이루어집니다. Rook 에서는 데몬 하나가 보통 파드 하나입니다.
| 데몬 | 하는 일 | 몇 개 | Rook 의 파드 이름 |
|---|---|---|---|
| MON(모니터) | 클러스터 맵의 원본을 지키고 클라이언트에게 나눠 준다. 인증(CephX)도 맡는다 | 보통 3, 홀수 권장 | rook-ceph-mon-a 등 |
| MGR(매니저) | 모니터링·오케스트레이션·플러그인 모듈의 끝점. 대시보드와 지표가 여기서 나온다 | 1(대기 1을 더하면 2) | rook-ceph-mgr-a 등 |
| OSD(Object Storage Daemon) | 디스크에 객체를 저장하고, 복제·회복·스크럽을 직접 한다 | 보통 디스크 하나에 하나 | rook-ceph-osd-0 등 |
| MDS(메타데이터 서버) | CephFS 의 디렉터리·소유자·권한 같은 파일 메타데이터를 관리한다 | CephFS 를 쓸 때만 | 파일시스템마다 활성·대기 한 쌍 이상 |
MON: 클러스터 맵과 Paxos 쿼럼
클라이언트가 Ceph 에서 무언가를 읽거나 쓰려면 먼저 MON 에 접속해 최신 클러스터 맵을 받아야 합니다. 공식 문서는 클러스터 맵이 사실 다섯 개의 맵 묶음이라고 설명합니다.
| 맵 | 담긴 것 | 보는 명령 |
|---|---|---|
| Monitor 맵 | 클러스터 fsid, 각 MON 의 이름·주소·포트 | ceph mon dump |
| OSD 맵 | 풀 목록, 복제 수, PG 수, 각 OSD 의 상태(up·down, in·out) | ceph osd dump |
| PG 맵 | PG 마다의 상태(예: active+clean), Up Set·Acting Set, 사용량 | (문서에 예가 없음) |
| CRUSH 맵 | 저장 장치 목록, 장애 단위 계층(장치·호스트·랙…), 배치 규칙 | ceph osd getcrushmap 후 crushtool -d |
| MDS 맵 | CephFS 메타데이터 풀, MDS 목록과 상태 | ceph fs dump |
맵마다 바뀔 때마다 올라가는 판 번호(epoch)가 있습니다. MON 이 하나뿐이면 그 MON 이 죽는 순간 아무도 맵을 받지 못하므로, 보통 여러 대를 둡니다. 여러 MON 이 서로 다른 맵을 내놓으면 안 되기 때문에 Ceph 는 Paxos 라는 합의 알고리즘으로 맵의 변경을 결정하고, 과반이 살아 있어야 결정을 내립니다. 문서의 예로 MON 이 1대면 1, 3대면 2, 5대면 3, 6대면 4가 과반입니다. 그래서 3대는 1대, 5대는 2대가 죽어도 버티고, 4대는 3대보다 견디는 수가 늘지 않습니다. 홀수를 권하는 이유입니다 (Monitor Config Reference). MON 은 맵의 변경을 하나의 Paxos 인스턴스로 기록하고, 그 결과를 RocksDB 키·값 저장소에 씁니다.
OSD 와 BlueStore
OSD 는 디스크 하나를 맡아 그 위에 객체를 저장하는 데몬입니다. 지금의 기본 저장 엔진인 BlueStore 는 디스크에 ext4 나 XFS 같은 파일시스템을 만들지 않고 장치에 직접(raw) 씁니다. 객체의 메타데이터는 내장된 RocksDB 에 둡니다. 장치는 최대 셋으로 나눌 수 있습니다(BlueStore Configuration Reference).
block: 데이터를 담는 주 장치. 가장 단순한 배치는 이것 하나만 쓰는 것입니다.block.db: RocksDB 메타데이터를 둘 더 빠른 장치(예: HDD 옆의 SSD). 가득 차면 주 장치로 넘칩니다.block.wal: 쓰기 선행 기록(write-ahead log)만 따로 둘 장치. 빠른 공간이 1GB 도 안 될 때만 의미가 있습니다.
BlueStore 는 쓰는 모든 데이터와 메타데이터에 체크섬을 붙이고(기본 crc32c), 읽을 때 검사합니다. 메모리는 캐시가 스스로
조절하며 osd_memory_target(기본 4Gi, 최솟값 896M)을 넘지 않도록 애씁니다. 이 값은 2편과 3편에서 Rook 의 자원 설정과 다시 만납니다.
OSD 는 이웃 OSD 와 서로 heartbeat 를 주고받습니다. 기본으로 6초마다 핑을 보내고(osd_heartbeat_interval), 20초 동안 소식이
없으면(osd_heartbeat_grace) 그 OSD 가 내려갔다고 MON 에 알립니다. MON 은 내려간(down) OSD 가 10분 동안
(mon_osd_down_out_interval) 돌아오지 않으면 그 OSD 를 데이터 배치에서 뺍니다(out). 그때부터 그 OSD 의 몫을 다른 OSD 가 나눠
받는 재배치가 시작됩니다. 다만 mon_osd_down_out_subtree_limit(기본 rack)보다 큰 단위가 통째로 내려가면 자동으로 빼지
않습니다. 랙 하나가 정전된 것을 데이터 대이동으로 받아치지 않기 위해서입니다
(Configuring Monitor/OSD Interaction).
OSD 는 또 스크럽을 합니다. 같은 PG 의 사본끼리 메타데이터를 비교하는 가벼운 스크럽은 보통 매일, 데이터를 비트 단위로 비교하는 깊은 스크럽은 기본으로 매주 돕니다. 가벼운 스크럽이 못 잡는 디스크의 나쁜 블록을 깊은 스크럽이 찾습니다.
MGR 과 MDS
MGR 은 MON 이 하기에는 무거운 일(지표 수집, 대시보드, 각종 모듈)을 맡는 끝점입니다. MDS 는 CephFS 를 쓸 때만 필요합니다. 디렉터리를
나열하거나 옮겨 다니는(ls, cd) 같은 메타데이터 작업이 OSD 를 괴롭히지 않도록 메타데이터를 메모리에 들고 따로 처리합니다.
파일의 데이터 자체는 MDS 를 거치지 않고 RADOS 객체로 바로 갑니다. 이 둘은 7절에서 다시 봅니다.
3. 객체가 OSD 를 찾아가는 두 번의 계산: 풀, PG, CRUSH
풀
풀(pool)은 객체를 담는 논리적인 구획입니다. 풀마다 적어도 세 가지가 정해집니다. 사본을 몇 벌 둘지(size), 어떤 CRUSH 규칙으로
놓을지, 그리고 PG 를 몇 개로 나눌지(pg_num)입니다. 블록 저장소용 풀, 파일시스템 메타데이터용 풀, S3 데이터용 풀처럼 용도마다
따로 만듭니다.
첫 번째 계산: 객체 이름에서 PG 로
PG(placement group, 배치 그룹)는 풀을 잘게 나눈 묶음입니다. Ceph 는 객체 하나하나가 아니라 PG 단위로 위치를 정하고 옮깁니다.
문서의 예를 그대로 쓰면, liverpool 풀(풀 번호 4)에 john 이라는 객체를 쓸 때 클라이언트는 이렇게 계산합니다.
- 객체 이름
john을 해시합니다. - 해시값을 풀의 PG 수로 나눈 나머지를 PG 번호로 씁니다(예: 58).
- 풀 번호를 앞에 붙여 PG ID 를 만듭니다:
4.58.
두 번째 계산: PG 에서 OSD 목록으로
다음으로 CRUSH(Controlled Replication Under Scalable Hashing) 알고리즘이 PG ID 와 CRUSH 맵과 풀의 규칙을 받아 「이 PG 를 담을 OSD 목록」 을 돌려줍니다. 목록의 첫 OSD 가 primary, 나머지가 secondary 입니다 (Architecture: Calculating PG IDs, Peering and Sets).
CRUSH 맵에는 저장 장치(OSD) 목록과, 그 장치를 묶는 버킷의 계층이 들어 있습니다. 기본 버킷 종류는 아래에서 위로
osd, host, chassis, rack, row, pdu, pod, room, datacenter, zone, region, root 입니다. OSD 가 새로 배포되면
그 노드 이름의 host 버킷 아래에 자동으로 들어갑니다. 풀의 규칙에는 failure domain(장애 단위)이 적혀 있어서, 이를
host 로 두면 CRUSH 는 사본마다 다른 호스트를 고릅니다. 호스트 하나가 통째로 죽어도 같은 PG 의 다른 사본은 다른 호스트에
남는다는 뜻입니다. 규모가 큰 곳에서는 장애 단위를 rack 으로 올리기도 합니다
(CRUSH Maps).
계산을 두 번으로 나누는 까닭은 간접 층입니다. 클라이언트가 「객체 X 는 OSD 3번에 있다」 를 직접 안다면 OSD 를 더하거나 뺄 때마다 모든 객체의 위치를 다시 알려야 합니다. 객체는 PG 에 고정되고 PG 만 OSD 사이를 옮겨 다니게 하면, 클러스터가 커지거나 줄 때 움직이는 단위가 객체 수십억 개가 아니라 PG 수천 개가 됩니다. CRUSH 자체는 Weil 등의 SC 2006 논문 「CRUSH: Controlled, Scalable, Decentralized Placement of Replicated Data」에서 처음 소개되었습니다.
PG 를 담는 OSD 들이 「이 PG 의 객체 상태가 모두 같다」 고 합의하는 과정을 peering 이라 합니다. 현재 그 PG 를 책임지는 OSD 묶음이 Acting Set, 그중 살아 있는 것이 Up Set 입니다. primary 가 죽으면 다음 OSD 가 primary 를 이어받습니다.
PG 는 몇 개가 좋은가: PG 오토스케일러
PG 가 너무 적으면 데이터가 OSD 사이에 고르게 퍼지지 않고, 너무 많으면 peering 트래픽과 메모리가 늘어납니다. 예전에는 사람이 계산해
pg_num 을 정했지만, 지금은 PG 오토스케일러가 풀마다 사용량을 보고 정합니다
(Placement Groups).
| 설정 | 기본값 | 뜻 |
|---|---|---|
osd_pool_default_pg_autoscale_mode |
on |
새 풀의 오토스케일 모드. on 이면 pg_num 을 정하지 않은 새 풀은 PG 1개로 시작한다 |
mon_target_pg_per_osd |
100 | OSD 하나가 맡을 PG 사본 수의 목표. 문서는 아주 작은 클러스터가 아니면 200 을 권한다 |
| 조정 임계 | 3배 | 권장값과 지금 값이 3배 넘게 차이 날 때만 바꾼다(늘릴 때는 권장값이 512·1024 면 1.0, 2048 이면 2.0 으로 낮춘다) |
bulk 풀 표시 |
꺼짐 | 켜면 「큰 풀」 로 보고 처음부터 PG 를 많이 준다 |
ceph osd pool autoscale-status 가 풀마다의 권장값(NEW PG_NUM)을 보여 줍니다. 3편에서 볼 Rook 차트의 오브젝트 스토어 데이터 풀은
bulk: "true" 를 켜 둡니다.
4. 복제와 이레이저 코딩
데이터를 지키는 방법은 두 가지입니다.
복제(replication)는 같은 객체를 통째로 여러 벌 둡니다. osd_pool_default_size 의 기본은 3입니다. min_size 는 「사본이
최소 몇 벌 쓰여야 클라이언트에게 성공을 알릴지」 이고, 기본 0은 size - (size / 2) 를 뜻합니다(정수 나눗셈이라 size 3 이면 2).
그래서 size 3 풀은 사본 하나를 잃어도 계속 읽고 쓰지만, 둘을 잃으면 그 PG 는 I/O 를 멈춥니다
(Pool, PG and CRUSH Config Reference).
이레이저 코딩(erasure coding, EC)은 객체를 K 개의 데이터 조각으로 자르고 M 개의 코딩 조각을 계산해 K+M 개를 서로 다른 OSD 에 둡니다. K 개만 남으면 원본을 되살릴 수 있습니다. 공간 비용은 (K+M)/K 입니다 (Erasure Code).
| 방식 | 견디는 동시 장애 | 원 데이터 1 GiB 에 드는 공간 | 공식 문서의 평가 |
|---|---|---|---|
| 복제 size 3 | 2 | 3.0 GiB | 기본값 |
| EC K=2, M=1 | 1 | 1.5 GiB | 가장 단순한 EC. M=1 은 운영 데이터에 강하게 비권장 |
| EC K=2, M=2 | 2 | 2.0 GiB | EC 풀의 기본 프로필 |
| EC K=4, M=2 | 2 | 1.5 GiB | 프로필을 고르기 어렵다면 이것을 고르라고 권함 |
문서는 EC 를 공짜 점심으로 착각하지 말라고 적습니다. 특히 HDD 와 회복·backfill 중에 성능 손해가 큽니다. M=1 이면 정비 중에
데이터를 못 쓰게 되고, 장애가 겹치면 데이터를 잃는다고 경고합니다. 또 EC 풀은 omap(객체에 붙는 키·값 데이터)을 지원하지 않으므로
RBD 와 CephFS 는 메타데이터를 복제 풀에 두고 데이터만 EC 풀에 두어야 하며, EC 풀에 allow_ec_overwrites 를 켜야 합니다.
EC 풀의 min_size 는 K+1 이상을 권합니다. 프로필은 풀을 만든 뒤 바꿀 수 없어서, 바꾸려면 새 풀을 만들어 데이터를 옮겨야 합니다.
한 가지 더, 장애 단위의 개수가 사본 수보다 적으면 CRUSH 가 규칙을 채울 수 없습니다. 장애 단위가 host 이고 호스트가 셋이면 복제
size 3 은 호스트마다 한 벌씩 놓이고, K+M=3 인 EC 2+1 도 딱 들어맞습니다. 그 대신 호스트 하나가 죽으면 사본을 옮겨 둘 네 번째 호스트가
없으므로, 그 호스트가 돌아올 때까지 PG 는 사본이 모자란(degraded) 채로 남습니다.
5. 쓰기 경로: 클라이언트는 왜 OSD 와 직접 이야기하는가
복제 풀에 객체를 쓰는 길은 공식 문서에 이렇게 적혀 있습니다.
- 클라이언트가 MON 에 접속해 CephX 로 인증하고 최신 클러스터 맵을 받습니다.
- 클라이언트가 3절의 두 계산으로 PG 와 OSD 목록을 스스로 구합니다. 이 계산에는 아무와도 통신하지 않습니다.
- 클라이언트가 PG 의 primary OSD 에 객체를 보냅니다.
- primary 가 자기 CRUSH 맵 사본으로 secondary OSD 를 찾아 객체를 복제합니다(문서는 이 복제를 subop 이라 부릅니다).
- secondary 들이 저장을 마쳤다고 primary 에 확인을 보냅니다.
- primary 가 클라이언트에게 성공을 알립니다.
복제를 클라이언트가 아니라 primary 가 하는 덕분에 클라이언트의 네트워크는 데이터를 한 번만 보냅니다. 그리고 데이터가 중앙의 게이트웨이나 메타데이터 서버를 거치지 않으므로, 중앙 구성 요소가 병목이나 단일 장애점이 되지 않습니다. MON 은 맵을 나눠 줄 뿐 데이터 경로에 서지 않습니다. 이것이 1절에서 말한 「목록 대신 계산」 의 실제 효과입니다.
EC 풀에서는 primary 가 객체를 받아 조각을 계산하고, 조각을 Acting Set 의 각 OSD 에 나눠 씁니다. 모든 조각이 확인되면 클라이언트에 성공을 알립니다.
맵은 바뀝니다. OSD 가 내려가거나 새로 들어오면 OSD 맵의 epoch 가 올라가고, 같은 PG 라도 CRUSH 계산의 결과가 달라집니다. 문서는 객체 위치를 서버에 물어보는 대화보다 클라이언트가 직접 계산하는 쪽이 훨씬 빠르다고 적고, OSD 의 장애도 대개 이웃 OSD 가 먼저 알아채 MON 에 보고하므로 MON 이 가벼운 프로세스로 남는다고 설명합니다. 낡은 맵을 받은 클라이언트와 OSD 가 정확히 어떤 메시지로 맵을 맞추는지는 이 글에서 다루지 않았습니다(미확인).
6. 그 위의 세 얼굴: RBD, CephFS, RGW
| 이름 | 무엇으로 보이나 | 핵심 동작 | 쿠버네티스에서 |
|---|---|---|---|
| RBD(RADOS Block Device) | 블록 장치(디스크) | 디스크 이미지를 여러 RADOS 객체로 줄 세워 나눠(striping) 저장한다. 씬 프로비저닝·스냅샷 지원 | 한 노드가 붙이는 RWO 볼륨, VM 디스크 |
| CephFS | POSIX 파일시스템 | 메타데이터는 MDS, 파일 데이터는 RADOS 객체. 커널 클라이언트나 FUSE 로 마운트 | 여러 파드가 함께 쓰는 RWX 볼륨 |
| RGW(RADOS Gateway) | HTTP 오브젝트 저장소 | S3·Swift 호환 REST API. 자체 사용자 DB·인증·접근 제어가 있다 | 버킷 요청(ObjectBucketClaim), S3 클라이언트 |
몇 가지는 짚어 둘 만합니다.
- RBD 의 이미지는 객체 여럿입니다. 디스크 하나의 블록들이 여러 객체에 나뉘고, 객체마다 다른 PG 와 OSD 로 가므로 큰 디스크의 I/O 가 여러 OSD 에 퍼집니다. 문서는 이 줄 세우기 덕분에 RBD 가 서버 한 대보다 빠를 수 있다고 적습니다.
- CephFS 의 MDS 는 대기조를 둡니다. 여분의 MDS 는 standby 로 기다리다가 활성 MDS 가 죽으면 MON 의 판단으로 넘겨받습니다. 저널을 포함한 모든 데이터가 RADOS 에 있으므로 넘겨받기가 쉽습니다. 활성 MDS 를 여럿 두면 디렉터리 트리를 나눠 맡습니다.
- S3 객체와 RADOS 객체는 1:1 이 아닙니다. S3 객체 하나가 RADOS 객체 여럿으로 저장될 수 있습니다. EC 풀은 기본으로 객체를 통째로 쓰거나 뒤에 덧붙이는 작업만 받는데, 문서는 그 예로 RGW 를 듭니다. 그래서 RGW 의 큰 데이터는 EC 풀과 잘 맞고, 버킷 인덱스처럼 omap 을 많이 쓰는 데이터(PG 문서가 「RGW index」 를 그 예로 듭니다)는 복제 풀에 둡니다.
7. 정리: 다음 편에서 볼 것
| 개념 | 한 줄 |
|---|---|
| RADOS | 블록·파일·S3 가 모두 내려앉는 객체 저장소 |
| MON | 클러스터 맵 원본, Paxos 과반으로 결정. 3대면 1대까지 잃어도 된다 |
| OSD + BlueStore | 디스크 하나에 데몬 하나, 파일시스템 없이 장치에 직접, 체크섬 |
| 풀 → PG → OSD | 해시로 PG, CRUSH 로 OSD 목록. 목록의 첫째가 primary |
| failure domain | 사본을 서로 다른 무엇(호스트·랙)에 둘지 |
| 복제 vs EC | 3.0배 공간으로 2개 장애, 또는 (K+M)/K 배 공간으로 M 개 장애 |
| 쓰기 경로 | 클라이언트 → primary → secondary → primary → 클라이언트. MON 은 데이터 경로 밖 |
여기까지는 Ceph 자체의 이야기입니다. 이 데몬들을 쿠버네티스 위에서 누가 띄우고, 디스크를 누가 고르고, MON 이 죽으면 누가 새로
세우는지가 2편의 주제입니다. 그리고 그 일을 어떤 값으로 조정하는지는 3편에서 차트의
values.yaml 을 한 줄씩 읽으며 봅니다.
티티 한마디. Ceph 에는 「이 파일은 어디 있어요?」 하고 물어볼 안내 데스크가 없어요. 대신 모두가 같은 지도(클러스터 맵)와 같은 계산법(CRUSH)을 들고 다니죠. 그래서 안내 데스크가 막힐 일도, 쓰러질 일도 없어요. 지도만 최신이면 돼요!
참고
문서는 모두 2026-10-29 에 열람했습니다. Ceph 문서는 Tentacle(v20) 판입니다.
- Ceph Architecture: https://docs.ceph.com/en/tentacle/architecture/
- Monitor Config Reference: https://docs.ceph.com/en/tentacle/rados/configuration/mon-config-ref/
- Configuring Monitor/OSD Interaction: https://docs.ceph.com/en/tentacle/rados/configuration/mon-osd-interaction/
- BlueStore Configuration Reference: https://docs.ceph.com/en/tentacle/rados/configuration/bluestore-config-ref/
- Pool, PG and CRUSH Config Reference: https://docs.ceph.com/en/tentacle/rados/configuration/pool-pg-config-ref/
- Placement Groups: https://docs.ceph.com/en/tentacle/rados/operations/placement-groups/
- CRUSH Maps: https://docs.ceph.com/en/tentacle/rados/operations/crush-map/
- Erasure Code: https://docs.ceph.com/en/tentacle/rados/operations/erasure-code/
- Weil, Brandt, Miller, Long, Maltzahn. 「Ceph: A Scalable, High-Performance Distributed File System」, OSDI 2006: https://www.usenix.org/legacy/event/osdi06/tech/weil.html
- Weil, Brandt, Miller, Maltzahn. 「CRUSH: Controlled, Scalable, Decentralized Placement of Replicated Data」, SC 2006: https://ceph.io/assets/pdfs/weil-crush-sc06.pdf