分享
 
 
 

使用Perl自动化UNIX系统管理

王朝perl·作者佚名  2008-05-18
窄屏简体版  字體: |||超大  

集中化的配置文件策略

Teodor Zlatanov (tzz@iglou.com)

程序员,Northern Light

2001 年 7 月

UNIX 系统管理总是一个棘手的问题,运用正确的工具会使这个问题变得容易。在这一部分中,Teodor 提出了关于使用 Perl 来简化和牢固系统管理的想法。在这种环境中,系统配置引擎 cfengine 是一个极其重要的工具。

要完成本文中的练习,系统中必须安装了 Perl 5.6.0。操作系统最好是主流 UNIX 安装(Linux、Solaris、BSD)的最近版本(2000 或更新)。在较早版本的 Perl 和 UNIX 以及其它操作系统上也可以使用本文中的示例,但应当将可能的功能故障作为练习来解决。

UNIX 管理具有挑战性的一大原因是每个 UNIX 供应商认为标准是针对低能傻瓜。所以,即使是同一供应商的操作系统(SunOS 4.x 和 Solaris 5.x)也可以是根本不同。在某些情况下,甚至根本没有供应商。例如,Linux 没有单独的供应商(虽然 Red Hat 目前是最大的 Linux 分发版),每一个版本的 Linux 都有其独到之处。如果 POSIX 标准化做得正确,那么它是解决这一问题的正确方向上的一个步骤。遗憾的是,它只能保证系统管理所需功能的一个小的子集。

正如我经常所说:了解您的工具。如果试图仅用一种工具、语言、或方法做每件事情,可能是一场噩梦。要具有灵活性。

如果存在一个系统管理公理,那就是:两次过后,没有系统管理任务是有趣的。如果您发现正在重复做单调而枯燥的事,那么自动化它。当然,有时很难自动化,但应该至少考虑这个问题,并且权衡其优势及自动化所花费的时间。

cfengine 工具

如果您对自动化系统管理是认真的,那么应该了解 cfengine 工具。仅当您宁愿把时间都花在 vi 编辑器时,可以不去了解 cfengine。

cfengine 是一种系统配置引擎。它获取配置脚本作为输入,然后根据这些脚本来行动。目前版本是 1.6.3(非常稳定的发行版),而且版本 2.0 也呼之欲出。有关 cfengine 开发的更多信息,请访问 cfengine 网站(请参阅本文后面的参考资料)。

不一定要用 cfengine 提供您的所有东西,而且您不可能立刻需要所有东西。一开始时,您的 cfengine 配置文件应该很简单,并且随着发现更多东西希望自动化而增长。

来自 cfengine 命令参考大全,这里有其最值得注意的特性:

可以监控和修改文件许可权和 ACL。例如,/etc/shadow 可以与 0400/root/sys 许可权保持一致,而且如果那些许可权发生变化,可以警告系统管理员或即刻纠正它们。

根据相应 fstab 变化,可自动安装和卸载 NFS 文件。

可以通过单一文件来管理子网掩码、DNS 配置、缺省路由和主网络接口;

文件和目录可以递归复制至另一位置,要么本地复制,要么从远程服务器复制。

可以编辑(这是一个非常强大的特性,提供了正则表达式和全局查找/替换)、轮转(譬如,日志文件)或删除文件。

可以链接文件(单一的和/或目录下的所有文件或与正则表达式匹配的文件)和整个目录。

可以根据进程表中正则表达式的匹配来启动、杀死、重启进程或发送任意信号。

可以运行任意命令。

上述所有这些根据操作系统类型和修订版本、一天中的时间、任意用户定义的类、文件中文件、目录或数据的有无等等可以是有条件的。

即使用 Perl 可以做 cfengine 所做的所有事情,为什么要从头开始呢?例如,如果想用另一个词替换某个词,编辑文件可以是简单的一行程序。当开始允许系统的子类型、逻辑系统部分以及所有其它杂项因素时,这一行程序会变成 300 行。为什么不在 cfengine 中做呢?它产生 100 行可读的配置代码。

根据我自己的经验,因为可以从最小配置文件开始,然后随着时间流逝逐步地向 cfengine 添加一些东西,所以将 cfengine 介绍给站点是很容易的。没有人喜欢突然的变化,所有系统管理员更是如此(因为如果任何事出错,他们理所当然地会受到责难)。

配置文件管理

管理配置文件是艰苦的。可以通过考虑 cfengine 是否胜任该任务开始。遗憾的是,cfengine 的编辑是面向行的,所以它可能不太适合复杂的配置文件。但对于如 TCP 包装器配置文件 /etc/hosts.allow 那样的简单文件 cfengine 是最适合的。

通常,希望保留配置文件的多个版本。譬如,可能需要在 /etc/resolv.conf 中有两组 DNS 配置设置,一组是用于外部机器,另一组是用于内部机器。很自然,外部 DNS resolv.conf 可以进入称为 "external" 的目录,而内部 resolv.conf 可以进入相应的 "internal" 目录。让我们假定这两个目录都在一个全局 "spec" 目录下,该目录是配置文件的一种根目录。

下列代码会遍历 spec 目录,搜索适合于给定机器的文件名。它将从 /usr/local/spec 开始,然后往下,寻找与请求相匹配的文件。而且,它将检查每个目录的名称是否与属于某些机器的类相同。因此,如果我们请求 locate_global('resolv.conf', 'wonka'),该函数将在 /usr/local/spec 目录下查找 resolv.conf 文件,该文件要么在根目录下,要么在该根目录的子目录下,它的名称应与 "wonka" 机器所属的类相匹配。所以,如果 "wonka" 属于 "chocolate" 类,并且如果有 /usr/local/spec/chocolate/resolv.conf 文件,那么 locate_global() 将返回 "/usr/local/spec/chocolate/resolv.conf"。

如果 locate_global() 找到与文件相匹配的多个版本(譬如,/usr/local/spec/chocolate/resolv.conf 和 /usr/local/spec/resolv.conf),则它会放弃。这里假设没有配置比有两个错误之一要好。还有,请注意,机器可以属于不止一个类。

可以构建这样的结构。譬如,

/usr/local/spec/external/chocolate/resolv.conf

/usr/local/spec/internal/chocolate/resolv.conf

/usr/local/spec/external/sugar/resolv.conf

/usr/local/spec/internal/sugar

将包含外部和内部 "chocolate" 以及 "sugar" 机器的文件。只需要正确地设置 your machine_belongs_to_class() 函数。

一旦 locate_global() 返回一个文件名,将它用 scp 或 rsync 复制至远程系统是相当简单的。请记住,总是要保持该文件的许可权和属性。scp 需要 "-p" 标志,rsync 需要 "-a" 标志。查阅想要使用的文件复制命令的文档。这样就有了一个统一的配置文件树。

清单 1:Spec 目录遍历

# {{{ locate_global: use spec directory to find a file matching the current class

sub locate_global($$)

{

# this code uses File::Find

my $spec_dir = '/usr/local/spec';

my $file = shift || return undef; # file name sought

my $machine = shift || return undef; # machine name

my @matches;

my $find_sub =

sub

{

print "found file $_\n";

push @matches, $File::Find::name if ($_ eq $file);

# the machine_belongs_to_class sub returns true if a machine

# belongs to a class; we stop traversing down otherwise

$File::Find::prune = 1 unless

machine_belongs_to_class($machine, $_) || $_ eq '.';

};

find($find_sub, $spec_dir);

if (scalar @matches > 1)

{

print "More than one match for file $file,",

"machine $machine found: @matches\n" ;

return undef;

}

elsif (scalar @matches == 1)

{

return $matches[0]; # this is the right match

}

else {

return undef; # no files found

}

}

# }}}

一旦建立了这种 /usr/local/spec 结构的一个问题是:我们怎么知道 resolv.conf 应当进入 /etc?要么没有如这里所示的漂亮层次结构,改写它(譬如,用 "+" 替代 "/" -一种危险的和有点丑陋的方法),要么在链接名与真实名之间保持单独的映射。譬如,"root-profile" 可以是 "~root/.profile" 的链接名。最后一种方法,也是我喜欢的方法,由于它平铺文件名并且消除了有隐藏文件名的问题。在一个目录结构下,每一样都是可见的和整洁的。当然,每次在将文件添加到列表时,需要多做一些工作。程序必须知道 "resolv.conf" 应该复制到远程系统的 "/etc/resolv.conf",并且 "dfstab" 应该进入 "/etc/dfs/dfstab"(共享 NFS 文件系统的 Solaris 文件)。

一旦设置完 spec 目录层次结构,现在让我们讨论可以做什么。如果想做,可以查找所有名为 Joe 的用户:

清单 2:查找所有 password 文件并用 grep 找出 Joe

grep Joe `find /usr/local/spec -name passwd`

或者可以使用工具,如 rep.pl(链接到 rep.pl),由 David Pitts 编写,来用另一个词替换每一个词:

清单 3:查找所有 host 文件并将 "wonka" 改成 "willy"

find /usr/local/spec -name hosts -exec rep.pl wonka willy {} \;

现在,如果愿意,可以用 Perl 编写清单 2 和 3;find2perl 就是为此编写的实用程序。虽然它非常简单,从开始只使用 find。它真的是极好的实用程序,每个系统管理员都应该使用。更重要的是,编写这两个清单只花了我 5 分钟。了解如何使用 find2perl,将它生成的代码存储在文件中,然后运行该文件,要花多长时间呢?自己试试看!

任务自动化

任务自动化是一个很泛的主题。我将本节仅限于非交互式 UNIX 命令的简单自动化。对于交互式命令的自动化,Expect 是当前可用的最好工具。应该要么了解它的语法,要么用 Perl Expect.pm 模块。可以从 CPAN 获取 Expect.pm;请参阅参考资料以了解更多详细信息。

利用 cfengine,可以根据任意标准自动化几乎任何任务。但是,它的功能非常象 Makefile 功能,对变量的复杂操作是很难处理的。当发现需要运行这样的命令,该命令的参数来自于散列或通过单独的函数时,通常最好切换到 shell 脚本或 Perl。由于 Perl 的功能,其可能是较好的选择。虽然,不应该将 shell 脚本弃为替代来使用。有时,Perl 是不必要的,您只需要运行一些简单的命令。

自动添加用户是一个常见问题。可以编写自己的 adduser.pl 脚本,或者用大多数现代 UNIX 系统提供的 adduser 程序。请确保使用的所有 UNIX 系统间语法是一致的,但不要尝试编写一个通用的 adduser 程序接口。它太难了,在您认为涵盖了所有 UNIX 变体后,迟早会有人要求 Win32 或 MacOS 版本。这不是仅仅用 Perl 就能解决的问题之一,除非您是非常有野心的。这里只是让脚本询问用户名、密码、主目录等等,并以 system() 调用来调用 adduser。

清单 4:用简单的脚本调用 adduser

#!/usr/bin/perl -w

use strict;

my %values; # will hold the values to fill in

# these are the known adduser switches

my %switches = ( home_dir => '-d', comment => '-c', group => '-G',

password => '-p', shell => '-s', uid => '-u');

# this location may vary on your system

my $command = '/usr/sbin/adduser ';

# for every switch, ask the user for a value

foreach my $setting (sort keys %switches, 'username')

{

print "Enter the $setting or press Enter to skip: ";

$values{$setting} = ;

chomp $values{$setting};

# if the user did not enter data, kill this setting

delete $values{$setting} unless length $values{$setting};

}

die "Username must be provided" unless exists $values{username};

# for every filled-in value, add it with the right switch to the command

foreach my $setting (sort keys %switches)

{

next unless exists $values{$setting};

$command .= "$switches{$setting} $values{$setting} ";

}

# append the username itself

$command .= $values{username};

# important - let the user know what's going to happen

print "About to execute [$command]\n";

# return the exit status of the command

exit system($command);

用 Perl 来处理的另一个常见任务是监控和重新启动进程。通常,这是用 Proc::ProcessTable CPAN 模块进行的,它浏览整个进程表,并返回给用户带许多重要属性的进程列表。然而,在这里,我必须推荐 cfengine。与快速的 Perl 工具相比,它提供了更好的进程监控和重新启动进程的选项。如果您想编写这样的工具,那么这只是在做别人做过的事情(而且 cfengine 已经偷了您的轮毂盖)。如果由于个人原因,不想用 cfengine,考虑一下大多数现代 UNIX 系统中附带的 pgrep 和 pkill 实用程序。pkill -HUP inetd 将用一条简洁的命令可以做四行或更多行 Perl 脚本所做的事情。这就是说,如果正在做的进程监控是很复杂或对时间敏感,那么应该明确用 Perl。

为了完整性缘故,这里是一个演示了如何使用 kill() Perl 函数的 Proc::ProcessTable 示例。"9" 作为参数,是最强的 kill() 参数,大体意味着“不管三七二十一,杀死进程再说”。不要以 root 运行这条命令,除非真想杀死 inetd 进程。

清单 5:遍历进程,然后杀死所有 inetd

use Proc::ProcessTable;

$t = new Proc::ProcessTable;

foreach $p (@{$t->table})

{

# note that we will also kill "xinetd" and all processes

# whose command line contains "inetd"

kill 9, $p->pid if $p->cmndline =~ 'inetd';

}

结束语

UNIX 系统管理最让人失望的部分是 UNIX 供应商逃避标准而找到的各种方式。由于这种原因,当 Perl 单独应付 UNIX 系统中所有问题时,它是无能为力的。如果没有象 cfengine 这样的工具,象密码文件语法、共享文件系统以及跟踪日志等问题很快就变得无法管理。然而,还是存在一些希望;毕竟,我们只是查看了 Perl 可简化系统管理的一些方法。

Perl 与 cfengine 结合得很好。可以用 Perl 生成定制的 cfengine 配置,或者可以从 cfengine 运行 Perl 脚本。我用过这两者,发现集成不难。然而,cfengine 受过分简单的配置语言和缺乏数据结构影响。我将在有关 cfengine 的未来文章中展开这一问题。

如果选择实现的话,本文中介绍的集中化配置文件策略应当是非常实用的。在我的站点上现在已经使用了六个月,而且获得了巨大成功。如果将完整的层次结构检入一个如 CVS 那样的版本控制系统,您还将享受到版本化系统文件的好处,即可以回复到已检入版本控制系统的任一状态。

参考资料

请访问 CPAN 以查找您所感兴趣的所有 Perl 模块。

Perl.com 有关于 Perl 信息和相关资源。

请获取 cfengine 模块,这是多合一的 UNIX 管理工具。

UNIX System Administration Handbook, 3rd Edition,由 Evi Nemeth、Garth Snyder、Scott Seebass 和 Trent R. Hein 著 (Prentice Hall 2000),描述了系统管理许多不同方面,从基本问题到 UNIX 秘籍,并且明确地包含了四种流行的系统:Solaris 2.7、Red Hat Linux 6.2、HP-UX 11.00 和 FreeBSD 3.4。

Programming Perl Third Edition,由 Larry Wall、Tom Christiansen 和 Jon Orwant 著 (O'Reilly & Associates 2000), 这本书是现今最好的、针对 Perl 5.005 和 5.6.0 的指南。

Perl for System Administration: Managing multi-platform environments with Perl, 由 David N. Blank-Edelman 著 (O'Reilly & Associates 2000),该书全面地总结了用 Perl 编写的用于系统管理的不同工具和技术。这里重点是放在可移植性。

UNIX Power Tools, 2nd Edition,由 Jerry Peek、Tim O'Reilly & Mike Loukides 著 (O'Reilly & Associates 1997),是 UNIX shell 和相关工具入门的较好的指南。虽然该书有点老,但仍然很优秀。

请访问 O'Reilly & Associates:Programming Perl 和许多其它好书的出版社。

如果想阅读由本文作者所写的相关文章,请参阅 developerWorks 上 “功能丰富的 Perl:一行程序 101”和“用于 C 和 Java 程序员的 Perl 5.6”。

请浏览 developerWorks 上更多 Linux 资源。

关于作者

Teodor Zlatanov 于 1999 年获取了波士顿大学计算机工程系硕士学位。他从 1992 年就开始做程序员,使用 Perl、Java、C 和 C++。他的兴趣是开放源码工作中有关文本语法分析、3 层客户机 -服务器数据库体系结构、UNIX 系统管理、CORBA 和项目管理。欢迎通过电子邮件提供建议和指正。可以通过 tzz@iglou.com 与他联系。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有