书写可重用的跨平台配置清单
每个系统管理员都梦想着使用统一的、同质的基础设施,相同的机器都运行相同的操作系统且其版本相同。 正如生活中的其他领域一样,现实往往是凌乱的、往往与理想情况不符。
你很可能会负责管理一堆使用年限不同、架构不同、运行不同发行版本的不同内核的服务器, 常常是分散在不同的数据中心和不同的互联网服务供应商(ISP)。
在这种情况下,会造成系统管理员心中对在 for 循环中执行 SSH("SSH in a for loop")的恐惧, 因为在每个服务器上即使执行相同的命令,也可能产生不同的、不可预知的、甚至是危险的结果。
当然,我们应该努力将旧服务器进行更新,并将一切尽可能工作在一个单一的参考平台之上, 从而使管理更简单,更廉价,更可靠。但在达到这一目标之前,我们可以使用 Puppet, 它可以使我们更容易地应对异构环境(heterogeneous environments)。
操作步骤
如果你有一些放置在不同数据中心的服务器,这些服务器需要略有不同的网络配置, 例如,使用节点继承技术来封装差异:
node wreckspace_server inherits server { include admin::wreckspace_specific }
你需要应用相同的配置清单到运行着不同 OS 发行版本的服务器,其重要差别可能在于软件包名、 服务名以及配置文件的存放位置。可以通过在一个类中使用选择器(selectors) 捕获这些差异并设置全局变量的值:
$ssh_service = $operatingsystem? { /Ubuntu|Debian/ => "ssh", default => "sshd", }
之后你就不用担心配置清单其他部分的差异了,当你要提及这些时, 可以放心的使用变量,它会根据具体环境正确的指向相应的正确值:
service { $ssh_service: ensure => running, }
我们经常需要配合不同的架构;这可能会影响共享库的路径,也可能需要不同版本的软件包。 同样地,尝试在一个单一的 architecture 类中封装所需要的全局变量的值:
$libdir = $architecture ? { x86_64 => "/usr/lib64", default => "/usr/lib", }
之后在需要一个架构相关的值时,你就可以引用这些全局变量的值, 无论是在配置清单中引用还是在模板中引用均可:
; php.ini [PHP] ; Directory in which the loadable extensions (modules) reside. extension_dir = <%= libdir %>/php/modules
工作原理
这种方法的优点(可以称为“自上而下”)是你仅需要进行一次选择。 另一种是自下而上的方法,使用这种方法,在全部配置清单中你随处可见 使用 selector 或 case 语句的设置:
service { $operatingsystem? {
/Ubuntu|Debian/ => "ssh",
default => "sshd" }:
ensure => running,
}
这不仅会产生许多重复代码,而且使代码难于阅读。另外,当需要管理一种新的操作系统时, 你必须在所有的配置清单中进行修改,而不是只修改一处。
更多用法
如果你正在为一个公共的发布(例如 Puppet Forge)编写模块, 使其尽可能的跨平台会让模块变得更有价值。尽你所能,在不同发布、不同平台、不同架构上测试, 并且添加适当的变量,使模块尽可能的应用到各种情况。
如果你正在使用一个公共模块,并修改它适应自己的环境, 如果你认为你的修改可能会帮助到其他人,可以考虑向公共版本提交你的更新。
即使你不想发布你的模块,请铭记:一个模块可能会在生产环境中运行很长一段时间, 并且可能会对其做许多适应环境的改变。 如果从设计模块的一开始就考虑到这些,那么你(或者最终维护你的代码人)的生活将会变得更轻松。
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
— Dave Carhart