CephCXIII · Multiple OSD FailureMultiple OSD Failure
Reading the shape of a correlated failure
What you'll learn
- Group simultaneous failures by CRUSH position
- Date failures from osdmap epochs
- Detect flapping before acting on it
- Recognise when automatic recovery will not start
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
Twenty OSDs down in one rack and twenty scattered across the cluster are opposite problems, and the health output presents them identically.
Grouping by position
ceph osd tree down
ceph osd tree --format json | python3 -c '
import sys,json,collections
t = json.load(sys.stdin)
node = {n["id"]: n for n in t["nodes"]}
parent = {}
for n in t["nodes"]:
for c in n.get("children", []):
parent[c] = n["id"]
def up(i, want):
while i in parent:
i = parent[i]
if node[i]["type"] == want:
return node[i]["name"]
return "-"
c = collections.Counter()
for n in t["nodes"]:
if n["type"] == "osd" and n.get("status") == "down":
c[(up(n["id"], "rack"), up(n["id"], "host"))] += 1
for (r, h), k in sorted(c.items()):
print("%-12s %-22s %3d down" % (r, h, k))'
| Shape | Reading |
|---|---|
| All within one host | a machine |
| All within one rack, several hosts | power, TOR switch, or PDU |
| Spread evenly across racks | a software or fleet-wide change |
| Two racks, nothing else | the case CRUSH was not designed for |
Dating it from the map
ceph osd dump | grep -E '^osd[.][0-9]+ ' | head -5
osd.23 down out weight 0 up_from 4412 up_thru 4478 down_at 4501 last_clean_interval [4380,4500)
ceph osd dump --format json | python3 -c '
import sys,json,collections
c = collections.Counter(o["down_at"] for o in json.load(sys.stdin)["osds"]
if o["up"] == 0)
for e, n in sorted(c.items()):
print("osdmap epoch %-8d %3d OSDs down" % (e, n))'
The map is a declaration, not a survey
ceph osd crush tree
ceph osd crush tree --show-shadow | head -20
| Mismatch | Consequence |
|---|---|
| Two CRUSH racks on one PDU | a single breaker crosses two domains |
| Two CRUSH racks on one TOR switch | a switch reload does the same |
| Host moved physically, CRUSH unchanged | replicas believed separated are adjacent |
| Default CRUSH location from hostname | a rack that exists only in the map |
Stabilising first
ceph osd set nodown
ceph log last 200 info cluster | grep -i 'osd[.]'
ceph osd unset nodown
`nodown` stops the monitors acting on failure reports while a fabric is
being repaired. It does not fix anything; it stops the map churning while
you work.
Quiz
Knowledge check · 4 questions
Q1. Why does counting OSDs by their `down_at` epoch identify a correlated failure?
Q2. OSDs in a failed rack are marked out automatically after the down-out interval, exactly as the OSDs of a failed host are.
Q3. Triage a multi-rack OSD failure.
Health reports 44 OSDs down. It is not yet known whether this is one event or several, and the cluster has been degraded for 25 minutes with no recovery activity.
Q4. What does the CRUSH map fail to tell you about a correlated failure?
Passing score: 75%. Answers are checked in this browser.
Production discipline
Group down OSDs by CRUSH position and by down_at epoch before touching
anything — the shape of the failure decides the response. Expect no
automatic recovery when a whole rack is down; the subtree limit suppresses
it until an operator marks the OSDs out.
Cross-course references
- Kubernetes: zone labels describe intent, and the scheduler cannot verify the physical topology
- Linux: two power supplies on one circuit are one power supply