使用虚拟资源

什么是虚拟资源(Virtual Resource),我们为什么需要它们? 下面我们来看一个可能会使用虚拟资源的典型例子。 你负责管理 facesquare 和 twitstagram 两个应用程序,他们都是运行在 Apache 上的 Web 应用程序。 facesquare 的定义看起来可能像这样:

class app::facesquare
{
    package { "apache2-mpm-worker": ensure => installed }
}

twitstagram 的定义看起来可能像这样:

class app::twitstagram
{
    package { "apache2-mpm-worker": ensure => installed }
}

一切都很好,直到你需要将两个应用程序同时应用到单台服务器上:

node micawber
{
    include app::facesquare
    include app::twitstagram
}

现在 Puppet 会出错,因为你试图用相同的名字 apache2-mpm-worker 定义两个 package 资源。 错误输出信息如下:

err: Could not retrieve catalog from remote server: Error 400 on SERVER:
Duplicate definition: Package[apache2-mpm-worker] is already defined in
file /etc/puppet/modules/app/manifests/facesquare.pp at line 2; cannot
redefine at /etc/puppet/modules/app/manifests/twitstagram.pp:2 on node
cookbook.bitfieldconsulting.com

你可以从其中的一个类中移除重复的包定义,但是这样话,如果试图在另一个服务器包含 app 类时,就会因为没有准备好 Apache 而失败。

通过在自己的类中放置 Apache 的包资源并使用 include apache 包含它,你就可以解决这个问题, 因为 Puppet 不介意多次包含一个相同的类。但是这有一个缺点,即每个具有潜在冲突的资源都必须有它自己的类。 虚拟资源可以解决这个问题。虚拟资源就像是个普通的资源,特别之处在于它以 @ 字符开始,例如:

@package { "apache2-mpm-worker": ensure => installed }

你可以把它看作是个 “FYI(仅供参考)” 资源:我只是告诉 Puppet 这个资源存在,但不希望用它做任何事情。 Puppet 将会读取并记住虚拟资源定义,但实际上不会创建这个资源,直到你明确指出要创建此资源。

要创建这个资源,使用如下的 realize 函数:

realize( Package["apache2-mpm-worker"] )

对于你想要的资源,可以多次调用 realize 而且不会产生冲突。 因此,虚拟资源用于:当在几个不同的类中都需要相同的资源,且它们可能会在相同的节点上共存的情况。

操作步骤

  1. 创建名为 app 的新模块:

    # mkdir -p /etc/puppet/modules/app/manifests
    
  2. 使用如下内容创建 /etc/puppet/modules/app/manifests/facesquare.pp 文件:

    class app::facesquare
    {
        realize( Package["apache2-mpm-worker"] )
    }
    
  3. 使用如下内容创建 /etc/puppet/modules/app/manifests/twitstagram.pp 文件:

    class app::twitstagram
    {
        realize( Package["apache2-mpm-worker"] )
    }
    
  4. 使用如下内容创建 /etc/puppet/modules/admin/manifests/virtualpackages.pp 文件:

    class admin::virtual-packages
    {
        @package { "apache2-mpm-worker": ensure => installed }
    }
    
  5. 在一个节点上包含如下代码:

    node cookbook
    {
        include admin::virtual-packages
        include app::facesquare
        include app::twitstagram
    }
    
  6. 运行 Puppet。

工作原理

你可以在 admin::virtual-packages 类中定义一个包的虚拟资源。 所有节点都可以包含这个类,并且你可以将所有虚拟资源都放在此类中。 这些虚拟资源都不会实际安装到节点上,直到你调用 realize:

class admin::virtual-packages
{
    @package { "apache2-mpm-worker": ensure => installed }
}

每个需要 Apache 包的类都可以对虚拟资源调用 realize :

class app::twitstagram
{
    realize( Package["apache2-mpm-worker"] )
}

Puppet 知道如何处理它,因为你设置了相应的虚拟资源,你打算多次引用同一个包, 而不会意外地创建具有相同名子的两个资源。所以,这正确地实现了我们的需求。

更多用法

为了实现(realize)虚拟资源,你也可以使用 collection 语法:

Package <| title = "apache2-mpm-worker" |>

使用这种语法的好处是,你不仅可以指定资源名,而且可以指定 tag,例如:

Package <| tag = "security" |>

或者,你可以指定资源类型的所有实例,在查询部分保留一个空格即可:

Package <| |>

参见本书