随着容器化的兴起,将应用程序部署为容器并通过编排系统大规模管理它们变得至关重要。如果您已经熟悉容器化,我假设您是因为本文需要基本的容器化知识。您应该做的第一步是为您的应用程序创建容器映像。
由于容器化在 Docker 的帮助下变得越来越流行,我假设您必须至少运行一次“docker build”。老实说,Docker 并不是创建容器镜像的唯一方法,还有很多工具可以实现相同的目标,例如Kaniko,apko,img,[BuildKit]100,buildx,Podman,Buildah,KO,JIB,BuildPacks和更多。在本文中,我们不会将它们的优缺点相互比较,我们将专注于 ko 项目,并从最旧到最新讨论其功能。我也非常兴奋地谈论 ko 在供应链安全领域的最新改进。
首先,让我们解释一下这篇文章的目标受众。由于 ko 项目是特定于 Golang 的,因此本文主要面向 Golang 开发人员。不过,我想您对 自动 SBOM 生成、可重现的图像构建和 多平台图像感到好奇。因此,您仍有机会在本文中找到有用的信息。
Ko 是一个用于 Go 应用程序的简单、快速的容器镜像构建器。 Ko 还简化了容器镜像的构建,它安全地通过提供诸如不需要任何运行的守护进程、不需要 Dockerfile、提供构建信息以实现可重现性、和自动创建/推送 SBOM(软件物料清单),如果您不熟悉这些,请不要担心,今天,我们将总体解释所有这些,并在 Kubernetes 上部署 Go 应用程序环境轻松快捷。所以,在我看来,每个人都应该熟悉这个项目,尤其是如果你是一个 Golang 开发者。
我相信,一旦您通过本文发现了 ko 项目的能力,它将成为您的日常工具之一,因为 ko 使您无需创建 Dockerfiles 并通过让您可以自由地专注于您的代码而无需了解容器化细节担心如何从中制作容器映像,同时为您提供性能和出色的用户体验。
总结 ko 的一般特性,ko 是从go-containerregistry库中分离出来的,它可以帮助您与容器注册表和镜像进行交互。有一个很好的理由,ko 的大部分功能都是使用这个 Go 模块实现的。最值得注意的是,这是 ko 所做的: -使用 ko更快地将您的 Go 应用程序运送到 Cloud Run
-
从容器注册表下载基础镜像
-
静态编译你的 Go 二进制文件
-
使用 Go 二进制文件创建一个新的容器镜像层
-
将该层附加到基础镜像以创建新镜像
-
将新镜像推送到远程容器注册表
让我们毫不费力地安装 ko 二进制文件,开始发现上面提到的这些好机会。如果你在 macOS 环境中,安装 ko 最简单的方法是通过 brew,一个 macOS 的包管理器。
$ brew install ko
或者你可以通过 go install 命令安装它
$ go install github.com/google/ko@latest
除了这些方法之外,如果您在 GitHub Actions 平台上并且想要安装 ko,感谢@imjasonh,幸运的是,还有一个 setup-ko 操作可以让您在工作流程中轻松安装 ko 二进制文件,将以下这些行添加到您的工作流文件中以将 ko 安装到您的环境中:
jobs:
publish:
name: Publish
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v2
with:
go-version: 1.15
- uses: actions/checkout@v2
- uses: imjasonh/setup-ko@v0.4
- run: ko version
要开始使用 ko,我已经在 GitHub 上创建了一个示例存储库,您可以克隆这个项目并开始在这个项目上使用 ko。
$ git clone https://github.com/developer-guy/hello-world-ko.git
在使用 ko 创建容器镜像之前,我们应该讨论的最重要的事情是“导入路径”,它位于 ko 的心中。一个这样的 Go 习惯用法是二进制文件被“import paths”引用; ko 也使用相同的模式来引用 Go 应用程序,所以一旦你通过 go mod init 初始化你的 Go 模块,你放在命令后面的名称将是你的导入路径,并且将由 ko 使用,同时给你的容器图像一个标签:
$ go mod init <import path> # github.com/developer-guy/hello-world-ko
在最基本的形式中,要构建和推送容器镜像,您需要做的就是运行以下这两行命令:
$ export KO_DOCKER_REPO=gcr.io/YOUR_PROJECT/my-app
$ ko build <import_path>
就是这样🤘
让我们为我们克隆的示例存储库构建并推送我们的第一个容器映像。
$ cd hello-world-ko
$ export KO_DOCKER_REPO=devopps # it will use DockerHub (docker.io) as a registry by default.
However, if you want to publish it to another registry, you can specify it as gcr.io/devopps for Google Container Registry.
By the way, devopps is my DockerHub username, don’t forget to set yours!
$ ko build github.com/developer-guy/hello-world-ko
2022/05/17 20:47:23 Using base gcr.io/distroless/static:nonroot@sha256:2556293984c5738fc75208cce52cf0a4762c709cf38e4bf8def65a61992da0ad for github.com/developer-guy/hello-world-ko
2022/05/17 20:47:24 Building github.com/developer-guy/hello-world-ko for linux/amd64
2022/05/17 20:47:49 Publishing index.docker.io/devopps/hello-world-ko-82db161e90a446983324a549d87a7dad:latest
2022/05/17 20:47:52 pushed blob: sha256:135ce68eadadd1473e0f5442fcf80f3308da789dd011549811c46122a3df26c2
2022/05/17 20:47:52 pushed blob: sha256:f4579510596da4c61433d328130bbdc920885626bb7bf130a525056bd7ce49dd
2022/05/17 20:47:52 index.docker.io/devopps/hello-world-ko-82db161e90a446983324a549d87a7dad:sha256-991b864323bdbdeca7ff349a37409f088cf7de718a17c6a677d20c978b648a39.sbom: digest: sha256:330c1b141d2a007145e4ae1701aa766f0b21fd3e6ce3fc65bc6b631fc7470c76 size: 367
2022/05/17 20:47:52 Published SBOM index.docker.io/devopps/hello-world-ko-82db161e90a446983324a549d87a7dad:sha256-991b864323bdbdeca7ff349a37409f088cf7de718a17c6a677d20c978b648a39.sbom
2022/05/17 20:47:54 pushed blob: sha256:250c06f7c38e52dc77e5c7586c3e40280dc7ff9bb9007c396e06d96736cf8542
2022/05/17 20:47:54 pushed blob: sha256:6d495c0263b798b4a4197f07c297cb8a0c2de4b371ced69d2801c1565c5e00d6
2022/05/17 20:47:58 pushed blob: sha256:ea76d64477d62cd7ef8cb9a737c115dbc48aea091d37f5845c7db145caf970d9
2022/05/17 20:48:00 pushed blob: sha256:36698cfa5275e0bda70b0f864b7b174e0758ca122d8c6a54fb329d70082c73f8
2022/05/17 20:48:00 index.docker.io/devopps/hello-world-ko-82db161e90a446983324a549d87a7dad:latest: digest: sha256:991b864323bdbdeca7ff349a37409f088cf7de718a17c6a677d20c978b648a39 size: 751
2022/05/17 20:48:00 Published index.docker.io/devopps/hello-world-ko-82db161e90a446983324a549d87a7dad@sha256:991b864323bdbdeca7ff349a37409f088cf7de718a17c6a677d20c978b648a39
index.docker.io/devopps/hello-world-ko-82db161e90a446983324a549d87a7dad@sha256:991b864323bdbdeca7ff349a37409f088cf7de718a17c6a677d20c978b648a39
让我们继续分析上面的输出,因为我们可以从中学到一些东西。
首先,您可能会注意到 ko 使用来自Distroless映像集合(gcr.io/distroless/static:nonroot映像)的安全且精简的基础映像,该映像不包含 shell 或其他可执行文件到减少容器的攻击面,正如我们前面提到的,ko 关心安全。您可以使用另一个名为“KO_DOCKER_REPO”的环境变量来覆盖此行为。 -覆盖基础图像
接下来,ko默认将容器镜像推送到我们在“KO_DOCKER_REPO”环境变量中定义的注册表中,如果要禁用它,应该使用“--pushu003dfalse*” *“ 为了这。 ko 还可以通过设置“KO_DOCKER_REPOu003dko.local”或传递“--local (-L)**”标志将图像加载到本地 Docker 守护程序(如果可用) .
ko 取决于在 Docker 配置中配置的身份验证(通常是 ~/.docker/config.json)。如果您可以使用 docker push 推送图像,则您已经通过 ko 身份验证。 -认证
接下来,为了保护您的软件供应链,不可避免地,它应该从了解正在使用的软件开始。所以,你必须列出你的软件是由什么组成的,比如库、依赖项、包等,让我们简称他们,软件成分。这个“成分”列表被称为软件材料清单 (SBOM)。从技术上讲,“软件物料清单 (SBOM)”是构建(即编译和链接)给定软件和供应所需的组件、库和模块的完整、正式结构化列表链。
如果您想在这里更进一步,您应该考虑查看awesome-sbom存储库以获取相关工具、框架、博客、播客和文章!由Batuhan Apaydın(a.k.a developer-guy) 维护
ko 提供的最令人兴奋的功能是 ko 生成一个 SBOM 并将其与图像一起发布到注册表,从输出中可以看到,有一行“Published SBOM..”,这意味着 ko默认启用此功能,要禁用此功能,应指定“--sbom”为false。如果要查看 SBOM 文件,可以使用另一个名为起重机的工具与远程图像和注册表进行交互。或者另一种方法,如果您要查看推送图像的 SBOM,您已经可以使用 cosign download sbom ... 或 ko deps (即时生成它并且不会推送任何新的 SBOM),请参阅。如果您想了解更多关于 ko 中自动 SBOM 生成功能的信息,您可以在Chainguard 博客上阅读Matt Moore撰写的一篇很棒的文章。
cosign 是一个用于在 OCI 注册表中进行容器签名、验证和存储的工具。要安装它,请参考。到安装页面。
$ crane ls index.docker.io/devopps/hello-world-ko-82db161e90a446983324a549d87a7dad
latest
sha256-991b864323bdbdeca7ff349a37409f088cf7de718a17c6a677d20c978b648a39.sbom
$ crane manifest index.docker.io/devopps/hello-world-ko-82db161e90a446983324a549d87a7dad:sha256-991b864323bdbdeca7ff349a37409f088cf7de718a17c6a677d20c978b648a39.sbom | jq
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 233,
"digest": "sha256:135ce68eadadd1473e0f5442fcf80f3308da789dd011549811c46122a3df26c2"
},
"layers": [
{
"mediaType": "text/spdx",
"size": 953,
"digest": "sha256:f4579510596da4c61433d328130bbdc920885626bb7bf130a525056bd7ce49dd"
}
]
}
$ crane blob index.docker.io/devopps/hello-world-ko-82db161e90a446983324a549d87a7dad@sha256:f4579510596da4c61433d328130bbdc920885626bb7bf130a525056bd7ce49dd
SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: github.com/developer-guy/hello-world-ko
DocumentNamespace: http://spdx.org/spdxpackages/github.com/developer-guy/hello-world-ko
Creator: Tool: ko v0.11.2
Created: 1970-01-01T00:00:00Z
##### Package representing github.com/developer-guy/hello-world-ko
PackageName: github.com/developer-guy/hello-world-ko
SPDXID: SPDXRef-Package-github.com.developer-guy.hello-world-ko
PackageSupplier: Organization: github.com/developer-guy/hello-world-ko
PackageDownloadLocation: https://github.com/developer-guy/hello-world-ko
FilesAnalyzed: false
PackageHomePage: https://github.com/developer-guy/hello-world-ko
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageCopyrightText: NOASSERTION
PackageLicenseComments: NOASSERTION
PackageComment: NOASSERTION
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-github.com.developer-guy.hello-world-ko
最后但同样重要的是,最终的镜像(index.docker.io/devopps/hello-world-ko-82db..)可能看起来有点奇怪,因为它提供了一些不同的策略来命名它推送的镜像,但是如果您没有为此指定任何内容,ko 在图像名称中添加 md5 部分,为了克服这个问题以获得更好的图像名称,我们可以使用“-B (--base-import-paths)”省略 md5 部分。 -命名图像
到目前为止,我们已经在最基本的层面上进行了交谈。接下来,让我们发现 ko 中其他可用的强大功能。
Ko 还擅长构建多平台容器镜像。要使用 ko 构建多平台容器映像,您唯一需要做的就是在您的目标操作系统和架构中添加一个名为“--platform”的标志,例如 *linux/amd64 *,因为 Go 原生支持交叉编译到其他 CPU 架构和操作系统。
$ ko build -B --platform linux/amd64,linux/arm64 --tags multiarch github.com/developer-guy/hello-world-ko 2022/05/17 21:56:51 Using base gcr.io/distroless/static:nonroot@sha256:2556293984c5738fc75208cce52cf0a4762c709cf38e4bf8def65a61992da0ad for github.com/developer-guy/hello-world-ko 2022/05/17 21:56:52 Building github.com/developer-guy/hello-world-ko for linux/amd64 2022/05/17 21:56:52 Building github.com/developer-guy/hello-world-ko for linux/arm64
…
$ crane manifest index.docker.io/devopps/hello-world-ko:multiarch | jq
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 751,
"digest": "sha256:991b864323bdbdeca7ff349a37409f088cf7de718a17c6a677d20c978b648a39",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 751,
"digest": "sha256:91523e52f520098faf21eef68db6a63caba9c1ba00bcd16e554fe4972ac0acec",
"platform": {
"architecture": "arm64",
"os": "linux"
}
}
]
}
ko 支持的最新功能之一是 ko 允许您在其配置文件中覆盖 Go 构建设置,顺便说一下,ko 还提供了一种通过名为“.ko.yaml*”的配置文件配置 ko 行为的方法*”或您在“KO_CONFIG_PATH”环境变量中指定的文件。默认情况下,ko 构建二进制文件时除了“-trimpath**”之外没有其他构建标志。您可以通过在 .ko.yaml 中使用受 GoReleaser 影响的构建配置部分提供构建标志和 ldflags 来替换默认构建参数。 -覆盖 Go 构建设置
现在我们来到我最喜欢的部分,默认情况下为图像清单设置基本图像注释以实现可重复性。 Jason Hall (@imjasonh) 付出了巨大的努力,他创建了一个 PR 来添加一条关于在构建映像时使用哪个基础映像的信息到Open Container Initiative的image-spec,要了解有关 PR 的更多详细信息,请参见。对于可能想要对这些基础镜像采取行动的人来说,这是一个重要的里程碑,因为大多数漏洞都来自这些镜像,这里的问题是,一旦构建了镜像,关于基础镜像的信息就会完全丢失,这一点很重要改变,现在,我们可以通过下面的这些注解来捕获基础信息:
-
org.opencontainers.image.base.name:对基础镜像的可变引用(例如,docker.io/alpine:3.14)
-
org.opencontainers.image.base.digest:在基础镜像上构建镜像时的不可变摘要(例如,sha256:adab384...)
如果你的基础镜像是 docker 镜像而不是 OCI 镜像,ko 会设置这些注解,如果它是单一平台镜像,它不支持注解,并且一些注册中心会拒绝它。所以你可以 --platformu003dall 并且应该取回注释。
除了 ko 获得的所有这些好处之外,ko 还包括对简单 YAML 模板的支持,这使其成为 Kubernetes 应用程序的强大工具,这意味着您可以替换“.spec.template.spec.xml”中的图像引用。 container.image” 以“ko://”为前缀,如下所示:
...
spec:
containers:
- name: my-app
image: ko://github.com/my-user/my-repo/cmd/app
现在,你唯一需要做的就是构建,将容器镜像推送到注册表,然后在 Kubernetes 上部署应用程序是运行“ko apply -f .”就是这样😎
结论
感谢 ko,您无需再将 Docker 安装到您的环境中并编写任何 Dockerfile,您仍然可以构建容器镜像并将其推送到您最喜欢的注册表中。总之,ko 是等待你发现的可用的伟大工具之一🧭