hi,大家好,我是 hhf。
最近在做 prometheus 生态的 cortex 优化工作,遇到一个比较坑的 go mod 的问题,这里分享一下。
我为什么将标题称为:如何欺骗 Go mod 呢?这个挺有意思的,这里先卖个关子,不过确实是突破了 Go mod 的相关特性。
在正式展开这个话题之前,需要简单的介绍下 cortex 和 thanos 这两个项目。
Prometheus 的局限性
说到业务开发基本上都离不开监控系统,Prometheus 做为云原生的宠儿,以优秀的设计,灵活的使用方式,以优异成绩从 CNCF 顺利毕业,也是很多公司做监控的首选。
但是呢,Promethues 也有其自身局限性,其中影响最大的就是其数据的高可用方案和集群方案。监控也是业务系统的重中一环,不能因为监控系统宕机导致报警无法及时发出。
Prometheus 官方也有提出联邦方案来解决集群问题,但是这个方案极其复杂而且很多问题还是解决不了,于是就造就了另外两个 CNCF 的沙箱项目:cortex 和 thanos。这两个项目都是为了解决 Promethues 的集群,高可用的。
由于这两个项目要解决问题的目的是一致的,所以就会出现很多功能都是可以相互复用的,于是有趣的事情就发生了。
cortex
话说因为某些的需求,不得已需要更改下 thanos 的相关代码。我本地调试的时候将 cortex 依赖的 thanos 给 replace 了一下。
replace github.com/thanos-io/thanos => /Users/hhf/goproject/cortex/thanos
再等我编译的时候,就编译不过了
# github.com/sercand/kuberesolver
../../../go/pkg/mod/github.com/sercand/[email protected]+incompatible/builder.go:108:82: undefined: resolver.BuildOption
../../../go/pkg/mod/github.com/sercand/[email protected]+incompatible/builder.go:163:32: undefined: resolver.ResolveNowOption
这就让人很无奈,别着急,我们看看这个 kuberesolver 是被谁依赖的。
先看下被 replace 之前:
▶ go mod graph| grep kuberesolver
github.com/weaveworks/[email protected] github.com/sercand/[email protected]+incompatible
github.com/weaveworks/[email protected] github.com/sercand/[email protected]+incompatible
github.com/weaveworks/[email protected] github.com/sercand/[email protected]+incompatible
github.com/weaveworks/[email protected] github.com/sercand/[email protected]+incompatible
github.com/weaveworks/[email protected] github.com/sercand/[email protected]+incompatible
github.com/weaveworks/[email protected] github.com/sercand/[email protected]+incompatible
github.com/thanos-io/[email protected] github.com/sercand/[email protected]+incompatible
可以看到正常版本下,[email protected] 被 thanos 所依赖,[email protected] 被 weaveworks 所依赖。
replace 之后
▶ go mod graph| grep kuberesolver
github.com/weaveworks/[email protected] github.com/sercand/[email protected]+incompatible
是不是很神奇,[email protected] 这个版本竟然消失了。由于 kuberesolver 的 v2.1.0 和 v2.4.0 是不兼容的,所以导致 replace 之后就无法编译了。
Gomod replace 语义
其实这并不神奇,这个涉及到 Go mod 的 replace 语义,不过也是很容易让人忽略的特性。
replace directives:(https://golang.org/ref/mod#go-mod-file-replace)
replace directives only apply in the main module’s go.mod file and are ignored in other modules. See Minimal version selection for details.
其实很简单,replace 只对主模块(也就是你的当前项目)是生效的。可以做如下的总结:
- 主模块的 replace 对于被依赖的模块是不生效的
- 被依赖的模块的 go.mod 的 replace 对主模块也是不生效的
所以,当 replace 之后,cortex 依赖的 thanos 的 replace 是不生效的。我们理一下依赖树:
- 主模块 cortex => require github.com/weaveworks/common v0.0.0-20210419092856-009d1eebd624
- weaveworks => requre github.com/sercand/kuberesolver v2.1.0+incompatible
- 于是整体上 kuberesolver 就只有了 v2.1.0 了
这个逻辑是跟 gomod 的 replace 语义是吻合的,也就是 replace 之后编译不过是正确的。
欺骗 gomod
那就更加神奇了,为何 cortex 直接 require thanos 就能编译成功,按照 gomod replace 语义来说,这也是编译不过的才是正确的。
因为根据文档我们知道,replace 仅仅作用于主模块,脱离了主模块是一律不生效的,这个是毋庸置疑的。
我做了个实验放在了 https://github.com/georgehao/gomodtestmain ,有兴趣的可以试一下,这个能验证 gomod 是遵循 gomod replace 语义 和 MVS (最小版本选择)算法的。
问题基本陷入了僵局,我们如何破局呢?
继续使用 go mod graph 功能,来查看 cortex 依赖的 thanos 的依赖树。
github.com/thanos-io/[email protected] gopkg.in/[email protected]
github.com/thanos-io/[email protected] github.com/Azure/[email protected]
github.com/thanos-io/[email protected] k8s.io/[email protected]
github.com/thanos-io/[email protected] gopkg.in/[email protected]
github.com/thanos-io/[email protected] go.uber.org/[email protected]
github.com/thanos-io/[email protected] go.elastic.co/apm/module/[email protected]
....
github.com/thanos-io/[email protected] github.com/gogo/[email protected]
由于这个依赖树太长(700多行),我就不贴了,基本上也能看出来,cortex 依赖了 thanos N 多个版本,其中在最后一个版本中的 go.mod 中我们发现了一个有意思的东西:
require (
github.com/sercand/kuberesolver v2.4.0+incompatible // indirect
)
也就是闹了半天,由于 thanos 某个很古老的版本的 gomod require [email protected],让 gomod 误以为 cortex 依赖的 thanos 依然是 require 了 [email protected] 了。虽然 thanos 早就改成了 repace kuberesolver,但也就让 cortex 顺利编译过去了。
这算不算 gomod 的 bug 呢?
为什么 cortex 会依赖 thanos 这么多版本呢?这就要回到开篇说的 cortex 和 thanos 功能复用的问题了。
目前 cortex 和 thanos 这个两个项目,基本上是这么依赖的:
cortex 1.9.0 -> thanos v0.19.1-0.20210729154440-aa148f8fdb28
thanos v0.19.1-0.20210729154440-aa148f8fdb28 -> cortex v1.8.1-0.20210422151339-cf1c444e0905
cortex v1.8.1-0.20210422151339-cf1c444e0905 -> thanos v0.13.1-0.20210401085038-d7dff0c84d17
....
cortex 与 thanos 之间的相互引用,就像俄罗斯套娃一样,简直就是 gomod 的噩梦。go mod replace 语义,竟然让这两个套娃给破解了。
如何解决
对应如何cortex replace thanos 的问题,其实知道问题的根本所在,解决起来就很简单了,有两种方式吧:
- 由于 gomod MVS 算法,我们直接在主项目 cortex 中指定 kuberesolver 的版本为 v2.4.1
- 方案 1 仅对于向下兼容的项目比较适用,如果某项目没有这个责任心的话,这么做可能是会出问题的,所以比较直接的解决办法,直接修改 thanos 的 go.mod, 将 thanos 的所依赖的 kuberesolver 从 replace 挪到 require 中
欢迎关注公众号。更多学习学习资料分享,关注公众号回复指令:
- 回复 0,获取 《Go 面经》
- 回复 1,获取 《Go 源码流程图》
|
请发表评论