ko: Easy Go Containers
ko 是一个简单、快速的 Go 应用程序容器映像生成器。
它非常适用于这样的情况:你的映像包含一个单一的 Go 应用程序,而没有任何/许多对操作系统基础映像的依赖(例如,没有 cgo,没有操作系统包的依赖)。
ko 通过在你的本地机器上有效执行 go build 来构建映像,因此不需要安装docker。这使得它很适合于轻量级的 CI/CD 使用案例。
ko 还包括对简单 YAML 模板的支持,这使得它成为 Kubernetes 应用程序的强大工具(见下文)。
设置
安装
从 发行版 安装
VERSION=TODO # 选择最新版本 OS=Linux # 或 Darwin ARCH=x86_64 # 或 arm64, i386, s390x curl -L https://github.com/google/ko/releases/download/v${VERSION}/ko_${VERSION}_${OS}_${ARCH}.tar.gz | tar xzf - ko chmod +x ./ko
使用 Homebrew 安装
brew install ko
从源代码构建和安装
使用 Go 1.16 以上版本,构建并安装最新发布的版本:
go install github.com/google/ko
认证
ko 取决于你的 Docker 配置(通常是 ~/.docker/config.json)中所配置的认证。如果你能用 docker push 推送一个映像,你就已经为 ko 认证了。
由于 ko 不需要 docker,ko login 也提供了一个用用户名和密码登录到容器映像注册表的界面,类似于 docker login
。
选择目标位置
ko 依赖于一个环境变量 KO_DOCKER_REPO,以确定它应该把构建的映像推送到哪里。通常,这将是一个远程注册表,例如:
KO_DOCKER_REPO=gcr.io/my-project
, orKO_DOCKER_REPO=my-dockerhub-user
构建一个映像
ko publish ./cmd/app 构建并推送一个容器映像,并将生成的映像摘要打印到stdout。
ko publish ./cmd/app ... gcr.io/my-project/app-099ba5bcefdead87f92606265fb99ac0@sha256:6e398316742b7aa4a93161dce4a23bc5c545700b862b43347b941000b112ec3e
因为 ko publish 的输出是一个映像参考,你可以很容易地把它传递给其他期望得到映像参考的工具。
要运行这个容器:
docker run -p 8080:8080 $(ko publish ./cmd/app)
或者,举例来说,将其部署到其他服务中,如 Cloud Run。
gcloud run deploy --image=$(ko publish ./cmd/app)
配置
除了 KO_DOCKER_REPO 之外,你可以使用 .ko.yaml 文件来配置 ko 的行为。这个文件的位置可以用 KO_CONFIG_PATH 来重写。
重写基础映像
默认情况下,ko 将映像建立在 gcr.io/distroless/static:nonroot 上。这是一个小型映像,提供了运行 Go 二进制文件的必要条件。
你可以通过两种方式覆盖这个基础映像。
- 要覆盖所有 ko 构建的基础映像,请在你的
.ko.yaml
文件中添加这一行:
defaultBaseImage: registry.example.com/base/image
- 要覆盖某些 importpaths 的基础映像:
baseImageOverrides: github.com/my-user/my-repo/cmd/app: registry.example.com/base/for/app github.com/my-user/my-repo/cmd/foo: registry.example.com/base/for/foo
命名映像
ko 提供了一些不同的策略来命名它所推送的映像,以解决某些注册表的限制和用户的偏好。
鉴于 KO_DOCKER_REPO=registry.example.com/repo,默认情况下,ko publish ./cmd/app 将产生一个名为 registry.example.com/repo/app-<md5> 的映像,其中包括完整导入路径的 MD5 散列,以避免碰撞。
--preserve-import-path (-P) 将包括整个 importpath:registry.example.com/repo/github.com/my-user/my-repo/cmd/app
--base-import-paths (-B) 将省略 MD5 部分:registry.example.com/repo/app
--bare 将只包括 KO_DOCKER_REPO:registry.example.com/repo
本地发布选项
ko 通常用于向容器映像注册表发布映像,由 KO_DOCKER_REPO 识别。
ko 也可以通过设置 KO_DOCKER_REPO=ko.local,或通过 --local (-L) 标志,将映像发布到本地 Docker 守护进程(如果有)。
本地发布的映像可以作为其他 ko 映像的基础映像:
defaultBaseImage: ko.local/example/base/image
ko 也可以通过设置 KO_DOCKER_REPO=kind.local 将映像发布到本地 KinD 集群(如果有)。默认情况下,这将发布到默认的 KinD 集群名称(kind)。要发布到另一个 KinD 集群,设置 KIND_CLUSTER_NAME=my-other-cluster。
多平台映像
由于 Go 支持交叉编译到其他 CPU 架构和操作系统,ko 擅长制作多平台映像。
要为配置的基本映像所支持的所有平台构建和推送映像,只需添加 --platform=all。这将指示 ko 在基本映像中查找所有支持的平台,执行 GOOS=<os> GOARCH=<arch> GOARM=<variant> 为每个平台进行构建,并产生一个包含每个平台映像的清单。
您也可以选择特定的平台,例如, --platform=linux/amd64,linux/arm64
静态资产
ko 还可以将静态资产捆绑到它生成的映像中。
根据惯例,名为 <importpath>/kodata/ 的目录的任何内容都将被捆绑到映像中,而映像中可用的路径将由环境变量KO_DATA_PATH确定。
<importpath>作为一个例子,你可以在你的映像中捆绑和提供静态内容。
cmd/ app/ main.go kodata/ favicon.ico index.html
然后,在你的 main.go 中:
func main() { http.Handle("/", http.FileServer(http.Dir(os.Getenv("KO_DATA_PATH")))) log.Fatal(http.ListenAndServe(":8080", nil)) }
你可以通过自己设置 KO_DATA_PATH 环境变量来模拟 ko 在容器映像之外的行为。
KO_DATA_PATH=cmd/app/kodata/ go run ./cmd/app
提示:kodata 中的符号链接也会被跟踪和包含。例如,你可以用以下方法在映像中包括 Git 提交信息。
ln -s -r .git/HEAD ./cmd/app/kodata/
Kubernetes 集成
你可以只停留在构建和推送映像上。
但是,由于使用 ko 构建映像非常容易,而且使用 ko 构建只需要一个字符串导入路径来识别映像,我们可以将其与 YAML 生成整合起来,使 Kubernetes 的使用情况更加简单。
YAML 变更
传统上,你可能有一个 Kubernetes 部署,在 YAML 文件中定义,运行一个映像:
apiVersion: apps/v1 kind: Deployment metadata: name: my-deployment spec: replicas: 3 ... template: spec: containers: - name: my-app image: registry.example.com/my-app:v1.2.3
...你可以用 kubectl apply 把它应用到你的集群上:
kubectl apply -f deployment.yaml
使用 ko,你可以通过导入路径来引用 Go 的二进制文件,前缀为 ko://。
... spec: containers: - name: my-app image: ko://github.com/my-user/my-repo/cmd/app
ko resolve
有了这个小改动,运行 ko resolve -f deployment.yaml 将指示 ko。
- 扫描 YAML 文件中带有
ko://
前缀的值, - 对于每个独特的
ko://
-前缀的字符串,执行ko publish <importpath>
来构建和推送一个映像, - 将输入YAML中的
ko://
-前缀的字符串替换为已构建映像的完全指定的映像参考,例如:
spec: containers: - name: my-app image: registry.example.com/github.com/my-user/my-repo/cmd/app@sha256:deadb33f...
- 将解析后的 YAML 打印到 stdout。
结果可以重定向到一个文件,以分发给其他人:
ko resolve -f config/ > release.yaml
综上所述,ko resolve 旨在使打包、推送和引用容器映像成为 Kubernetes 部署中不可见的实施细节,并让你专注于用 Go 编写代码。
ko apply
要应用所解析的 YAML 配置,你可以将 ko resolve 的输出重定向到 kubectl apply。
ko resolve -f config/ | kubectl apply -f -
由于这是一个比较常见的用例,同样的功能可以用 ko apply 来实现。
ko apply -f config/
注意:这需要 kubectl 是可用的。
ko delete
要拆掉使用 ko apply 应用的资源,你可以运行 ko delete。
ko delete -f config/
这纯粹是 kubectl 删除的一个方便的别名,并不执行任何构建,或删除任何先前构建的映像。
常见问题
如何设置 ldflags?
使用 -ldflags 是在 go 二进制文件中嵌入版本信息的一种常见方式(事实上,我们为 ko 做了这个!)。不幸的是,由于 ko 封装了 go 的构建,所以不可能直接使用这个标志;不过,你可以使用 GOFLAGS 环境变量来代替。
GOFLAGS="-ldflags=-X=main.version=1.2.3" ko publish .
为什么我的映像都是 1970 年创建的?
为了支持可重复的构建,ko 默认不会在生成的映像中嵌入时间戳;然而,ko 确实尊重 SOURCE_DATE_EPOCH
环境变量。
例如,您可以通过执行以下命令将其设置为当前时间戳:
export SOURCE_DATE_EPOCH=$(date +%s)
或最新的 git 提交的时间戳:
export SOURCE_DATE_EPOCH=$(git log -1 --format='%ct')
我可以优化映像以 支持 eStargz吗?
可以! 设置环境变量 GGCR_EXPERIMENT_ESTARGZ=1 来生成 eStargz 优化的映像。
ko 支持自动完成吗?
是的!ko 完成会生成一个 Bash 完成脚本,你可以把它添加到你的 bash_completion 目录中。
ko completion > /usr/local/etc/bash_completion.d/ko
或者,你可以直接将它作为源码。
source <(ko completion)
ko 能与 Kustomize 一起工作吗?
是的!ko resolve -f - 将从 stdin 读取和处理输入,所以你可以让 ko 轻松地处理 kustomize 命令的输出。
kustomize build config | ko resolve -f -
ko 能与 OpenShift 内部注册表一起工作吗?
是的! 遵循这些步骤:
- 连接到你的 OpenShift 安装: https://docs.openshift.com/container-platform/4.7/cli_reference/openshift_cli/getting-started-cli.html#cli-logging-in_cli-developer-commands
- 暴露 OpenShift 内部注册表,以便你可以推送到它: https://docs.openshift.com/container-platform/4.7/registry/securing-exposing-registry.html
- 将你的令牌导出到
$HOME/.docker/config.json
:
oc registry login --to=$HOME/.docker/config.json
- 创建一个命名空间来推送你的图片,例如:
ko-images
- 执行这个命令来设置
KO_DOCKER_REPO
,以便将映像发布到内部注册表。
export KO_DOCKER_REPO=$(oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}')/ko-images
鸣谢
这项工作在很大程度上是基于为 Bazel 构建 Docker 和 Kubernetes 支持所获得的经验。这项工作在 此介绍过。
讨论
有问题吗?评论?想法?请在 Kubernetes Slack 的 #ko-project 频道中与我们讨论 ko! 那里见!
(The first version translated by vz on 2020.12.12)
(vz Revised on 2021.06.12)