ko

在 Kubernetes 上构建和部署 Go 应用。「Build and deploy Go applications on Kubernetes」

Github stars Tracking Chart

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, or
  • KO_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 二进制文件的必要条件。

你可以通过两种方式覆盖这个基础映像。

  1. 要覆盖所有 ko 构建的基础映像,请在你的 .ko.yaml 文件中添加这一行:
defaultBaseImage: registry.example.com/base/image
  1. 要覆盖某些 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。

  1. 扫描 YAML 文件中带有 ko:// 前缀的值,
  2. 对于每个独特的 ko://-前缀的字符串,执行 ko publish <importpath> 来构建和推送一个映像,
  3. 将输入YAML中的 ko://-前缀的字符串替换为已构建映像的完全指定的映像参考,例如:
spec:
  containers:
    - name: my-app
      image: registry.example.com/github.com/my-user/my-repo/cmd/app@sha256:deadb33f...
  1. 将解析后的 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 内部注册表一起工作吗?

是的! 遵循这些步骤:

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 构建 DockerKubernetes 支持所获得的经验。这项工作在 介绍过。

讨论

有问题吗?评论?想法?请在 Kubernetes Slack 的 #ko-project 频道中与我们讨论 ko! 那里见!


(The first version translated by vz on 2020.12.12)
(vz Revised on 2021.06.12)

Overview

Name With Ownerko-build/ko
Primary LanguageGo
Program languageGo (Language Count: 2)
PlatformKubernetes, Linux, Mac
License:Apache License 2.0
Release Count32
Last Release Namev0.15.2 (Posted on )
First Release Namev0.1 (Posted on )
Created At2019-03-21 19:24:01
Pushed At2024-04-25 07:12:22
Last Commit At
Stargazers Count7.2k
Watchers Count44
Fork Count373
Commits Count1k
Has Issues Enabled
Issues Count373
Issue Open Count53
Pull Requests Count765
Pull Requests Open Count12
Pull Requests Close Count138
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private

ko: Easy Go Containers

Travis Build Status
GitHub Actions Build Status
GoDoc
Go Report Card

ko is a simple, fast container image builder for Go applications.

It's ideal for use cases where your image contains a single Go application
without any/many dependencies on the OS base image (e.g., no cgo, no OS package
dependencies).

ko builds images by effectively executing go build on your local machine,
and as such doesn't require docker to be installed. This can make it a good
fit for lightweight CI/CD use cases.

ko also includes support for simple YAML templating which makes it a powerful
tool for Kubernetes applications (See below).

Setup

Install

Install from Releases

VERSION=TODO # choose the latest version
OS=Linux     # or Darwin
ARCH=x86_64  # or 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

Install using Homebrew

brew install ko

Build and Install from Source

With Go 1.16+, build and install the latest released version:

go install github.com/google/ko

Authenticate

ko depends on the authentication configured in your Docker config (typically
~/.docker/config.json). If you can push an image with docker push, you are
already authenticated for ko.

Since ko doesn't require docker, ko login also provides a surface for
logging in to a container image registry with a username and password, similar
to
docker login.

Choose Destination

ko depends on an environment variable, KO_DOCKER_REPO, to identify where it
should push images that it builds. Typically this will be a remote registry,
e.g.:

  • KO_DOCKER_REPO=gcr.io/my-project, or
  • KO_DOCKER_REPO=my-dockerhub-user

Build an Image

ko publish ./cmd/app builds and pushes a container image, and prints the
resulting image digest to stdout.

ko publish ./cmd/app
...
gcr.io/my-project/app-099ba5bcefdead87f92606265fb99ac0@sha256:6e398316742b7aa4a93161dce4a23bc5c545700b862b43347b941000b112ec3e

Because the output of ko publish is an image reference, you can easily pass it
to other tools that expect to take an image reference:

To run the container:

docker run -p 8080:8080 $(ko publish ./cmd/app)

Or, for example, to deploy it to other services like
Cloud Run:

gcloud run deploy --image=$(ko publish ./cmd/app)

Configuration

Aside from KO_DOCKER_REPO, you can configure ko's behavior using a
.ko.yaml file. The location of this file can be overridden with
KO_CONFIG_PATH.

Overriding Base Images

By default, ko bases images on gcr.io/distroless/static:nonroot. This is a
small image that provides the bare necessities to run your Go binary.

You can override this base image in two ways:

  1. To override the base image for all images ko builds, add this line to your
    .ko.yaml file:
defaultBaseImage: registry.example.com/base/image
  1. To override the base image for certain 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

Naming Images

ko provides a few different strategies for naming the image it pushes, to
workaround certain registry limitations and user preferences:

Given KO_DOCKER_REPO=registry.example.com/repo, by default,
ko publish ./cmd/app will produce an image named like
registry.example.com/repo/app-<md5>, which includes the MD5 hash of the full
import path, to avoid collisions.

  • --preserve-import-path (-P) will include the entire importpath:
    registry.example.com/repo/github.com/my-user/my-repo/cmd/app
  • --base-import-paths (-B) will omit the MD5 portion:
    registry.example.com/repo/app
  • --bare will only include the KO_DOCKER_REPO: registry.example.com/repo

Local Publishing Options

ko is normally used to publish images to container image registries,
identified by KO_DOCKER_REPO.

ko can also publish images to a local Docker daemon, if available, by setting
KO_DOCKER_REPO=ko.local, or by passing the --local (-L) flag.

Locally-published images can be used as a base image for other ko images:

defaultBaseImage: ko.local/example/base/image

ko can also publish images to a local KinD
cluster, if available, by setting KO_DOCKER_REPO=kind.local. By default this
publishes to the default KinD cluster name (kind). To publish to another KinD
cluster, set KIND_CLUSTER_NAME=my-other-cluster.

Multi-Platform Images

Because Go supports cross-compilation to other CPU architectures and operating
systems, ko excels at producing multi-platform images.

To build and push an image for all platforms supported by the configured base
image, simply add --platform=all. This will instruct ko to look up all the
supported platforms in the base image, execute
GOOS=<os> GOARCH=<arch> GOARM=<variant> go build for each platform, and
produce a manifest list containing an image for each platform.

You can also select specific platforms, for example,
--platform=linux/amd64,linux/arm64

Static Assets

ko can also bundle static assets into the images it produces.

By convention, any contents of a directory named <importpath>/kodata/ will be
bundled into the image, and the path where it's available in the image will be
identified by the environment variable KO_DATA_PATH.

As an example, you can bundle and serve static contents in your image:

cmd/
  app/
    main.go
    kodata/
      favicon.ico
      index.html

Then, in your main.go:

func main() {
    http.Handle("/", http.FileServer(http.Dir(os.Getenv("KO_DATA_PATH"))))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

You can simulate ko's behavior outside of the container image by setting the
KO_DATA_PATH environment variable yourself:

KO_DATA_PATH=cmd/app/kodata/ go run ./cmd/app

Tip: Symlinks in kodata are followed and included as well. For example,
you can include Git commit information in your image with:

ln -s -r .git/HEAD ./cmd/app/kodata/

Kubernetes Integration

You could stop at just building and pushing images.

But, because building images is so easy with ko, and because building with
ko only requires a string importpath to identify the image, we can integrate
this with YAML generation to make Kubernetes use cases much simpler.

YAML Changes

Traditionally, you might have a Kubernetes deployment, defined in a YAML file,
that runs an image:

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

...which you apply to your cluster with kubectl apply:

kubectl apply -f deployment.yaml

With ko, you can instead reference your Go binary by its importpath, prefixed
with ko://:

    ...
    spec:
      containers:
      - name: my-app
        image: ko://github.com/my-user/my-repo/cmd/app

ko resolve

With this small change, running ko resolve -f deployment.yaml will instruct
ko to:

  1. scan the YAML file(s) for values with the ko:// prefix,
  2. for each unique ko://-prefixed string, execute ko publish <importpath> to
    build and push an image,
  3. replace ko://-prefixed string(s) in the input YAML with the fully-specified
    image reference of the built image(s), for example:
spec:
  containers:
    - name: my-app
      image: registry.example.com/github.com/my-user/my-repo/cmd/app@sha256:deadb33f...
  1. Print the resulting resolved YAML to stdout.

The result can be redirected to a file, to distribute to others:

ko resolve -f config/ > release.yaml

Taken together, ko resolve aims to make packaging, pushing, and referencing
container images an invisible implementation detail of your Kubernetes
deployment, and let you focus on writing code in Go.

ko apply

To apply the resulting resolved YAML config, you can redirect the output of
ko resolve to kubectl apply:

ko resolve -f config/ | kubectl apply -f -

Since this is a relatively common use case, the same functionality is available
using ko apply:

ko apply -f config/

NB: This requires that kubectl is available.

ko delete

To teardown resources applied using ko apply, you can run ko delete:

ko delete -f config/

This is purely a convenient alias for kubectl delete, and doesn't perform any
builds, or delete any previously built images.

Frequently Asked Questions

How can I set ldflags?

Using -ldflags
is a common way to embed version info in go binaries (In fact, we do this for
ko!). Unfortunately, because ko wraps go build, it's not possible to use
this flag directly; however, you can use the GOFLAGS environment variable
instead:

GOFLAGS="-ldflags=-X=main.version=1.2.3" ko publish .

Why are my images all created in 1970?

In order to support reproducible builds, ko
doesn't embed timestamps in the images it produces by default; however, ko
does respect the
SOURCE_DATE_EPOCH
environment variable.

For example, you can set this to the current timestamp by executing:

export SOURCE_DATE_EPOCH=$(date +%s)

or to the latest git commit's timestamp with:

export SOURCE_DATE_EPOCH=$(git log -1 --format='%ct')

Can I optimize images for eStargz support?

Yes! Set the environment variable GGCR_EXPERIMENT_ESTARGZ=1 to produce
eStargz-optimized images.

Does ko support autocompletion?

Yes! ko completion generates a Bash completion script, which you can add to
your bash_completion directory:

ko completion > /usr/local/etc/bash_completion.d/ko

Or, you can source it directly:

source <(ko completion)

Does ko work with Kustomize?

Yes! ko resolve -f - will read and process input from stdin, so you can have
ko easily process the output of the kustomize command.

kustomize build config | ko resolve -f -

Does ko work with OpenShift Internal Registry?

Yes! Follow these steps:

oc registry login --to=$HOME/.docker/config.json
  • Create a namespace where you will push your images, i.e: ko-images
  • Execute this command to set KO_DOCKER_REPO to publish images to the internal
    registry.
   export KO_DOCKER_REPO=$(oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}')/ko-images

Acknowledgements

This work is based heavily on learnings from having built the
Docker and
Kubernetes support for
Bazel. That work was presented
here.

Discuss

Questions? Comments? Ideas? Come discuss ko with us in the #ko-project
channel on the Kubernetes Slack! See you there!

To the top