指定资源的依赖关系

Remove wrapper, open mouth, insert muffin, eat.

— Instructions on 7-11 muffin packaging

为确保事物以正确的顺序发生,你可以在 Puppet 中指定一个资源依赖另一个资源, 例如:你必须先安装软件包 X 然后再启动它提供的服务,因此应该标记这项服务依赖于软件包 X。 Puppet 会按要求的顺序排出它遇到的所有依赖。

在一些配置管理系统中,资源按照你的书写顺序被应用,换句话说,资源被应用的顺序是隐式的。 Puppet 则不是这种情况,资源或多或少以一种随机(但一致)顺序被应用, 除非你明确地使用依赖关系指出应用的顺序。 一些人更喜欢隐式的方式,因为你可以按照你需要的执行顺序书写资源定义, 并且这就是它们被执行的方式。

另一方面,许多情况下资源的顺序无关紧要。使用隐式风格的系统时, 你不能明确地告诉系统,资源 B 是否被写在了资源 A 之后,由于资源 B 依赖于 A; 或者说书写的顺序是否正确,即资源 A 写在了资源 B 之前。 这使得重构更加困难,因为移动资源有可能会打破一些隐式的依赖关系。

虽然 Puppet 会让你多做一点儿工作,就是预先指定依赖关系, 但是这样产生的代码会更清晰且更易于维护。让我们看一个例子。

操作步骤

  1. 使用如下的内容创建一个新文件 /etc/puppet/modules/admin/manifests/ntp.pp:

    class admin::ntp {
        package { "ntp":
            ensure => installed,
        }
    
        service { "ntp":
            ensure  => running,
            require => Package["ntp"],
        }
    
        file { "/etc/ntp.conf":
            source  => "puppet:///modules/admin/ntp.conf",
            notify  => Service["ntp"],
            require => Package["ntp"],
        }
    }
    
  2. 复制已经存在的 ntp.conf 文件到 Puppet 的如下目录:

    # cp /etc/ntp.conf /etc/puppet/modules/admin/files
    
  3. 在 nodes.pp 中将 admin::ntp 类添加到你的服务器:

    node cookbook {
        include admin::ntp
    }
    
  4. 现在删除系统中已存在的 ntp.conf 文件:

    # rm /etc/ntp.conf
    
  5. 运行 Puppet:

    # puppet agent --test
    info: Retrieving plugin
    info: Caching catalog for cookbook.bitfieldconsulting.com
    info: Applying configuration version '1302960655'
    notice: /Stage[main]/Admin::Ntp/File[/etc/ntp.conf]/ensure:
    defined content as '{md5}3386aaad98dd5e0b28428966dac9e1f5'
    info: /Stage[main]/Admin::Ntp/File[/etc/ntp.conf]: Scheduling
    refresh of Service[ntp]
    notice: /Stage[main]/Admin::Ntp/Service[ntp]: Triggered 'refresh'
    from 1 events
    notice: Finished catalog run in 2.36 seconds
    

工作原理

在本例中演示了两种类型的依赖: require 和 notify。在第一种情况中, ntp 服务要求 ntp 包资源首先被应用:

service { "ntp":
    ensure  => running,
    require => Package["ntp"],
}

在第二种情况中,NTP 的配置文件设置成了 notify(通知)ntp 服务;换句话说, 一旦发现配置文件有变化,Puppet 就应该使用新的配置文件重新启动 ntp 服务:

file { "/etc/ntp.conf":
    source  => "puppet:///modules/admin/ntp.conf",
    notify  => Service["ntp"],
    require => Package["ntp"],
}

这意味着服务依赖于配置文件以及所安装的软件包,Puppet 会按照如下的正确顺序来应用这三个资源:

Package["ntp"] -> File["/etc/ntp.conf"] ~> Service["ntp"]

事实上,这是指定相同依赖关系链的另一种方法。添加上面这行到你的配置清单中, 就会产生和上例中使用 require 和 notify 参数的同样效果 (-> 表示 require,~> 表示 notify)。然而,我更喜欢使用 require 和 notify, 因为依赖关系被定义成了资源的一部分,因此更容易看清将会发生什么。 不过,对于复杂的依赖关系链,你可能想使用 -> 符号来代替。

更多用法

你也可以指定一个资源依赖于某个类:

require => Class["my-apt-repo"]

你不仅可以指定资源和类之间的依赖关系,甚至可以指定 collections 之间的依赖关系:

Yumrepo <| |> -> Package <| provider == yum |>

这是一种功能强大的表达方式,所有 provider 是 yum 的 package 资源被应用之前, 所有的 yumrepo 资源首先都应该被应用。

注记 历史说明:

在 Puppet 2.7 版本之前, 所有资源编目都以非确定性的方式被应用, 这意味着每次 Puppet 运行资源的顺序都会不同。这可能会导致一些有趣的问题, 比如一个 Puppet 配置清单在一台机器上能运行成功而在另一台机器上却运行失败。 经过 Puppet Labs 的努力,现在这种情况已经不会发生。现在 Puppet 既保证可靠成功,又保证可靠失败("either succeed reliably, or fail reliably")。 如果你还在使用早期版本并且遇到了这种问题,请更新到新版本。