如何将k8s.io/kubernetes包导入你的项目
问题现象
用例
我有一个项目,在其中,导入的包如下所示:
import (
"crypto/x509"
"fmt"
"io/ioutil"
"log"
"path/filepath"
"github.com/spf13/cobra"
"k8s.io/client-go/util/cert"
"k8s.io/client-go/util/keyutil"
"k8s.io/kubernetes/test/utils"
)
所以它由几个内置包组成,然后是 CLI 库Cobra和几个客户端包。唯一不同寻常的是k8s.io/kubernetes/test/utils
包。
直接使用go get导入
让我们在构建代码之前整理一下依赖项:
$ go get github.com/spf13/cobra \
> k8s.io/client-go/util/cert \
> k8s.io/client-go/util/keyutil \
> k8s.io/kubernetes/test/utils
go: downloading k8s.io/client-go v1.5.2
go: downloading github.com/spf13/cobra v1.1.3
go: downloading k8s.io/kubernetes v1.21.1
go: downloading k8s.io/client-go v0.21.1
go get: k8s.io/kubernetes@v1.15.0-alpha.0 updating to
k8s.io/kubernetes@v1.21.1 requires
k8s.io/api@v0.0.0: reading k8s.io/api/go.mod at revision v0.0.0: unknown revision v0.0.0
现在,这是一个奇怪的错误:
k8s.io/api@v0.0.0: reading k8s.io/api/go.mod at revision v0.0.0: unknown revision v0.0.0
错误的原因是在kubernetes主仓中,也使用了公共库,不过go.mod
文件中所有公共库版本都指定为了v0.0.0(显然这个版本不存在), 然后通过Go Module的replace机制,将版本替换为子目录./staging/src/k8s.io
对应的依赖。
解决方案
注意:在你尝试使用此解决方案之前,请阅读“注意事项”部分。
将以下 bash 脚本保存在download-deps.sh
:
#!/bin/bash
VERSION=${1#"v"}
if [ -z "$VERSION" ]; then
echo "Please specify the Kubernetes version: e.g."
echo "./download-deps.sh v1.21.0"
exit 1
fi
set -euo pipefail
# Find out all the replaced imports, make a list of them.
MODS=($(
curl -sS "https://raw.githubusercontent.com/kubernetes/kubernetes/v${VERSION}/go.mod" |
sed -n 's|.*k8s.io/\(.*\) => ./staging/src/k8s.io/.*|k8s.io/\1|p'
))
# Now add those similar replace statements in the local go.mod file, but first find the version that
# the Kubernetes is using for them.
for MOD in "${MODS[@]}"; do
V=$(
go mod download -json "${MOD}@kubernetes-${VERSION}" |
sed -n 's|.*"Version": "\(.*\)".*|\1|p'
)
go mod edit "-replace=${MOD}=${MOD}@${V}"
done
go get "k8s.io/kubernetes@v${VERSION}"
go mod download
使脚本可用:
chmod u+x download-deps.sh
使用以下脚本。它将go.mod
使用所需的依赖项更新文件:
$ ./download-deps.sh v1.21.0
go: downloading k8s.io/kubernetes v1.21.0
go get: added k8s.io/kubernetes v1.21.0
现在你可以构建你的代码了!继续阅读以了解为什么会发生这种情况。
感谢Andy Bursavich编写上述脚本。我从这个评论中获取了脚本并做了一些小的修改。
问题分析
导致该错误k8s.io/api@v0.0.0: reading k8s.io/api/go.mod at revision v0.0.0: unknown revision v0.0.0
的原因是,是因为在 Kubernetes 中go.mod
,你会发现以下代码段:
require (
...
k8s.io/api v0.0.0
k8s.io/apiextensions-apiserver v0.0.0
k8s.io/apimachinery v0.0.0
k8s.io/apiserver v0.0.0
...
)
replace (
...
k8s.io/api => ./staging/src/k8s.io/api
k8s.io/apiextensions-apiserver => ./staging/src/k8s.io/apiextensions-apiserver
k8s.io/apimachinery => ./staging/src/k8s.io/apimachinery
k8s.io/apiserver => ./staging/src/k8s.io/apiserver
...
)
在 Kubernetes 存储库中,require
指令下列出的包被标记为伪版本v0.0.0
,但随后被staging
目录中的本地代码替换。
replace
指令在构建 Kubernetes 本身时工作正常,但在有人导入它时不起作用。Golang 文档说明了以下关于replace
指令的内容:
replace
指令仅适用于主模块的 go.mod 文件,在其他模块中被忽略。
通常,伪版本有一个 format v0.0.0-Time-commit_id
,但在这种情况下,它实际上是一个死胡同。为什么 Kubernetes 在他们的项目中做这样的事情?为什么不使用实际的伪版本格式进行标记?好吧,因为代码在本地可用。文件夹中的目录staging
作为独立项目发布,供人们以后使用。
所以最终效果是我正在尝试使用 导入代码go get
,但它被标记为伪版本v0.0.0
,因此go get
失败。这个问题的解决方案是自己找到这些依赖项的正确版本,并将它们添加到你的代码的go.mod
文件的replace
指令中。
这正是脚本正在做的事情。
最终的go.mod
所以在我们的项目中,go.mod
在运行脚本之前成功导入的样子是这样的:
module foobar
go 1.16
require (
github.com/spf13/cobra v1.1.3 // indirect
k8s.io/client-go v0.21.1 // indirect
)
运行脚本后,它是这样的:
module foobar
go 1.16
require (
github.com/spf13/cobra v1.1.3 // indirect
k8s.io/client-go v0.21.1 // indirect
k8s.io/kubernetes v1.21.0 // indirect
)
replace k8s.io/api => k8s.io/api v0.21.0
replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.21.0
replace k8s.io/apimachinery => k8s.io/apimachinery v0.21.1-rc.0
replace k8s.io/apiserver => k8s.io/apiserver v0.21.0
...
注意事项
Kubernetes upstream 警告不要像我们在此博客中所做的那样使用这些软件包(感谢Dims让我注意到这一点):
要将 Kubernetes 代码用作其他应用程序中的库,请参阅已发布组件列表。
不支持将
k8s.io/kubernetes
模块或k8s.io/kubernetes/...
包用作库。
如果你要导入Kubernetes upstream社区不支持作为库的包,那么你可能会追赶上来。不受支持的非库包可能会在没有任何公开警告的情况下更改 API 定义。如果你的生产代码依赖于这样的包,它可能会影响你。到那时,你无权指责Kubernetes upstream没有公开这些变化。
话虽如此,如果你发现有任何被广泛使用并应该发布的库,请将它们告知Kubernetes upstream社区。他们肯定会帮助出版它们。这通过使用来自广泛发布的地方的库并避免任何人和每个人赶上来帮助更广泛的社区。但是除非它们不作为库发布,否则会消耗大量代码。
参考链接:
https://github.com/kubernetes/kubernetes#to-start-using-k8s
https://github.com/kubernetes/kubernetes/blob/master/staging/README.md
https://pkg.go.dev/k8s.io/api?tab=versions
https://blog.wubw.fun/2021/12/build-kubernetes-scheduler-framework-plugin.html
https://suraj.io/post/2021/05/k8s-import/
https://github.com/kubernetes/kubernetes/issues/90358
https://github.com/kubernetes/kubernetes/issues/79384