0%

kubernetes版本号是怎么生成的

通过源码编译kubernetes时,可以使用go build(或go install)单独编译某个组件,例如对于apiserver,可以cd到k8s.io/kubernetes/cmd/kube-apiserver,然后执行:

1
go install -i -v -gcflags='-N -l'

编译结果安装到GOBIN下,即GOBIN/kube-apiserver,使用这种方式编译时有一个小问题,版本号是一段奇怪的字符串:

1
2
kube-apiserver --version
Kubernetes v0.0.0-master+$Format:%h$

在遇到一些需要依赖kubernetes版本号的场景就会有问题,例如使用helm安装chart时,有些chart对kubernetes版本号有要求,就会无法安装。

有哪些版本号

kubernetes在很多场合都会看到版本号,我们先梳理一下。

  1. –version

    每个组件有–version参数,这时输出本组件的版本号。

  2. kubectl version

    1
    2
    3
    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的版本号。

  3. kubectl get nodes

    1
    2
    3
    4
    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分支上。

版本号如何生成

那么版本号是如何写入到程序中的,主要有两个步骤:

  1. 从git获取源码版本信息

    v1.17.0-alpha.3.227+7d13dfe3c34f44这一段是通过如下命令获取,并稍作转换:

    1
    2
    git describe --tags --match='v*' --abbrev=14
    v1.17.0-alpha.3-227-g7d13dfe3c34f44

    如果tag之后没有再提交过,则后面就没有提交次数和最新提交id,只有tag,这通常发生在release分支上最后释放时。

    -dirty是通过如下命令获取,如果源码修改过则版本号添加-dirty:

    1
    git status --porcelain
  1. 通过ldflags将上述获取版本信息写入程序

    golang中在链接阶段可以覆盖程序中的全局变量,通过在go build时增加如下参数。

    1
    -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的内容是一样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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+$Format:%h$就是出在这里。当然直接修改这个文件也可以得到正确的版本号,但是每次源码版本变化都需要修改,非常麻烦,采用ldflags就非常简单:

    1
    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+$Format:%h$

为了解决上述问题,只需要对k8s.io/kubernetes/hack/lib/version.sh稍作改造,复用其逻辑即可:

首先修改version.sh,因为启用了go module,如果你没有显示启用可以不需要修改:

1
2
3
4
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}'"
)

改为:

1
2
3
4
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如下:

1
2
3
4
5
6
7
8
#!/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,然后在单个组件目录下就可以直接执行:

1
goinstall
-------------本文结束感谢您的阅读-------------