kubernetes版本号是怎么生成的

通过源码编译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在很多场合都会看到版本号,我们先梳理一下。

  1. --version

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

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

  3. 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分支上。

    版本号如何生成

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

    1. 从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
    2. 通过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+$Format:%h$就是出在这里。当然直接修改这个文件也可以得到正确的版本号,但是每次源码版本变化都需要修改,非常麻烦,采用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+$Format:%h$。

    为了解决上述问题,只需要对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

欢迎访问钟潘的博客

相关推荐