内容


Perl 模块部署自动化

使用 CPAN 之外的脚本简化网络安装

Comments

Perl 模块安装

在安装 Perl 的同时,您会开始认识到有多少模块组成了该语言的“基础”安装。随着时间的推移,还要安装越来越多的模块,以扩展 Perl 安装的功能。安装模块并不复杂。大多数模块都打包为简单的压缩 tarball。该 tarball 包含一些必要的文件 —— 如果需要,还包括用于第三方库集成的 C/C++ 代码。

大多数 Perl 模块都使用 MakeMaker 系统,它是安装基础 Perl 时一起安装的另一种 Perl 模块,它提供了关于 Perl 安装的信息,以及在适当位置构建和安装组件的机制。MakeMaker 将简单配置文件转换为标准 makefile,该 makefile 可以与大家熟悉的 make 命令一起使用。

使用 CPAN

如果还不了解 Comprehensive Perl Archive Network(CPAN),那么可以参考随每次安装提供的主页。CPAN 可以以两种方式工作:通过交互 shell 或通过一系列函数,可以将这些函数用作范围较广泛的基于 Perl 脚本的解决方案的一部分。最简单地说,可以使用 CPAN,只通过输入 perl -MCPAN -e "install modulename" 来安装模块,其中 modulename 是要安装的包、绑定或完整 Perl 模块的名称。CPAN 将执行其他操作。

CPAN 自动化

大多数人都熟悉作为构建和安装 Perl 模块的交互 shell 的 CPAN。许多人不知道的是,提供这项功能的 CPAN 模块实际上只是内部应用程序接口(API)的包装器。例如,要生成已安装的且与 CPAN 上可用版本相比已经过时的模块的列表,可以仅调用 CPAN::Shell->r。清单 1 是这条命令及其输出的例子。

清单 1. 生成过时模块的列表的代码
$ perl -MCPAN -e 'CPAN::Shell->r'
CPAN: Storable loaded ok
Going to read /export/build/solaris/cpan/Metadata
  Database was generated on Sun, 25 Jul 2004 02:10:00 GMT
Package namespace         installed    latest  in CPAN file
B::Assembler                   0.06      0.07  N/NW/NWCLARK/perl-5.8.5.tar.gz
Cwd                            2.19      2.20  K/KW/KWILLIAMS/Cwd-2.20.tar.gz
DBD::mysql                   2.9003    2.9004  R/RU/RUDY/DBD-mysql-2.9004.tar.gz
DateTime                     0.2101      0.22  D/DR/DROLSKY/DateTime-0.22.tar.gz
DateTime::TimeZone             0.27      0.28  D/DR/DROLSKY/DateTime-TimeZone-0.28.tar.gz
File::Scan                     1.23      1.25  H/HD/HDIAS/File-Scan-1.25.tar.gz
File::Spec                     0.87      0.88  K/KW/KWILLIAMS/File-Spec-0.88.tar.gz
Image::Magick                 5.5.7       6.0  J/JC/JCRISTY/PerlMagick-6.02.tar.gz
Mail::ClamAV                   0.08      0.11  S/SA/SABECK/Mail-ClamAV-0.11.tar.gz
Module::CoreList               1.94      1.95  R/RC/RCLAMP/Module-CoreList-1.95.tar.gz
POE                          0.2802      0.29  R/RC/RCAPUTO/POE-0.29.tar.gz
XML::RSS::Parser               2.11      2.15  T/TI/TIMA/XML-RSS-Parser-2.15.tar.gz
htmlop                       v0.2.6      0.26  J/JA/JANL/w3mir-1.0.10.tar.gz
5 installed modules have a version number of 0
890 installed modules have no parseable version number

自动安装这些模块,这样就可以保持最新状态,将对 CPAN::Shell->r 的调用嵌入到对 CPAN::Shell->install 函数的调用中:

$ /usr/local/bin/perl -MCPAN -e 'CPAN::Shell->install(CPAN::Shell->r)'

该代码转储清单 1 中的信息,然后安装每个模块,使其处于最新状态。

网络中的 CPAN 自动化

CPAN 是一个非常好的工具,但是该系统也有它的局限性。主要缺点之一就是它是单系统解决方案,如果以单一计算机为基础来管理 Perl 安装,那么这个解决方案很好。但是如果一组 Perl 模块分布在 Web 服务器农场中,或者甚至分布在实验室中的一系列平台上,那么在所有计算机中保持最新状态可能会非常花费时间,即使使用上面讨论的自动化技术也是如此。

CPAN 还会消耗许多 Internet 带宽,因为对于使用 CPAN 的每台计算机,CPAN 要从一个中心 CPAN 存储库下载文件的副本。CPAN 还依赖于配置,因此必须为每台计算机修改配置:只有在可以以完全一致的方式配置平台时,才可以复制配置文件,该配置文件位于 /perlinstalldirectory/CPAN/Config.pm 中。

使用 CPAN 时,一个明显的方法是使用 autobundle 函数。此函数生成 Perl 安装中所有已安装模块的列表,然后将该列表转储为可以执行的格式,将选择项重新安装到同一台计算机上(更新为最新的 Perl 版本后)或重新安装到另一台计算机上。清单 2 中的代码显示了如何创建新绑定。

清单 2. 用于创建 CPAN 自动绑定的代码
$ perl -MCPAN -e autobundle
...
Package namespace         installed    latest  in CPAN file
AnyDBM_File                    1.00      1.00  N/NW/NWCLARK/perl-5.8.5.tar.gz
Apache::ASP                    2.57      2.57  C/CH/CHAMAS/Apache-ASP-2.57.tar.gz
Apache::ASP::ApacheCommon     undef     undef  C/CH/CHAMAS/Apache-ASP-2.57.tar.gz
Apache::ASP::Application      undef     undef  C/CH/CHAMAS/Apache-ASP-2.57.tar.gz
Apache::ASP::CGI              undef     undef  C/CH/CHAMAS/Apache-ASP-2.57.tar.gz
Apache::ASP::CGI::Table       undef     undef  C/CH/CHAMAS/Apache-ASP-2.57.tar.gz
Apache::ASP::CGI::Test        undef     undef  C/CH/CHAMAS/Apache-ASP-2.57.tar.gz
Apache::ASP::Collection       undef     undef  C/CH/CHAMAS/Apache-ASP-2.57.tar.gz
Apache::ASP::CollectionItem     undef     undef  C/CH/CHAMAS/Apache-ASP-2.57.tar.gz
Apache::ASP::Date             undef     undef  C/CH/CHAMAS/Apache-ASP-2.57.tar.gz
...
Wrote bundle file
	/export/build/solaris/cpan/Bundle/Snapshot_2004_08_08_00.pm

生成的文件位于 CPAN 安装目录内。可以从这个位置复制此文件,如果正升级到 Perl 的更高版本,那么只需运行 bundle 模块来重新安装所有东西即可:

$ perl -MCPAN -e 'install Bundle::Snapshot_2004_08_08_00'

虽然 bundle 函数很好用,但我必须承认,因为某些原因,我不是非常喜欢将绑定系统作为网络解决方案。首先,绑定依赖于每台计算机具有相同的配置,如果每项配置都相同,那么这个方法会很好。但是如果具有不同的平台和环境,那么就会很容易发生问题。其次,因为自动绑定功能可以列出安装的每个模块,还可以列出多个包(每个包可能包含多个模块)。不过,该功能还可以列出对 Perl 的更高版本的引用(作为一些模块更新的源)。

不过,最后的问题是绑定依赖于让一台计算机包含所有模块的最新版本。不能使用这项功能将标准套件升级到最新的版本,无论这些版本是什么,这一限制使系统在网络环境中的使用变得更复杂。

如果想构建定制解决方案,有两种可用方法。您可以使用中心 CPAN 安装 目录,也可以使用中心 CPAN 目录。

使用中心 CPAN 安装目录

在网络中配置 CPAN 的相对简单的方法(如果计算机都相同,则此方法很好)是创建在 Network File System(NFS)上共享的单一 CPAN 目录,将 CPAN/Config.pm 文件复制到每台计算机上,然后使用前面讨论过的 CPAN::Shell 技术在每台计算机上安装模块。在主计算机(通常是在 NFS 上共享 CPAN 目录的那台计算机)上安装“基础”模块集合。对于其他每台计算机,构建和安装每个必需模块的过程应该很快,因为源 tarball 已经存在。因此,不必再下载 tarball,这可以节约宝贵的 Internet 带宽和大量时间。

这项技术的主要问题是:对于完全相同的计算机来说,会浪费处理器时间,您已经编译过一次,再完全重新编译就是在浪费时间。而且您还会将自己置于危险境地,在这种情况下,同一构建目录可用于多台计算机,这真的不是什么好主意,因为不同的计算机可能同时编译源,从而导致计时错误,此外,在跨平台环境中,进行连接时会出现坏的目标文件。

使用中心 CPAN 源目录

对上面的解决方案的修正是使用相同源目录和基本 CPAN 配置,但要手工更改配置文件,这样,每台主机的配置信息都是特定于主机的。用这种方法可以解决构建目录的问题,但是仍然要使用 tarball 的本地(即,在您的网络上)缓存版本。清单 3 提供了这类配置文件。

清单 3. CPAN::Config 文件的例子
# This is CPAN.pm's systemwide configuration file. This file provides
# defaults for users, and the values can be changed in a per-user
# configuration file. The user-config file is being looked for as
# ~/.cpan/CPAN/MyConfig.pm.
$CPAN::Config = {
  'build_cache' => q[200],
  'build_dir' => q[/export/build/solaris/cpan/build],
  'cache_metadata' => q[1],
  'cpan_home' => q[/export/build/solaris/cpan],
  'dontload_hash' => {  },
  'ftp' => q[/usr/bin/ftp],
  'ftp_proxy' => q[proxy.mcslp.pri:8080],
  'getcwd' => q[cwd],
  'gpg' => q[],
  'gzip' => q[/usr/bin/gzip],
  'histfile' => q[/export/build/solaris/cpan/histfile],
  'histsize' => q[100],
  'http_proxy' => q[proxy.mcslp.pri:8080],
  'inactivity_timeout' => q[0],
  'index_expire' => q[1],
  'inhibit_startup_message' => q[0],
  'keep_source_where' => q[/export/build/solaris/cpan/sources],
  'lynx' => q[],
  'make' => q[/usr/local/bin/make],
  'make_arg' => q[-j3],
  'make_install_arg' => q[UNINST=1],
  'makepl_arg' => q[],
  'ncftp' => q[],
  'ncftpget' => q[],
  'no_proxy' => q[],
  'pager' => q[/usr/bin/less],
  'prerequisites_policy' => q[follow],
  'scan_cache' => q[atstart],
  'shell' => q[/usr/local/bin/bash],
  'tar' => q[/usr/local/bin/tar],
  'term_is_latin' => q[1],
  'unzip' => q[/usr/bin/unzip],
  'urllist' => [q[ftp://cpan.etla.org/pub/CPAN], q[ftp://cpan.teleglobe.net/pub/CPAN],
    q[ftp://ftp.demon.co.uk/pub/CPAN/], q[ftp://ftp.flirble.org/pub/languages/perl/CPAN/],
    q[ftp://ftp.mirror.ac.uk/sites/ftp.funet.fi/pub/languages/perl/CPAN/],
    q[ftp://ftp.mirror.anlx.net/CPAN/], q[ftp://ftp.plig.org/pub/CPAN/],
    q[ftp://usit.shef.ac.uk/pub/packages/CPAN/], q[http://cpan.hambule.co.uk/]],
  'wget' => q[],
};
1;
			__END__

需要将所有配置目录参数更改为每台计算机的本地信息,例如,将行 'build_dir' => q[/export/build/solaris/cpan/build], 改为指向本地计算机中的目录。同时,共享主计算机上的 CPAN 源目录,然后将 keep_source_where 参数改为指向 NFS 挂载的目录。

不幸的是,这个解决方案也存在一些问题。首先,无法方便地记录要在每台计算机上安装的模块。还会遇到下面的问题:保持源目录(以及其他 CPAN 目录)中的信息正确且最新,而无需更新或下载 tarball 的更新版本,同时不会扰乱计算机的现状。这个问题可以导致每个系统中已安装模块版本之间的微小但很重要的差别,这是我们正尝试解决的关键问题。

使用 CPAN 创建严格安装

以上两种解决方案的问题在于它们都依赖于对 CPAN 的使用,以及一些使 CPAN 工作的配置文件的操作。在局域网内(LAN),即使使用上面的技巧,CPAN 也不是理想的解决方案。因此,需要避开这些系统限制。为此,我将展示如何配置主分布,如何为每个平台创建安装集合,然后在每台计算机上运行安装。

配置主分配

在第一步中,将使用我们已经介绍过的一些元素。开始先像往常一样配置一台计算机,并设置默认模块集合。然后,使用 NFS 设置共享目录,使用这个目录将必需的文件分布到网络中的其他计算机中。现在编辑清单 4 中看到的 $destdir 变量,然后运行清单 4 脚本。该脚本执行以下三项操作:

  • 标识模块。
  • 下载源文件。
  • 将源文件复制到分发目录中。

第一个阶段通过检查 perllocal.pod 文件的内容,确定已安装模块的列表。每次使用 MakeMaker 系统安装模块时,都要更新这个文件。通过使用该文件,而不是使用 CPAN 中的内置模块,可以获取安装基础 Perl 时一起安装的第三方模块的列表。在第二阶段中使用 CPAN 下载源 tarball,这项操作发生最后复制 tarball 或 .zip 文件之前,通过在 CPAN 自身内部配置的任意一个方法来实现。作为这个过程的一部分,还要创建已复制模块的列表,在其他计算机上安装文件时要使用这个列表。

清单 4. 创建基础分布的代码
use CPAN;
use Config;
use File::Copy;
use File::Spec::Functions;
# Set up the directories to store the module packages
my $basedestdir = catfile('export','cpaninst');
mkdir($basedestdir);
my $destdir = catfile($basedestdir,'srcs');
mkdir($destdir);
# Extract a list of the third party modules installed on this machine
my $files = {};
my $podfile = catfile($Config{'archlibexp'},'perllocal.pod');
open(DATA,$podfile) or die "Can't open module list ($podfile): $!";
while(<DATA>)
{
    if (m/.*C<Module> L<(.*)>/)
    {
        my ($module) = split /\|/,$1,0;
        my $mod = expand('Module',$module);
        next unless $mod;
        $mod->get(); #Make sure to download the version again, in case we no
                        #longer have it locally
        $files->{$mod->{RO}->{CPAN_FILE}} = 1; #Save the location of the file
    }
}
close(DATA);
# Now copy each source installer to the source destination directory
# We save a copy of the module file, for reference, as part of the process
my $modulelist = catfile($basedestdir,'modules.lst');
open(DATA,">$modulelist") or die "Can't open the module list file ($modulelist): $!";
foreach my $file (keys %{$files})
{
    my $src = catfile($CPAN::Config->{'keep_source_where'},$file);
    my ($vol,$path,$filename) = splitpath($file);
    my $dest = catfile($destdir,$filename);
    copy($src,$dest) or warn "Copy of $src failed: $!\n";
    print DATA "$filename\n";
}
close(DATA);

现在就有了包含所有源安装包的目录,使用该目录可以为那些将给其分布相应 Perl 模块的每个平台构建一组已经编译的分布。下面将如何继续则取决于您的环境。如果具有同构环境,那么只需对整个网络将下一阶段的操作执行一次即可。然而,如果拥有一个具有各种不同平台的异构网络,那么需要对每个不同环境重复下一步骤。

为每个平台创建安装集合

对于每个平台,都必须提取源文件,运行 MakeMaker,然后运行安装程序。要自动执行此任务,则需要使用清单 5 中的脚本。不幸的是,并不是所有的 MakeMaker 安装都是完全自动的,所以每次构建分布时都必须遵循交互系统,例如,设置目录位置或默认选项。

清单 5. 在每个平台上构建模块的代码
srcdir="/export/cpan"
srcfile="$srcdir/module.lst"
platformdir="/export/cpan/solaris-x86"
for file in `cat $srcfile`
  do
    cd $platformdir
    tar zxf $srcdir/$file
    dir=`echo $file|sed -e "s/\.tar\.gz//g"`
    cd $dir
    perl Makefile.PL
    make
done

在每台计算机上运行安装

运行清单 5 中的脚本后,您会拥有一个目录,该目录中包含给定平台的所有“准备安装”模块。要在使用相同平台的其他任何计算机上安装这些模块,则只需转入每个目录并运行 make install 即可。同样,可以使用脚本完成这个过程,比如清单 6 中的脚本。

清单 6. 在每台主机上安装文件的代码
srcdir="/export/cpan"
srcfile="$srcdir/module.lst"
platformdir="/export/cpan/solaris-x86"
for file in `cat $srcfile`
  do
    cd $platformdir
    tar zxf $srcdir/$file
    dir=`echo $file|sed -e "s/\.tar\.gz//g"`
    cd $dir
    make install
done

结束语

尽管 Perl、CPAN 和 MakeMaker 系统提供了一些功能,但仍然无法仍将 Perl 模块的安装和分布完全自动化。但是可以使该过程对于某一范围内的计算机更简单些。单个模块都有自己的配置,单个平台也有自己的细微差别,这些使它们变得比原先更加复杂。通过使用本文中的脚本,可以减少许多困难,同时减少通常可能与这个过程有关的时间花费。

这个解决方案并不能解决所有问题。例如,第三方模块经常使用没有使用该方法安装的第三方库。请参阅我的其他 developerWorks 文章,以获得对此有帮助的信息。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux, Open source
ArticleID=59113
ArticleTitle=Perl 模块部署自动化
publish-date=04072004