通过源码编译kubernetes时,可以使用go build(或go install)单独编译某个组件,例如对于apiserver,可以cd到k8s.io/kubernetes/cmd/kube-apiserver,然后执行:
go install -i -v -gcflags='-N -l'
编译结果安装到GOBIN下,即GOBIN/kube-apiserver,使用这种方式编译时有一个小问题,版本号是一段奇怪的字符串:
kube-apiserver --version
Kubernetes v0.0.0-master+$Format:%h$
在遇到一些需要依赖kubernetes版本号的场景就会有问题,例如使用helm安装chart时,有些chart对kubernetes版本号有要求,就会无法安装。
有哪些版本号
kubernetes在很多场合都会看到版本号,我们先梳理一下。
-
--version
每个组件有--version参数,这时输出本组件的版本号。
-
kubectl version
kubectl version Client Version: version.Info{Major:"", Minor:"", GitVersion:"v0.0.0-master+$Format:%h$", GitCommit:"$Format:%H$", GitTreeState:"", BuildDate:"1970-01-01T00:00:00Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"17+", GitVersion:"v1.17.0-alpha.3.227+7d13dfe3c34f44-dirty", GitCommit:"7d13dfe3c34f44ff505afe397c7b05fe6e5414ed", GitTreeState:"dirty", BuildDate:"2019-11-18T14:42:24Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"}
Client Version即kubectl的版本号,Server Version即apiserver的版本号。
-
kubectl get nodes
kubectl get nodes NAME STATUS ROLES AGE VERSION 10.10.10.15 Ready <none> 12d v1.17.0-alpha.3.227+7d13dfe3c34f44-dirty 10.10.10.16 Ready <none> 11d v1.17.0-alpha.3.227+7d13dfe3c34f44-dirty
此处的版本号为kubelet的版本号。
版本号的含义
上述长长的一串版本号v1.17.0-alpha.3.227+7d13dfe3c34f44-dirty,看起来非常复杂,其实熟悉git的同学一眼就能看出正是git的源码版本信息。
分解字段 | 说明 |
---|---|
v1.17.0-alpha.3 | 上一次tag,表示v1.17.0的第3个alpha版本 |
227 | 上一次tag之后提交数 |
7d13dfe3c34f44 | 最新一次提交id |
dirty | 源码是否修改 |
这里描述了kubernetes的版本管理规则,据此可以看出上述版本一定是在master分支上。
版本号如何生成
那么版本号是如何写入到程序中的,主要有两个步骤:
-
从git获取源码版本信息
v1.17.0-alpha.3.227+7d13dfe3c34f44这一段是通过如下命令获取,并稍作转换:
git describe --tags --match='v*' --abbrev=14 v1.17.0-alpha.3-227-g7d13dfe3c34f44
如果tag之后没有再提交过,则后面就没有提交次数和最新提交id,只有tag,这通常发生在release分支上最后释放时。
-dirty是通过如下命令获取,如果源码修改过则版本号添加-dirty:
git status --porcelain
-
通过ldflags将上述获取版本信息写入程序
golang中在链接阶段可以覆盖程序中的全局变量,通过在go build时增加如下参数。
-ldflags -X importpath.name=value
其中importpath为包的导入路径,name为变量名,value为新的值。
对于kubernetes,版本信息相关的变量,服务端定义在k8s.io/component-base/version包中,源码见k8s.io/component-base/version/base.go,客户端定义在k8s.io/client-go/pkg/version中,源码见k8s.io/client-go/pkg/version/base.go,两个base.go的内容是一样的:
var ( // TODO: Deprecate gitMajor and gitMinor, use only gitVersion // instead. First step in deprecation, keep the fields but make // them irrelevant. (Next we'll take it out, which may muck with // scripts consuming the kubectl version output - but most of // these should be looking at gitVersion already anyways.) gitMajor string = "" // major version, always numeric gitMinor string = "" // minor version, numeric possibly followed by "+" // semantic version, derived by build scripts (see // https://git.k8s.io/community/contributors/design-proposals/release/versioning.md // for a detailed discussion of this field) // // TODO: This field is still called "gitVersion" for legacy // reasons. For prerelease versions, the build metadata on the // semantic version is a git hash, but the version itself is no // longer the direct output of "git describe", but a slight // translation to be semver compliant. // NOTE: The $Format strings are replaced during 'git archive' thanks to the // companion .gitattributes file containing 'export-subst' in this same // directory. See also https://git-scm.com/docs/gitattributes gitVersion string = "v0.0.0-master+$Format:%h$" gitCommit string = "$Format:%H$" // sha1 from git, output of $(git rev-parse HEAD) gitTreeState string = "" // state of git tree, either "clean" or "dirty" buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') )
一开始出现的奇怪的字符串v0.0.0-master+就是出在这里。当然直接修改这个文件也可以得到正确的版本号,但是每次源码版本变化都需要修改,非常麻烦,采用ldflags就非常简单:
go build -ldflags='-X k8s.io/component-base/version.gitVersion=v1.17.0-alpha.3.227+7d13dfe3c34f44-dirty -X k8s.io/client-go/pkg/version.gitVersion=v1.17.0-alpha.3.227+7d13dfe3c34f44-dirty'
上述命令就将gitVersion变量修改为从git中得到的源码版本号,这个过程通过脚本完全可以做到自动化,这个脚本就是k8s.io/kubernetes/hack/lib/version.sh。
go build时植入版本号
在使用make编译kubernetes时会自动从git获取版本信息并通过ldflags植入程序中,但是通过go build编译时kubernetes并没有提供脚本实现上述过程,这就导致了版本号总是v0.0.0-master+。
为了解决上述问题,只需要对k8s.io/kubernetes/hack/lib/version.sh稍作改造,复用其逻辑即可:
首先修改version.sh,因为启用了go module,如果你没有显示启用可以不需要修改:
ldflags+=(
"-X '${KUBE_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.${key}=${val}'"
"-X '${KUBE_GO_PACKAGE}/vendor/k8s.io/component-base/version.${key}=${val}'"
)
改为:
ldflags+=(
"-X '${KUBE_GO_PACKAGE}/client-go/pkg/version.${key}=${val}'"
"-X '${KUBE_GO_PACKAGE}/component-base/version.${key}=${val}'"
)
然后在k8s.io/kubernetes/hack目录下添加脚本文件goinstall如下:
#!/usr/bin/env bash
basepath=$(cd `dirname $0`; pwd)
KUBE_ROOT=${basepath}/..
source "${KUBE_ROOT}/hack/lib/version.sh"
KUBE_GO_PACKAGE="k8s.io"
ldflags=$(kube::version::ldflags)
echo ldflags:${ldflags}
go install -i -v -gcflags='-N -l' -ldflags="${ldflags}"
将k8s.io/kubernetes/hack目录添加到path,然后在单个组件目录下就可以直接执行:
goinstall
欢迎访问钟潘的博客
有疑问加站长微信联系(非本文作者)