オープンソースなシステム自動管理ツール Puppet

第4回マニフェスト応用編(その1)

前回はマニフェストはじめの一歩と題して、マニフェストの基礎であるリソースの宣言方法について解説しました。今回は更に高度なマニフェストの宣言方法について解説します。

ノード(node)

前回のマニフェスト適用例では、マニフェストをすべてのPuppetクライアントに適用するために、puppetmasterd起動時に--nonodesオプションを指定していました。

実際のシステム管理の現場ではこの様なことは稀で、システムの役割によって適用すべきマニフェストは異なります。特定のPuppetクライアントにのみマニフェストを適用するには、次の様に宣言します。

node 'client.example.com' {
    file {
        '/etc/passwd':
            owner => 'root',
            group => 'root',
            mode  => 644;
    }
}

これにより、client.example.comにのみ、このマニフェストが適用され、他のPuppetクライアントには適用されません(puppetmasterd起動時には、--nonodesオプションを外す必要があります⁠⁠。

ノード宣言されていないPuppetクライアントでpuppetdを実行すると、以下の様なエラーが表示されます。

err: Could not retrieve configuration: Could not find client.example.com with names client.example.com, client

ノード名に英数字と-⁠ハイフン)以外の文字が含まれる場合には、クォートする必要があります。

複数のノードを宣言することももちろん可能です。

node 'mail.example.com' {
    file {
        '/etc/passwd':
            owner => 'root',
            group => 'root',
            mode  => 644;
    }
}

node 'www.example.com' {
    file {
        '/etc/passwd':
            owner => 'root',
            group => 'root',
            mode  => 644;
    }
}

node default {
    file {
        '/etc/passwd':
            owner => 'root',
            group => 'root',
            mode  => 644;
    }
}

どのノード名にもマッチしない場合には、defaultノードで宣言されているマニフェストが適用されます。

また、ノードは継承することもできます。

node base {
    file {
        '/etc/passwd':
            owner => 'root',
            group => 'root',
            mode  => 644;
    }
}

node 'client.example.com' inherits base {

}

client.example.comには直接マニフェストが宣言されていませんが、baseノードを継承しているので、baseノードで宣言されたマニフェストが適用されます。

この様に、継承を利用することにより、同じ内容のマニフェストを何度も宣言する必要がなくなり、マニフェストを簡略化できます。

注意点として、現在の安定版であるバージョン0.22.4では、継承元のノード名(上の例ではbase)を以下の様にクォートすると、puppetmasterd起動時にSyntax errorが出てしまいます。

node 'client.example.com' inherits 'base' {

}

おそらくバグだと思われますが、ノードの継承を行う場合にはご注意ください。

クラス(Class)

クラスにより複数のリソースをひとまとめにすることができます。クラスは以下の様に宣言します。

class unix {
    file { '/etc/passwd':
        owner => 'root',
        group => 'root',
        mode  => 644;
    }

    file { '/etc/shadow':
        owner => 'root',
        group => 'root',
        mode  => 440;
    }

    exec { 'blah':
        path => '/usr/bin',
        cwd  => '/tmp',
    }
}

このままではクラスが宣言されただけで、マニフェストの内容は適用されません。適用するにはinclude関数を実行します。

node client.example.com { include 'unix' }

この例では、client.example.comノードに、unixクラスのマニフェストが適用されます。

クラスもノードと同様に、継承することが可能です。

class freebsd inherits unix {
    File['/etc/passwd'] { group => 'wheel' }
    File['/etc/shadow'] { group => 'wheel' }
}

freebsdクラスがunixクラスを継承し、unixクラスで宣言されたリソースのgroupパラメータを上書きしています。既に宣言されているリソースは上の例の様に『Type['Resource Name']』で参照することができます。

また、リソースのパラメータは上書きだけではなく、+>オペレータにより追加することも可能です。

class apache {
    service { 'apache': require => Package['httpd'] }
}

class apache-ssl inherits apache {
    # host certificate is required for SSL to function
    Service[apache] { require +> File['apache.pem'] }
}

この例では、apacheクラスで宣言されているapacheサービスリソースは、httpdパッケージを必要としていますが、apacheクラスを継承したapache-sslクラスでは、httpdパッケージにプラスして、apache.pemファイルも必要としている、ということになります。

定義済みタイプ(Defined type)

既存のリソースタイプを組み合わせて、新たなリソースタイプを定義することができます。

define svn_repo($path) {
    exec { "/usr/bin/svnadmin create $path/$title":
        unless => "/bin/test -d $path/$title",
    }
}

この例では、svnadmin createを実行するsvn_repoというリソースタイプを新たに定義しています。svn_repoタイプのリソースを宣言してシステムに適用にするには、以下の様に記述します。

svn_repo { puppet: path => '/var/svn' }

このマニフェストが適用されると、/var/svn/pupptが存在しなければ、svnadmin create /var/svn/puppetが実行されます。

定義済みタイプはノードやクラスと異なり、継承はできません。

複数の既存リソースタイプを組み合わせて、もっと複雑な定義済みタイプを定義することもできます。例として、Puppet本家WikiのAutoFS Recipeをご参照ください。

リソースの依存関係

マニフェストではリソース間の依存関係を宣言することができます。

before

beforeは、あるリソースが指定されたリソースよりも前に適用されることを宣言します。

package { 'ssh':
    eusure => latest,
    before => Service[ssh],
}

service { 'ssh':
    name   => sshd,
    ensure => running,
}

この例では、sshサービスが起動される前に、sshパッケージがインストールされている必要があることを宣言しています。sshパッケージがインストールされていなければ、sshサービスは起動されません。

require

beforeとは反対にrequireは、あるリソースが指定されたリソースよりも後に適用されることを宣言します。beforeの例と同じ結果となるマニフェストを、requireを用いて書き直すと以下の様になります。

package { 'ssh':
    eusure => latest,
}

service { 'ssh':
    name    => sshd,
    ensure  => running,
    require => Package[ssh] 
}

notify

notifyはリソースに変更があった場合、指定されたリソースに対してイベント通知します。イベント通知を受けたリソースでは、リソースタイプに応じたアクションが実行されます。例えば、リソースタイプがserviceであれば、サービスが再起動されますし、execであればコマンドが実行されます。

package { 'ssh':
    eusure => latest,
    notify => Service[ssh]
}

file { '/etc/ssh':
    source  => puppet://server/module/ssh,
    recurse => true,
    notify  => Service[ssh]
}

service { 'ssh':
    name   => sshd,
    ensure => running,
}

この例では、sshパッケージが更新された場合、または/etc/ssh以下のファイルに変更があった場合に、sshサービスが再起動されます。

subscribe

notifyとは逆にsubscribeは、指定された他のリソースに変更があった場合に、リソースタイプに応じたアクションが実行されます。notifyの例と同じ結果となるマニフェストを、subscribeを用いて書き直すと以下の様になります。

package { 'ssh':
    eusure => latest,
}

file { '/etc/ssh':
    source  => puppet://server/module/ssh,
    recurse => true,
}

service { 'ssh':
    name      => sshd,
    ensure    => running,
    subscribe => [ Package[ssh], File['/etc/ssh'] ]
}

今回はより高度なマニフェストの宣言方法について解説しましたが、マニフェストについて覚えるべきことはもう少しありますので、更に次回に続きます。

参考URL

おすすめ記事

記事・ニュース一覧