一、buildkit 简介
buildkit 是一个将 source code 转换为 build artifacts 的开源工具,它于 2017 年由 docker 的创始人 tonistiigi 提议,将 docker build
的能力独立成一个项目,针对 构建 的底层技术进行协作,更好复用和定制构建技术,该将项目即为 buildkit。
BuildKit是Docker官方社区推出的下一代镜像构建神器--可以更加快速,有效,安全地构建docker 镜像。Docker v18.06已经集成了该组件。BuildKit可用于多种导出格式(例如OCI或Docker)以及前端支持(Dockerfile),并提供高效缓存和运行并行构建操作等功能。BuildKit仅需要容器运行时就能执行,当前受支持的运行时包括containerd和runc。
1、优势
1、构建步骤优化
Docker提供的原始构建最令人沮丧的问题之一是Dockerfile指令执行构建步骤的顺序性。在引入多阶段构建之后,可以将构建步骤分组为单独的逻辑构建任务在同一个Dockerfile中。
有时,这些构建阶段是彼此完全独立的,这意味着它们可以并行执行-或根本不需要执行。遗憾的是,传统的Docker镜像构建无法满足这种灵活性。这意味着构建时间通常会比绝对必要的时间更长。
相比之下,BuildKit会创建一个构建步骤之间的依赖关系图,并使用该图来确定可以忽略构建的哪些元素;可以并行执行的元素;需要顺序执行的元素。这可以更有效地执行构建,这对开发人员来说很有价值,因为他们可以迭代其应用程序的镜像构建。
2、高效灵活的缓存
虽然在旧版Docker镜像构建中缓存构建步骤非常有用,但效率却不如预期。作为对构建后端的重写,BuildKit在此方面进行了改进,并提供了更快,更准确的缓存机制。使用为构建生成的依赖关系图,并且基于指令定义和构建步骤内容。
BuildKit提供的另一个巨大好处是以构建缓存导入和导出的形式出现,正如Kaniko和Makisu允许将构建缓存推送到远程注册表一样,BuildKit也是如此,但是BuildKit使您可以灵活地将缓存嵌入到内部注册表中。镜像(内联)并将它们放在一起(虽然不是每个注册表都支持),或者将它们分开导入。也可以将缓存导出到本地目录以供以后使用。
当从头开始建立构建环境而没有任何先前的构建历史时,导入构建缓存的能力就发挥了自己的作用:导入“预热”缓存,对于临时CI/CD环境特别有用。
3、工件
当使用旧版Docker镜像构建器构建镜像时,将生成的镜像添加到Docker守护进程管理的本地镜像的缓存中。需要单独的docker push
将该镜像上载到远程容器镜像注册表。新的工件构建工具通过允许您在构建调用时指定镜像推送来增强体验,BuildKit也不例外,它还允许以几种不同格式输出镜像;本地目录中的文件,本地tarball,一个本地OCI镜像tarball,一个Docker镜像tarball,一个存储在本地缓存中的Docker镜像以及一个推送到注册表的Docker镜像,有很多格式!
4、扩展语法
对于docker构建体验而言,经常重复出现的众多功能请求之一就是安全处理镜像构建过程中所需的机密信息。Moby项目抵制了这一要求很多年了,但是,借助BuildKit灵活的“前端”定义,为Buildkit提供了一个实验性前端,它扩展了Dockerfile语法。扩展后的语法为RUN Dockerfile指令提供了有用的补充,其中包括安全性功能。
2、使用场景
1、Docker
BuildKit 默认为Docker Desktop上的所有用户启用。如果您已安装 Docker Desktop,则无需手动启用 BuildKit。如果您在 Linux 上运行 Docker,则可以通过使用环境变量或将 BuildKit 设为默认设置来启用 BuildKit。
要在运行命令时设置 BuildKit 环境变量docker build
,请运行:
DOCKER_BUILDKIT=1 docker build .
要默认启用 docker BuildKit,请将/etc/docker/daemon.json
feature 中的 daemon configuration 设置为true
并重新启动 daemon。如果该daemon.json
文件不存在,则创建名为的新文件daemon.json
,然后将以下内容添加到文件中。
{
"features": {
"buildkit" : true
}
}
在此配置中,由于Docker守护程序中的当前限制,Docker并未充分展现BuildKit的全部功能。因此,Docker客户端CLI已扩展为提供插件框架,该框架允许使用插件扩展提供了可用的CLI功能。一个名为Buildx
的实验性插件会绕过守护程序中的旧版构建函数,并使用BuildKit后端进行所有构建,它提供所有熟悉的镜像构建命令和功能,但通过一些特定于BuildKit的附加功能对其进行了扩充。
BuildKit以及Buildx的都支持多个构建器实例,这是一项重要功能,这实际上意味着可以共享一个构建器实例场以进行构建;也许是一个项目被分配了一组构建器实例。
# docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
default * docker
default default running linux/amd64, linux/386
2、Kubernetes
Kubernetes1.24以及版本之后已经将dockershim启用,所以无法使用docker进行构建镜像,所以需要使用BuildKit作为专门的组件进行构建镜像。
二、BuildKit 安装
BuildKit 由 buildkitd
守护进程和 buildctl
客户端组成。 buildctl
客户端可用于 Linux、macOS 和 Windows,而 buildkitd
守护程序目前仅可用于 Linux。
buildkitd
守护进程需要安装以下组件:
- runc or crun
- containerd
1、安装 buildkit 客户端和buildkitd 服务
# wget https://mirror.ghproxy.com/https://github.com/moby/buildkit/releases/download/v0.11.2/buildkit-v0.11.2.linux-amd64.tar.gz
# tar zxf buildkit-v0.11.2.linux-amd64.tar.gz
# ls bin/
buildctl buildkitd buildkit-qemu-aarch64 buildkit-qemu-arm buildkit-qemu-i386 buildkit-qemu-mips64 buildkit-qemu-mips64el buildkit-qemu-ppc64le buildkit-qemu-riscv64 buildkit-qemu-s390x buildkit-runc
# cp bin/buildctl /usr/local/bin
# cp bin/builkitd /usr/local/bin
配置 systemd 服务
cat > /usr/lib/systemd/system/buildkitd.service <<EOF
[Unit]
Description=/usr/local/bin/buildkitd
ConditionPathExists=/usr/local/bin/buildkitd
After=containerd.service
[Service]
Type=simple
ExecStart=/usr/local/bin/buildkitd
User=root
Restart=on-failure
RestartSec=1500ms
[Install]
WantedBy=multi-user.target
EOF
# systemctl daemon-reload && systemctl restart buildkitd && systemctl enable buildkitd
构建镜像
基于 Dockerfile 构建镜像,但不导出
buildkit 默认不导出产物,下述操作仅起到构建效果:
# cat > Dockerfile <<EOF
FROM nginx:1.21.0
RUN sed -i 's#http://deb.debian.org#https://mirrors.cloud.tencent.com#g' /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y curl
RUN mkdir -p /app/bin/
RUN cd /app/bin && curl -LO https://github.com/fullstorydev/grpcurl/releases/download/v1.8.6/grpcurl_1.8.6_linux_x86_64.tar.gz \
&& tar xvf grpcurl_1.8.6_linux_x86_64.tar.gz
WORKDIR /app/bin/
CMD ["nginx", "-g", "daemon off;"]
EOF
# buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=.
产出不同的 output
下述操作将基于上述 Dockerfile 进行。
将输出导入到本地目录 ./tmp
# buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --output type=local,dest=./tmp
将输出导入到本地 tar 包
$ buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --output type=tar,dest=out.tar
将输出导出为 docker image
$ buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --output type=docker,name=myimage | docker load
将输出导出为 oci tar 包
$ buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --output type=oci,dest=output.tar
cache
buildkit 将构建过程中产生的 layer 作为 cache,使用层面包含 export-cache 和 import-cache 两种操作,即导出 cache 和使用 cache。
对于 export-cache,buildkit 支持如下几种类型:
inline
:将 cache 嵌入到镜像中,并推送镜像到镜像仓库registry
:将 cache 和镜像分别推送到镜像仓库local
:将 cache 导出到本地目录gha
:将 cache 导出到 GitHub Actions 的 cache 服务中s3
:将 cache 导出到 S3 对象存储中
对于 import-cache,buildkit 支持如下几种类型:
registry
:使用镜像仓库中的 cache,对应上述使用inline
/registry
类型导出的 cachelocal
:使用本地的 cache,对应上述使用local
类型导出的 cachegha
:使用 GitHub Actions 中的 cache,对应上述使用gha
类型导出的 caches3
:使用 S3 中的 cache,对应上述s3
类型导出的 cache
与此同时,buildkit 支持将不同阶段的 layer 作为 cache:
- 只导出最终镜像的 layers:对应
min
模式 - 导出所有步骤的 layers:对应
max
模式
下述给出常见的几种使用 cache 的操作。
使用 inline 类型导出缓存,使用 registry 类型使用缓存
# 首次执行时仅有 `导出缓存` 的操作,重复执行将会既有 `使用缓存` 又有 `导出缓存` 的操作
$ buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --output type=image,name=ccr.ccs.tencentyun.com/flyer103/demo-grpcurl:0.1,push=true --export-cache type=inline --import-cache type=registry,ref=ccr.ccs.tencentyun.com/flyer103/demo-grpcurl:0.1
使用 registry 类型导出和使用缓存
# 首次执行时仅有 `导出缓存` 的操作,重复执行将会既有 `使用缓存` 又有 `导出缓存` 的操作
$ buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --output type=image,name=ccr.ccs.tencentyun.com/flyer103/demo-grpcurl:0.1,push=true --export-cache type=registry,ref=ccr.ccs.tencentyun.com/flyer103/demo-grpcurl:buildcache --import-cache type=registry,ref=ccr.ccs.tencentyun.com/flyer103/demo-grpcurl:buildcache
使用 local 模式导出和使用缓存
# 导出缓存
$ buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --output type=image,name=ccr.ccs.tencentyun.com/flyer103/demo-grpcurl:0.1,push=true --export-cache type=local,dest=./tmp
# 再次构建使用缓存
$ buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --output type=image,name=ccr.ccs.tencentyun.com/flyer103/demo-grpcurl:0.1,push=true --import-cache type=local,src=./tmp
查看 buildkitd 中的缓存
$ buildctl du -v
清理 buildkitd 中的缓存
$ buildctl prune
buildkitd 中 cache 管理的策略
可参见 buildkitd.toml 配置中的描述,不赘述。
构建多平台镜像
$ buildctl build --frontend=dockerfile.v0 --opt platform=linux/amd64,linux/arm64 --local context=. --local dockerfile=. --output type=image,name=ccr.ccs.tencentyun.com/flyer103/demo-grpcurl:0.1,push=true
对 Dockerfile 语法增强
Docker 从 18.09
版本开始,新增内置 buildkitd 作为 backend 执行构建,同时基于 Dockerfile 提供的 Parser directives 特性,可以使用不同版本的 Dockerfile 语法。
如下为不同版本的 Dockerfile 增强的语法:
buildkit
buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --output type=image,name=httpserver:v4
frontend:使用dockerfile作为前端,也可以使用gateway.v0(未测试)。
local context: 指向当前目录,这是Dockerfile执行构建时的路径上下文,比如在从目录中拷贝文件到镜像里
local dockerfile:指向当前目录,表示Dockerfile在此目录
output 的 name: 表示构建的镜像名称
DOCKER_BUILDKIT=1 docker build -t 192.168.0.28:7000/web/publicquery:test -f Dockerfile .
docker build --no-cache -t notarization_supervise/admin:dev -f Dockerfile-dev.yaml .
# syntax = docker/dockerfile:experimental
FROM node:14.21.1-alpine3.17 AS build-env
WORKDIR /app
COPY package.json /app/
RUN --mount=type=cache,target=/app/node_modules,id=my_app_npm_module,sharing=locked \
--mount=type=cache,target=/root/.npm,id=npm_cache \
yarn config set registry http://8.142.113.6:8081/repository/npm/
COPY ..
RUN --mount=type=cache,target=/app/node_modules,id=my_app_npm_module,sharing=locked \
# --mount=type=cache,target=/app/dist,id=my_app_dist,sharing=locked \
yarn install && yarn run build:production
FROM 192.168.0.28:7000/txhy/nginx:alpine
ADD qualification.conf /etc/nginx/conf.d/
# COPY --from=builder /app/dist /app/dist
# 为了更直观的说明 from 和 source 指令,这里使用 RUN 指令
RUN --mount=type=cache,target=/tmp/dist,from=builder,source=/app/dist \
# --mount=type=cache,target/tmp/dist,from=my_app_dist,sharing=locked \
cp -r /tmp/dist /usr/share/nginx/html
EXPOSE 349
CMD ["/bin/sh", "-c", "nginx -g \"daemon off;\"" ]
评论区