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

第14回Facterの拡張

今回はFacterを拡張して、デフォルトでは取得できないシステム情報を取得する方法、ならびに拡張したFacterをPuppetでどのように利用するかについて解説します。

Facterとは?

Facterとは、第5回でも解説していますが、システムに関する情報(プロセッサアーキテクチャ、利用OSとそのバージョン、ドメイン名、FQDN、IPアドレスなど)を収集するための、クロスプラットフォームなRubyライブラリです。PuppetではこのFacterによってセットされるFacter変数を利用することにより、システム条件に応じて挙動を変更するようなマニフェストを記述したり、テンプレートにシステム情報を埋め込んだり、といったことが実現できます。

たとえば、OSの違いによるntpdパッケージ名の違いを吸収するためには、以下のようにマニフェストを記述します。

$ntp_package = $operatingsystem ? {
    freebsd => 'openntpd',
    default => 'ntp',
}

package { $ntp_package: ensure => present }

operatingsystemやipaddressなどFacterによって取得できる項目を"fact"と呼びます。

カスタムfactの作成

Facterを拡張するための準備として、カスタムfact用のファイルを置くディレクトリを作成し、rubyから参照できるように、環境変数RUBYLIBを設定します。

$ mkdir -p ~/lib/ruby/facter
$ export RUBYLIB=~/lib/ruby

カスタムfact用ファイルの内容は以下のようになり、~/lib/ruby/facter の下に置きます。

Facter.add("custom_fact") do

    setcode do
        # カスタムfactの値を返す
    end
end

たとえば、環境変数$HOMEを取得するためのカスタムfactファイル ~/lib/ruby/facter/home.rb を以下の内容で作成します。

Facter.add("home") do

    setcode do
        ENV['HOME']
    end
end

この状態でfacterコマンドを実行すると、環境変数$HOMEの値が取得できます。

$ facter home
/home/mizzy

特定の条件のみで値を返したい場合には、confineを利用します。例えば、kernelがlinuxの場合にのみ値を返すカスタムfactは、以下のように記述します。

Facter.add("custom_fact") do
    confine :kernel => :linux

    setcode do
        # カスタムfactの値を返す
    end
end

Puppetでのカスタムfactの配布

FacterはPuppetクライアント上で実行されるため、カスタムfactを利用するためには、すべてのPuppetクライアントにカスタムfactファイルを配布する必要があります。

Puppetサーバ側では以下の様な設定を/etc/puppet/fileserver.confに記述し、Puppetクライアントがファイルサーバ経由でカスタムfactファイルを取得できるようにします(/var/puppet/facts以下にカスタムfactファイルが置かれていると仮定します⁠⁠。

[facts]
  allow *
  path  /var/puppet/facts

Puppetクライアント側では、/etc/puppet/puppetd.conf(0.22.x)や/etc/puppet/puppet.conf(0.23.x以降)に以下の様に記述し、Puppetサーバからカスタムfactファイルを取得するように設定します。

[puppetd]
factsync = true

上記の様な設定を行い、Puppetクライアント上でpuppetdを実行すると、以下のように表示されカスタムfactが取得できていることがわかります。

$ sudo puppetd --server puppet.example.org --verbose
notice: Starting Puppet client version 0.22.4
info: Retrieving facts
notice: /fact_collector/File[/var/puppet/facts]/ensure: created

notice: /fact_collector/File[/var/puppet/facts/home.rb]/ensure: created
info: Loading fact home
info: Facts have changed; recompiling
...

ここで説明したカスタムfactの配布方法は、Puppet 0.24.x以降では変更されますが、0.24.xはまだstableではないため、今回は説明を割愛します。詳細はPuppetオフィシャルWikiのPlugins in Modulesをご参照ください。

カスタムfactサンプル

eth1のIPアドレスを取得するためのカスタムfact

eth0にグローバルなIPアドレスを、eth1にプライベートなIPアドレスを割り当て、特定のデーモンをプライベートなIPアドレスにのみバインドしたい、という場合、Puppetでこれを実現しようとすると、eth1のIPアドレスを取得する必要があります。ですがFacterでは、ipaddressはeth0のIPアドレスを返します(インターフェース名はOSによって異なりますので、適宜読み替えてください⁠⁠。

そこで、privateipaddressというカスタムfactを追加し、eth1のIPアドレスを取得できるようにします。そのためのカスタムfactファイルの内容は以下のようになります。

Facter.add(:privateipaddress) do
  confine :kernel => :linux

  setcode do
    ip = nil
    output = %x{/sbin/ifconfig}

    output.split(/\n\n/).each { |str|
      if str !~ /eth1/

        next
      end

      if str =~ /inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/
        tmp = $1

        unless tmp =~ /127\./
          ip = tmp
          break

        end
      end
    }

    ip
  end
end

これを利用して、例えばmemcachedをプライベートIPアドレスにのみバインドしたい場合は、Red Hat系Linuxであれば以下のような/etc/sysconfig/memcached用のPuppetテンプレートファイルを作成すれば実現できます。

PORT="11211"
USER="nobody"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS="-l <%= privateipaddress %> "

XenのDom0かDomUかを区別するためのカスタムfact

もうひとつのサンプルは、Xenを利用している場合に、Puppetクライアントが動作しているXenカーネルが、Dom0なのかDomUなのかを区別するためのカスタムfactです。内容は以下のようになり、Dom0の場合にはvirtualの値がxen0に、DomUの場合にはxenuになります。

Facter.add("virtual") do
  confine :kernel => :linux

  ENV["PATH"]="/bin:/sbin:/usr/bin:/usr/sbin"

  result = "physical"

  setcode do
    if FileTest.exists?("/proc/xen/capabilities")
      txt = File.read("/proc/xen/capabilities")

      if txt =~ /control_d/i
        result = "xen0"
      else

        result = "xenu"
      end
    end

    result

  end
end

これを利用して、DomUの場合には、Dom0上の/homeをautofsでマウントするために、autofsパッケージをインストールしたい、という場合には、以下の様なPuppetマニフェストを記述することで実現できます。

case $virtual {
  'xenu': {
    package { 'autofs':
        ensure => installed;
    }
  }
}

PuppetのオフィシャルWikiにも、カスタムfactのサンプルがありますのでご参照ください。

次回は独自のリソースタイプを作成して、Puppetを拡張する方法について解説する予定です。

おすすめ記事

記事・ニュース一覧