0. 概述

最近在处理 prometheus bug 的时候,遇到了一个问题,日志内容类似于:

[root@liqiang.io]# tail /var/log/prometheus/prometheus.log
... ...
level=info ts=2020-06-28T10:39:01.407890072Z caller=manager.go:460 component="rule manager" msg="Stopping rule manager..."
level=info ts=2020-06-28T10:39:01.40790516Z caller=manager.go:466 component="rule manager" msg="Rule manager stopped"
level=info ts=2020-06-28T10:39:01.407920683Z caller=notifier.go:512 component=notifier msg="Stopping notification manager..."
level=info ts=2020-06-28T10:39:01.407920157Z caller=main.go:394 msg="Scrape discovery manager stopped"
level=info ts=2020-06-28T10:39:01.407943273Z caller=main.go:407 msg="Notify discovery manager stopped"
level=info ts=2020-06-28T10:39:01.407954136Z caller=main.go:426 msg="Scrape manager stopped"
level=info ts=2020-06-28T10:39:01.407969264Z caller=main.go:573 msg="Notifier manager stopped"
level=error ts=2020-06-28T10:39:01.408718994Z caller=main.go:582 err="Opening storage failed open DB in /mnt/data/prometheus/data: Locked by other process"
level=info ts=2020-06-28T10:39:01.40879027Z caller=main.go:584 msg="See you next time!"
level=warn ts=2020-06-28T10:39:01.408796227Z caller=web.go:461 component=web msg="error serving gRPC" err="grpc: the server has been stopped"

这个问题可以直接从日志中看出来了,而且对于这个锁我也是有所了解了,清楚是啥来的,但是,就是因为了解,所以才比较好奇为什么会出现这个问题,于是,顺带梳理了一下 prometheus 中关于文件锁的相关内容。

1. prometheus 文件锁的演变

首先,先来说下为什么 prometheus 中的代码中为什么会出现这个锁,这是因为,有一次,有一个人提了一个 issue 说如果在同一个机器上运行 2 个以上的 prometheus,那么,prometheus 就会出现数据异常,并且会导致磁盘空间占用急剧增大,最终导致磁盘被占满。

这是 Prometheus 核心开发者的一个介绍 Source

图 1:为什么初版用的是 lockfile

然而,后面就出现了我上面一样的问题,例如如果进程突然崩掉了,那么重启之后,这个文件没有被释放,就会导致同样的 Prometheus 起不来。当然,这里有个前提是,这个文件锁内容所对应的 PID 是真的有进程在使用的,如果没有,那么 Prometheus 还是会删除,然后再创建一个新的。

后面,一些参与者出于其他目的,给 prometheus 添加了一个新的启动参数:--no-lock-file,这样,在启动的时候就不会去检查这个锁了,虽然在一定程度上解决了这个问题,但是,这还是会带来单机运行多个 prometheus ,导致数据异常的问题。

于是,后面就继续改进,将 fblock 替换了 PID Lock,这样,就解决了锁的释放问题,但是,这并不是一个完美的解决方案,因为它又引入了一个新的问题。

2. 最新锁机制的局限

因为最新版本的 Prometheus 采用了 Unix 系统中的 flock(其他系统雷同),于是可以保证不同进程无法同时运行,一个时间内,只会有一个 Prometheus 在正常工作。但是,因为引入了 flock,于是引发了一些新的问题,例如我们最关注的:Prometheus 不支持 NFS。

因为 fblock 无法被 NFS 识别,所以如果你需要使用 NFS 卷来存储 Prometheus 的数据,要么自己开一个 remote write adapter,要么开启参数:--no-lock-file,所以,当你解决了一个 Bug 的时候,又冒出了一堆 Bug。

3. 题外话:关于 flock

flock 在 Linux 操作系统中既可以是一个建议性锁,也可以是一个强制锁;同时 flock 可以锁住整个文件系统,也可以锁住文件中的范围数据。但是,一个很重要的点就是,锁是针对于进程的,也就是说,同个进程中的多个线程或者协程是不受限制的,如果不同的 goroutine 对一个文件加多次锁,那么最后生效的是最后一次加的锁,前面加的锁会被覆盖掉,这个需要很注意。

4. Ref