[Homelab] Ceph가 매시간 30~60초씩 응답을 멈춘 이유: 스냅샷 정리(snaptrim)와 mClock


홈랩 Ceph 클러스터가 매시 15분만 되면 30초에서 1분 동안 클라이언트 요청을 처리하지 못했습니다. CephFS 메타데이터 풀도, VM 디스크(RBD) 풀도 같이 막혔고요. 처음 눈치챈 건 Nextcloud가 그 시각마다 “not ready”를 띄워서였지만, Nextcloud는 피해자였을 뿐 진짜 문제는 그 아래 Ceph였습니다. 같은 시각에 VM들 iowait도 같이 튀었으니까요.

시계처럼 정확한 장애는 보통 누군가 cron으로 부르고 있다는 뜻입니다. ㅎㅎ 결론부터 말하면 범인은 CephFS 스냅샷 정리가 일으킨 snaptrim 폭주였고, 인터넷에서 흔히 보는 처방(osd_snap_trim_sleep)은 mClock 스케줄러에서는 아예 통하지 않았습니다. 그래서 통하는 손잡이를 찾아야 했습니다.

🧭 환경

  • Proxmox 노드 5대 위의 Ceph Squid 19.2.5, OSD 24~26개, 스케줄러는 기본값인 mClock
  • Nextcloud 사용자 데이터는 CephFS(cephfs-userdata-nvme, 데이터 풀 64 PG), VM 디스크는 RBD 풀
  • 그 트리를 매시 :15에 외부 cron이 스냅샷합니다. (fs snap-schedule이 아니라 직접 짠 스크립트)
  • 오래된 스냅샷은 같은 스크립트가 보존 기간에 맞춰 지웁니다

🔎 증거: ceph.log에 :15마다 찍히는 것들

/var/log/ceph/ceph.log를 :15 전후로 잘라 보면 매번 똑같은 게 찍혀 있었습니다.

SLOW_OPS ... slow requests
MDS_SLOW_METADATA_IO
slow requests on cephfs metadata pool, osdpool_rbd_diskimage_nvme

CephFS 메타데이터 풀뿐 아니라 VM 디스크 RBD 풀에서도 요청이 막혔습니다. VM iowait가 같이 튄 이유가 이겁니다. 순서는 이렇습니다.

  1. :15에 새 스냅샷을 만들고, 보존 기간이 지난 스냅샷을 지움
  2. 스냅샷 삭제는 즉시 끝나지만, 실제 오브젝트 정리는 OSD가 snaptrim으로 나중에 처리
  3. 64개 PG가 한꺼번에 snaptrim을 시작하면서 같은 OSD를 쓰는 클라이언트 I/O가 30~60초 밀림

🚫 흔한 처방이 안 먹는 이유

검색하면 거의 다 osd_snap_trim_sleep을 올리라고 합니다. 그런데 mClock에서는 이렇습니다.

$ ceph config show osd.11 | grep -E 'snap_trim_sleep|scrub_sleep'
osd_scrub_sleep             0.000000   override
osd_snap_trim_sleep         0.000000   override
osd_snap_trim_sleep_hdd     0.000000   override
osd_snap_trim_sleep_hybrid  0.000000   override
osd_snap_trim_sleep_ssd     0.000000   override

mClock은 자기가 직접 속도를 조절한다는 이유로 osd_snap_trim_sleep*과 osd_scrub_sleep을 0으로 강제합니다. 되살리려면 스케줄러를 wpq로 바꾸고 OSD를 전부 재시작해야 합니다.

그럼 mClock의 QoS 클래스로 snaptrim만 제한하면 되지 않을까요? 이것도 안 됩니다. mClock에서 backfill, scrub, snaptrim, PG 삭제가 전부 background_best_effort라는 한 클래스에 묶여 있습니다(recovery만 background_recovery로 따로). 이 클래스를 조이면 backfill과 scrub도 같이 느려집니다. 저는 노드를 정기적으로 재부팅해서 backfill이 자주 도는 클러스터라, 그쪽 성능은 건드리고 싶지 않았습니다.

🛠️ snaptrim에만 걸리는 손잡이 세 개

찾아보니 mClock과 상관없이 snaptrim에만 적용되는 설정이 있었습니다.

설정기본변경의미
osd_pg_max_concurrent_snap_trims21PG 하나가 동시에 트림하는 오브젝트 수
osd_max_trimming_pgs21OSD 하나에서 동시에 트림하는 PG 수
osd_snap_trim_cost1 MiB250 MiB (이후 500 MiB)mClock이 트림 1건에 매기는 비용

핵심은 세 번째입니다. mClock은 작업마다 “비용”을 매기고, 클래스 예산 안에서 비용만큼 시간을 배분합니다. snaptrim 1건을 1MiB가 아니라 250MiB짜리 작업으로 치게 하면, 같은 best_effort 예산 안에서 snaptrim만 수백 배 비싸져서 천천히 돕니다. backfill(osd_recovery_cost 20MiB)과 scrub(osd_scrub_cost 50MiB)은 자기 비용 그대로라 영향이 없습니다.

ceph config set osd osd_pg_max_concurrent_snap_trims 1
ceph config set osd osd_max_trimming_pgs 1
ceph config set osd osd_snap_trim_cost 262144000   # 250 MiB

ceph config에 들어가니 OSD 재시작 없이 바로 적용되고, 재부팅해도 유지됩니다.

📈 비용은 계단식으로 올렸다

한 번에 큰 값을 넣지 않고 50 → 150 → 250 MiB로 올리면서 매시 :15를 지켜봤습니다. 비용을 너무 높이면 트림이 한 시간 안에 안 끝나서 다음 스냅샷 정리와 겹치기 때문입니다. 특히 스냅샷을 한꺼번에 많이 지운 직후가 위험합니다.

변경 전변경 후 (250 MiB)
:15 이후클라이언트 I/O 30~60초 막힘트림이 60~165초 동안 천천히 진행
SLOW_OPS매시간0
MDS_SLOW_METADATA_IO매시간0
OSD 호스트 CPU/load—거의 변화 없음
Ceph 위 서비스 (Nextcloud “not ready”, VM iowait)매시간사라짐

트림이 걸리는 시간은 오히려 길어졌는데, 그게 정확히 원하던 겁니다. 같은 일을 더 길게 펴서 클라이언트가 못 느끼게 만든 거죠.

추가 (2026-10-14): 이후 비용을 500 MiB까지 올려서 운영하고 있습니다. 바꾼 뒤 일주일치 로그(10/7~10/14)를 다시 훑어봤는데 :15 언저리 SLOW_OPS는 한 건도 없었습니다. 그 기간에 딱 한 번 난 SLOW_OPS는 OSD가 내려간 별개 사건이었습니다.

↩️ 되돌리는 법

ceph config rm osd osd_pg_max_concurrent_snap_trims
ceph config rm osd osd_max_trimming_pgs
ceph config rm osd osd_snap_trim_cost

ceph config rm은 키를 한 번에 하나씩만 받습니다.

📝 정리

  • 시계처럼 정확한 장애는 cron부터 의심합니다. 이번엔 CephFS 스냅샷 보존 정리가 일으킨 snaptrim 폭주가 OSD를 30~60초씩 붙잡았습니다.
  • 앱(Nextcloud)이 먼저 아파 보여도 같은 시각 다른 풀(RBD)까지 막혔다면 원인은 스토리지 쪽입니다.
  • mClock에서는 osd_snap_trim_sleep이 0으로 강제됩니다. 흔한 처방이 안 먹는 이유입니다.
  • mClock은 snaptrim, backfill, scrub을 한 클래스로 묶어서, QoS 클래스로는 snaptrim만 따로 못 조입니다.
  • 대신 osd_snap_trim_cost를 올리면 같은 예산 안에서 snaptrim만 비싸집니다. 동시성 두 개(osd_pg_max_concurrent_snap_trims, osd_max_trimming_pgs)를 1로 같이 내리면 효과가 확실합니다.
  • 비용은 계단식으로 올리고, 트림이 다음 주기 전에 끝나는지 꼭 확인합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다