Cfengine 企业配置管理
许多系统管理员都认为 Cfengine 是一个出色的工具,可以自动化 UNIX 和基于 Linux 的机器上的手动任务。它也是在运行不同操作系统的多台服务器上执行管理 shell 脚本的最全面的框架。虽然 cfengine 当然非常适合这些用途,但它也被广泛认为是可用于配置管理的最佳开源工具。通过使用 cfengine,拥有大量安装(例如 800 台机器)的系统管理员可以快速获得有关其环境的信息,否则这些信息需要数月才能收集,并且能够立即更改环境。例如,如果您有一组 Linux 机器需要使用不同的 /etc/nsswitch.conf,然后需要重新启动某些进程,则无需连接到每台机器并执行这些步骤,甚至无需编写脚本并在识别机器后在其上运行。您只需告诉 cfengine,所有运行 Fedora/Debian/CentOS 且具有 XGB RAM 或更多内存的 Linux 机器都需要使用特定的 /etc/nsswitch.conf,直到指定更新的版本为止。Cfengine 可以用一行语句完成所有这些操作。
Cfengine 的配置管理功能可以通过几种不同的方式工作。在本文中,我将重点介绍“使其如此并保持如此”的方法。让我们考虑一个小型托管公司配置,其中有三名管理员和两个数据中心(图 1)。
每位管理员都可以使用 Subversion/CVS 沙箱来保存每个数据中心的存储库。cfengine 客户端将在每台客户端机器上运行,可以通过 cron 作业或 cfengine 执行守护程序,并从服务器拉取适用于每台机器的 cfengine 配置文件。如果该特定机器有工作要做,它将被执行并报告给服务器。如果要复制配置文件,则客户端主机上处于活动状态的文件将被 cfengine 服务器上的副本替换。(如果复制过程是部分或不完整的,Cfengine 将不会替换文件。)
cfengine 实施有三个主要组成部分
版本控制:这通常包括版本控制系统,例如 CVS 或 Subversion。
Cfengine 内部组件:cfservd、cfagent、cfexecd、cfenvd、cfagent.conf 和 update.conf。
Cfengine 命令:processes、files、shellcommands、groups、editfiles、copy 等等。
cfservd 是主守护程序,使用 /etc/cfservd.conf 配置,并在端口 5803 上侦听与 cfengine 服务器的连接。此守护程序控制连接到它的所有客户端机器的安全性和目录访问权限。cfagent 是在主机上运行 cfengine 的客户端程序。它将从 cron、手动或从 cfengine 的执行守护程序 cfexecd 运行。运行 cfagent 的常用方法是从 cron 使用非守护程序模式的 cfexecd 执行它。同时使用两者的主要原因是启用 cfengine 的日志记录系统。这是通过以下方式完成的
*/10 * * * * /var/cfengine/sbin/cfexecd -F
作为 Linux 上的 cron 条目(除非 Solaris 开始理解 */10)。请注意,这相当频繁,仅适用于少量服务器。我们不希望 800 台服务器在相同的十分钟内更新。
cfenvd 是在 cfengine 实施的客户端运行的“环境守护程序”。它收集有关主机的信息,例如主机名、操作系统和 IP 地址。cfenvd 检测有关主机的这些因素,并使用它们来确定机器属于哪些组。这实际上为每台机器创建了一个配置文件,cfengine 使用该配置文件来确定要在每台主机上执行哪些工作。
每台主机的主配置文件是 cfagent.conf。此文件可以包含主机、主机子集或 cfengine 网络中所有主机的全部配置信息和 cfengine 代码。此文件通常只是一个起点,所有配置都存储在其他文件中,并以与 Nagios 配置文件非常相似的方式“导入”到 cfagent.conf 中。update.conf 文件是客户端的基本配置文件。它主要只是标识 cfengine 服务器并获取 cfagent.conf 的副本。
如果主机机器上的当前副本不同,则 update.conf 文件会告诉 cfengine 服务器部署新的 cfagent.conf 文件(也可能是其他文件)。这为发送出损坏的 cfagent.conf 或从未发送过 cfagent.conf 的情况增加了一些保护。虽然您可以使用 cfengine 分发 update.conf,但应将其手动复制到每台主机。
Cfengine “命令”不是在命令行中输入的。它们构成了 cfengine 配置语言的语法。由于 cfengine 是一个框架,因此系统管理员必须在 cfengine 配置文件中编写必要的命令才能移动和操作数据。例如,让我们看一下 files 命令在 cfagent.conf 文件中的外观
files: /etc/passwd mode=644 owner=root action=fixall /etc/shadow mode=600 owner=root action=fixall
这将将所有机器的 /etc/passwd 和 /etc/shadow 文件的权限设置为文件中列出的权限(644 和 600)。如果发现这些设置不同,它还会将文件的所有者更改为 root 并修复所有这些设置,每次 cfengine 运行时都会如此。重要的是要记住,此特定 files 命令没有组限制。如果 cfengine 没有为此命令列出组,则它假定您指的是任何主机。这也可以写成
files: any:: /etc/passwd mode=644 owner=root action=fixall /etc/shadow mode=600 owner=root action=fixall
这使我们进入了构建 cfengine 实施的一个重要主题:组。有一个 groups 命令可用于根据各种条件将主机分配给组。以这种方式创建的自定义组称为软组。由 cfenvd 守护程序自动填充的组称为硬组。要使用 cfengine 的 groups 功能并分配一些软组,只需创建一个 groups.cf 文件,并告诉 cfagent.conf 在文件的开头某处导入它
import: any:: groups.cf
Cfengine 将在 /var/cfengine/inputs 的默认目录中查找 groups.cf 文件。现在您可以根据任何条件创建任意组。重要的是要记住,术语组和类在 cfengine 中是完全可互换的
groups: development = ( nfs01 nfs02 10.0.0.17 ) production = ( app01 app02 !development )
您还可以组合 cfenvd 发现的硬组和软组
groups: legacy = ( irix compiled_on_cygwin sco )
让我们整理一下我们的测试设置。首先,在服务器和客户端或工作站上安装 cfengine。Cfengine 几乎在所有系统上都已编译,因此您的操作系统/发行版应该有一个软件包。由于源代码通常是最新版本,并且许多版本都是错误修复,因此我建议您自己编译它。安装 cfengine 会在每台机器上为您提供服务器和客户端二进制文件和实用程序,因此请注意不要在客户端机器上运行服务器守护程序 (cfservd),除非您专门打算这样做。安装后,您应该有一个 /var/cfengine/ 目录和前面提到的二进制文件。
在任何主机可以实际与 cfengine 服务器通信之前,必须在两者之间交换密钥。Cfengine 密钥类似于 SSH 密钥,只是它们是单向的。也就是说,服务器和客户端都必须拥有彼此的公钥才能进行通信。多年的系统管理员偏执让我建议手动复制所有密钥,并且不信任任何东西。将服务器上的 /var/cfengine/ppkeys/localhost.pub 复制到所有客户端,并将客户端上的 /var/cfengine/ppkeys/root-10.11.0.1.pub 复制到服务器上的同一目录,其中 IP 为 10.11.0.1。
在服务器端,必须配置 cfservd.conf 以允许客户端访问特定目录。为此,请创建一个 AllowConnectionsFrom 和一个 admit 部分
#cfservd.conf control: AllowConnectionsFrom = ( 192.168.0.0/24 ) admit: /configs/datacenter1 *.example1.com /configs/datacenter2 *.example2.com
要测试您的示例客户端以查看它是否正在连接到 cfengine 服务器,请确保它们之间的端口 5803 已清除,并使用以下命令运行服务器
cfservd -v -d2
并且,在客户端上运行
cfagent -v --no-splay
这将为您提供服务器端的大量调试信息,以查看哪些工作正常,哪些工作不正常。
现在,让我们看一下分发配置文件。虽然 cfengine 在 editfiles 命令中有一个功能齐全的文件编辑器,但不建议使用此方法分发配置。copy 命令会将文件从服务器移动到客户端机器,并将 .cfnew 附加到文件名。然后,一旦文件完全复制,它会重命名该文件并将旧副本保存为指定目录中的 .cfsaved。以下是 copy 命令语法
copy: class:: <<master-file>> dest=target-file server=server mode=mode owner=owner group=group backup=true/false repository=backup dir recurse=number/inf/0 define=classlist
只需要 dest= 以及要保存在目标位置的文件名。这些可以不同。这是另一个例子
copy: linux:: ${copydir}/linux/resolv.conf dest=/etc/resolv.conf server=cfengine.example1.com mode=644 owner=root group=root backup=true repository=/var/cfengine/cfbackup recurse=0 define=copiedresolvdotconf
此 copy 语句中的最后一行将此主机分配给名为 copiedresolvdotconf 的组。虽然在复制此特定文件后我们不必执行任何操作,但我们可能希望对刚刚成功发送此文件的所有主机执行某些操作,例如发送电子邮件或重新启动进程。例如,如果您更新附加到守护程序的配置文件,您可能希望向该进程发送 SIGHUP 以使其重新读取配置文件。这在 Apache 的 httpd.conf 或 inetd.conf 中很常见。如果复制不成功,则此服务器将不会添加到 copiedresolvdotconf 类。您可以查询网络中的所有服务器以查看它们是否是成员,如果不是,则找出问题所在。
对配置文件进行版本控制的一个好方法是使用 cfengine 变量来控制要复制的文件名,以控制分发哪个版本。这样一行可能看起来像这样
copy: linux:: ${copydir}/linux/${resolv_conf}
或者,更好的是,您可以使用 cfengine 的类特定变量,其范围仅限于与其关联的类。这使得 copy 语句更加优雅,并且可以简化随着 cfengine 文件扩展的更改
control: # ${resolve_conf} value depends on context, # is this a linux machine or hpux? linux:: resolve_conf = ( "${copydir}"/linux/resolv.conf ) hpux:: resolve_conf = ( "${copydir}"/hpux/resolv.conf ) copy: linux:: ${resolve_conf}
这是一个完整的 cfagent.conf 文件,其中使用了我到目前为止介绍的所有内容。它还添加了一些关于如何使用 cfengine 完成系统管理工作的实用示例
# cfagent.conf control: actionsequence = ( files editfiles processes ) AddInstallable = ( cron_restart ) solaris:: crontab = ( /var/spool/cron/crontabs/root ) linux:: crontab = ( /var/spool/cron/root ) files: solaris:: ${crontab} action=touch linux:: ${crontab} action=touch editfiles: solaris:: { ${crontab} AppendIfNoSuchLine "0,10,20,30,40,50 * * * * ↪/var/cfengine/sbin/cfexecd -F" DefineClasses "cron_restart" } linux:: { ${crontab} AppendIfNoSuchLine "0,10,20,30,40,50 * * * * ↪/var/cfengine/sbin/cfexecd -F" #linux doesn't need a cron restart. } shellcommands: solaris.cron_restart:: "/etc/init.d/cron stop" "/etc/init.d/cron start" import: any:: groups.cf copy.cf
上面是一个完整的 cfagent 配置,它将 cron 的 cfengine 执行添加到每个客户端(如果它是 Linux 或 Solaris)。因此,实际上,一旦您首次使用此 cfagent.conf 文件手动运行 cfengine,cfengine 将继续从该主机每五分钟运行一次,但您无需编辑或重新启动 cron。cfagent.conf 的 control 部分是您可以定义一些变量来控制 cfengine 如何处理配置文件的地方。actionsequence告诉 cfengine 执行每个命令的顺序,并且AddInstallable是一个变量,它保存稍后在文件中的“define”语句中定义的软组,例如在 editfiles 命令之后,该行为DefineClasses "cron_restart"。使用 AddInstallable 的原因是,有时 cfengine 会跳过在命令执行后定义的组,而在 control 部分中定义该组可确保该命令在整个配置中得到识别。
能够从版本控制系统检出配置文件并将其分发到一组服务器是一个强大的系统管理工具。许多独立的工具将完成 cfengine 工作的子集(例如 rsync、ssh 和 make),但没有其他工具允许一小组系统管理员管理如此庞大的一组服务器。集中配置管理具有信息和控制的双重好处,而 cfengine 在免费的开源工具中为您的基础设施和应用程序环境提供了这些好处。
Scott Lackey 是一位独立的科技顾问,他为从 NASA 到华尔街的各行各业开发和部署了配置管理解决方案。通过 slackey@violetconsulting.net, www.violetconsulting.net 联系他。