容器构建有两大趋势:使用基本镜像和从头开始构建。每个都有工程上的权衡。
有人说 Linux 发行版不再与容器有关。像 distroless 和 scratch 容器等可替代的方法,似乎是风靡一时。看来,我们在考虑和做出技术决策时,更多的是基于时尚感和即时的情感满足,而不是考虑我们选择的次要影响。我们应该问这样的问题:这些选择将如何影响未来六个月的维护?工程权衡是什么?这种范式转换如何影响我们的大规模构建系统?
这真让人沮丧。如果我们忘记了工程是一个零和游戏,有可衡量的利弊权衡,有不同方法的成本和收益 —— 这样对我们自己不利,对雇主不利,对最终维护我们的代码的同事不利。最后,我们对所有的维护人员(向维护人员致敬! )都是一种伤害,因为我们不欣赏他们所做的工作。
理解问题所在
为了理解这个问题,我们必须首先研究为什么我们使用 Linux 发行版。我将把原因分为两大类:内核和其他包。编译内核实际上相当容易。Slackware 和 Gentoo( 我的小心脏还是有点害怕)教会了我们这一点。
另一方面,对于一个可用的 Linux 系统需要打包大量的开发软件和应用软件,这可能会让人望而生畏。此外,确保数百万个程序包可以一起安装和工作的唯一方法是使用旧的范例:即编译它并将它作为一个工件(即 Linux 发行版)一起发布。那么,为什么 Linux 发行版要将内核和所有包一起编译呢?很简单:确保事情协调一致。
首先,我们来谈谈内核。内核很特别。在没有编译好的内核的情况下引导 Linux 系统有点困难。它是 Linux 操作系统的核心,也是我们在系统启动时首先依赖的。内核在编译时有很多不同的配置选项,这些选项会对硬件和软件如何在一个内核上运行产生巨大影响。这方面中的第二个问题是,系统软件(如编译器 、C 库和解释器)必须针对内核中内置的选项进行调优。Gentoo 的维基以一种发自内心的方式教我们这一点,它把每个人都变成了一个微型的开发版维护者。
令人尴尬的是(因为我在过去五年里一直在使用容器),我必须承认我最近才编译过内核。我必须让嵌套的 KVM 在 RHEL7 上工作,这样我才能在笔记本电脑上的 KVM 虚拟机中运行 OpenShift on OpenStack 虚拟机,以及我的 Container Development Kit(CDK)。我只想说,当时我在一个全新的 4.X 内核上启动了 RHEL7。和任何优秀的系统管理员一样,我有点担心自己错过了一些重要的配置选项和补丁。当然,我也的确错过了一些东西。比如睡眠模式无法正常工作,我的扩展底座无法正常工作,还有许多其他小的随机错误。但它在我的笔记本电脑上的一个 KVM 虚拟机上,对于 OpenStack 上的 OpenShift 的实时演示来说已经足够好了。来吧,这很有趣,对吧?但我离题了……
现在,我们来谈谈其他的软件包。虽然内核和相关的系统软件可能很难编译,但从工作负载的角度来看,更大的问题是编译成千上万的包,以提供一个可用的 Linux 系统。每个软件包都需要专业知识。有些软件只需要运行三个命令:./configure
、make
和 make install
。另一些则需要大量的专业知识,从在 etc
中添加用户和配置特定的默认值到运行安装后脚本和添加 systemd 单元文件。对于任何一个人来说,调试好你可能用得到的成千上万种不同软件所需要的一套技能都是令人望而生畏的。但是,如果你想要一个可以随时尝试新软件的可用系统,你必须学会如何编译和安装新软件,然后才能开始学习使用它。这就是没有 Linux 发行版的 Linux。当你放弃使用 Linux 发行版时,那么你就得自己编译软件。
关键是,你必须将所有内容构建在一起,以确保它能够以任何合理的可靠性级别协同工作,而且构建一个可用的包群需要大量的知识。这是任何一个开发人员或系统管理员都无法合理地学习和保留的知识。我描述的每个问题都适用于你的 容器主机(内核和系统软件)和 容器镜像(系统软件和所有其他包)——请注意:在容器镜像中还包含有编译器 、C 库、解释器和 JVM。
解决方案
所以你也看到了,其实使用 Linux 发行版就是解决方案。别再看了,给离你最近的软件包维护者(再次向维护人员致敬!)发张电子卡吧(等等,我是不是把我的年龄告诉别人了?)。不过说真的,这些人做了大量的工作,这真的是被低估了。Kubernetes、Istio、Prometheus,还有 Knative:我在看着你们。你们的时代要来了,到时候你们会进入维护模式,被过度使用,被低估。大约七到十年后,我将再次写这篇文章,可能是关于 Kubernetes 的。
容器构建的首要原则
从零开始构建和从基础镜像构建之间存在权衡。
从基础镜像构建
从基础镜像构建的优点是,大多数构建操作只不过是安装或更新包。它依赖于 Linux 发行版中包维护人员所做的大量工作。它还有一个优点,即六个月甚至十年后的修补事件(使用 RHEL)是运维/系统管理员事件 (yum update
),而不是开发人员事件(这需要通过代码找出某些函数参数不再工作的原因)。
你想想,应用程序代码依赖于许多库,从 JSON mung 库到对象关系映射器。与 Linux 内核和 Glibc 不同,这些类型的库很少改变 API 兼容性。这意味着三年后你的修补事件可能会变成代码更改事件,而不是 yum 更新事件。因此让他自己深入下去吧。开发人员,如果安全团队找不到防火墙黑客来阻止攻击,你将在凌晨 2 点收到短信。
从基础镜像构建不是完美的;还有一些缺点,比如所有被拖入的依赖项的大小。这几乎总是会使容器镜像比从头开始构建的镜像更大。另一个缺点是你并不总是能够访问最新的上游代码。这可能会让开发人员感到沮丧,尤其是当你只想使用依赖项中的一部分功能时,但是你仍然不得不将你根本用不着的东西一起打包带走,因为上游的维护人员一直在改变这个库。
如果你是一个 web 开发人员,正在对我翻白眼,我有一个词可以形容你:DevOps。那意味着你带着寻呼机,我的朋友。
Scratch 构建
Scratch 构建的优点是镜像非常小。当你不依赖容器中的 Linux 发行版时,你有很多控制权,这意味着你可以根据需要定制所有内容。这是一个最佳模型,在某些用例中是很有效的。另一个优势是你可以访问最新的软件包。你不必等待 Linux 发行版更新任何内容。是你自己在控制,所以你可以自行选择什么时候去费功夫纳入新的软件。
记住,控制一切都是有代价的。通常,更新到具有新特性的新库会拖如不必要的 API 更改,这意味着修复代码中的不兼容(换句话说,这就像给牦牛剪毛)。在凌晨 2 点应用程序不起作用的时候给牦牛剪毛是不好玩的。幸运的是,使用容器,你可以在下一个工作日回滚并给牦牛剪毛,但它仍会占用你为业务提供新价值、为应用程序提供新功能的时间。欢迎来到系统管理员的生活。
好吧,也就是说,有些时候,Scratch 构建是有意义的。我完全承认,静态编译的 Golang 程序和 C 程序是 scratch/distorless 构建的两个不错的候选程序。对于这些类型的程序,每个容器构建都是一个编译事件。三年后你仍然需要担心 API 的损坏,但是如果你是一个 Golang 商店,你应该有能力随着时间的推移修复问题。
结论
基本上,Linux 发行版做了大量工作来节省你在常规 Linux 系统或容器上的时间。维护人员所拥有的知识是巨大的,而且没有得到真正的赞赏。容器的采用使得问题更加严重,因为它被进一步抽象了。
通过容器主机,Linux 发行版可以让你访问广泛的硬件生态系统,从微型 ARM 系统到 128 核 CPU x86 巨型机箱,再到云服务商的虚拟机。他们提供可工作的容器引擎和开箱即用的容器运行时环境,所以你只需启动你的容器,让其他人担心事情的进展。
对于容器镜像,Linux 发行版为你的项目提供了对大量软件的轻松访问。即使你从头开始构建,你也可能会看到一个包维护人员是如何构建和运送东西的 —— 一个好的艺术家是一个好的偷学者,所以不要低估这项工作的价值。
所以,感谢 Fedora、RHEL(Frantisek,你是我的英雄 )、Debian、Gentoo 和其他 Linux 发行版的所有维护人员。我很感激你所做的工作,尽管我是个“容器工人”
via: https://opensource.com/article/19/2/linux-distributions-still-matter-containers
作者:Scott McCarty 选题:lujun9972 译者:Chao-zhi 校对:wxy
本文由 LCTT 原创编译,Linux中国 荣誉推出