容器镜像构建工具和方案介绍
简介
本节一起看一下都有哪些可用于构建容器镜像
的工具和方案
。可能你会疑问,构建镜像不就是使用docker build
就可以吗,即使将来docker真的退出历史舞台,不还有containerd吗,前面第5节的时候也介绍了在containerd下可以使用nerdctl+buildkit
构建容器镜像。 如果你只是在单机本地构建镜像的话,使用docker build
或nerdctl+containerd+buildkit
确实没有问题,但实际中由于不同场景和技术背景下的限制,如docker build
依赖于docker daemon
,这些限制使得在一些特殊的场景下本机构建镜像的方式不再可用。
下图是一个一个CI/CD流水线中的分布式构建架构,结合了容器技术,将Jenkins用于构建的Slave节点放到容器中,并基于K8S来动态调度管理这些Jenkins Slave容器。这样一来Jenkins就具备了基于容器的分布式构建能力, 一个应用从源码经过各个阶段到最终的镜像是采用分布式构建实现的,各个阶段也都是在容器中进行的。

上面基于容器的分布式构建,首先要面对的问题就是要在jenkins slave容器
的内部构建镜像。由于docker是以docker cli
和docker daemon
的C/S
形式工作,如果把docker cli
放到jenkins slave
容器里面执行docker build
,那么docker build
所需的docker daemon
放哪儿呢? 于是就有了Docker in Docker
和Docker outside of Docker
两种方案。
Docker in Docker
Docker in Docker
简称DiD
。DiD模式是指在一个容器内部安装完整的docker服务,启动一个Docker Daemon
,然后就可以在容器内部使用docker cli进行镜像的构建。 此方案的优点是容器中的Docker Daemon与外部完全隔离,隔离性较好,而缺点是容器内部比较臃肿复杂,同时要解决容器内docker服务的持久化存储、构建缓存的问题,在安全上DiD还需要以特权(--privileged
)形式启动容器,有安全风险。 因此这种方案在实际好少使用,也不推荐使用。
Docker outside of Docker
Docker outside of Docker
简称DooD
。DooD模式是指将容器外部宿主机上的docker daemon
的socket
挂载到容器内,让容器内的docker cli
误认为本地启动了docker daemon
,这样进行docker build
等命令操作时由容器外部宿主机上的docker daemon
处理请求。
优点是容器内部不需要安装docker daemon,构建在宿主机上进行效率较高。
缺点是没有与外部隔离,构建的镜像与宿主机上的镜像存在冲突的可能,同一宿主机上不支持并行构建。 在早些年前使用DooD这种方案的人比较多。
Kaniko
Kaniko
是Google
开源的一款容器镜像构建工具,可以用来在容器中或者Kubernetes
集群中进行镜像的构建。 在Kaniko
出现之前,使用Dockerfile和Docker cli
来构建镜像时,需要将构建的上下文发送至docker daemon
,Docker in Docker
和Docker outside of Docker
也不例外。 Kaniko
构建容器镜像时并不依赖于Docker daemon
,也不需要特权权限,这一特性使得kaniko
成为DinD
或者DooD
之外的一种全新的解决方案。
Kaniko
构建容器镜像时,需要三个输入: Dockerfile
,构建上下文
,以及构建成功后镜像在仓库中的存放地址。 kaniko executor
需要在容器执行,前面三个输入可以挂载到kaniko executor
的容器中。
Kaniko支持多种方式将构建上下文挂载到容器中:
- 可以使用本地文件夹
- GCS bucket
- S3 bucket等等方式,使用GCS 或者 S3时需要把上下文压缩为tar.gz,kaniko会自行在构建时解压上下文。
Kaniko executor
在找到Dockerfile
后会逐条解析Dockerfile
内容,并且执行相关的命令。在用户目录中形成容器镜像的文件层,每条Dockerfile中的指令都执行完毕后,Kaniko会将新生成的镜像推送到指定的镜像仓库中去。 整个过程中,完全不依赖于docker daemon。
下面是使用docker启动kaniko容器完成容器镜像构建的一个例子:
1 | docker run --rm \ |
下面是k8s中使用kaniko的启动pod的例子:
1 | apiVersion: v1 |
Jib
Jib也是Google开源的一款Java容器镜像构建工具,通过使用Jib,Java 开发人员可以使用他们熟悉的Java构建工具来构建容器镜像。 Jib负责处理将应用程序打包到容器镜像中所需的所有步骤,它不需要我们编写Dockerfile或安装docker daemon,而是直接把镜像构建功能集成到了java构建工具gradle和maven中(通过将插件添加到构建中),即用java的构建工具直接完成容器镜像的构建。
以下示例将使用Jib提供的gradle插件集成到一个spring boot项目的构建中,并展示Jib如何简单快速的构建镜像。
首先,在项目的build.gradle
构建文件中引入jib插件:
1 | buildscript{ |
配置Jib相关参数,以下参数还可以通过 gradle jib -Djib.from.image=’********’ 这种形式从gradle命令行提供:
1 | jib { |
执行以下命令可以直接触发构建生成容器镜像:
1 | // 构建jib.to.image指定的镜像,并且推送至镜像仓库 |
buildkit
buildkit是docker官方社区开源出来下一代的构建工具包,可以更加快速、高效、安全的构建OCI标准的镜像。 前面我们在学习containerd的时候已经结合nerdctl体验过它。它主要包含服务端buildkitd
和客户端buildctl
。 可以看出buildkit也是C/S架构,但buildctl
和buildkitd
可以不在同一台服务器上。 buildkitd可以监听TCP端口将服务以gRPC的形式暴露出来,供buildctl调用。 例如,可以将buildkitd部署到k8s集群中,buildctl作为工具放到k8s上的jenkins slave pod容器中,buildctl在k8s集群内以tcp的形式调用buildctl完成容器镜像的构建。
总结
通过本节的学习,可以看出有了kaniko, buildkit等工具,在构建容器镜像时已经完全可以脱离docker daemon, 而且这些工具都能很好的与k8s集成,支持在容器环境下完成构建。