在容器的远古时代 (差不多就是 4 年前),docker 是这场游戏的唯一玩家。但现在情况已经不一样了,docker 不再是唯一玩家,而只是一个容器引擎而已。我们可以用 docker 构建、运行、拉取、推送或检查容器镜像,但对于这里的每一项任务,都有其他可替代的工具,它们可能比 docker 做得更好。所以,让我们来探究一下它们,然后卸载和忘掉 docker……
为什么说不要用 docker 了?
如果你已经使用 docker 很长时间了,那么要说服你考虑使用其他的工具可能需要费点唇舌。
首先,docker 是一个整体性的工具,它试图做所有的事情,但这通常不是最好的方法。大多数情况下,选择一种专门的工具会更好,它可能只做一件事,但会做到最好。如果你害怕使用不同的工具,可能是因为你要学习使用不同的 cli、不同的 api 或接受不同的概念。不过请放心,选择本文介绍的工具都是完全无缝衔接的,因为它们 (包括 docker) 都遵循 oci (open container initiative) 规范。oci 包含了容器运行时、容器分发和容器镜像的规范,涵盖了使用容器所需的所有特性。多亏了 oci,你可以选择一套最适合自己的工具,同时又能够继续使用与 docker 一样的 api 和 cli 命令。
所以,如果你愿意尝试新的工具,那么就让我们来比较一下 docker 和其他工具的优缺点和特性,看看是否有必要考虑放弃 docker,并转向其他一些新的工具。
容器引擎
在比较 docker 和其他工具时,我们需要将其分解为组件,首先我们要讨论的是容器引擎。容器引擎是一种工具,它为处理镜像和容器提供了用户界面,这样你就不需要处理 seccomp 规则或 selinux 策略之类的事情。它的工作还包括从远程存储库提取镜像并将其解压到磁盘。它似乎也运行容器,但实际上它的工作是创建容器清单和包含了镜像层的目录。然后它将它们传到容器运行时,例如使用 runc 或 crun(稍后我们将讨论这个)。目前有很多可用的容器引擎,不过 docker 最突出的竞争对手是由 red hat 开发的 podman。与 docker 不同,podman 不需要守护进程,也不需要 root 特权,这是 docker 长期以来一直存在的问题。从它的名字就可以看出来,podman 不仅可以运行容器,还可以运行 pod。pod 是 kubernetes 的最小计算单元,由一个或多个容器 (主容器和所谓的边车) 组成,podman 用户在以后可以更容易地将他们的工作负载迁移到 kubernetes。以下演示了如何在一个 pod 中运行两个容器:
~ $ podman pod create --name mypod ~ $ podman pod list pod id name status created # of containers infra id 211eaecd307b mypod running 2 minutes ago 1 a901868616a5 ~ $ podman run -d --pod mypod nginx # first container ~ $ podman run -d --pod mypod nginx # second container ~ $ podman ps -a --pod container id image command created status ports names pod pod name 3b27d9eaa35c docker.io/library/nginx:latest nginx -g daemon o... 2 seconds ago up 1 second ago brave_ritchie 211eaecd307b mypodd638ac011412 docker.io/library/nginx:latest nginx -g daemon o... 5 minutes ago up 5 minutes ago cool_albattani 211eaecd307b mypoda901868616a5 k8s.gcr.io/pause:3.2 6 minutes ago up 5 minutes ago 211eaecd307b-infra 211eaecd307b mypod
podman 提供了与 docker 完全相同的 cli 命令,因此你只需执行 alias docker=podman,然后就像什么都没有发生改变一样。除了 docker 和 podman 之外,还有其他容器引擎,但我认为它们没有出路或者都不适合用于本地开发。不过如果你想要对容器引擎有一个较为完整的了解,我们可以列出一些:
lxd——是 lxc (linux 容器) 的容器管理器 (守护进程)。这个工具提供了运行系统容器的能力,这些系统容器提供了类似于 vm 的容器环境。它比较小众,没有很多用户,所以除非你有特定的用例,否则最好使用 docker 或 podman。
cri-o——如果你在网上搜索 cri-o 是什么东西,你可能会发现它被描述为一种容器引擎。不过,它实际上是一种容器运行时。除了不是容器引擎之外,它也不适合用于“一般”的情况。我的意思是,它是专门为 kubernetes 运行时 (cri) 而构建的,并不是给最终用户使用的。
rkt——rkt(“rocket”) 是由 coreos 开发的容器引擎。这里提到这个项目只是为了清单的完整性,因为这个项目已经结束了,它的开发也停止了——因此它不应该再被使用。
镜像的构建
从容器引擎方面来说,除了 docker 之外只有一种选择。但是,在构建镜像方面,我们有很多选择。首先是 buildah。buildah 是 red hat 开发的一款工具,可以很好地与 podman 配合使用。如果你已经安装了 podman,可能会注意到 podman build 子命令,它实际上是经过包装的 buildah。在特性方面,buildah 遵循了与 podman 相同的路线——它是无守护进程的,可以生成符合 oci 的像,并保证以相同的方式来运行使用 docker 构建的镜像。它还能基于 dockerfile 或 containerfile(它们实际上是同一个东西,只是叫法不一样)构建镜像。
除此之外,buildah 还提供了对镜像层更精细的控制,支持提交大量的变更到单个层。在我看来,它与 docker 之间有一个出乎人意料的区别,使用 buildah 构建的镜像是特定于用户的,因此你可以只列出自己构建的镜像。你可能会问,既然 buildah 已经被包含在 podman cli 中,为什么还要使用单独的 buildah cli?buildah cli 是 podman build 所包含的命令的超集,你可能不需要使用 buildah cli,但是通过使用它,你可能会发现一些额外有用的特性。
我们来看看一个小演示:
~ $ buildah bud -f dockerfile . ~ $ buildah from alpine:latest # create starting container - equivalent to from alpine:latest getting image source signatures copying blob df20fa9351a1 done copying config a24bb40132 done writing manifest to image destination storing signatures alpine-working-container # name of the temporary container ~ $ buildah run alpine-working-container -- apk add --update --no-cache python3 # equivalent to run apk add --update --no-cache python3 fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86_64/apkindex.tar.gz fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/community/x86_64/apkindex.tar.gz ... ~ $ buildah commit alpine-working-container my-final-image # create final image getting image source signatures copying blob 50644c29ef5a skipped: already exists copying blob 362b9ae56246 done copying config 1ff90ec2e2 done writing manifest to image destination storing signatures 1ff90ec2e26e7c0a6b45b2c62901956d0eda138fa6093d8cbb29a88f6b95124c ~ # buildah images repository tag image id created size localhost/my-final-image latest 1ff90ec2e26e 22 seconds ago 51.4 mb
从上面的脚本可以看到,你可以直接使用 buildah bud 构建镜像,其中 bud 表示使用 dockerfile 来构建镜像,你也可以使用其他更多的命令,如 from、run 和 copy,它们分别对应 dockerfile 中的 from、run、copy。
第二个工具是谷歌的 kaniko。kaniko 也基于 dockerfile 构建容器镜像,而且与 buildah 类似,它也不需要守护进程。与 buildah 的主要区别在于,kaniko 更专注于在 kubernetes 中构建镜像。
kaniko 本身是作为镜像 (gcr.io/kaniko-project/executor) 运行的,这对于 kubernetes 来说是没有问题,但对于本地构建来说不是很方便,并且在某种程度上违背了构建镜像的目的,因为你需要使用 docker 来运行 kaniko 镜像来构建镜像。
如果你正在寻找在 kubernetes 集群中构建镜像的工具 (例如在 ci/cd 管道中),那么 kaniko 可能是一个不错的选择,因为它是无守护进程的,而且 (可能) 更安全。
从我个人的经验来看——我在 kubernetes/openshift 集群中使用了 kaniko 和 buildah 来构建镜像,我认为两者都能很好地完成任务,但在使用 kaniko 时会随机出现构建故障,在将镜像推送到注册表时也会随机地出现失败的情况。
第三个是 buildkit,也可以称之为下一代 docker build。它是 moby 项目的一部分,在运行 docker 时通过 docker_buildkit=1 docker build 就可以启用它,作为 docker 的一个实验性特性。
那么,这到底会给你带来什么呢?它带来了很多改进和很酷的特性,包括并行构建步骤、跳过未使用的阶段、更好的增量构建和无根构建。但是,它仍然需要运行守护进程 (buildkitd)。所以,如果你不想摆脱 docker,同时又想要一些新的特性和更好的改进,那么使用 buildkit 可能是最好的选择。
跟之前的章节一样,这里也将提及一些工具,它们满足了一些特定的使用场景,但并不是我的首选:source-to-image (s2i) 是一个不使用 dockerfile 直接从源代码构建镜像的工具包。
这个工具在简单可预期的场景和工作流中表现良好,但如果你需要多一些定制化,或者你的项目没有预期的结构,那么它就会变得烦人和笨拙。如果你对 docker 还不是很有信心,或者如果你在 openshift 集群上构建镜像,可能可以考虑使用 s2i,因为使用 s2i 构建镜像是它的一个内置特性。
jib是谷歌开发的一款工具,专门用于构建 java 镜像。它提供了 maven 和 gradle 插件,可以让你轻松地构建镜像,不需要理会 dockerfile。
最后一个是 bazel,它是谷歌的另一款工具。它不仅用于构建容器镜像,而且是一个完整的构建系统。如果你只是想构建镜像,那么使用 bazel 可能有点大材小用,但这绝对是一个很好的学习体验,所以如果你愿意,可以将 rules_docker为入手点。
容器运行时
最后一个是负责运行容器的容器运行时。
容器运行时是整个容器生命周期的一部分,除非你对速度、安全性等有一些非常具体的要求,否则你很可能不会对其加以干扰。所以,如果你已经感到厌倦了,可以跳过这一部分。
但是,如果你想知道有哪些可选择的容器运行时,可以看看以下这些:runc是符合 oci 容器运行时规范的容器运行时。docker(通过 containerd)、podman 和 cri-o 都在使用它,它是 (几乎) 所有东西的默认配置,所以即使你在阅读本文后放弃使用 docker,很可能仍然会使用 runc。
runc 的另一种替代品是 crun。这是 red hat 开发的一款工具,完全用 c 语言开发 (runc 是用 go 开发的),所以它比 runc 更快,内存效率更高。因为它也是兼容 oci 的运行时,所以你应该可以很容易上手。尽管它现在还不是很流行,但作为 rhel 8.3 版本的技术预览,它将作为一个可选的 oci 运行时,又因为它是 red hat 的产品,它可能最终会成为 podman 或 cri-o 的默认配置。
前面我说过,cri-o 实际上不是容器引擎,而是容器运行时。这是因为 cri-o 没有提供诸如镜像推送之类的特性,而这些特性是容器引擎应该具备的。
cri-o 在内部使用 runc 来运行容器。你不应该在自己的机器上尝试使用这个运行时,因为它是作为运行在 kubernetes 节点上的运行时而设计的,并被描述为“kubernetes 所需的运行时”。因此,除非你正在运行 kubernetes 集群 (或 openshift 集群——cri-o 已经是默认设置了),否则不应该接触这个。
最后一个是 containerd,它是 cncf 的一个毕业项目。它是一个守护进程,作为各种容器运行时和操作系统的 api 外观。在后台,它依赖 runc,是 docker 引擎的默认运行时。谷歌 kubernetes 引擎 (gke) 和 ibm kubernetes 服务 (iks) 也在使用它。它是 kubernetes 容器运行时接口的一个实现 (与 cri-o 一样),因此它是 kubernetes 集群运行时的一个很好的候选对象。
镜像的检查与分发
最后一部分内容是镜像的检查与分发,主要是替代 docker inspect,并 (可选地) 增加远程注册表之间复制镜像的能力。
我这里要提到的一个可以完成这些任务的工具是 skopeo。它由 red hat 公司开发,可以与 buildah、podman 和 cri-o 配套使用。除了基本的 inspect 之外,skopeo 还提供了 skopeo copy 命令来复制镜像,可以直接在远程注册表之间复制镜像,无需将它们拉取到本地注册表。如果你使用了本地注册表,这个命令也可以作为拉取 / 推送的替代方案。
另外,我还想提一下 dive,这是一个检查、探索和分析镜像的工具。它对用户更友好一些,提供了更可读的输出,可以更深入地挖掘镜像,并分析和衡量其效率。它也适合被用在 ci 管道中,用于衡量你的镜像是否“足够高效”,或者换句话说——它是否浪费了太多空间。
结论
本文的目的并不是要说服你完全抛弃 docker,而是向你展示构建、运行、管理和分发容器及其镜像的整个场景和所有可选项。包括 docker 在内的每一种工具都有其优缺点,评估哪一组工具最适合你的工作流程和场景才是最重要的,希望本文能在这方面为你提供一些帮助。
Epoll封装类实现
textCNN论文与原理——短文本分类
明基宣布了为设计师量身定做的新型明基PD3420Q超宽显示器
PID控制规律及特点
小米6和华为P10都要涨价,你们准备好了吗?
为什么说不要用Docker了?
VSD?啥是VSD?VSD应用场景你知道吗?
提高伺服电机动态性能的重要性
方壳锂电池焊接3D视觉应用详解
虹科视频监控解决方案维护城市安全
亿纬动力与Aksa拟在土耳其组建合资公司
数字芯片和模拟芯片有哪些特性
西门子S7系列PLC定时器工作状态
为什么工程师创业总是失败
索尼α9发布:售31999 元,黑科技终问世,索尼大法好!
超低噪声的高速差分放大器ADA4930的性能分析
马云微博致信阿里巴巴全体投资者,称真正的使命是促进世界经济普惠共享
恩智浦恩智浦发布高性能77GHz毫米波雷达解决方案
以色列AR公司Lumus获融资4500万美元 HTC、盛大参投
推荐三款CVBS/AHD转USB的转换IC