시리즈 소개
이 시리즈는 물리 노드 3개에 Proxmox 하이퍼바이저를 사용하여 CEPH 클러스터를 구축하는 전 과정을 다룹니다. 세 번째 글에서는 실제 운영 중 발생한 메타데이터 성능 문제를 진단하고 해결하는 과정을 공유합니다.
환경 및 문제 정의
클러스터 구성
- 노드 6개 (VM 기반, Proxmox)
- OSD 36개 (전부 HDD, 각 24TB)
- CPU 14코어, RAM 50GB, 10GbE 네트워크
- 총 용량 약 780TB, 3중 복제
- MDS 3개 (2 active + 1 standby)
- 사용자 약 10명, 홈디렉토리로 마운트 사용
데이터 현황
- 전체 파일 수: 약 1억 4,600만 개
- 메타데이터 사용량: 약 68GB
- Raw data 사용량: 약 422TiB
문제 증상
ls,conda activate,git,pip같은 작업에서 체감 지연 발생- 대용량 파일 읽기/쓰기는 정상이지만 작은 파일 작업이 느림
- fio 벤치마크는 순차 읽기 9.4Gbps, 쓰기 6.4Gbps로 양호
fio와 실제 성능 차이의 원인
fio는 파일 하나를 열고 블록 I/O만 반복하므로 MDS를 거의 거치지 않습니다. 반면 실제 작은 파일 작업은 파일마다 MDS 왕복이 발생합니다. fio는 "데이터 플레인"을 측정하지만, 실제 사용자 경험은 "메타데이터 플레인" 성능에 의존합니다.
진단 결과
MDS perf dump 분석
| 지표 | 측정값 | 정상 범위 | 상태 |
|---|---|---|---|
| jlat (journal latency) | 335ms | 1-10ms | 30배 이상 느림 |
| reply_latency | 31ms | - | 캐시 덕분에 양호 |
| readdir_latency | 104ms | 20-50ms | 느림 |
| lookup_latency | 49ms | - | - |
| mkdir_latency | 11ms | - | - |
| rmdir_latency | 94ms | - | - |
OSD 측 지표
- slow_ops 누적: 58,363건
- OSD commit_latency 평균: 150-400ms (HDD 한계)
- OSD 16 commit_latency: 1,903ms (디스크 문제 의심)
캐시 상태
- 캐시 히트율: 94% (traverse_hit 138M / traverse 147M)
- 읽기는 캐시로 커버되지만 쓰기(journal commit)는 HDD 병목으로 직행
근본 원인
메타데이터 풀이 HDD 기반이라 MDS journal commit이 335ms씩 소요됩니다.
시도 1: MDS 캐시 메모리 증가
변경 내용
ceph config set mds mds_cache_memory_limit 17179869184 # 4GB -> 16GB
결과
- 캐시 히트율 94% 달성
- reply_latency 31ms (캐시 히트 시 빠름)
- 쓰기 작업의 journal 병목은 여전히 335ms
평가
읽기 성능은 개선되었으나 쓰기 병목은 해결되지 않습니다.
시도 2: NVMe 메타데이터 풀 마이그레이션
목표
journal latency를 NVMe로 개선합니다.
구성 변경
- 메타데이터 풀 OSD: HDD 36개 → NVMe 1개 (osd.36)
- replica size: 3 → 1 (테스트용)
- PG 배치: 48개 (16 PG × 3 replica) → 16개 (16 PG × 1 replica)
CRUSH 설정
# NVMe 전용 CRUSH 구조 생성
ceph osd crush add-bucket nvme_root root
ceph osd crush add-bucket ceph3-2-nvme host
ceph osd crush move ceph3-2-nvme root=nvme_root
ceph osd crush set osd.36 0.46579 host=ceph3-2-nvme
ceph osd crush rule create-replicated nvme_rule nvme_root host
# 메타데이터 풀에 적용
ceph osd pool set cephfs_metadata min_size 1
ceph osd pool set cephfs_metadata size 1
ceph osd pool set cephfs_metadata crush_rule nvme_rule
Backfill 최적화
초기에 backfilling이 1개씩만 진행되는 문제가 발생합니다.
ceph config set osd osd_max_backfills 8
ceph config set osd osd_recovery_max_single_start 5
적용 후 동시 backfilling 8개로 증가하여 마이그레이션이 완료됩니다.
VM Passthrough 문제
NVMe가 HDD로 인식되는 문제가 발생합니다 (bluestore_bdev_rotational: 1).
echo 0 > /sys/block/sdh/queue/rotational
echo 0 > /sys/block/dm-7/queue/rotational
systemctl restart ceph-osd@36
적용 후 bluestore_bdev_type이 ssd로 정상 인식됩니다.
성능 측정 결과
용어 설명
- reply_latency: 클라이언트 요청부터 응답까지 전체 시간
- jlat (journal): 저널에 메타데이터를 쓰는 시간
- mkdir_latency: 디렉토리 생성 작업 시간
MDS perf dump 비교
| 지표 | 이전 (HDD) | 이후 (NVMe 1개) | 변화 |
|---|---|---|---|
| reply_latency | 31.3ms | 19.5ms | 37% 개선 |
| jlat (journal) | 335ms | 4,780ms | 14배 악화 |
| mkdir_latency | 11ms | 19.5ms | 악화 |
rados bench 결과 (cephfs_metadata 풀)
Write 테스트 (4MB 블록, 60초)
- Total writes: 189
- Bandwidth: 9.3 MB/sec
- Average IOPS: 2
- Average Latency: 5.74초
Read 테스트 (4MB 블록, rand, 16 threads)
- Total reads: 7,636
- Bandwidth: 442 MB/sec
- Average IOPS: 110
- Average Latency: 137ms
mdtest 중 iostat 분석 (osd.36 - NVMe OSD)
- w/s: 30,000~37,000 (쓰기 요청 폭주)
- w_await: 4-7ms (디스크 자체는 빠름)
- aqu-sz: 129~287 (정상: 1-10, 큐 대기열 폭증)
- %util: 92-98% (완전 포화)
핵심 발견: 읽기 중 쓰기 발생
현상
rados bench rand (읽기 전용) 실행 중 iostat를 확인한 결과, 읽기만 하는데 쓰기가 발생합니다.
- r/s: 22~4,418 (읽기)
- w/s: 150~450 (읽기만 하는데 쓰기 발생)
원인
- RocksDB compaction: 백그라운드에서 LSM-tree 정리 작업
- BlueStore cache writeback: 내부 버퍼 플러시
- OSD 내부 메타데이터 업데이트
결과
단일 OSD에서는 "순수 읽기"가 불가능한 구조입니다. 읽기/쓰기/compaction이 하나의 큐에서 경쟁하면서 블로킹이 발생합니다.
증거
- mdtest stat (MDS 캐시): 171,320 IOPS (OSD 안 거침, 빠름)
- rados bench rand (OSD 경유): 18 IOPS, 734ms (내부 쓰기와 경쟁, 느림)
결론
악화 원인 분석
| 구성 | PG 분산 | RocksDB 인스턴스 | OSD당 w/s |
|---|---|---|---|
| HDD 36개 | 48개 PG → 36개 OSD | 36개 | 약 1,000 |
| NVMe 1개 | 16개 PG → osd.36 | 1개 | 30,000+ |
단일 RocksDB 인스턴스의 write 처리량 한계, Write Amplification (LSM-tree compaction), 큐 대기열 폭증으로 인해 쓰기 성능이 악화됩니다.
핵심 발견
"느린 디스크 36개 > 빠른 디스크 1개"
병렬성(parallelism)이 단일 디스크 성능보다 더 중요합니다.
NVMe 개수와 성능 관계
| NVMe 개수 | RocksDB | 작업 큐 | 상태 |
|---|---|---|---|
| 1개 | 1개 | 1개 (경쟁) | 블로킹 발생 |
| 3개 이상 | 3개 | 3개 (분산) | 병렬 처리 가능 |
NVMe 3개 이상으로 분산하면 쓰기 병목이 해소되고 읽기도 정상화될 것으로 예상합니다.
캐시 미스와 NVMe 관계
- 캐시 미스 시 데이터를 가져오는 곳은 메타데이터 풀 OSD
- HDD면 느리고 (200ms), NVMe면 빠름 (10-20ms)
- NVMe 메타데이터 풀의 가치는 캐시 미스 시에도 빠른 응답
현재 상태
메타데이터 풀을 HDD 36개로 원복합니다.
ceph osd pool set cephfs_metadata crush_rule replicated_rule
ceph osd pool set cephfs_metadata size 3
ceph osd pool set cephfs_metadata min_size 2
journal latency 335ms로 복귀하며 사용자 체감은 기존과 동일합니다.
시도 3: NVMe WAL/RocksDB 마이그레이션
앞선 실험에서 NVMe 1개로 메타데이터 풀 전체를 마이그레이션했을 때 병렬성 부족으로 성능이 악화되었습니다. 이번에는 접근 방식을 변경하여 OSD의 WAL과 RocksDB만 NVMe로 분리합니다.
용어 설명
| 용어 | 설명 |
|---|---|
| WAL | Write-Ahead Log. 데이터를 디스크에 쓰기 전 먼저 기록하는 로그 (데이터 무결성 보장) |
| RocksDB | Ceph가 메타데이터(파일명, 권한 등)를 저장하는 키-값 데이터베이스 |
| RADOS | Ceph의 핵심 스토리지 계층. 파일시스템을 거치지 않고 직접 OSD와 통신 |
| BlueStore | Ceph의 스토리지 백엔드. WAL과 RocksDB를 사용하여 데이터 저장 |
핵심 결과 요약
NVMe WAL/RocksDB 마이그레이션으로 쓰기 집약적 워크로드에서 대폭적인 성능 향상을 달성합니다.
주요 성능 향상 (상위 5개)
| 순위 | 워크로드 | 개선율 | Before → After |
|---|---|---|---|
| 1 | 소규모 파일 생성 | 42배 | 67 → 2,823 files/s |
| 2 | 메타데이터 생성 | 23배 | 41 → 961 files/s |
| 3 | 4K 랜덤 쓰기 IOPS | 17배 | 85 → 1,410 IOPS |
| 4 | 메타데이터 삭제 | 10배 | 321 → 3,136 files/s |
| 5 | 순차 쓰기 대역폭 | 8배 | 122 → 957 MB/s |
NAS vs Ceph 비교 (동일 조건 테스트)
| 작업 | 쉘 명령어 | NAS | Ceph (HDD) | Ceph (NVMe) | 최고 성능 |
|---|---|---|---|---|---|
| 파일 생성 | touch |
378 ops/s | 41 ops/s | 961 ops/s | Ceph NVMe |
| 파일 조회 | stat, ls |
464 ops/s | 821 ops/s | 899 ops/s | Ceph NVMe |
| 파일 삭제 | rm |
521 ops/s | 321 ops/s | 3,136 ops/s | Ceph NVMe |
| 소규모 파일 생성 | dd (4KB×5000) |
990 files/s | 67 files/s | 2,823 files/s | Ceph NVMe |
| 순차 읽기 | cat |
525 files/s | 83 files/s | 1,201 files/s | Ceph NVMe |
| 랜덤 읽기 | cat (random) |
818 files/s | 469 files/s | 1,329 files/s | Ceph NVMe |
동일 조건 테스트에서 Ceph NVMe가 NAS를 1.6~6배 능가합니다. 최대 쓰기 레이턴시가 24초 → 0.5초로 53배 감소하여 레이턴시 스파이크 문제가 해소됩니다.
RADOS 벤치마크 (순수 OSD 성능)
순차 I/O 성능
| 테스트 | 지표 | Before | After | 개선율 |
|---|---|---|---|---|
| 순차 쓰기 | 대역폭 | 122 MB/s | 957 MB/s | 7.8배 |
| 평균 레이턴시 | 482 ms | 67 ms | 7.2배 단축 | |
| 최대 레이턴시 | 24,356 ms | 457 ms | 53배 감소 | |
| 순차 읽기 | 대역폭 | 830 MB/s | 1,211 MB/s | 1.5배 |
| 평균 레이턴시 | 76 ms | 52 ms | 31% 단축 |
랜덤 I/O 성능
| 테스트 | 지표 | Before | After | 개선율 |
|---|---|---|---|---|
| 4K 랜덤 쓰기 | IOPS | 85 | 1,410 | 16.6배 |
| 평균 레이턴시 | 187 ms | 11 ms | 16.5배 단축 | |
| 최대 레이턴시 | 8,106 ms | 435 ms | 18.6배 감소 | |
| 4M 랜덤 읽기 | 대역폭 | 998 MB/s | 1,279 MB/s | 1.3배 |
| 최대 레이턴시 | 7,246 ms | 714 ms | 10배 감소 |
4K 랜덤 쓰기는 WAL 성능의 핵심 지표입니다. 16.6배 향상은 NVMe WAL이 소규모 랜덤 쓰기를 HDD로 플러시하기 전에 즉시 커밋하기 때문입니다.
FIO 벤치마크 (CephFS 종단간 성능)
1MB 블록 순차 I/O
| 테스트 | 대역폭 | 평균 레이턴시 | p99 레이턴시 | 개선율 |
|---|---|---|---|---|
| 순차 쓰기 Before | 81 MB/s | 1,291 ms | 17,113 ms | |
| 순차 쓰기 After | 770 MB/s | 166 ms | 994 ms | 9.5배 |
| 순차 읽기 Before | 1,180 MB/s | 108 ms | 776 ms | |
| 순차 읽기 After | 1,622 MB/s | 79 ms | 633 ms | 1.4배 |
4K 블록 랜덤 I/O
| 테스트 | IOPS | 평균 레이턴시 | p99 레이턴시 | 개선율 |
|---|---|---|---|---|
| 랜덤 쓰기 Before | 2,053 | 209 ms | 1,636 ms | |
| 랜덤 쓰기 After | 13,105 | 39 ms | 190 ms | 6.4배 |
| 랜덤 읽기 Before | 3,209 | 159 ms | 4,865 ms | |
| 랜덤 읽기 After | 9,392 | 54 ms | 1,334 ms | 2.9배 |
혼합 워크로드 (64K, 70/30 R/W)
| 지표 | Before | After | 개선율 |
|---|---|---|---|
| 읽기 대역폭 | 45 MB/s | 233 MB/s | 5.2배 |
| 쓰기 대역폭 | 19 MB/s | 100 MB/s | 5.2배 |
| 쓰기 레이턴시 | 249 ms | 49 ms | 5.1배 단축 |
메타데이터 및 소규모 파일 성능
메타데이터 작업 (1,000개 파일)
| 작업 | Before | After | 개선율 |
|---|---|---|---|
| 파일 생성 | 41 files/s | 961 files/s | 23배 |
| 파일 삭제 | 321 files/s | 3,136 files/s | 10배 |
| Stat 조회 | 821 ops/s | 899 ops/s | 1.1배 |
소규모 파일 워크로드 (5,000 × 4KB 파일)
| 작업 | Before | After | 개선율 |
|---|---|---|---|
| 파일 생성 | 67 files/s | 2,823 files/s | 42배 |
| 순차 읽기 | 83 files/s | 1,201 files/s | 14.5배 |
| 랜덤 읽기 | 469 files/s | 1,329 files/s | 2.8배 |
컴포넌트별 영향 분석
| 컴포넌트 | 역할 | 핵심 개선 |
|---|---|---|
| NVMe WAL | 소규모 쓰기 버퍼링 | 4K 랜덤 쓰기 17배 향상 |
| NVMe RocksDB | 메타데이터 저장 | 파일 생성 23배 향상 |
| BlueFS on NVMe | 전체 쓰기 경로 | 순차 쓰기 8~10배 향상 |
| I/O 경합 감소 | 레이턴시 안정화 | 최대 레이턴시 36배 감소 |
테스트 환경
RADOS 벤치마크
├── 테스트 시간: 60초
├── 동시 스레드: 16개
└── 오브젝트 크기: 4MB (순차) / 4KB (랜덤)
FIO 벤치마크
├── 실행 시간: 120초
├── I/O 엔진: libaio (direct I/O)
├── 순차 테스트: 1MB 블록, 4 jobs, iodepth=32
├── 랜덤 테스트: 4KB 블록, 8 jobs, iodepth=64
└── 혼합 테스트: 64KB 블록, 70/30 R/W
메타데이터/소규모 파일 테스트 (NAS, Ceph 동일 조건)
├── 메타데이터: 1,000개 파일 (touch/stat/rm, 단일 프로세스)
└── 소규모 파일: 5,000 × 4KB (dd, 8 프로세스 병렬)
최종 결론
마이그레이션 효과
- WAL 성능 검증: 4K 랜덤 쓰기 17배 향상으로 마이그레이션 목표 달성
- 메타데이터 성능: 파일 생성/삭제 10~23배 향상
- 레이턴시 안정성: 예측 불가능한 레이턴시 스파이크 해소 (최대 레이턴시 53배 감소)
- 읽기 성능: 데이터가 HDD에 있어 제한적 향상 (1.4~2.9배)
- NAS 대비 성능: 동일 조건 테스트에서 Ceph NVMe가 NAS를 1.6~6배 능가
핵심 교훈
- 메타데이터 풀 전체를 NVMe 1개로 마이그레이션: 병렬성 부족으로 실패
- OSD의 WAL/RocksDB만 NVMe로 분리: 기존 병렬성 유지하며 성공
NVMe 도입 시 전체 풀을 옮기는 것보다 WAL/RocksDB만 분리하는 것이 효과적입니다. 이 방식은 HDD OSD의 병렬성을 유지하면서 쓰기 병목만 해소합니다.
마무리
이번 트러블슈팅을 통해 Ceph 메타데이터 성능에서 병렬성이 단일 디스크 성능보다 중요하다는 점을 확인합니다. NVMe 도입 시에는 메타데이터 풀 전체를 마이그레이션하기보다 OSD의 WAL/RocksDB만 NVMe로 분리하는 것이 효과적입니다. 이 방식으로 쓰기 성능 42배, 메타데이터 성능 23배 향상을 달성합니다.