乐趣区

关于golang:如何欺骗-Go-Mod

hi,大家好,我是 haohongfan。

最近在做 prometheus 生态的 cortex 优化工作,遇到一个比拟坑的 go mod 的问题,这里分享一下。

我为什么将题目称为:如何坑骗 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/kuberesolver@v2.1.0+incompatible/builder.go:108:82: undefined: resolver.BuildOption
../../../go/pkg/mod/github.com/sercand/kuberesolver@v2.1.0+incompatible/builder.go:163:32: undefined: resolver.ResolveNowOption

这就让人很无奈,别着急,咱们看看这个 kuberesolver 是被谁依赖的。

先看下被 replace 之前:

▶ go mod graph| grep kuberesolver
github.com/weaveworks/common@v0.0.0-20210419092856-009d1eebd624 github.com/sercand/kuberesolver@v2.1.0+incompatible
github.com/weaveworks/common@v0.0.0-20210112142934-23c8d7fa6120 github.com/sercand/kuberesolver@v2.1.0+incompatible
github.com/weaveworks/common@v0.0.0-20200206153930-760e36ae819a github.com/sercand/kuberesolver@v2.1.0+incompatible
github.com/weaveworks/common@v0.0.0-20201119133501-0619918236ec github.com/sercand/kuberesolver@v2.1.0+incompatible
github.com/weaveworks/common@v0.0.0-20200914083218-61ffdd448099 github.com/sercand/kuberesolver@v2.1.0+incompatible
github.com/weaveworks/common@v0.0.0-20200625145055-4b1847531bc9 github.com/sercand/kuberesolver@v2.1.0+incompatible
github.com/thanos-io/thanos@v0.13.1-0.20200731083140-69b87607decf github.com/sercand/kuberesolver@v2.4.0+incompatible

能够看到失常版本下,kuberesolver@2.4.0 被 thanos 所依赖,kuberesolver@v2.1.0 被 weaveworks 所依赖。

replace 之后

▶ go mod graph| grep kuberesolver
github.com/weaveworks/common@v0.0.0-20210419092856-009d1eebd624 github.com/sercand/kuberesolver@v2.1.0+incompatible

是不是很神奇,kuberesolver@v2.4.0 这个版本居然隐没了。因为 kuberesolver 的 v2.1.0 和 v2.4.0 是不兼容的,所以导致 replace 之后就无奈编译了。

Gomod replace 语义

其实这并不神奇,这个波及到 Go mod 的 replace 语义,不过也是很容易让人疏忽的个性。

replace directives:(https://golang.org/ref/mod#go…)

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/…,有趣味的能够试一下,这个能验证 gomod 是遵循 gomod replace 语义 和 MVS (最小版本抉择)算法的。

问题根本陷入了僵局,咱们如何破局呢?

持续应用 go mod graph 性能,来查看 cortex 依赖的 thanos 的依赖树。

github.com/thanos-io/thanos@v0.19.1-0.20210729154440-aa148f8fdb28 gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307
github.com/thanos-io/thanos@v0.13.1-0.20210401085038-d7dff0c84d17 github.com/Azure/azure-pipeline-go@v0.2.2
github.com/thanos-io/thanos@v0.8.1-0.20200109203923-552ffa4c1a0d k8s.io/utils@v0.0.0-20191114200735-6ca3b61696b6
github.com/thanos-io/thanos@v0.13.1-0.20210204123931-82545cdd16fe gopkg.in/yaml.v2@v2.3.0
github.com/thanos-io/thanos@v0.13.1-0.20201030101306-47f9a225cc52 go.uber.org/goleak@v1.1.10
github.com/thanos-io/thanos@v0.13.1-0.20200807203500-9b578afb4763 go.elastic.co/apm/module/apmot@v1.5.0
....
github.com/thanos-io/thanos@v0.13.1-0.20200731083140-69b87607decf github.com/gogo/protobuf@v1.3.1

因为这个依赖树太长(700 多行),我就不贴了,基本上也能看进去,cortex 依赖了 thanos N 多个版本,其中在最初一个版本中的 go.mod 中咱们发现了一个有意思的货色:

require (
  github.com/sercand/kuberesolver v2.4.0+incompatible // indirect)

也就是闹了半天,因为 thanos 某个很古老的版本的 gomod require kuberesolver@v2.4.0,让 gomod 误以为 cortex 依赖的 thanos 仍然是 require 了 kuberesolver@v2.4.0 了。尽管 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 的问题,其实晓得问题的基本所在,解决起来就很简略了,有两种形式吧:

  1. 因为 gomod MVS 算法,咱们间接在主我的项目 cortex 中指定 kuberesolver 的版本为 v2.4.1
  2. 计划 1 仅对于向下兼容的我的项目比拟实用,如果某我的项目没有这个责任心的话,这么做可能是会出问题的,所以比拟间接的解决办法,间接批改 thanos 的 go.mod, 将 thanos 的所依赖的 kuberesolver 从 replace 挪到 require 中

欢送关注公众号。更多学习学习材料分享,关注公众号回复指令:

  • 回复 0,获取《Go 面经》
  • 回复 1,获取《Go 源码流程图》

退出移动版