Dawn's Blogs

分享技术 记录成长

0%

阅读dubbo-kubernetes的启发 (3) OCI与Buildpack

本文内容不谈 dubbo-kubernetes,因为 dubbo-kubernetes 在构建镜像时可以使用 Buildpack 来构建 OCI 镜像,所以本文来谈一谈 Buildpack。

dubboctl 在 build 镜像时可以选择 buildpack 或者 dockerfile 的方式。

OCI

随着 Kubernetes 成为了重启编排的事实标准,为了确保所有的容器运行时都都运行任何构建工具生成的镜像(而不仅仅是 Docker),OCI(Open Container Initiative)开放容器协议被提出,OCI 制定了围绕容器镜像格式(image-spec)和运行时(runtime-spec)的行业标准。给定一个 OCI 镜像,任何实现 OCI 运行时标准的容器运行时都可以使用该镜像运行容器。

Docker 镜像和 OCI 镜像有什么区别?

答案是:几乎没有区别。有一部分旧的 Docker 镜像在 OCI 规范之前就已经存在了,它们被成为 Docker v1 规范,与 Docker v2 规范是不兼容的。而 Docker v2 规范捐给了 OCI,构成了 OCI 规范的基础。

下面说一说 OCI 的镜像和运行时标准。

image-spec

docker 镜像

将 docker 镜像导出后得到 tar 文件解压后,就可以得到景象文件结构。docker 镜像包括了:

  • manifest.json:声明了镜像的配置文件、tag 和包含的层级 layer。
  • 每一个 layer 文件夹 包含一个 json 文件,包含当前层的配置和自己的 parent layer,lay.tar 为当前层的数据。
  • Sha256.json 为镜像的配置文件,包含了 Dockerfile 中定义的 EntryPoint、ENV 等信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
├── 1a58e6937db044ef6 ...		# layer 1
│ ├── json
│ ├── layer.tar
│ └── VERSION
├── 98867178f60349f16 ... # layer 2
│ ├── json
│ ├── layer.tar
│ └── VERSION
├── c12f86d2a60fc27a1 ... # layer 3
│ ├── json
│ ├── layer.tar
│ └── VERSION
├── d39aa2f569c9d3100 ... # layer 4
│ ├── json
│ ├── layer.tar
│ └── VERSION
├── fa903e5799bb733ed87 ... .json
├── manifest.json
└── repositorie

OCI 镜像

OCI 格式镜像由如下部分组成:

  • Index.json 文件就是索引文件,指明了 manifest 文件清单(manifest 在 blobs/sha256 中)。如果镜像包含多个不同平台版本软件包,那么每个版本各对应一个 manifest 项。
  • oci-layout 是一个 json 文件,只有一个字段 imageLayoutVersion 指明了镜像组织的版本。
  • blobs/sha256 中包含了镜像 manifest 文件、镜像 config 文件和 layer 压缩文件
1
2
3
4
5
6
7
8
9
10
├── blobs
│ └── sha256
│ ├── c2274a1a0e2786ee9101b
│ ├── d45802acb2a6c862e2d55
│ ├── d4a44c93e6326fd854b55
│ ├── e4d6c83503a9bf0b4922d
│ ├── e7c96db7181be991f19a9
│ └── f910a506b6cb1dbec7667
├── index.json
└── oci-layout

所以:镜像 = 一份文件清单 manifest+ 一个或者多个 layer 压缩包 + 一份配置文件(指明了启动命令、环境变量等)

runtime-spec

Docker 不仅捐赠了镜像编排方式,同样向 OCI 捐献了自己的容器运行时 runC,OCI 根据 Docker 的捐赠产生了镜像标准和容器运行时标准,所以 runC 可以直接运行 OCI 镜像。

Buildpack

Dockerfile vs buildpack

  • Dockerfile 构建容器是一种脚本化的方法,就是把构建命令写成脚本的形式,组合成为 Dockerfile。
  • buildpacks 源于这样一种想法,即对于给定类型的大多数应用程序,将应用程序源代码转换为容器或多或少是相同的。这意味着可以为这个过程设计可重用的程序。

所以仅需要 Buildpacks 工具(Cloud Native Buildpacks, CNB) + 源代码,不需要 Dockerfile,就可以生成 OCI 镜像。所以 Buildpacks 为构建应用程序提供了更高层次的抽象构建,保证应用构建的安全性和合规性,而无需开发者干预,屏蔽了 Dockerfile 的复杂性。

img

CNB 的工作原理

CNB 主要由三个组件构成:Builder、Buildpack、Stack。

  • Buildpack:Buildpack 本质是一个可执行单元的集合,一般包括检查程序源代码、构建代码、生成镜像等。一个典型的 Buildpack 需要包含以下三个文件:
    • buildpack.toml:提供 buildpack 的元数据信息。
    • bin/detect:检查是否可以参与构建。
    • bin/build:构建逻辑,用于生成镜像。

buildpack 在构建镜像时,主要包括两个阶段 Detect 和 Build:

  • Detect:每个 buildpack 检测它是否可以参与容器构建。例如,对于 Python 应用程序,会检查 requirements.txt 文件、对于 Go 应用程序会检查 go.mod 文件。
  • Build:所有可以参与构建的 buildpack 都会参与镜像的构建。

how

  • Builder:通常为了完成一个应用的构建,会使用到多个 Buildpacks,那么 Builder 就相当于一个 Buildpacks 的编排器。Builders 就是 Buildpacks 的有序组合,包含一个基础镜像叫 build image、一个 lifecycle 和对另一个基础镜像 run image 的应用。Builders 负责将应用源代码构建成应用镜像(app image)。
    • build image:为 Builders 提供基础环境。
    • run image:为应用程序的镜像提供基础环境,build image 和 run image 的组合被称为 Stack。
    • lifecycle:就是对 buildpacks 的编排流程。

img

  • Stack:就是 build image 和 run image 的组合,他定义了构建时的执行环境和应用的运行环境。

Platform

Platform 其实是 Lifecycle 的执行者,它的作用是将 Builder 作用于给定的源代码上,完成 Lifecycle 的指令。

在这个过程中,Builder 会将源代码构建为 app,这个时候 app 是在 build image 中的。这个时候根据 Lifecycle 中的 Rebase 接口,底层逻辑是是用 ABI(application binary interface)将 app 工件从 build image 转换到 run image 上,这就是最后的 OCI 镜像

img

常用的 Platform 有 Tekton 和 CNB 的 Pack

Rebase

上文提到了在构建 OCI 镜像时,app 通过 Rebase 从 build image 迁移到了 run image,由此完成 镜像的构建。

这种机制也是 CNB 对比 Dockerfile 最具优势的地方,在 Dockerfile 构建镜像的过程中,如果发生了基础镜像变更,重新构建所有上面的层。而在 CNB 中,只需要做一次 Rebase 即可完成向新的基础镜像的迁移。