UML软件工程组织

 

 

自动化应用程序构建和发布的过程
 
作者:Martin C. Brown 来源:IBM
 
本文内容包括:
您已经足够多地考虑了为单一类型的系统构建开放源代码应用程序的情形,但是,如果您正在构建的应用程序需要在多种不同的、不兼容的机器上发布,又该怎么办呢?这个问题不太容易解决,但是,可以使用一些准则和定制的脚本来简化这一过程。本文研究的是如何创建一个构建和发布应用程序的结构,包括高度定制的版本,以及一种能在多种机器上尽可能轻松地传播应用程序(手工地或者自动地)的简单途径。

我总是在自己的系统亲自动手构建应用程序,而不去使用可用的二进制文件或 RPM 发行版本。这不是因为我不相信 RPM,而是因为对于很多系统来说,我使用的是一个定制的环境。我可能需要启用调试,或者需要一个简化或者特定的扩展名、模块或者其他选项的集合。有时,我甚至会为一个特定项目创建一个完全不同的目录结构。

因为复杂的缘故,我使用了大量使用开放源代码项目的平台,其中包括 Linux™、BSD、OS X,以及各种商业 UNIX® 发行版本。对于其中一些平台,我可能只有一台机器安装了这种平台;而对另一些平台来说,每种平台可能已经安装在 10 台、20 台甚至 50 台机器上。当软件某个部分的新版本开始可用时,手工地去更新每一台机器会是一项工作量极大的任务。实际的构建和安装过程(假设我们不考虑定制)通常非常简单,如清单 1 所示:

清单 1. 构建和安装过程
 
				

$ bunzip2 -c latest-release.tar.bz|tar xf -
$ cd latest-release
$ ./configure
$ make
$ make install

如果没有某些准则,我很可能要在成百上千台机器上手工去做这些工作。有时,感觉这像是一份只不过设法与最新版本(更不用说出于测试目的而使用某些程序包的 beta 和 alpha 版本了)保持同步的全职工作。

几年前,我决定通过遵循一些简单的规则并向该过程添加结构,来非常有效地简化这一过程:

  1. 为每种 OS 创建一个目录。
  2. 在每个 OS 目录中为每一个软件程序包创建一个目录。
  3. 为每一个版本创建一个目录。
  4. 为每种操作系统构建一次。
  5. 在每台机器上多次部署。

您可以先为所有跨网络共享的软件构建一个合适的结构。

创建一个中央构建目录

第一步是创建一个将通过 NFS 共享的目录,它将用来保存将要构建的应用程序。一旦完成构建,您就可以使用这个目录来保存系统所需要的所有内容,包括源文件目录、配置文件以及其他组件。不过,NFS 是关键。要让系统工作,就要能够通过网络,从需要访问正构建和安装的应用程序的所有机器访问这个中央构建目录。

创建一个顶层结构

首先要为需要在网络上支持的每一种操作系统创建一个顶层结构。每一个目录都应该明确对应于一种特定的操作系统、体系结构和版本编号(如果变化显著的话)。这些目录应该与您的网络上的机器是相匹配的。例如,在我的网络上,有非常多的机器,所以我的顶层目录结构数量非常大,如清单 2 所示:

清单 2. 操作系统层目录 in my build environment
 
total 58
drwxrwxrwx   2 root     other        512 Jul 25 12:01 aix/
drwxrwxrwx   2 root     other        512 Jul 25 13:14 darwin-ppc/
drwxrwxrwx   2 root     other        512 Jul 25 13:14 darwin-x86/
drwxrwxrwx   2 root     other        512 Jul 25 12:02 freebsd-x86/
drwxrwxrwx   2 root     other        512 Jul 25 13:14 freedbsd-sparc/
drwxrwxrwx   3 root     other       1024 Mar  9  2003 incoming/
drwxrwxrwx   2 root     other        512 Jul 25 12:02 linux-debian-x86/
drwxrwxrwx   2 root     other        512 Jul 25 12:02 linux-fedora-1-x86/
drwxrwxrwx   2 root     other        512 Jul 25 12:02 linux-fedora-2-x86/
drwxrwxrwx  11 root     other        512 Apr 13 16:56 linux-redhat-9.0-x86/
drwxr-xr-x   3 root     root        8192 Feb  6  2002 lost+found/
drwxrwxrwx  13 root     other        512 Jun 15 15:02 macosx/
drwxrwxrwx   2 root     other        512 Jul 25 12:02 netbsd-x86/
drwxrwxrwx   2 root     other        512 Jul 25 12:02 openbsd/
lrwxrwxrwx   1 root     other         12 Jul 25 13:13 solaris -> solaris8-x86/
drwxrwxrwx   2 root     other        512 Jul 25 13:13 solaris8-sparc/
drwxrwxrwx  32 root     other       1024 Jul 25 13:12 solaris8-x86/
drwxrwxrwx   2 root     other        512 Jul 25 13:13 solaris9-sparc/
drwxrwxrwx   2 root     other        512 Jul 25 13:13 solaris9-x86/

现在不要过多担心应用程序。这些操作系统目录将用来为每一个 OS 修订版本保存相应的应用程序目录。对应用程序构建的定制将单独存放于每个目录中。

我使用一个单独的文件系统作为构建目录,导出为 /export/build。这个文件系统有足够的空间;对于构建目录,您需要有足够的空间来保存应用程序在每一种操作系统上使用每一个配置的完全构建版本。例如,Perl v5.9.0 编译占用了大约 90 MB 的空间。 Apache 占用了大约 27 MB 的空间。

创建应用程序结构

您需要在每一个操作系统目录中为想要安装的每一个应用程序另外创建一个目录。注意,该目录是明确对应于每一种 OS 的,您应该只创建实际需要的应用程序目录。例如,您可能希望在 Fedora 机器上安装 Perl,但不想在其他机器上安装它。在清单 3 中可以看到一个示例规划:

清单 3. 一个 OS 目录中的应用程序目录
 
drwxrwxrwx   4 mc       staff        512 May 12 12:30 apache/
drwxrwxrwx   4 root     other        512 Apr 16  2003 assassin/
drwxrwxrwx   3 501      other        512 Jun 11 10:11 bind/
drwxrwxrwx   3 root     other        512 Jul 24 15:37 ccache/
drwxrwxrwx   5 root     other        512 Jun 21 17:12 clamav/
drwxrwxrwx   6 root     other        512 Jul 25 15:28 cpan/
drwxrwxrwx   4 root     other        512 Jul 25 12:35 cpanplus/
drwxrwxrwx   4 root     other        512 Sep  6  2003 cyrus/
drwxrwxrwx   3 root     other        512 Jun  6 16:29 dhcp/
drwxrwxrwx   3 root     other        512 Apr 11 15:42 distcc/
drwxrwxrwx   3 root     other        512 May 11 10:39 eggdrop/
drwxrwxrwx   3 root     other        512 Apr  9 05:50 ethereal/
drwxrwxrwx   5 root     other        512 Jun  8 19:12 gawk/
drwxrwxrwx   2 root     other        512 May 15 08:19 gcc/
drwxrwxrwx   4 root     other        512 Jul 28  2003 ircd/
drwxrwxrwx   4 root     other        512 Apr  9 05:44 libpcap/
drwxrwxrwx   5 root     other        512 Jul 25 13:22 mysql/
drwxrwxrwx   3 root     other        512 Mar 25 13:07 nmap/
drwxrwxrwx   3 root     other        512 Nov 21  2003 ogsi-lite/
drwxrwxrwx   6 root     other        512 Jul 25 13:33 perl/
drwxrwxrwx   3 root     other        512 Aug 19  2003 php/
drwxrwxrwx   6 root     other        512 Mar  5 15:09 ppro/
drwxrwxrwx   3 root     other        512 May 15 09:02 python/
drwxrwxrwx   4 root     other        512 Apr 22 06:06 razor/
drwxrwxrwx   3 root     other        512 Aug 24  2003 razorsdk/
drwxrwxrwx   4 root     other        512 Dec  4  2003 sendmail/
drwxrwxrwx   3 root     other        512 May 23 09:37 ssh/
drwxrwxrwx   4 root     other        512 May 11 10:25 tcl/
drwxrwxrwx   3 root     other        512 Dec 22  2003 tcpwrappers/
drwxrwxrwx   6 root     other        512 Apr 24 07:13 xmltv/

您需要再在每一个目录中创建一个或两个结构,这取决于应用程序以及组织和配置您的单独构建的选项。创建的每一个目录将用来保存 live 应用程序(已配置和构建的应用程序版本)。您可以以两种方式进行组织:使用对应每个版本的目录,或者直接使用应用程序版本。

  • 对应每个版本的目录:对应于应用程序每个版本的另外一层目录。对那些想要每一个版本创建很多不同构建配置的应用程序来说,这很实用。例如,您可能会创建一个名为 perl-5.9.0/debug-build 的目录,以及另一个名为 perl-5.9.0/std-build 的目录。
  • 直接版本目录:目录中的每个子目录都是原始 tarball 所创建的。如果您在所有机器上对给定的 OS 使用相同的配置,那么您可以这样进行组织。

根据您想如何构建和部署应用程序,可以使用任意一种结构,或者两者都用。只是需要确保您知道每种情况下的结构,因为在开始部署时,您应该知道目录的规划。例如,对于 Apache,我可能要为每个版本创建三四个目录,用来保存调试的不同配置,同时还要有一些用于开发、阶段测试(staging)和产品版本的安装目录。这样得到了如清单 4 中所示的一个规划:

清单 4. 支持不同活动配置的 Apache 规划
 
./httpd-2.0.46
./httpd-2.0.46/staging-build
./httpd-2.0.46/devel-build
./httpd-2.0.46/prod-build
./httpd-2.0.49
./httpd-2.0.49/staging-build
./httpd-2.0.49/devel-build

devel-build 目录中包含的目录与解开原始 tarball 时创建的目录相同。通过比较,清单 5 展示了开发机器上的 Perl 的布局,在这台机器上,Perl 的每一个版本都是以标准构建方式安装的。从清单中可以看出,我们有了一个更简单的结构。

清单 5. 直接版本应用程序目录
 
./perl/
./perl/perl-5.8.2
./perl/perl-5.8.3
./perl/perl-5.8.5
./perl/perl-5.9.0

这些目录都准备就绪后,您就可以开始为想要支持的每种操作系统和配置进行每个应用程序的一次构建了。对每个应用程序/配置进行一次构建后,将其部署到多个操作系统中应该是相对简单和直观的。实际上,您也可以简化这一过程,只是需要进行一些深入的考虑。

配置单独的构建

支持多种不同应用程序和不同配置的另一个问题在于,容易忘记用于给定应用程序和情形的具体配置。例如,构建 Apache 时,我指定了另一个部署目录,以及我想要支持的模块和组件的一个显式列表。在第一次完成我所期望的任务时,记住这些配置没什么问题,但是,当三个月之后,我要再进行这个过程,并决定对最新修订版本进行新一次构建时,让我再回忆起该配置的某些方面无异于一个梦魇。

为了解决这一问题,我使用了只结合了一些简单的、单行的脚本而创建的目录结构,我通常将这些脚本中的行用于配置方面。例如,我为 Apache 阶段测试服务器(staging servers)使用了如清单 6 所示的脚本:

清单 6. Apache 阶段测试服务器的脚本
 
./configure --enable-so --enable-mods-shared=most --prefix=/export/httpd2/staging

您可以将这个行放入到一个脚本中,赋予其合适的名称,并将其存放于应用程序的目录中。此外,您既可以基于每版本使用此方法(对那些配置系统发生了变化的应用程序来说至关重要,比如从 Apache 1.3.x 到 Apache 2.x),也可将此方法用于普通的应用程序目录。例如,我可以将这个文件命名为 apache/staging-config,以指明我可以在任何版本和构建目录中使用它来配置 Apache 阶段测试服务器。

需要根据给定的操作系统、应用程序和配置实际执行配置时,我可以如下进行:

清单 7. 执行配置和构建
 
				

$ cd redhat-linux-9.0/apache/httpd-2.0.46/staging-build
$ ../../staging-config
$ make

自动化安装

一旦有了针对某个操作系统的应用程序的首次编译版和准备安装版(ready-to-install version),在网络中的不同机器上实际发布和安装这个应用程序就会变得非常容易。您不必再在每台机器上单独运行配置/构建过程 —— 倘若机器是相同的。所有您需要运行的只是 make install

通过在每个操作系统目录中创建了一个文件,其中包含我想要在每台机器上安装的应用程序列表,我逐个系统(system-by-system)地自动化了这一过程。对于其中一些操作系统,我甚至创建了大量不同的文件,来处理我必须进行的所有不同类型的安装。该文件只是应用程序构建目录的一个列表,如清单 8 所示:

清单 8. 操作系统应用程序选择
 
perl/perl-5.9.0
python/Python-2.3
ccache/ccache-2.3
distcc/distcc-2.13

这个文件由清单 9 中所示的脚本处理。这里没有任何技巧;只需遍历列出的每一个目录并在其中运行 make install 命令即可。

清单 9. 自动安装脚本
 
#!/bin/sh
os=$1
src=$2
currentdir=`pwd`
basedir=/export/build
if [ -z "$os" ]
then
	echo "No operating system definition specified"
	exit 1
fi
if [ -z "$src" ]
then
	echo "No application definition specified"
	exit 1
fi
cd $basedir
if [ -r "$basedir/$os/$src" ]
then
	for dir in `cat $os/$src`
	  do
	  cd $basedir/$os/$dir
	  make install
	done
else
	echo "No application definition specified"
	exit 1
fi

现在,要在一台机器上安装一组特定的应用程序,所有您需要做的就是运行这个脚本,并给出操作系统文件夹和应用程序选择文件,如清单 10 所示:

清单 10. 在一台机器上安装一组特定的应用程序
 
				

$ appinstall.sh solaris8-x86 standard

脚本以及精心构造的目录结构将完成其余的任务。

结束语

使用这个方法来安装和发布应用程序,需要一些时间来进行设置和熟悉,但是,一旦开始运转,它几乎是自动进行的。实际上,因为您使用可以安装哪些应用程序的信息来填充文件,所以,通过将该脚本作为 cron 作业运行,您还可以使用同一个系统来自动更新网络上的机器。我每周运行一次我的脚本,不过您可以一个月才运行一次。对于 99% 的安装器来说,当前版本与将要安装的版本是否相同没有关系。所有安装程序要做的只是覆盖上一次运行安装程序时可能用到的文件的相同副本。

如果您使用这个自动化的方法,那么将您的所有机器更新至最新将会是一件非常简单的事,只需使用最新版本的构建目录来更新应用程序选择文件即可,这太棒了!您所有的机器在预定的更新过程中将安装最新的安装(installation)。不需要特别的脚本,也不需要使用 rsync、ssh 或者 rsh。您可以开始根据需要配置和定制您的应用程序,并允许不同的机器使用不同的应用程序设置。

正如我在文章开头部分所讲述的,所有您需要的只是一些准则。您可能还需要大量磁盘空间。

参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
  • 如果您需要关于通过源代码编译软件的帮助,请参阅 Compiling and installing software from sources教程。
  • 您自己构建的应用程序才是最适合您的。阅读 调试 configure (developerWorks,2003 年 12 月)来了解当自动配置脚本无效时应如何去做。
  • 处理 Linux 配置文件可能会占用您很多宝贵时间,不过, 管理 Linux 配置文件(developerWorks,2004 年 6 月)向您展示了如何使用一个您可能没有考虑过的工具来简化这项任务:即CVS。
  • 在由三部分组成的 developerWorks 系列文章中,学习如何 用 RPM 打包软件,第 1 部分,RPM 是 Red Hat 程序包管理器。第 1 部分介绍了如何包装简单的软件,第 2 部分展示了如何 用 RPM 打包软件,第 2 部分,第三部分解释了如何 用 RPM 打包软件,第 3 部分
  • 创建 Debian Linux 软件包(developerWorks,2003 年 7 月)给出了 Debian 程序包装(packaging)系统的基本概念。
  • 作为除了 RPM 或 Debian 程序包装以外的另一选择,了解如何 使用 Stow 管理软件包(developerWorks,2003 年 2 月),Stow 是一个基于 Perl 的 tarfile 工具。
  • 关于 Linux 和其他许多平台上应用的具有工业力度(industrial-strength)的软件和系统配置管理的更多信息,请阅读 Tivoli Change and Configuration Management Solution
  • developerWorks 的 Tivoli 专区 提供了关于实现和管理 Tivoli® 产品的技术资料。
  • developerWorks Linux 专区 可以找到更多为 Linux 开发者准备的参考资料。
  • 从 developerWorks 的 Speed-start your Linux app 专区,可以下载运行于 Linux 之上的、经过精挑细选的 developerWorks Subscription 产品免费测试版本,其中包括 WebSphere Studio Site Developer、WebSphere SDK for Web services、WebSphere Application Server、DB2 Universal Database Personal Developers Edition、Tivoli Access Manager 和 Lotus Domino Server。想更快地开始上手,请参阅针对各个产品的 how-to 文章和技术支持。
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号