初探地底阵容 (触探编程)

初探硬件家族,初探自媒体

容器镜像是打包应用程序的一种流行的标准格式。

容器镜像将一个程序及其依赖关系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 生成容器镜像存在以下问题:

  1. 每个开发团队为每个deployable unit(可部署单元)创建一个 Dockerfile。base image(基础镜像)和包含的framework对于每个镜像都是不同的。这使得管理和维护变得困难。
  2. 非标准的 docker 文件无法让构建“不可变”(non-reproducible)。比如使用最新tag或 apt-get 安装软件包。
  3. 镜像可能没有被优化。
  4. 镜像可能不安全。供应商为 CVE 发布的安全补丁不会自动应用于镜像。
  5. 用于生成镜像的 docker 引擎容易受到漏洞的影响。
  6. 运维痛点不断干扰开发循环(development loop),这对开发团队来说并不理想。
  7. 手动创建 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 提供以下好处:

  1. 不再需要开发团队使用 Dockerfile 手动创建容器镜像。
  2. 它与语言无关,可为多种开发语言的应用程序构建容器镜像,如Java, .NET Core,Ruby,Node.js,Go,Python 等等。
  3. 构建容器镜像的流程标准化、自动化。
  4. 由于缓存,构建时间更快。
  5. 可重复的镜像构建(不可变,Reproducible)。
  6. 在大规模上,是提升安全的最佳实践。
  7. 通过rebase快速升级base镜像,容器的base image升级无需rebuild应用程序。
  8. 生成一个软件物料清单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构建包由四部分组成:

  1. buildpack.toml - buildpack 配置文件,提供buildpack的元数据 。
  2. package.toml - 可选的配置文件,用于将 buildpack 打包为 docker 镜像。
  3. detect - 一个脚本,运行它可以测试是否应该使用buildpack来构建应用程序。如果测试通过,则选择 buildpack在下一步使用来构建应用程序。例如,Java 构建包会测试.java或 .jar 文件是否存在,NPM构建包会测试 package.json 文件是否存在。
  4. build - 构建容器镜像的脚本,脚本包含build和runtim dependencies 。该脚本设置环境变量,创建一个layer,包含必要的二进制文件,并把应用程序依赖项添加到容器镜像。

Lifecycle

初探硬件家族,初探自媒体

Lifecycle负责编buildpack的执行。它从buildpack创建的layer中组装容器镜像。Lifecycle由以下步骤组成:

  1. Detect (探测)- 这阶段每组buildpack对应的源代码都会测试。当测试通过时,查询一组已排序的 buildpacks,用于生成build步骤。Buildpack是按照builder中的顺序执行的。例如,Node 的buildpack可以测试 package.json 这个文件是否存在,而yarn这个buildpack可以测试 yarn.lock 文件是否存在。
  2. Restore (还原)- 还原任何之前缓存的依赖项,用于优化build和export两个步骤。缓存位于平台的本地。
  3. Analyze (分析)-收集之前镜像构建的元数据,在export阶段使,避免重新上传没有改变的layer。
  4. Build (构建)- 每个在detect阶段中选择的buildpack,执行build函数来build容器镜像。
  5. Export (导出)- 组装buildpack所创建的layer,从而创建 OCI 镜像。这些layer是按照buildpack的执行顺序来创建的。它结合了analyze阶段的信息,以确保只更新已改变的layer。
  6. Cache (缓存)- 缓存buildpack所创建的layer。在下一次构建的restore阶段,这些layer会被检索到。

Platform平台

Platform的角色是根据每个lifecycle阶段的顺序去执行的。 Pack、 Kpack、 Tekton templates等等都属于platform。

Stack

在镜像的形式中,Stack提build-time和run-time环境。

Stack由以下组件组成:

  1. Build image - build image提供build环境所依据的base image。
  2. 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