
容器镜像是打包应用程序的一种流行的标准格式。
容器镜像将一个程序及其依赖关系bundle到root文件系统下的一个单独的artifact中。
容器镜像是根据Container Image Format(CIF)的规范创建的,CIF由Open Container Initiative (OCI)成立。
CIF 是 Docker、 Kubernetes 和其他容器编排平台使用的容器镜像格式。
DockerFile 是构建容器镜像最常用的机制。它是 Dockerfile 格式和 Dockerfile 语法的组合。Dockerfile 是一个描述如何构建容器镜像的text文件。它指定一个基本镜像以及在其上层执行的一系列指令。这些指令是用类似于 shell 命令的简单语言编写的。Docker 构建引擎处理 docker 文件,以生成符合 OCI 规范的容器镜像。
使用DockerFile 带来的挑战
然而,使用 Dockerfile 生成容器镜像存在以下问题:
- 每个开发团队为每个deployable unit(可部署单元)创建一个 Dockerfile。base image(基础镜像)和包含的framework对于每个镜像都是不同的。这使得管理和维护变得困难。
- 非标准的 docker 文件无法让构建“不可变”(non-reproducible)。比如使用最新tag或 apt-get 安装软件包。
- 镜像可能没有被优化。
- 镜像可能不安全。供应商为 CVE 发布的安全补丁不会自动应用于镜像。
- 用于生成镜像的 docker 引擎容易受到漏洞的影响。
- 运维痛点不断干扰开发循环(development loop),这对开发团队来说并不理想。
- 手动创建 Docker 文件导致的人为错误。
下面是开发团队使用 docker 文件创建容器镜像的一个简单示例。

上面的每个容器都是一个单独的镜像。这些镜像打包了一个基于 Linux 节点的应用程序。每个镜像使用不同的 Linux版本和不同的发行版。这些镜像还使用应用程序的不同版本的依赖关系,比如Node。这些镜像没有标准化,没有针对性能进行优化,它们不可重复,不安全,而且镜像没有针对运维痛点进行优化。任何基本镜像或其依赖项中的安全漏洞(CVE)都需要运维和开发团队检查所有镜像是否存在漏洞,并手动应用补丁。这都不能很好地进行伸缩,并且容易出现错误和故障。
Cloud Native Buildpacks
对于上述问题,市面上有一系列的解决方案,Cloud Native Buildpacks就是其中一个。Cloud Native Buildpacks是 Heroku 2011年发布的 Buildpacks 升级版。Heroku 和 Pivotal 合作创建了 Cloud Native BuildPacks 解决方案。Cloud Native Buildpacks是一组用于构建容器镜像的工具,它通过创建一个标准化、自动化的流程来构建容器镜像。Cloud Native Buildpacks 生成的容器镜像符合 OCI 镜像规范,可以在任何容器平台上运行。这些镜像的性能、可重复性、安全性、针对运维痛点都得到了优化,提升了打包流程(应用-->容器镜像),提供流水线的开发体验,是最佳实践的容器镜像工具。

与 Dockerfiles 相比,Buildpacks 提供了更高层次的抽象。它无需开发团队使用 Dockerfile 手动创建容器镜像。Cloud Native Buildpacks 分析应用程序的源代码,以确定最佳的构建包,并自动生成容器镜像。它与语言无关,可用于为多种语言开发的应用程序构建容器镜像,如Java, .NET Core,Ruby,Node.js,Go,Python 等等。开发团队可以专注于应用,而不用担心容器镜像的细节。它允许运维人员专注于runtime的技术支持和DAY 2运维,而开发人员可以专注于应用的代码。它减少了容器构造的负担,允许开发团队关注代码速度。在扩展规模上,它为安全运维团队提供了实现安全性和遵从性的简化途径。
Buildpacks 提供以下好处:
- 不再需要开发团队使用 Dockerfile 手动创建容器镜像。
- 它与语言无关,可为多种开发语言的应用程序构建容器镜像,如Java, .NET Core,Ruby,Node.js,Go,Python 等等。
- 构建容器镜像的流程标准化、自动化。
- 由于缓存,构建时间更快。
- 可重复的镜像构建(不可变,Reproducible)。
- 在大规模上,是提升安全的最佳实践。
- 通过rebase快速升级base镜像,容器的base image升级无需rebuild应用程序。
- 生成一个软件物料清单Software Bill of Materials(SBOM) ,可用于跟踪软件依赖关系和版本控制。

什么是Cloud Native Buildpacks?
Cloud Native Buildpacks是由 Pivotal 和 Heroku 在2018年发起的 CNCF的sandbox项目。谷歌、微软和其他公司也为该项目做出了贡献。Cloud Native Buildpacks的主要组件包括 Platform, Lifecycle, Stacks and Buildpacks 。它为这些组件提供了一组定义良好的规范。这些规范让各平台的实施成为可能,并使用构建包contract来获取源代码,生成符合 OCI 的容器镜像。这使得生态合作供应商能够为所有场景构建平台和模块化的构建包。它还允许开发人员构建自己的构建包和平台。
BuildPack API 定义了应该由任何 BuildPack 实现的contract。项目本身并不提供任何构建包。这些构建包主要由 Paketo、 Google Cloud Platform、 Heroku 和其他公司领导的社区共同开发和维护。
该平台使 Buildpack 功能对终端用户可用,通常用作 CLI 工具、云平台组件或构建pipeline的一部分。最终用户与平台交互使用 Buildpacks。Pack CLI 是平台 API 的参考实现,它用于创建和管理构建包。Pack, Kpack, Tekton templates等等,都是通过lifecycle,实现Platform API 对buildpack执行的调度。

上图:Lifecycle编排buildpack并生成最终的镜像。它通过一系列步骤来生成最终 OCI 合规镜像。
Buildpacks构建包
Buildpack 是一个将源代码转换为容器镜像的程序。Buildpack封装了为语言生态系统创建容器镜像所需的所有工具链。有 Java的buildpack . NET Core的buildpack,Ruby/Node.js/Go/Python 等等的buildpack。Buildpack包含用于识别语言和构建镜像的脚本,负责创建容器镜像,并被分组到叫builder的集合。Builder中的每个buildpack都将分析源代码并确定它是否应该加入build流程。Buildpack将按builder中列出的顺序去执行。
一个buildpack构建包由四部分组成:
- buildpack.toml - buildpack 配置文件,提供buildpack的元数据 。
- package.toml - 可选的配置文件,用于将 buildpack 打包为 docker 镜像。
- detect - 一个脚本,运行它可以测试是否应该使用buildpack来构建应用程序。如果测试通过,则选择 buildpack在下一步使用来构建应用程序。例如,Java 构建包会测试.java或 .jar 文件是否存在,NPM构建包会测试 package.json 文件是否存在。
- build - 构建容器镜像的脚本,脚本包含build和runtim dependencies 。该脚本设置环境变量,创建一个layer,包含必要的二进制文件,并把应用程序依赖项添加到容器镜像。
Lifecycle

Lifecycle负责编buildpack的执行。它从buildpack创建的layer中组装容器镜像。Lifecycle由以下步骤组成:
- Detect (探测)- 这阶段每组buildpack对应的源代码都会测试。当测试通过时,查询一组已排序的 buildpacks,用于生成build步骤。Buildpack是按照builder中的顺序执行的。例如,Node 的buildpack可以测试 package.json 这个文件是否存在,而yarn这个buildpack可以测试 yarn.lock 文件是否存在。
- Restore (还原)- 还原任何之前缓存的依赖项,用于优化build和export两个步骤。缓存位于平台的本地。
- Analyze (分析)-收集之前镜像构建的元数据,在export阶段使,避免重新上传没有改变的layer。
- Build (构建)- 每个在detect阶段中选择的buildpack,执行build函数来build容器镜像。
- Export (导出)- 组装buildpack所创建的layer,从而创建 OCI 镜像。这些layer是按照buildpack的执行顺序来创建的。它结合了analyze阶段的信息,以确保只更新已改变的layer。
- Cache (缓存)- 缓存buildpack所创建的layer。在下一次构建的restore阶段,这些layer会被检索到。
Platform平台
Platform的角色是根据每个lifecycle阶段的顺序去执行的。 Pack、 Kpack、 Tekton templates等等都属于platform。
Stack
在镜像的形式中,Stack提build-time和run-time环境。
Stack由以下组件组成:
- Build image - build image提供build环境所依据的base image。
- Run image - run image提供应用程序镜像所需的base image。
创建容器镜像

Platform协调容器镜像的创建,它按指定顺序执行lifecycle阶段。Lifecycle运行detect阶段来标识要使用的buildpack,然后执lifecycle步骤、执行build、执行cache镜像layer,最后export镜像。Lifecycle还使用build image和run image作为应用程序镜像的 base image。
使用 Pack 构建镜像
按照以下说明进行安装Pack,一旦安装了 pack,我们就可以使用它将应用构建和打包到容器镜像中。让我们创建一个简单的.Net 核心应用程序,并使用 pack 将其打包到容器镜像中。下面我将创建一个 web api 项目示例。
# Create a folder for project
mkdir weatherapi
# Create a sample .Net core application
dotnet new webapi
# Build the application to make sure there are no build errors
dotnet build
使用 pack 来创建一个容器镜像。
# Package the application into a container image
pack build weatherapi:0.1 --builder paketobuildpacks/builder:base
使用 paketobuildpacks/builder: base builder 这个命令创建应用的镜像。Builder是一个包含执行build所需的全部组件的镜像,有来自 Paketo、 Heroku、Google等等。以下已对输出进行了编辑,只包含所需的信息。
base: Pulling from paketobuildpacks/builder
f96b7da34e6d: Pulling fs layer
c1d2444ef86c: Pulling fs layer
b02872305ff6: Pulling fs layer
081b556e652e: Waiting
327ee35261c4: Waiting
357fefdf9bc9: Waiting
.....
.....
Digest: sha256:c2a5de88935fb072e9d17f92acb293d4f08957967f3b9b2ad96d0a3c3f8d61fe
Status: Downloaded newer image for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:099a4fd96ffbc2a54d98594a99c1a1dbe6c2dc8e5cec60d0f45fcfcd7dd353d9
Status: Downloaded newer image for paketobuildpacks/run:base-cnb
===> ANALYZING
Previous image with name "weatherapi:0.1" not found
===> DETECTING
7 of 12 buildpacks participating
paketo-buildpacks/ca-certificates 3.1.0
paketo-buildpacks/dotnet-core-runtime 0.5.6
paketo-buildpacks/dotnet-core-aspnet 0.5.5
paketo-buildpacks/dotnet-core-sdk 0.5.7
paketo-buildpacks/icu 0.1.1
paketo-buildpacks/dotnet-publish 0.7.0
paketo-buildpacks/dotnet-execute 0.8.1
===> RESTORING
===> BUILDING
Paketo CA Certificates Buildpack 3.1.0
.....
.....
.....
===> EXPORTING
Adding layer 'paketo-buildpacks/ca-certificates:helper'
Adding layer 'paketo-buildpacks/dotnet-core-runtime:dotnet-core-runtime'
Adding layer 'paketo-buildpacks/dotnet-core-aspnet:dotnet-core-aspnet'
Adding layer 'paketo-buildpacks/dotnet-core-sdk:dotnet-env-var'
Adding layer 'paketo-buildpacks/icu:icu'
Adding layer 'paketo-buildpacks/dotnet-execute:port-chooser'
Adding layer 'launch.sbom'
Adding 1/1 app layer(s)
Adding layer 'launcher'
Adding layer 'config'
Adding layer 'process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Setting default process type 'web'
Saving weatherapi:0.1...
*** Images (01d4df0163b9):
weatherapi:0.1
Adding cache layer 'paketo-buildpacks/dotnet-core-runtime:dotnet-core-runtime'
Adding cache layer 'paketo-buildpacks/dotnet-core-aspnet:dotnet-core-aspnet'
Adding cache layer 'paketo-buildpacks/dotnet-core-sdk:dotnet-core-sdk'
Adding cache layer 'paketo-buildpacks/icu:icu'
Adding cache layer 'paketo-buildpacks/dotnet-publish:nuget-cache'
Successfully built image weatherapi:0.1
Pack *载下**build image(paketobuildpacks/builder: base)和run image(paketobuildpacks/run: base-cnb)。然后执行Analyze, Detect, Restore, Build 和 Export lifecycle阶段。Builder执行analyze阶段以检查镜像是否以前构建过。这样做是为了使用之前构建过的缓存layer,这会让构建更快。然后,它运行detect阶段来识别构建应用镜像所需的buildpacks。这个阶段确定了构建应用程序所需的7个buildpacks。然后执行build阶段,每个buildpacks都执行build函数,以创建镜像所需的layer。Build阶段通过组合每个buildpacks创建的layer来创建应用程序镜像。Build阶段使用run image stack作为base image来创建应用镜像。
通过运行以下命令来检查应用镜像是否已创建。
# Check that the application image was created
docker images
命令产生如下输出,指示 weathapi: 0.1镜像已经构建。
REPOSITORY TAG IMAGE ID CREATED SIZE
paketobuildpacks/run base-cnb 88b28b285df4 3 days ago 87.2MB
registry 2 8948869ebfee 3 weeks ago 24.2MB
kindest/node v1.19.7 62d6655a44bc 14 months ago 1.24GB
weatherapi 0.1 0ff93b964b12 42 years ago 232MB
paketobuildpacks/builder base e1e22b936fd6 42 years ago 857MB
现在可以运行该应用程序。
# Run the application
docker run -d -p 8080:8080 -e PORT=8080 weatherapi:0.1
通过运行以下命令来检查应用程序是否正在运行。该应用程序在8080端口上运行,并生成随机的天气预报读数。
curl http://localhost:8080/weatherforecast | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 501 0 501 0 0 6592 0 --:--:-- --:--:-- --:--:-- 6592
[
{
"date": "2022-04-06T06:19:41.9398735+00:00",
"temperatureC": 29,
"temperatureF": 84,
"summary": "Hot"
},
{
"date": "2022-04-07T06:19:41.9406486+00:00",
"temperatureC": 42,
"temperatureF": 107,
"summary": "Hot"
},
{
"date": "2022-04-08T06:19:41.9406506+00:00",
"temperatureC": -17,
"temperatureF": 2,
"summary": "Bracing"
},
{
"date": "2022-04-09T06:19:41.9406508+00:00",
"temperatureC": 44,
"temperatureF": 111,
"summary": "Mild"
},
{
"date": "2022-04-10T06:19:41.9406509+00:00",
"temperatureC": 46,
"temperatureF": 114,
"summary": "Sweltering"
}
]
通过运行以下命令来检查应用镜像。
# Check the application image
pack inspect weatherapi:0.1
这将产生以下带有镜像信息的输出。我们可以识别使用的base image和run image,用于创建镜像的buildpack以及进程类型。
```shell
Inspecting image: weatherapi:0.1
REMOTE:
(not present)
LOCAL:
Stack: io.buildpacks.stacks.bionic
Base Image:
Reference: 88b28b285df4397d4037cb89e66b8e776815c16ec856ae5bd5d1166222d8b441
Top Layer: sha256:f5bd5d59c5c4f8e5b544e6e78f15b13fcb0b08d8c8064a25546e552fdc774c9e
Run Images:
index.docker.io/paketobuildpacks/run:base-cnb
gcr.io/paketo-buildpacks/run:base-cnb
Buildpacks:
ID VERSION HOMEPAGE
paketo-buildpacks/ca-certificates 3.1.0 https://github.com/paketo-buildpacks/ca-certificates
paketo-buildpacks/dotnet-core-runtime 0.5.6 https://github.com/paketo-buildpacks/dotnet-core-runtime
paketo-buildpacks/dotnet-core-aspnet 0.5.5 https://github.com/paketo-buildpacks/dotnet-core-aspnet
paketo-buildpacks/dotnet-core-sdk 0.5.7 https://github.com/paketo-buildpacks/dotnet-core-sdk
paketo-buildpacks/icu 0.1.1 https://github.com/paketo-buildpacks/icu
paketo-buildpacks/dotnet-publish 0.7.0 https://github.com/paketo-buildpacks/dotnet-publish
paketo-buildpacks/dotnet-execute 0.8.1 https://github.com/paketo-buildpacks/dotnet-execute
Processes:
TYPE SHELL COMMAND ARGS
web (default) /workspace/weatherapi
Rebase容器镜像
我们可以将容器镜像rebase到一个新的base image。当出现安全问题或当前 CVE 的base image需要更改时,或者,我们想要将base image更新为较新的版本,这也非常有用。我们可以只rebase 镜像中的OS层,而无需rebuild应用。例如,我们可以将示例应用程序的base image从当前run image更改为另一个版本。让我们尝试rebase容器镜像,从当前 base-cnb 版本到1.3.28-full-cnb 。
# Rebase the image to the 1.3.29-full-cnb image
pack rebase weatherapi:0.1 --run-image paketobuildpacks/run:1.3.29-full-cnb
上面的命令将需要切换到1.3.29版本的容器镜像的layer拉出,然后用新的layer对容器镜像进行补丁。该命令生成此输出以表示成功rebase镜像成功。
1.3.29-full-cnb: Pulling from paketobuildpacks/run
f96b7da34e6d: Already exists
ae83efb71859: Pull complete
2a8782654e49: Pull complete
9295f68ae444: Pull complete
Digest: sha256:6710ad7f7fdfbc3489fe2e535d2a5ee6653c9bb8437e6cc94508af05a86965fb
Status: Downloaded newer image for paketobuildpacks/run:1.3.29-full-cnb
Rebasing weatherapi:0.1 on run image paketobuildpacks/run:1.3.29-full-cnb
Saving weatherapi:0.1...
*** Images (1f6d9f0c2b50):
weatherapi:0.1
Rebased Image: 1f6d9f0c2b504243169e49f30ecfe357914a3cc054fc89ae27744dc35612b2d6
Successfully rebased image weatherapi:0.1
这是该平台的一个非常强大的特性。它允许我们更改容器的base image,而无需rebuild应用。像 KPack 这样的平台工具已经进一步完善了这一点,允许我们对所有受影响的镜像进行rebase重建。
生成软件物料清单(SBOM)
Software Bill of Materials(软件物料清单,SBOM)是组成应用程序的所有软件组件的列表。它包含了应用程序所依赖的库、插件、扩展和其他附加组件的信息。它还包括这些组件的版本信息。通过运行以下命令,我们可以使用 pack 生成 SBOM。
# Generate an SBOM
pack sbom download weatherapi:0.1 --output-dir /sbom
生成的 sbom 如下
{
"Artifacts": [
{
"ID": "91e3bfd9933331fb",
"Name": "helper",
"Version": "3.1.0",
"Type": "UnknownPackage",
"FoundBy": "libpak",
"Locations": [
{
"Path": "ca-certificates-helper"
}
],
"Licenses": [
"Apache-2.0"
],
"Language": "",
"CPEs": [
"cpe:2.3Apaketo-buildpacks/ca-certificates:ca-certificates-helper:3.1.0:*:*:*:*:*:*:*"
],
"PURL": "pkg:generic/paketo-buildpacks/ca-certificates@3.1.0"
}
],
"Source": {
"Type": "directory",
"Target": "/layers/paketo-buildpacks_ca-certificates/helper"
},
"Descriptor": {
"Name": "syft",
"Version": "0.32.0"
},
"Schema": {
"Version": "1.1.0",
"URL": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.1.0.json"
}
}
Cloud Native Buildpacks是创建容器镜像的自然进化,它允许开发人员专注于开发方面,而不用担心运维痛点,干扰到整个内部开发循环。它允许运维人员定义build过程和应用的生命周期。
文章翻译来自:
https://pradeepl.com/blog/kubernetes/cloudnativebuildpacks/#buildpacks