❯ monitoring · Running

Le disque qui chauffait chaque nuit : un cycle de firmware de SSD pris dans un seuil de disque dur The Disk That Ran Hot Every Night: An SSD Firmware Cycle Caught by a Hard-Drive Threshold

Une alerte de température disque sonnait presque chaque nuit sur un worker Raspberry Pi, sans charge, à une heure qui dérivait d'environ deux minutes par jour. En cause : une règle écrite pour des disques à plateaux, appliquée à tous les disques SATA depuis que le dernier HDD a quitté le cluster. Séparer les règles sur la vitesse de rotation rapportée par smartctl a fait taire la nuit sans rendre aveugles les disques du NAS. A disk temperature alert fired almost every night on a Raspberry Pi worker, with no load and at a time that drifted by about two minutes a day. The culprit was a rule written for spinning disks, applied to every SATA device after the last hard drive left the cluster. Splitting the rules on smartctl's rotation rate ended the nightly noise without blinding the NAS drives.

· 7 min de lecture 7 min read ·
monitoringvmalertpromqlsmartssdincident
🇫🇷 Lire cet article en français

Context: the Raspberry Pis’ SMART exporter was not reading any disk until the Grafana dashboard review, and the cluster’s last hard drive was replaced by an SSD at the end of September. Both changes were good news. Together, they produced one alert per night.

The symptom: 52 °C at two in the morning

The alert is called DiskTempHighHDD. It says a disk has been above 50 °C for 15 minutes. It fires at night, almost every night, and always on the same node: a Raspberry Pi worker whose system disk is a SanDisk SSD PLUS attached over USB.

Each alert also triggers the automatic investigation script, which collects context and writes a report. So the report became a daily one too. And every time it said the same thing: nothing is happening on this node.

The numbers, from the metrics database history:

Observation
Peak temperature52 to 53 °C
Peak durationabout 20 minutes
Host I/O during the peaknone
Scheduled job at that timenone
Peak time on October 1102:15 UTC
Peak time on October 2501:45 UTC

A disk that heats up without doing any work is already odd. A disk that heats up without doing any work at a time that keeps moving earlier is a clue.

The investigation: a drifting time

The first naive hypothesis is a scheduled job: a weekly fstrim, a backup, a compaction. It does not hold. A cron job or a systemd timer fires at a fixed wall-clock time, and here the time moves back 30 minutes in 14 days, about 2 minutes per day. Nothing on the host runs on a 23 h 58 period.

The second hypothesis is load. It does not hold either: the node’s I/O metrics are flat during the 20-minute peak. The disk heats up without the system asking it for anything.

What remains is what happens inside the disk. An SSD has a life of its own: garbage collection of freed blocks, wear levelling, background scans of its cells. These are triggered by the firmware, on its own clock, which knows nothing about NTP or time zones. An internal period that is not exactly 24 hours produces exactly what we see: a regular peak, of constant length, slowly sliding along the wall clock.

I have no vendor documentation to confirm it. It is the only candidate that fits all three observations: no I/O, stable duration, steady drift. And above all, it is not a problem: 53 °C is far from the 70 °C this SSD is rated for in operation.

The problem was not the disk. It was the rule.

The cause: a rule written for spinning disks

Here is the expression as it was:

smartctl_device_temperature{temperature_type="current", device=~"sd.*"} > 50

The device=~"sd.*" selector meant “SATA disk”, and when the rule was written, a SATA disk in this cluster was a hard drive. The thresholds follow that logic: 50 °C as warning, 58 °C as critical, sensible values for an HDD, whose mechanics age badly in the heat.

Two things changed since then, without anyone re-reading the rule:

  1. The cluster’s last hard drive left on September 26, replaced by an SSD. Every sd* on the Pis is now a USB-attached SSD.
  2. The SMART exporter started reading disks again once its image was fixed. The Pis’ five SSDs began reporting their temperature.

The rule stayed the same. What it was watching did not. A 50 °C threshold applied to an SSD doing its nightly housekeeping at 52 °C means one alert per night, guaranteed.

The criterion: rotation rate

The temptation at this point is to exclude the node by name, or to raise the threshold to 55 °C. Both just hide the symptom. A node’s name says nothing about the disk it carries, and a single threshold cannot be right for both an HDD and an SSD.

What is needed is a criterion that describes the hardware itself. smartctl provides one: rotation rate. A spinning disk reports its RPM; an SSD has none. In this homelab, the exporter only exposes the smartctl_device_rotation_rate series for the NAS’s two WD Red drives (5400 RPM). The Pis’ SSDs do not have it at all.

Writing the filter as > 0 rather than relying on the series being absent covers both ways an exporter can behave: a missing series, or a series at zero for an SSD. Either way, the disk is classified as an SSD.

The HDD rules keep their thresholds, but only for disks that spin:

- alert: DiskTempHighHDD
  expr: |
    smartctl_device_temperature{temperature_type="current", device=~"sd.*"} > 50
    and on(instance, device) smartctl_device_rotation_rate > 0
  for: 15m
  labels: { severity: warning }

And two new rules take care of the SSDs, with thresholds designed for them:

- alert: DiskTempHighSSD
  expr: |
    smartctl_device_temperature{temperature_type="current", device=~"sd.*"} > 60
    unless on(instance, device) smartctl_device_rotation_rate > 0
  for: 15m
  labels: { severity: warning }
- alert: DiskTempCriticalSSD
  expr: |
    smartctl_device_temperature{temperature_type="current", device=~"sd.*"} > 68
    unless on(instance, device) smartctl_device_rotation_rate > 0
  for: 5m
  labels: { severity: critical }

and on(instance, device) keeps the series that have a rotation rate; unless on(instance, device) keeps those that do not. It is the same pair of operators that made two restart rules mutually exclusive in 48 Alerts for 24 Pods, for the same reason: a disk falls into exactly one family, never two, never none.

The SSD thresholds start from the operating limit: 60 °C leaves 10 °C of headroom below the vendor’s 70 °C and sits 7 °C above the observed nightly peak; 68 °C is the point where someone needs to look right away. NVMe drives already had their own rule, at 70 °C; it does not change.

The check: replaying last night’s peak

A rewritten rule that “should” work proves nothing. The metrics database, however, still holds the previous night’s peak. On October 25 at 01:58 UTC, the worker’s SSD was at 52 °C. Evaluated at that exact moment:

ExpressionResult at the peak
Old HDD rulethe worker’s SSD
New HDD rulenothing
New SSD rulesnothing
smartctl_device_rotation_rate > 0the NAS’s 2 WD Red drives
sd* disks without a rotation ratethe Pis’ 5 SSDs

The criterion puts every disk in the homelab on the right side, and the alert that fired that night no longer exists under the new rules. One last check before pushing: vmalert -dryRun on the rules file, with no errors.

In fairness: a 52 °C peak does not exercise the “SSD genuinely too hot” path. The SSD rules have not yet been put to the test by a real breach. The day one of them fires, it will be for a good reason, or for a new lesson.

Lessons learned

1. A threshold encodes an assumption about the hardware

> 50 °C is not a truth about “disks”, it is a truth about hard drives. The day the hardware changes, the rule goes wrong silently, then very loudly. A hardware migration (here, HDD to SSD) should always trigger a review of the rules that cover it.

2. A drifting time is not a cron job

An event that comes back every day at a slightly different time does not come from a scheduled job on the host: those are pinned to the wall clock. A steady drift of a few minutes a day points to a clock that does not follow NTP, typically a firmware. Recording the exact time of the symptom over two weeks costs one query and rules out a whole family of hypotheses.

3. Classify by what the hardware is, not where it lives

Excluding a node by name would have worked until the next migration. smartctl_device_rotation_rate describes the disk itself and will follow the next SSD, the next HDD, the next node, with no changes.

4. and on / unless on: two families, no gap

When a population splits in two, the same condition used once with and and once with unless guarantees that each series lands in exactly one rule. No duplicates, nothing left out.

5. Replay the symptom on history before pushing

The metrics database keeps yesterday’s peak. Evaluating the old and new expressions at that moment proves both that the fix silences the false positive and that the criterion classifies every disk correctly. That is more convincing than re-reading YAML, and it does not require waiting for the next night.

The known limit

A hard drive behind a USB bridge that hides its rotation rate would land in the SSD family, with thresholds too generous for it. That is not the case today. If an HDD ever comes back to the cluster, the first thing to check will be that it reports its RPM.

The alert channel no longer rings at night. The SSD, for its part, keeps doing its housekeeping at 52 °C, somewhere between 1 and 2 a.m., a little earlier every day.

monitoringvmalertpromqlsmartssdincident