Gentoo 的非常规理由
我有一个秘密要坦白。我使用 Gentoo Linux。我在参加的各种 Linux 用户组会议上的同事们认为我疯了。众所周知,Gentoo 是一个基于源代码的 Linux 发行版。Gentoo 的名声(很大程度上是由开发该发行版的人们推动的)是它适合那些想要超级疯狂优化的人,并且它确实只适合那些使用桌面的人。但事实上,Gentoo 非常适合许多其他意想不到的理由。令我惊讶的是,人们实际上正是出于这些原因在生产环境中使用 Gentoo。
由于原始 i386 处理器的所有后代都具有二进制兼容性,因此其他 Linux 发行版(更不用说为 Microsoft Windows 编写软件的所有人)可以发布为其通用 i386 平台编译的软件的预打包二进制版本,并利用它可以在任何地方工作的优势。另一方面,这些发行版无法利用您的高级 CPU 可能提供的任何新优化,这很可惜。
Gentoo 是一个从源代码构建的发行版;但是,您可以指定在为系统构建软件时要使用的编译器标志。特别是 GCC 允许指定代码将在哪种 CPU 上运行。通过指定处理器类型,例如 Intel Pentium III 或 AMD Athlon Thunderbird,编译器能够生成处理器特定的代码,理论上,这会产生更好、更快的机器代码。
Gentoo 系统更快吗?轶事证据褒贬不一。似乎 Gentoo 系统比运行更流行的发行版的配置相同的系统运行速度稍快一些。但是,如果系统未正确安装、配置和调整,则任何小的性能优势都会完全浪费掉。因为我们中的许多人不知道如何做到这一点,并且因为 Gentoo 提供了很大的自由度来做您自己的事情,如果您做了一些愚蠢的事情,很容易失去稍微快一点的程序带来的好处。
因此,从速度的角度来看,您使用从源代码构建的发行版还是二进制包发行版真的无关紧要。如果提高速度不是使用 Gentoo 的理由,那么您为什么要使用这种从源代码构建的东西呢?
人们因各种原因对他们的计算机感到恼火。我们在这里关注的是较新版本的问题,现代操作系统以两种方式遇到这个问题。第一种方式是当用户需要发行版中未包含的工具包、实用程序或其他项目时。结果,用户需要自己滚动它。第二种方式是当应用程序或工具的较新版本可用时,而预打包发行版中包含的版本较旧。
在这两种情况下,一个关键问题都在起作用:操作的简易性。操作系统是否可以帮助您应对管理系统带来的挑战?令我惊讶的是,事实证明 Gentoo Linux 在这方面确实做得很好。
使用 Gentoo,人们通过下载源代码然后编译它们来安装新软件包。您想要一个软件?没问题——发出指令,稍等片刻,它就安装好了。这与 Debian 的用户体验相同。
然而,当用户需要软件的较新版本时,Gentoo 真正闪耀。例如,假设我正在使用 bluefish HTML 编辑器,并且一个错误让我很烦恼。较新版本的 bluefish 可能在 Portage(Gentoo 的软件包管理系统)中可用,因此我可能会要求升级。Gentoo 有一个方便的命令叫做 etcat,它可以确定有哪些可用版本
# etcat versions bluefish
etcat 告诉我我安装了 0.9 版本的 bluefish,现在 0.12 版本可用。从阅读 bluefish 网站,我知道问题在 0.12 版本中已修复,所以我肯定想要升级。
emerge 命令告诉您如果我执行升级会发生什么
# emerge --pretend bluefish
显然,此版本的 bluefish 需要一个名为 libpcre 的库。Portage 向我表明,除了升级 bluefish 之外,它还将引入 libpcre。所以,我们开始吧
# emerge bluefish
首先,Portage 下载、构建和安装 libpcre,然后对 bluefish 执行相同的操作。四分钟后,我完成了升级。非常容易。
您可能已经注意到它没有说要安装 0.13 版本。那是因为目前 0.13 版本被屏蔽了,这就是为什么它以红色显示。在这种情况下,0.13 刚刚发布,现在有一个 ebuild 文件。但是,ebuild 文件仍在测试中,以查看软件是否实际安装以及是否存在明显的错误。如果我真的需要它,我可以覆盖 Portage 并告诉它引入 0.13。同样,如果我有理由这样做,我可以选择 0.11 版本。这种灵活性是 Gentoo 最强大的优势之一。
当我需要安装系统未提供的软件时,会出现更棘手的情况。各种发行版建立软件包管理工具的重要原因之一是为了对系统上安装的内容有一个统一的视图。对于每个软件,无论是基本的系统工具、核心库、服务器程序还是用户应用程序,都会创建一个软件包。当每个软件包安装在您的系统上时,操作系统会记录哪些文件放在哪里以及软件包已安装。这样,其他依赖于这些软件包的软件就可以安装,因为知道它们的前提条件已就位。
但是,如果您安装了较新版本的软件并且没有适合您操作系统的软件包会发生什么情况?您通常会执行与构建软件包的人相同的构建步骤,只是您可能会执行以下两件事之一
将其安装在某个私有位置,可能是 /usr/local/bin,然后努力确保您的程序正在运行,而不是旧程序。
盲目地将您的软件安装在根文件系统中,希望您不会在此过程中破坏任何内容,并祈祷将来没有任何东西会覆盖您已安装的程序和文件。
想一下这个问题。不必担心这些事情是否让您觉得有点傻?毕竟,软件包管理系统不正是应该防止这种情况发生的吗?
我提出的问题不是“是否存在制作软件包的能力”,因为答案是“普遍存在”,我也不是在问“您是否可以创建自己的软件包”。相反,我想知道这样做有多容易。
假设您拥有操作系统提供的 bogofilter 副本和 0.16.1 版本的 .rpm。突然,bogofilter 的作者发现了一个愚蠢但严重的错误已经潜入,并在此后不久发布了 0.16.2。
问题是您不得不等待您的发行版发布新版本的 .rpm、.deb、.pkg 等等,这可能需要很长时间,让您处于想要自己制作的位置。这就是麻烦开始出现的地方。从概念上讲,创建您自己的新 .rpm 或 .deb 软件包很容易。“只需使用现有的 0.16.1 软件包作为原型。” 但是对于大多数人来说,也就是说,任何不处于向导级别的人,有时甚至不处于向导级别的人,实际上都很难做到。您必须
下载软件包描述或以某种方式从现有软件包文件中提取它。
手动下载上游 .tar.gz(或其他任何)源代码的新版本并解压缩它。
移植构建描述(在 Debian 的情况下,移植到新的上游源代码中),甚至可能针对这些源代码进行修补。
您可能必须修改构建脚本以指示它有关新版本的信息。
实际尝试创建软件包。这涉及编译它,这可能还需要您安装大量以前不知道的 -dev 软件包。
然后您安装并测试。
所有这些都是可以做到的,但是获得这里需要的技能有一个相当陡峭的学习曲线(特别是对于新手而言)。更重要的是,这是一项您宁愿不做的大量工作。
从概念上讲,与上面概述的过程没有什么不同,在 Gentoo 系统上构建软件包更容易。神奇之处在于 Gentoo 中的软件包描述文件 ebuild,遵循简单的格式。它们基本上是 shell 脚本(ebuild 将在本文后面介绍)。在此过程中,您指定从哪里获取源代码 tarball。当您构建时,Portage 会下载源代码,然后继续解压缩并编译它。因为它们是 shell 脚本,所以它们可以有效地使用 shell 变量。特别是,它们通过解析 ebuild 文件名并将版本号放入脚本可以使用的变量中来获取版本号。
在我们上面的 bogofilter 示例中,软件包文件(称为 bogofilter-0.16.1.ebuild)包含这样一行
SRC_URI="http://sourceforge.net/downloads/bogofilter-${PV}.tar.gz"
当您去构建和安装 bogofilter 时,Portage 会根据文件名将 $PV 设置为 0.16.1,并获取相应的 .tar.gz。然后它解压缩它并继续执行 ./configure; make; make install,然后按照指示构建软件包。要为您想要的新版本 0.16.2 创建 ebuild 脚本,请执行以下操作
# cd /usr/portage/net-mail/ # cp bogofilter-0.16.1.ebuild bogofilter-0.16.2.ebuild # ebuild bogofilter-0.16.2.ebuild digest
假设软件包描述、解压缩说明等内容都不需要更新,那么您所要做的就是这些。
还有一些需要了解的。例如,您可能需要在 /usr/portage 树的私有副本中执行上述操作,这样当主树更新时,您就不会丢失您的更改。Portage 明确支持这一点;查看在线文档中或 /etc/make.conf 中 PORTAGE_OVERLAY 变量的描述,了解如何告诉 Portage 您的自定义 ebuild 在哪里。现在您可以告诉 Portage# emerge bogofilter
您就有了新版本。
Gentoo 在其全球镜像站点上拥有其各种软件包所需的源代码 tarball 的副本。通常,Portage 从其中一个站点获取源代码。但是,如果您要构建的内容不在 Gentoo 的镜像站点中,也没问题。Portage 只需访问原始上游下载站点。
Portage 使用 md5sum 来确保您获得未损坏的下载。这就是上面的第三个命令(ebuild ... digest)的目的;它下载源代码,然后为您计算 md5sum。因为您是执行版本升级的人,所以您有责任确保您实际下载的是未损坏的文件。因此,您可能应该执行ebuild ... unpack首先获取下载,确保它没问题,然后再执行 digest 命令。
最后,如果您想要一个您的操作系统未提供的软件包,您必须自己编写一个。使用 Gentoo,编写自定义 .ebuild 很简单。
Gentoo 的软件包描述是用 bash 编写的。各种指令都放在由 Portage 在此过程中调用的函数中。主要的函数有
pkg_setup() src_unpack() src_compile() src_install() pkg_preinst() pkg_postinst()
它们按顺序调用。要告诉 Portage 如何构建您的软件,请为每个步骤编写函数,并在每个步骤中提供一些信息,例如前面讨论的 SRC_URI。
要编译您的源代码,您可以使用
src_compile () { ./configure --prefix=/usr make }
关于这些 shell 脚本的惊人之处在于,它们可以通过重载函数来提供合理的默认值。事实上,src_compile() 的默认值几乎就是我上面展示的,这对于许多软件包来说是完美的。事实上,您可以编写一个依赖于默认值并且根本没有定义自定义函数的 ebuild。
有时您可能想要./configure根据您拥有的系统类型以不同的方式配置软件包。Portage 有一个名为 USE 的环境变量,在 /etc/make.conf 中设置,并且可以在命令行上覆盖,其中包含可用于描述和自定义系统的令牌。假设您有一个软件包,可以根据您是否需要 X Window System 支持或 IPv6 支持来以不同的方式构建。您的 src_compile() 函数可能如下所示
src_compile () { use X && conf="${conf} --with-x" use ipv6 || conf="${conf} --without-ipv6" ./configure --prefix=/usr ${conf} make }
您可以看到正在使用的 shell 脚本的各种功能。在此示例中,如果您的系统上有 X,则会告诉此软件包继续构建 X 支持。如果是服务器,并且您不需要任何这些,则您的软件将在没有额外开销的情况下构建。USE 变量可以在命令行上被覆盖,因此如果您需要,您可以进行更精确的控制。
src_unpack() 的工作方式相同。如果您不包含一个,Portage 会继续前进,在默认位置解压缩源代码 tarball,更改目录并相应地设置工作目录环境变量 $WORKDIR。另一方面,如果必须发生一些不寻常的事情——例如,应用补丁——那么您可以自己编写一个简单的 unpack 函数
src_unpack () { unpack ${A} epatch ${FILESDIR}/fixit.patch }
我用一个完整的例子来总结。我有一个客户专门使用 ssh.com 对 SSH2 协议的实现。所以,我需要在多台机器上安装它。请参阅清单 1。
ebuild 首先设置一些环境变量,包括
SLOT:通常用于库。当 ebuild 作者知道可以在系统上同时安装同一软件包的多个版本时,他或她可以分配一个槽号来区分它们。在我的一个系统上,我有 Berkeley DB 版本 1.85 (SLOT 1)、版本 3.2.9 (SLOT 3)、版本 4.0.14 (SLOT 4) 甚至版本 4.1.25_p1 (SLOT 4.1)。有很多软件是为使用旧的 API 而编写的;没有理由不能安装它们。如果在 4.0 系列中发布了较新版本作为稳定版本,例如 4.0.17 版本,只要它保持在 SLOT 4 中,我的系统就会为我提供从 4.0.14 升级的版本,而无需删除已安装的其他版本。诚然,Berkeley DB 是其中一个更复杂的例子,但它展示了 Gentoo 槽实现背后的强大功能。大多数 ebuild 都不需要任何这些,并说 SLOT=“0”。
KEYWORDS:您可以在其中指示对不同架构的支持。在该示例中,我已经展示了此 ebuild 在 x86 系列平台上已知是工作和稳定的。~ppc 中的 ~ 表示它被屏蔽了。我知道以前的版本是在 PowerPC 系统上构建的,但我手头没有可用于测试的系统,因此其他人在决定安装此版本之前可能需要谨慎。在官方 Portage 树中,像这样的 ebuild 会保持这种状态几周,直到使用 PowerPC 的人们能够测试 ebuild。在收到几个正面报告后,ebuild 将被取消屏蔽。
DEPEND, RDEPEND:其中列出了依赖项。这是一个相当完整的语法,可以列出必要软件包的特定版本。最常见的修饰符是 >=,它表示必须安装至少该版本,通常是因为我们的程序依赖的 API;和 !,用于表示此软件包与另一个软件包的存在冲突。两者不能同时安装。
RDEPEND:运行时依赖项,使用软件包必须安装的东西。DEPEND 是首先构建它的依赖项;仅当您安装在其他地方构建的二进制软件包时,差异才会显现出来。
RESTRICT:可以对 Portage 的功能进行各种细粒度的控制。在这种情况下,因为这是一个我自己编写的 ebuild,所以我使用 nomirror 告诉 emerge 不要费心在 Gentoo 的镜像系列中查找。这实际上并不意味着我不能使用上游作者提供的镜像。事实上,如果您查看 SRC_URI,您会看到我已经列出了一个离我较近的镜像,我知道我应该能够从中获取我需要的 .tar.gz。
然后,我们继续重载控制软件包构建方式的各种函数。src_compile() 函数是有趣的部分。我已经采用了上面的示例并对其进行了稍微扩展。您可以看到某些选项由 USE 变量控制,而其他选项是我们指定的,例如我们希望配置文件去哪里。我们实际上不需要 die 失败消息,但它们说明了我们如何拥有完整的语义和可用的 shell 脚本的功能。
最后,在 src_install() 函数中,我们可以依赖默认值,但在我的系统上,/etc/init.d 中的文件没有附加 .rc。更重要的是,这旨在替换部署此 ebuild 的目标系统上的 OpenSSH。因此,我想明确表示 RC 脚本与 OpenSSH 放置的脚本不同。
Portage 提供了丰富的辅助函数库,可以简化常见任务的执行。我们利用其中一个函数来说明我们希望 RC 脚本去哪里,并查看它是否标记为可执行文件。现在,您将 ebuild 放入 Portage 树的本地覆盖层中,并告诉 emerge 执行其操作。
此示例仅触及表面。有关更多详细信息,请参阅 www.gentoo.org/proj/en/devrel/handbook/handbook.xml?part=2&chap=1#doc_chap2,以及以下命令的输出emerge --help以及任何 Gentoo 系统上 ebuild(1) 和 ebuild(5) 的手册页。
清单 1. ssh2-3.2.9.1.ebuild
DESCRIPTION="ssh.com's implementation of SSH2" SRC_URI=" ftp://mirror.aarnet.edu.au/pub/ssh/ssh-${PV}.tar.gz ftp://ftp.ssh.com/pub/ssh/ssh-${PV}.tar.gz" HOMEPAGE="http://www.ssh.com/products" SLOT="0" LICENCE="free-noncomm" KEYWORDS="x86 ~ppc" RDEPEND="virtual/glibc !net-misc/openssh >=sys-libs/zlib-1.1.4" DEPEND="${RDEPEND} dev-lang/perl >=sys-apps/sed-4" PROVIDE="virtual/ssh" IUSE="X ipv6" RESTRICT="nomirror" # we're calling the package ssh2; the source # tarballs are all ssh-x.y.z So, we have to # overwrite S to specify the actual name of the # directory as unpacked S="${WORKDIR}/ssh-${PV}" # probably could have relied on the default here src_unpack() { unpack ${A} } # Of the large number of configure options that # is offered, we offer customization of # whether X windows and IPv6 support are # compiled in. src_compile() { local conf use X && conf="${conf} --with-x" use ipv6 || conf="${conf} --without-ipv6" ./configure ${conf} --host="${CHOST}" \ --prefix="/usr" \ --with-ssh-agent1-compat \ --with-etcdir="/etc/ssh2" \ || die "configuration failed" make || die "compile failed" } # again, almost the default pattern, but # we want to change the name of the rc script src_install() { make DESTDIR=${D} install exeinto /etc/init.d newexe ${FILESDIR}/sshd2.rc sshd2 }
考虑一下这些问题不仅发生在单个桌面上,而且发生在由数十台服务器或数千个工作站组成的生产平台环境中。坦率地说,没有多少操作系统可以为您提供太多帮助。关于基础设施管理,有大量的文献。可悲的是,仍然发生大量的临时部署。尽管许多供应商都有工具可以帮助您首次构建一系列系统,但长期维护它们的任务留给各个站点处理。较新版本的问题不是关于单台机器,而是关于它们的整个网络。
那么 Gentoo 在生产环境中的表现如何呢?这是来自基于源代码的发行版的另一个惊喜:可以告诉 Portage 构建二进制软件包。这允许您让一台机器在角落里完成所有编译工作。然后,软件包可以共享并供您的所有目标机器使用,而不是让它们自己构建软件包。您可能会想说“这不是其他 Linux 发行版所做的吗?” 不同之处在于选择正确的软件包组合是站点决策,而较新版本的问题绝对是站点要处理的负担。Gentoo 为本地系统团队提供了自己解决这些版本问题的工具。
通过使用本地构建服务器,您可以有效地集中处理能力和版本管理,但仍然有本地自定义的空间。暂存环境很容易设置。然后,一旦您对已测试的版本集感到满意,您只需创建这些二进制软件包的快照,并将它们共享给您的普通机器。Portage 的最新版本包括对从本地文件服务器获取您创建的二进制软件包的内置支持,因此所有这些都开箱即用。
创建您自己的软件包或私下版本升级现有软件包——较新版本的问题总是会出现。更主流的软件包管理工具虽然成熟,但需要付出更大的努力才能完成这些任务。然而,从概念上讲,这些任务是微不足道的。令我非常惊讶的是,因为他们没有宣传这方面,Gentoo 工具的设计使您可以轻松地自己完成这些任务。
Andrew Cowie 运营着 Operational Dynamics (www.operationaldynamics.com),这是一家运营和基础设施工程咨询公司。他通过关注人和与人相关的流程来帮助组织从他们的技术中获得价值,这可能就是为什么他如此痴迷于寻找更简单的方法来做事。您可以通过 andrew@operationaldynamics.com 或在 irc.freenode.net 上以 AfC 的身份联系他。