译者 | 核子可乐

策划 | 褚杏娟

GitLab 高等专业做事工程师、DevOps 顾问 J. B. Crawford 最近写了一篇关于抱怨 Docker 的文章,在网上引发了开拓者们的谈论。
有人力挺,也有人反对:“我不明白没有 Docker 的堆栈管理怎么会更好。

删除分卷php逻辑Docker 的咒骂曾认为它是最终解法最后倒是罪孽深重 React

J. B. Crawford 在文章中表示:“我不太确定 Docker 帮助节约的韶光有没有超过对它的管理本钱。
”下面让我们详细看看他为什么对 Docker 感到不满。

系统管理中的根本问题

打包软件一贯是系统管理中的一大根本问题。
它非常主要,对系统的利用办法有着巨大影响,乃至让包管理器成为区分操作系统的一项主要指标。

以 Windows 为例:在很多“Linux 派”眼中,这款操作系统最不讨喜的便是缺少包管理机制,微软先后多次考试测验引入这个观点也都没能得到广泛认可。
比较之下,Linux 天下中各发行版间的紧张差异,就集中在其管理软件 repo 的办法上。
我所指的不但是 dpkb 和 rpm 之间的差异,而是在强调更底层的设计,例如硬性指定还是上游配置、稳定 repo 还是滚动发布。
拿 RHEL 和 Arch 来说,二者虽然共享绝大多数实现,但管理风格却截然不同。

大多数 Linux 发行版都会明确强调某种软件该当如何打包(乃至规定了多久打包一次),而且这些系统也都有着一项共通理念:将依赖项集中起来。
该当把库声明为依赖项,并把所依赖的包安装在公共位置以供链接器利用。
但这也可能带来寻衅,由于不同的软件每每依赖于不同的库版本,而各版本之间可能并不兼容。
这也是在掩护 Linux 发行版时最范例的老大难问题:如何供应能够良好协作的软件 repo 版本。
像 RHEL 这类稳定发行版的一大上风,便是它们在这方面表现得相称可靠;至于缺陷,便是这种可靠性实在是通过谢绝大部分新版本软件来实现的。

由于须要供应大量可以相互兼容的软件版本,并确保发行版遵守各种构建规范(例如支持自由软件等开拓理念、以及配置文件布局等详细规则),以是向 Linux 发行版引入新软件时每每极度麻烦且繁琐。
对付软件掩护职员来说,他们须要面对一大堆有着特定构建和配置差异的旧版本,并想办法把它们塞进发行版中去。
而在发行版和软件包掩护者这边,则须要通盘考虑各种上游软件是否符合发行版策略,并办理版本和依赖项问题。
虽然行业中已经出台了一系列干系规范,但详细操作仍旧令人头痛,弘大的事情量也几近猖獗。

这就形成了一种双输的诡异局势:希望自己的软件能够广泛传播的开拓者必须忍受发行版的怪癖,而想要壮大自身软件生态的发行版也得顺应开拓者。
每个人都不愉快,每个人都很怠倦。

有问题,自然有人考试测验办理。
业界已经在这方面做出了各种探索,但也正是由于方法多种多样,以是至今没有真正涌现一种能够统领全局的办理方案。
在桌面环境中,常见的软件分发选项有 Flatpak、Snap 和 AppImage 等。
这些系统的镜像或运用程序能够将软件及其依赖项共同打包,供应一套完全的独立环境,从而担保在任何发行版上都能正常事情。

但在实际利用时,我自己曾不止一次对 flatpak 文件进行逆向工程以修正个中的依赖项,以是说上述工具的宣扬效果在日常运用时并不一定能发挥浸染。
但公正地讲,软件在与各种要素的交互过程中,难免会涌现猜想之外的状况——比如运行时无法精确将各种要素彼此隔离开来。
视频技能栈便是个范例,我们每每须要删除或更换掉包中缺点的 OpenGL 库,才能使其跟特定图形驱动程序共同运行。

只管如此,我还是承认上述工具的运行效果不错,乃至值得进一步扩展并普遍利用。
它们所依托的桌面运用形式紧张强调与用户交互,并通过自身界面吸收用户发来的配置选项。
只管在与文件系统交互时仍旧不足顺畅丝滑,但这种将交互界面限定在 GUI 形式内的作法确实让沙箱变得既可行、又极具现实意义。

须要强调的是,本文不会对沙箱问题做过多延伸。
沙箱是一项主要的安全和稳定性保障技能,但这里谈论的紧张是软件打包和分发问题。
毕竟沙箱软件也可以通过更传统的办法进行分发,在其当代外壳之下的打包机制实在远没有人们想象的那么前辈。

让我无法忍受的是什么?

总而言之,我想抱怨的实在是做事器真个软件运行体系。
这里的软件打包方案基本只有一个:Docker。
当然,人们偶尔也会利用 Podman 等兼容性的工具选项。

Docker 的涌现被广泛视为做事器运营最佳实践的里程碑事宜。
只管 Docker 是一种软件分发办法,但其最初彷佛紧张是为了将容器编排引入大规模可扩展环境。
但终极随着不断发展和思想领悟,Docker 成为一种面向开拓者和单节点用例的常见软件分发办法。

现如今,Docker 也成为 Linux 上最常见的做事器端软件分发选项。
而我,对它恨之入骨。

千万别误会,我在这里要批评的并不是容器技能本身。
容器化非常精妙、有着诸多优点,虽然未必像炒作中说的那样全面碾压轻量化虚拟机,但它的亮点还是非常突出。
我不太确定 Docker 帮助节约的韶光有没有超过对它的管理本钱,但公正地讲,身为一名 DevOps 顾问,实践履历见告我精确运行 Docker 镜像并不算特殊麻烦。

真正让我无法忍受的,便是在非 DevOps 环境中盲目利用 Docker 镜像。
镜像须要集上钩划和管理,也便是说 Docker 该当是向用户分发软件时的最小公共集,该当是种最低的保障性选项。
而每当看到开源做事器端软件以 Docker 镜像的形式提交过来,乃至是更糟糕的 Docker Compose 栈时,我的第一反应便是愤怒。
跟传统 Linux 软件包分发、或者利用源代码直接构建的软件比较,Docker 镜像总是须要耗费更多韶光才能正常起效。

很多朋友可能不太理解,Docker 不是把所有元素都独立隔离开来,降落了支配难度吗?确实,但接下来我打算聊几个常见问题,也便是“Docker 不适用的情形”。

配置

Docker 在分发中最大的问题之一,便是短缺统一的配置约定。

绝大多数做事器端 Linux 软件须要读取文本文件来获取配置,这种古老的办法当然有自己的问题……但至少它有着统一的框架和准则。

可 Docker 镜像就不同了。

如果大家听说过 12 成分运用原则,就会意识到 Docker 镜像的最佳配置办法该当是通过环境变量。
这样做的好处在于,启动容器时可以在命令行上轻松实现;至于缺陷,便是环境变量不太适宜通报构造化数据,而且由于大多须要通过 shell 脚本进行交互,这些脚本在处理长值或繁芜值也显得比较笨拙。

DevOps 环境中利用的许多 Docker 镜像确实会从环境变量中获取配置,但出于前面这些现实问题,它们每每要通过避免繁芜配置(例如假设 TLS 将被「他方」终止)或者掌握信息获取量来实现。
而配置内容,则来自网络上的数据库或做事。

不过对付大多数终极用户软件来说,其配置过于繁芜或冗长,单靠环境变量根本无法容纳。
这时他们就只能乞助于配置文件,即以某种办法将配置文件纳入容器的文件系统当中。
Docker 倒是供应了多种操作实行办法,于是不同软件包的文档会根据干系推举而有所差异,乃至常常触发关于所有权和权限的警告。

更糟糕的是,很多 Docker 镜像还试图通过供应某种入口点 shell 脚本来降落配置难度,这些脚本卖力向容器供应更大略的文档以天生完全配置。
这种抽象级别在实践中每每短缺相应的记录痕迹,这就让故障排查变得更加困难。

我自己就无数次经历过软件无法正常启动的“惊喜”,缘故原由便是脚本引用了一些配置中未供应的键,导致我们必须查阅 Docker 镜像构建解释和入口点脚本来反推它的启动过程。
这好吗?这一点也不好。

最要命的是,很多配置入口点脚本还有自己的设计方向性。
别看“设计方向性”这词彷佛比较中性,但它的真实含义便是“除了开拓者自己,别人都弄不明白”。
我至少有十几次都被迫自己构建 Docker 镜像版本,来更换掉那些没有公开底层软件参数的入口点脚本。

更夸年夜的是,某些 Docker 镜像乃至根本不供应任何解释文档。
用户必须深入研究、四下探寻,才能找出当前软件所利用的配置文件的详细位置。
我认为 Docker 镜像至少也得给出最基本的 README 信息,帮助我们理解打包的软件到底该当如何配置。

文件系统

Docker 供应的沙箱或者说隔离机制肯定是个优点,但这也代表着它一定有着统统沙箱所面临的共同问题。

沙箱隔离机制跟 Linux 的文件系统兼容性很差,哪怕不涉及 UID 行为、纯挚在 Docker Compose 栈中利用命名分卷就足以引发意外。
在须要利用虚拟容器跟命名分卷中的文件进行交互时,比如实行备份之类的日常操作(都不说故障排查这类更繁芜的需求),结果都很可能让人头痛欲裂。
随着韶光推移,命名分卷得到了大幅改进,但看似大略的操作在不同 Docker 版本之间仍常常涌现奇怪的冲突,更不用说还得考虑如何兼容 Podman 等其他工具了。

当然,UID 也有自己的问题。
Docker 的一大原罪,便是哀求以 root 身份运行软件。

没错,Docker 确实供应一定程度的隔离,但从纵深防御的角度来看,以 root 身份公开运行用户操作仍旧不是明智之举。
为此,我须要频繁重构软件来适应 Docker 的这种怪癖,而且全体过程相称繁芜。
而且在轻微繁琐的环境中利用 Docker,都有很大概率引发涉及 UID 分配的 NFS 难题。
利用命名分卷当然能缓解这些问题,但分卷本身又有自己的痛点,切实其实是要人老命。

容器可移植性很差

对付强调分布式特性的 Docker(特殊是 Docker Compose)来说,最讽刺的还在于很多常规实践会大大影响其可移植性。

在 Docker Compose 中对网络实行的任何非默认操作,都有可能导致其在网络设置比较繁芜的打算机上无法正常事情。
很多 Docker Compose 栈都会将那些众所周知的端口默认为可供侦听器利用。
它们还会启用各种底层软件功能,又不供应禁用的方法,乃至用到很多在详细环境中并不可用的通用值。

就个人而言,最让我恼火的便是 TLS。
前文已经提到,我认为 Docker 容器不应该终止掉 TLS。
只有接管 TLS 连接,才许可访问私钥信息。
只管长达 90 天的临时 TLS 证书和普遍懈怠的安全意识已经毁坏了保障能力,但我仍旧认为私钥信息该当受到严密保护。
这部分信息只应存储在一个位置,且只能由一名主体进行访问。
当然,能不能实现这种安全毁坏还在其次,很多用户可能根本就搞不定 TLS 配置。

不少自行托管软件的朋友都会选择 SNI 或者虚拟托管的办法,个中每每存在涉及多个子域的通用证书。
这统统最好都能在少量、乃至是单一专用点上处理。
而一旦碰着在构建中假设在各个点上单独处理 TLS 的 Docker 镜像,可就倒了大霉了。
纵然完备不考虑 TLS,我个人也永久不想把 Docker 运用容器直接暴露在互联网上,在前面支配反向代理才是精确的选择。

此外,Docker Compose 栈还总想要利用 ACME 为终极用户软件颁发自有证书,我们得深入研究解释文档才能搞清如何禁用这一行为。

单一用场打算设备

我前面说的这些问题在业余人士开拓的软件中最为常见,我现在脑海中就浮现了 HomeAssistant 和 Nextcloud 这两个例子。
请别误会,我所说的“业余”并不质疑软件本身,而是在强调普通用户的利用习气。

遗憾的是,由于 Raspberry Pi 设备的价格越来越低廉,很多业余爱好者失落去了那份严谨态度。
可能我说的有点夸年夜,但他们在专用硬件上运行的“自托管”软件包已经多到了荒谬的程度。
在我看来,软件名称中带有“pi”基本便是个危险旗子暗记,代表着开拓者“没考虑过在共享设备上运行须要做哪些改动”。
大家可以说是我太守旧,但我认为打算设备就不该只能实行一项任务,特殊是那些须要 24/7 全天候开机的设备。

HomeAsistant 可能便是个中最大的罪魁罪魁,我自己就在一台设备上通过 Docker 运行它,还有其他几款运用程序。
但 HomeAsistantr 明显不想跟其他软件共存,每次更新后都会弹出“检测到不支持软件”的提醒。
这也太过分了,有必要管得这么宽吗?

后来我决定考试测验一下 Nextcloud,大概花了两个小时想让打包的 Docker 镜像在自己的环境上正常运行。
终极,我决定放弃并转为手动安装,结果创造它便是一款平平无奇的 PHP 运用,跟十几年前的程序没有任何差异。
这么大略的东西,你把它打包成 Docker 镜像干什么嘛?直接用 config.php 不好吗?

“罪大恶极,罄竹难书”

当然,大家可能以为前面的问题都是操作细节,只要负责制作 Docker 镜像就能避免。
没错,确实可以!
大概 Docker 最大的问题便是它门槛太低了。
创建 RPM 或者 Debian 软件包就有一定的技能难度,纵然是履历丰富的开拓者须要多次考试测验才能让 rpmbuild 顺利运行起来(这里建议只用 copr 和 rpkg)。

我的抱怨在于,纯以 Docker 镜像的形式做分发,每每代表着开拓者并没有对自己的项目投入足够的心力、或者至少没有专门做过项目分发设计。
要想让自己的成果在各种非标环境中运行起来,就一定得预先考虑到可能涌现的意外。

当然,大家该当能明白,我的用词只是种夸年夜和讽刺。
我曾经把 Docker 誉为稳定可靠的终极办理方案,只是后来创造必须得有一定的配置履历和管理水平才能实现。

写在末了

当然,以上只是我的一点个人不雅观点,相信很多朋友会抱有完备不同的见地——比如我坚信 Docker Compose 是容器时期最大的缺点之一。

15 年前我曾写过一篇类似的文章,讲述自己在开拓小型项目时在 RPM 中碰着的各种问题。
Docker 最让我惊异的一点,便是它能让项目发展到很大的方案、得到企业的广泛支持,同时还通过 Docker Compose 栈大大降落了分发的技能门槛。

我在前面提到的很多问题也确实源自这种“大略性”,由于很多项目根本就没有固定的分布式工程职员。
面对不断变革的软件发展格局,他们只是想用廉价的单片机搭配上 Docker 容器技能,回避掉相对更麻烦的虚拟机镜像。
当然,我也承认随着年纪愈长,我这人说话也越来越不中听了。

原文链接:

https://computer.rip/2023-11-25-the-curse-of-docker.html