Puppet - 可以在模块中使用软件设计模式吗?

Puppet - 可以在模块中使用软件设计模式吗?

在使用 Puppet 时,我发现自己想要自动化更复杂的设置,例如 X 个网站的虚拟主机。随着我的 Puppet 清单变得越来越复杂,我发现很难应用 DRY(不要重复自己)原则。下面是我想要的简化片段,但它不起作用,因为 Puppet 会根据我使用类还是定义而抛出各种错误。我想从一些经验丰富的 Puppet 大师那里得到一些反馈,了解他们如何实现这个解决方案。

# site.pp
import 'nodes'

# nodes.pp
node nodes_dev {
    $service_env = 'dev'
}

node nodes_prod {
    $service_env = 'prod'
}

import 'nodes/dev'
import 'nodes/prod'

# nodes/dev.pp
node 'service1.ownij.lan' inherits nodes_dev {
    httpd::vhost::package::site { 'foo': }
    httpd::vhost::package::site { 'bar': }
}

# modules/vhost/package.pp
class httpd::vhost::package {

    class manage($port) {

        # More complex stuff goes here like ensuring that conf paths and uris exist
        # As well as log files, which is I why I want to do the work once and use many

        notify { $service_env: }
        notify { $port: }
    }    

    define site {

        case $name {

            'foo': {

                class 'httpd::vhost::package::manage':
                    port => 20000
                }
            }

            'bar': {

                class 'httpd::vhost::package::manage':
                    port => 20001
                }
            }
        }
    }
}

该代码片段给了我一个Duplicate declaration: Class[Httpd::Vhost::Package::Manage]错误,如果我将manage类切换为定义,并尝试访问全局变量或传入 foo 和 bar 共同的变量,我就会收到错误Duplicate declaration: Notify[dev]

有什么建议可以让我实施 DRY 原则并且让 Puppet 正常工作?

- 更新 -

我仍然无法确保我的一些虚拟主机(可能共享父目录)设置正确。如下所示:

node 'service1.ownij.lan' inherits nodes_dev {
    httpd::vhost::package::site { 'foo_sitea': }
    httpd::vhost::package::site { 'foo_siteb': }
    httpd::vhost::package::site { 'bar': }
}

我需要的是 sitea 和 siteb 有相同的父“foo”文件夹。我遇到的问题是当我调用定义以确保“foo”文件夹存在时。下面是我拥有的站点定义,希望它能理解我想要实现的目标。

class httpd::vhost::package {

    File {
        owner   => root,
        group   => root,
        mode    => 0660
    }

    define site() {

        $app_parts = split($name, '[_]')

        $app_primary = $app_parts[0]

        if ($app_parts[1] == '') {
            $tpl_path_partial_app = "${app_primary}"
            $app_sub = ''
        } else {
            $tpl_path_partial_app = "${app_primary}/${app_parts[1]}"
            $app_sub = $app_parts[1]
        }

        include httpd::vhost::log::base

        httpd::vhost::log::app { $name:
            app_primary => $app_primary,
            app_sub     => $app_sub
        }
    }
}

class httpd::vhost::log {

    class base {

        $paths = [ '/tmp', '/tmp/var', '/tmp/var/log', '/tmp/var/log/httpd', "/tmp/var/log/httpd/${service_env}" ]

        file { $paths:
            ensure  => directory
        }
    }

    define app($app_primary, $app_sub) {

        $paths = [ "/tmp/var/log/httpd/${service_env}/${app_primary}", "/tmp/var/log/httpd/${service_env}/${app_primary}/${app_sub}" ]

        file { $paths:
            ensure  => directory
        }
    }
}

运行include httpd::vhost::log::base良好,因为它是“包含的”,这意味着它只实现一次,即使site被调用多次。我得到的错误是:Duplicate declaration: File[/tmp/var/log/httpd/dev/foo]。我研究过使用exec,但不确定这是正确的路线,肯定其他人以前也处理过这个问题,任何见解都会受到赞赏,因为我已经为此努力了几个星期。谢谢。

答案1

首先 - 您绝对应该使用definefor httpd::vhost::package::manage,而不是class,因为它将为同一个节点定义多次。

notify { $service_env: }总是会搞砸,因为的两次调用$service_env都是。您永远不能用相同的名称再次声明相同的资源。devdefine

您可以通过更改为类似的内容来解决这个问题notify { "${service_env}-${port}": },但如果它没有正确构建以处理设置为定义类型,您可能会遇到其他问题。

整个班级结构都很httpd::vhost::package::manage奇怪 - 我建议真正将事情精简为一个班级结构,这将简化事情并允许您只拥有所需的资源。

httpd (init.pp - have it include the install, config, service classes)
httpd::install (install.pp - have it define Package[httpd] to install)
httpd::service (service.pp - have it define Service[httpd]; the vhost defines can
                notify this to update)
httpd::config (config.pp - any configuration, such as handling of httpd.conf,
               that won't be per-vhost)
httpd::vhost (vhost.pp - the defined type that can be used multiple times.  Have
              it include httpd so that the base classes are handled, and
              notify httpd::service from your vhost config file resources)

vhost 管理的一个简单例子实际上是文档中定义类型的示例- 如果您需要任何其他说明如何将其设置为可重复使用的配置,请告诉我!

编辑:

更新共享父目录的问题。

在 Puppet 中管理来自两个不同位置的文件是一件很麻烦的事,以前也给我带来过麻烦。当两个资源声明之间没有冲突(如您当前的难题)时,尤其令人沮丧。

您说得对,exec资源很可能就是您最好的答案。

define app($app_primary, $app_sub) {
    exec { "mkdir_${service_env}_${app_primary}_${app_sub}":
        command  => "/bin/mkdir -p /tmp/var/log/httpd/${service_env}/${app_primary}/${app_sub}",
        creates  => "/tmp/var/log/httpd/${service_env}/${app_primary}/${app_sub}",
        require  => Class["httpd::vhost::log::base"],
    }
}

这适用于两种情况(有和没有子目录),因为$app_sub如果没有使用,则将其设置为空字符串。

我认为不共享目录树(用作log/httpd/foo_sitea日志目录而不是log/httpd/foo/sitea)可能更简洁,但这应该可以解决问题。拆分字符串并基于此进行不同的行为,以及所有这些单独的defines,而这实际上可以存在于site类中。确保保持简单!

相关内容