From 88115b6308dcaea437d9f75a80ed2422610f8a6d Mon Sep 17 00:00:00 2001 From: Ivan Mikheykin Date: Tue, 17 Apr 2018 18:50:25 +0300 Subject: [PATCH 1/3] strip rootfs prefix for run in docker - relates to #66 Signed-off-by: Ivan Mikheykin --- README.md | 13 ++++++++++++- collector/filesystem_linux.go | 10 +++++++--- collector/paths.go | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 674e058a34..0a22fe3c01 100644 --- a/README.md +++ b/README.md @@ -144,12 +144,23 @@ The node\_exporter is designed to monitor the host system. It's not recommended to deploy it as Docker container because it requires access to the host system. Be aware that any non-root mount points you want to monitor will need bind-mounted into the container. +If you start container for host monitoring, specify `path.rootfs` argument. +This argument must match path in bind-mount of host root. The node\_exporter will use +`path.rootfs` as prefix to filter entries in ${path.procfs}/mounts and to +cleanup it from `mountpoint` label. You also need bind-mount host proc and host sys +with `path.procfs` and `path.sysfs` arguments. +Also you need to use `bind-propagation=rslave` option for bind-mount host rootfs to +propagate host mounts changes to container (option available since 17.05.0). ```bash docker run -d \ --net="host" \ --pid="host" \ - quay.io/prometheus/node-exporter + -v "/proc:/host/proc:ro" -v "/sys:/host/sys:ro" \ + --mount "type=bind,source=/,target=/rootfs,readonly,bind-propagation=rslave" \ + quay.io/prometheus/node-exporter \ + --path.procfs /host/proc --path.sysfs /host/sys \ + --path.rootfs /rootfs ``` On some systems, the `timex` collector requires an additional Docker flag, diff --git a/collector/filesystem_linux.go b/collector/filesystem_linux.go index 04c21a698d..ad89f190bf 100644 --- a/collector/filesystem_linux.go +++ b/collector/filesystem_linux.go @@ -70,7 +70,7 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { go stuckMountWatcher(labels.mountPoint, success) buf := new(syscall.Statfs_t) - err = syscall.Statfs(labels.mountPoint, buf) + err = syscall.Statfs(rootfsFilePath(labels.mountPoint), buf) stuckMountsMtx.Lock() close(success) @@ -86,7 +86,7 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { labels: labels, deviceError: 1, }) - log.Debugf("Error on statfs() system call for %q: %s", labels.mountPoint, err) + log.Debugf("Error on statfs() system call for %q: %s", rootfsFilePath(labels.mountPoint), err) continue } @@ -143,6 +143,10 @@ func mountPointDetails() ([]filesystemLabels, error) { scanner := bufio.NewScanner(file) for scanner.Scan() { parts := strings.Fields(scanner.Text()) + // skip non rootfs paths if rootfsPath defined + if !rootfsPathDetect(parts[1]) { + continue + } // Ensure we handle the translation of \040 and \011 // as per fstab(5). @@ -151,7 +155,7 @@ func mountPointDetails() ([]filesystemLabels, error) { filesystems = append(filesystems, filesystemLabels{ device: parts[0], - mountPoint: parts[1], + mountPoint: rootfsStripPrefix(parts[1]), fsType: parts[2], options: parts[3], }) diff --git a/collector/paths.go b/collector/paths.go index f8e6e6e535..92a99edb22 100644 --- a/collector/paths.go +++ b/collector/paths.go @@ -15,6 +15,7 @@ package collector import ( "path" + "strings" "github.com/prometheus/procfs" "gopkg.in/alecthomas/kingpin.v2" @@ -22,8 +23,9 @@ import ( var ( // The path of the proc filesystem. - procPath = kingpin.Flag("path.procfs", "procfs mountpoint.").Default(procfs.DefaultMountPoint).String() - sysPath = kingpin.Flag("path.sysfs", "sysfs mountpoint.").Default("/sys").String() + procPath = kingpin.Flag("path.procfs", "procfs mountpoint.").Default(procfs.DefaultMountPoint).String() + sysPath = kingpin.Flag("path.sysfs", "sysfs mountpoint.").Default("/sys").String() + rootfsPath = kingpin.Flag("path.rootfs", "rootfs mountpoint.").Default("").String() ) func procFilePath(name string) string { @@ -33,3 +35,30 @@ func procFilePath(name string) string { func sysFilePath(name string) string { return path.Join(*sysPath, name) } + +func rootfsFilePath(name string) string { + if name == "/" { + return *rootfsPath + } + return path.Join(*rootfsPath, name) +} + +func rootfsStripPrefix(path string) string { + if path == *rootfsPath { + return "/" + } + if strings.HasPrefix(path, *rootfsPath) { + return strings.TrimPrefix(path, *rootfsPath) + } + return path +} + +func rootfsPathDetect(path string) bool { + if *rootfsPath == "" { + return true + } + if strings.HasPrefix(path, *rootfsPath) { + return true + } + return false +} From 1cbcea38ff1ec028127a1696ccf43bc3766e1e6f Mon Sep 17 00:00:00 2001 From: Yecheng Fu Date: Mon, 3 Sep 2018 11:26:22 +0800 Subject: [PATCH 2/3] No need to mount `/proc` and `/sys` because we share host's PID namespace, which allows processes within the container to see all of the processes on the system. Signed-off-by: Yecheng Fu --- README.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0a22fe3c01..b554407a2b 100644 --- a/README.md +++ b/README.md @@ -147,20 +147,15 @@ into the container. If you start container for host monitoring, specify `path.rootfs` argument. This argument must match path in bind-mount of host root. The node\_exporter will use `path.rootfs` as prefix to filter entries in ${path.procfs}/mounts and to -cleanup it from `mountpoint` label. You also need bind-mount host proc and host sys -with `path.procfs` and `path.sysfs` arguments. -Also you need to use `bind-propagation=rslave` option for bind-mount host rootfs to -propagate host mounts changes to container (option available since 17.05.0). +cleanup it from `mountpoint` label. ```bash docker run -d \ --net="host" \ --pid="host" \ - -v "/proc:/host/proc:ro" -v "/sys:/host/sys:ro" \ - --mount "type=bind,source=/,target=/rootfs,readonly,bind-propagation=rslave" \ + -v "/:/host:ro,rslave" \ quay.io/prometheus/node-exporter \ - --path.procfs /host/proc --path.sysfs /host/sys \ - --path.rootfs /rootfs + --path.rootfs /host ``` On some systems, the `timex` collector requires an additional Docker flag, From 82b9186ae16954e850176b6fdf161110a39f7067 Mon Sep 17 00:00:00 2001 From: Yecheng Fu Date: Mon, 3 Sep 2018 19:11:20 +0800 Subject: [PATCH 3/3] Use `/` as default value of path.rootfs, and parse mounts from `/proc/1/mounts`. See https://github.com/prometheus/node_exporter/pull/1058. Signed-off-by: Yecheng Fu --- README.md | 3 +-- collector/filesystem_linux.go | 8 ++------ collector/fixtures/proc/1/mounts | 32 ++++++++++++++++++++++++++++++++ collector/paths.go | 26 +------------------------- 4 files changed, 36 insertions(+), 33 deletions(-) create mode 100644 collector/fixtures/proc/1/mounts diff --git a/README.md b/README.md index b554407a2b..cc1c18fb60 100644 --- a/README.md +++ b/README.md @@ -146,8 +146,7 @@ Be aware that any non-root mount points you want to monitor will need bind-mount into the container. If you start container for host monitoring, specify `path.rootfs` argument. This argument must match path in bind-mount of host root. The node\_exporter will use -`path.rootfs` as prefix to filter entries in ${path.procfs}/mounts and to -cleanup it from `mountpoint` label. +`path.rootfs` as prefix to access host filesystem. ```bash docker run -d \ diff --git a/collector/filesystem_linux.go b/collector/filesystem_linux.go index ad89f190bf..2d4d484741 100644 --- a/collector/filesystem_linux.go +++ b/collector/filesystem_linux.go @@ -133,7 +133,7 @@ func stuckMountWatcher(mountPoint string, success chan struct{}) { } func mountPointDetails() ([]filesystemLabels, error) { - file, err := os.Open(procFilePath("mounts")) + file, err := os.Open(procFilePath("1/mounts")) if err != nil { return nil, err } @@ -143,10 +143,6 @@ func mountPointDetails() ([]filesystemLabels, error) { scanner := bufio.NewScanner(file) for scanner.Scan() { parts := strings.Fields(scanner.Text()) - // skip non rootfs paths if rootfsPath defined - if !rootfsPathDetect(parts[1]) { - continue - } // Ensure we handle the translation of \040 and \011 // as per fstab(5). @@ -155,7 +151,7 @@ func mountPointDetails() ([]filesystemLabels, error) { filesystems = append(filesystems, filesystemLabels{ device: parts[0], - mountPoint: rootfsStripPrefix(parts[1]), + mountPoint: parts[1], fsType: parts[2], options: parts[3], }) diff --git a/collector/fixtures/proc/1/mounts b/collector/fixtures/proc/1/mounts new file mode 100644 index 0000000000..7452d495a6 --- /dev/null +++ b/collector/fixtures/proc/1/mounts @@ -0,0 +1,32 @@ +rootfs / rootfs rw 0 0 +sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 +proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 +udev /dev devtmpfs rw,relatime,size=10240k,nr_inodes=1008585,mode=755 0 0 +devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0 +tmpfs /run tmpfs rw,nosuid,relatime,size=1617716k,mode=755 0 0 +/dev/dm-2 / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0 +securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0 +tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0 +tmpfs /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0 +tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0 +cgroup /sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd 0 0 +pstore /sys/fs/pstore pstore rw,nosuid,nodev,noexec,relatime 0 0 +cgroup /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0 +cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0 +cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0 +cgroup /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0 +cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0 +cgroup /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0 +cgroup /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0 +systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct 0 0 +mqueue /dev/mqueue mqueue rw,relatime 0 0 +debugfs /sys/kernel/debug debugfs rw,relatime 0 0 +hugetlbfs /dev/hugepages hugetlbfs rw,relatime 0 0 +fusectl /sys/fs/fuse/connections fusectl rw,relatime 0 0 +/dev/sda3 /boot ext2 rw,relatime 0 0 +rpc_pipefs /run/rpc_pipefs rpc_pipefs rw,relatime 0 0 +binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc rw,relatime 0 0 +tmpfs /run/user/1000 tmpfs rw,nosuid,nodev,relatime,size=808860k,mode=700,uid=1000,gid=1000 0 0 +gvfsd-fuse /run/user/1000/gvfs fuse.gvfsd-fuse rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0 +/dev/sda /var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[vsanDatastore]\040bafb9e5a-8856-7e6c-699c-801844e77a4a/kubernetes-dynamic-pvc-3eba5bba-48a3-11e8-89ab-005056b92113.vmdk ext4 rw,relatime,data=ordered 0 0 +/dev/sda /var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[vsanDatastore]\011bafb9e5a-8856-7e6c-699c-801844e77a4a/kubernetes-dynamic-pvc-3eba5bba-48a3-11e8-89ab-005056b92113.vmdk ext4 rw,relatime,data=ordered 0 0 diff --git a/collector/paths.go b/collector/paths.go index 92a99edb22..a3a61ad11e 100644 --- a/collector/paths.go +++ b/collector/paths.go @@ -15,7 +15,6 @@ package collector import ( "path" - "strings" "github.com/prometheus/procfs" "gopkg.in/alecthomas/kingpin.v2" @@ -25,7 +24,7 @@ var ( // The path of the proc filesystem. procPath = kingpin.Flag("path.procfs", "procfs mountpoint.").Default(procfs.DefaultMountPoint).String() sysPath = kingpin.Flag("path.sysfs", "sysfs mountpoint.").Default("/sys").String() - rootfsPath = kingpin.Flag("path.rootfs", "rootfs mountpoint.").Default("").String() + rootfsPath = kingpin.Flag("path.rootfs", "rootfs mountpoint.").Default("/").String() ) func procFilePath(name string) string { @@ -37,28 +36,5 @@ func sysFilePath(name string) string { } func rootfsFilePath(name string) string { - if name == "/" { - return *rootfsPath - } return path.Join(*rootfsPath, name) } - -func rootfsStripPrefix(path string) string { - if path == *rootfsPath { - return "/" - } - if strings.HasPrefix(path, *rootfsPath) { - return strings.TrimPrefix(path, *rootfsPath) - } - return path -} - -func rootfsPathDetect(path string) bool { - if *rootfsPath == "" { - return true - } - if strings.HasPrefix(path, *rootfsPath) { - return true - } - return false -}