prometheus / prometheus/node_exporter
Bind-mounting a filesystem to its original mount path causes a duplicate metric error
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 13.8k
- Forks
- 2.7k
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 8
Description
When a path that is already a mount point is bind-mounted to itself, e.g. with
mount -ttmpfs - /foo
mount -obind /foo /foo
a duplicate metric error occurs and there is currently no way to collect metrics for the original mount.
Background
This may seem like a weird issue at first, so let me explain why this situation occurs under real use:
-
Nix, the package manager, keeps its files at
/nix/store. Since it wants to prevent accidental modification of these files (by the root user or errant software or whatnot), the nix daemon opens the folder for itself so it can write to it, and then bind-mounts/nix/storeread-only, e.g.mount -obind,ro /nix/store /nix/store. -
/nix/storetends to contain a lot of files and folders (100s of GBs in 100k's of files in one folder). I like to put that all onto a different filesystem or even disk. -
I can't meaningfully ignore the bind mount with
--collector.filesystem.fs-types-exclude, since the fstype of the bind mount is the same as that of the underlying fs. -
I can ignore everything mounted at
/nix/storewith--collector.filesystem.mount-points-exclude, but then I'll won't know whether that is about to fill up.
My current ugly workaround is to --collector.filesystem.mount-points-exclude=/nix/store, and then to bind-mount /nix/store to yet another unrelated path, e.g. mount -obind,ro /nix/store /run/.export.nix.store.
Full error
Feb 27 19:42:16 spaniel node_exporter[26585]: time=2025-02-27T10:42:16.940Z level=ERROR source=http.go:169 msg="error gathering metrics: 8 error(s) occurred:\n* [from Gatherer #2] collected metric \"node_filesystem_device_error\" { label:{name:\"device\" value:\"/dev/sda3\"} label:{name:\"device_error\" value:\"\"} label:{name:\"fstype\" value:\"bcachefs\"} label:{name:\"mountpoint\" value:\"/nix/store\"} gauge:{value:0}} was collected before with the same name and label values\n* [from Gatherer #2] collected metric \"node_filesystem_readonly\" { label:{name:\"device\" value:\"/dev/sda3\"} label:{name:\"device_error\" value:\"\"} label:{name:\"fstype\" value:\"bcachefs\"} label:{name:\"mountpoint\" value:\"/nix/store\"} gauge:{value:0}} was collected before with the same name and label values\n* [from Gatherer #2] collected metric \"node_filesystem_size_bytes\" { label:{name:\"device\" value:\"/dev/sda3\"} label:{name:\"device_error\" value:\"\"} label:{name:\"fstype\" value:\"bcachefs\"} label:{name:\"mountpoint\" value:\"/nix/store\"} gauge:{value:9.8579775488e+10}} was collected before with the same name and label values\n* [from Gatherer #2] collected metric \"node_filesystem_free_bytes\" { label:{name:\"device\" value:\"/dev/sda3\"} label:{name:\"device_error\" value:\"\"} label:{name:\"fstype\" value:\"bcachefs\"} label:{name:\"mountpoint\" value:\"/nix/store\"} gauge:{value:7.520336384e+10}} was collected before with the same name and label values\n* [from Gatherer #2] collected metric \"node_filesystem_avail_bytes\" { label:{name:\"device\" value:\"/dev/sda3\"} label:{name:\"device_error\" value:\"\"} label:{name:\"fstype\" value:\"bcachefs\"} label:{name:\"mountpoint\" value:\"/nix/store\"} gauge:{value:7.4046388736e+10}} was collected before with the same name and label values\n* [from Gatherer #2] collected metric \"node_filesystem_files\" { label:{name:\"device\" value:\"/dev/sda3\"} label:{name:\"device_error\" value:\"\"} label:{name:\"fstype\" value:\"bcachefs\"} label:{name:\"mountpoint\" value:\"/nix/store\"} gauge:{value:1.17505256e+09}} was collected before with the same name and label values\n* [from Gatherer #2] collected metric \"node_filesystem_files_free\" { label:{name:\"device\" value:\"/dev/sda3\"} label:{name:\"device_error\" value:\"\"} label:{name:\"fstype\" value:\"bcachefs\"} label:{name:\"mountpoint\" value:\"/nix/store\"} gauge:{value:1.17505256e+09}} was collected before with the same name and label values\n* [from Gatherer #2] collected metric \"node_filesystem_mount_info\" { label:{name:\"device\" value:\"/dev/sda3\"} label:{name:\"major\" value:\"8\"} label:{name:\"minor\" value:\"3\"} label:{name:\"mountpoint\" value:\"/nix/store\"} gauge:{value:1}} was collected before with the same name and label values"
The first line of that error formatted:
* [from Gatherer #2] collected metric "node_filesystem_device_error" {
label:{name:"device" value:"/dev/sda3"}
label:{name:"device_error" value:""}
label:{name:"fstype" value:"bcachefs"}
label:{name:"mountpoint" value:"/nix/store"}
gauge:{value:0}
}
was collected before with the same name and label values
Possible approaches
Albeit Go isn't exactly my home ground, I'd like to try my hand at a fix. Do you have any recommendation on which way to go with that?
- (Provide a way to or default-)ignore all bind mounts (not as easy as it sounds)
- Deduplicate (also tricky, there may be situations with real duplicates I'm not aware of)
- Provide a way to ignore filesystems based on mount options, to ignore e.g. read-only file systems
- Change the reported fstype of bind mounts (wrong as far as linux is concerned and might cause weird breakage)
- Find a way to disambiguate multiple mounts to the same folder (That would also solve situations such as
for x in 1 2; do mount -ttmpfs - foo; done, though I don't think those are a relevant problem) (not much to go by for this except the mount ID, and that is a terrible label) - …?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the filesystem collector and reproduce the self-bind mount using the commands in the issue. Trace how both mounts become metrics and determine whether filtering or deduplication can preserve the original mount without duplicate label sets. Done means collection succeeds and the original mount remains observable, with coverage for the reproduced case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, linux, prometheus
- Domain
- observability, operating-systems
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100