CephXCII · Removing Storage NodesRemoving Storage Nodes
Verifying an evacuation is complete
What you'll learn
- Define what a completed evacuation looks like
- Verify it thoroughly
- Recognise a stalled evacuation
- Confirm before proceeding to removal
Prerequisites
None — start here.
Verified against Ceph Tentacle 20.2.x · Ceph Squid 19.2.x (supported previous) · cephadm matches the verified Ceph release · podman 4.x · csi-rbd and csi-cephfs current · RBD / CephFS / RGW current (matches Ceph release) · Linux kernel 5.15+ (5.10 minimum) · Ubuntu 24.04 LTS (Ceph host baseline) · Debian 12 (Bookworm) (Ceph host baseline) · Rocky Linux / RHEL / AlmaLinux 9.x (Ceph host baseline) · Proxmox VE 9.x (cross-course integration) · Kubernetes 1.31+ (cross-course integration) · 2026-08-18
Why this matters in production
Removing an OSD that still holds PGs the cluster needs is a data loss event. Verifying emptiness is what prevents it.
What complete means
An evacuated OSD:
holds zero PGs
reports zero used bytes beyond its overhead
appears in no acting set
the cluster is HEALTH_OK with no degraded or misplaced objects
All four, not just the first.
# PGs on the OSD
ID=12
ceph pg ls-by-osd ${ID} | wc -l
# its reported usage
ceph osd df | awk -v o=${ID} '$1==o {print $6, $7}'
# whether it appears in any acting set
ceph pg dump pgs --format json 2>/dev/null | python3 -c '
import sys, json
osd = ${ID}
d = json.load(sys.stdin)
n = sum(1 for pg in d.get("pg_stats", []) if osd in pg.get("acting", []) or osd in pg.get("up", []))
print("appears in", n, "PG sets")'
Verifying thoroughly
for id in $(ceph osd ls-tree ceph-07); do
printf 'osd.%-4s pgs=%-5s ' "$id" "$(ceph pg ls-by-osd $id 2>/dev/null | tail -n +2 | wc -l)"
ceph osd df | awk -v o="$id" '$1==o {printf "used=%s\n", $7}'
done
osd.84 pgs=0 used=1.1G
osd.85 pgs=0 used=1.1G
A small residual usage is BlueStore’s own overhead and is expected; the PG count reaching zero is the signal.
ceph -s
data:
pgs: 4096 active+clean
Every PG active+clean with no misplaced or degraded objects means the movement finished rather than stopped.
Recognising a stall
| Signal | Meaning |
|---|---|
| Misplaced count static | movement stopped |
| PGs remaining on the OSD, unchanged | those PGs cannot be placed |
backfill_toofull present | destinations are full |
ceph orch osd rm status shows no progress | the drain is stuck |
ID=12
ceph orch osd rm status
ceph health detail | grep -i backfill
ceph pg ls-by-osd ${ID} | head
# PGID: a PG id from the ceph pg ls-by-osd output above
PGID=3.1f
# why can a specific PG not move?
ceph pg "$PGID" query | python3 -c '
import sys,json; d=json.load(sys.stdin)
print("up:", d.get("up"), "acting:", d.get("acting"))
for s in d.get("recovery_state", [])[:1]:
print(s.get("name"), s.get("blocked_by"))'
Confirming before removal
The gate before removing anything:
every OSD on the node reports zero PGs
the cluster is HEALTH_OK
no misplaced or degraded objects remain
ceph orch osd rm status shows the drain complete
ceph -s | grep -E 'HEALTH_OK' && \
[ "$(for id in $(ceph osd ls-tree ceph-07); do
ceph pg ls-by-osd $id 2>/dev/null | tail -n +2 | wc -l; done | paste -sd+ | bc)" = "0" ] && \
echo "SAFE TO REMOVE" || echo "NOT COMPLETE"
Quiz
Knowledge check · 4 questions
Q1. A drained OSD reports zero PGs but still shows 1.1 GB used. What does this indicate?
Q2. Zero PGs on an OSD is sufficient confirmation that it can be removed.
Q3. Confirm an evacuation before removal.
A node drain has been running for six hours. Some OSDs report zero PGs and others still hold a few. The misplaced count has not changed in an hour.
Q4. What four conditions define a completed evacuation?
Passing score: 75%. Answers are checked in this browser.
Production discipline
Require zero PGs on every OSD and HEALTH_OK with no misplaced
objects before removing anything — the PG count alone can be zero while
the cluster is still settling. Ignore residual usage on a drained OSD;
BlueStore’s own structures never reach zero.
Cross-course references
- Kubernetes: a drained node with no pods is not the same as a settled cluster
- Linux: an evacuated array member must be confirmed unused before removal