このページは[Introduction]を翻訳したものです。 翻訳の間違いや誤字、元ページの更新などに気づいた方はぜひ修正お願いします。
Puppetの存在理由
管理するマシンがたった二台しかなくても数千台あっても、システム管理者は作業を楽にする簡単なツールを仕事の合間に書くのがこれまでの伝統でした。 そうして書かれたソフトウェアのほとんどは組織の外で公開されず、広く再利用され成熟したツールとなるケースは極めて稀でした。 ツールの著作権を組織が持っているせいで、新しい会社に行くたびにシステム管理者はツールを最初から書き直す必要に迫られました。
Puppetは、システム管理者のコミュニティが成熟したツールを共有することで、同じ問題に皆がバラバラに取り組んでいる状況を打破するために作られました。 Puppetは、二つの方法によってそれを行います。
- システム管理者が行う技術的なタスクの大部分をシンプルに記述するためのフレームワークの提供
- 普通のプログラムコードのように共有可能な、システム管理者の仕事を記述できるPuppet独自の言語
あなたのシステム管理者としての作業はもっと速くなります。Puppetのコードはシステム管理者の作業のほとんどを詳細に記述することができます。また、他のシステム管理者が書いたコードをダウンロードして使うこともできます。 Puppet言語による実装はほとんどの場合、少なくとも一つか二つの、他の開発者によって提供されたモジュールを利用しています。CookBookには既に数十個のレシピが登録されています。
ツールとしてのPuppet
Puppet言語で書かれたコードの大部分は、包括的な設定管理ツールあるいは機能を絞ったツールとして、他のプロジェクトでも差分に注意を払った上で再利用できます。 あなたが望む方法でPuppetを動作させられますが、推奨されるデフォルトの動作は下記の通りになります。
Puppetはいわゆるスター型の構成を取っており、全てのクライアントは一つか二つ以上のサーバと通信を行います。 各クライアントは定期的に(デフォルトでは30分に1回)サーバにアクセスして最新の設定を受信し、同期作業を実行します。 同期作業が完了するごとに、クライアントはサーバに結果を報告することができるので、あなたは同期の失敗に気づくことができます。 この図はPuppetの通常の実装におけるデータフローを示しています。
Puppetの機能はいくつかの層に分かれています。 情報が各層を通過する際には厳格な制御が行われます。
Puppetを活用するには、各層についての理解と、各層がPuppetの中で担っている機能を理解する必要があります。
冪等性
Puppetが多くの手動管理ツールと非常に異なっている一つの点は、冪等です。すなわちPuppetの設定記述は何度でも安全に再利用することができます。 あなたが設定を書けば、あなたのマシンは定期的にそれを受信し(デフォルトでは30分に1回)、Puppetは設定内容が現在のマシンの状況と異なっている部分だけを適用します。
このPuppetの挙動はトランザクション層が提供しています。 Puppetは完全なトランザクションはサポートしていません。トランザクションは記録されませんし、ロールバックを行うこともできません。しかしシステムに何が起こったかを管理する基礎的な要素は備えています。 noop(何もしない)やdry-run(予行練習)で実行したい時には、コマンドライン引数として指示することができます。 また、何らかの変更があった際には、トランザクションシステムはログを記録することを保証するでしょう。
これによりマシンのライフサイクルのあらゆるシーンでPuppetを利用することができます。 最初のインストールから、継続的なアップデート、そして最後にあなたがそのサービスを他のサーバに移すまで。 SunのJampstartやRedHatのKickstartなどのインストールツールとは違って、Puppetの設定は何年も継続して利用可能です。 一度正しく設定すれば、もう二度と触る必要はありません。 Puppetの利用者は通常、彼らのホストにPuppetをインストールして動作させることだけを行い、残りの作業は全てpuppetが行います。
抽象化Resource層
Puppetの主要な目標は、システム管理者が本当に集中すべき事柄だけに取り組めるようにすることです。 実装の細部や、コマンドや引数や、ファイル形式などは本質的ではありません。ユーザーの情報を格納するのがNetInfoであっても/etc/passwdであっても、あなたのツールは同じように扱うので、ツールが格納場所に与える影響を考慮する必要はありません。 それらのシステム構成要素はResourceと呼ばれます。 PuppetはResourceを容易に管理し、それらの間の関連性を定義することができます。
Provider
Puppetはファイル、ユーザー、パッケージをはじめとした多くのResourceType(documented in the type reference)を扱います。 Mac OS Xがユーザー情報を格納するのにNetInfoを使用していることや、各OSで全く異なるパッケージ管理システムが採用されていることなど、プラットホームによってResourceTypeの実装は大きく異なります。 そのためPuppetは一つのResourceTypeに対する複数の実装をサポートしており、それらの実装をProviderと呼びます。 PuppetはResourceTypeに対するProviderを選択します。通常はあなたの環境に最適なデフォルトのProviderが選択されます。選択される値をオーバーライドすることも容易に可能です。
システムに対する変更
PuppetのResourceはディスク上のビットを直接管理できるものです。そのためには、直接システムに対する変更を記述するのではなく、変更する部分をResourceとして指定して変更を記述する必要があります。 この結果、あなたはfstabの書式に気を配ったりする代わりに、本来の作業に集中することができます。
全てのResourceはAttributeの単純な集合です。それらのいくつかはResourceの機能やResourceそのものを管理するもの(Propertyと呼ぶ)です。 たとえば、ユーザーはAttributeとしてホームディレクトリを持ちます。これは直接ユーザーを編集することを意味せず、ただユーザーがいかに作成されるかを変更するだけです。 Attributeのほとんどはuid,gid,ホームディレクトリのような注意すべきPropertyです。 あなたがそれらを変更した時、それらはシステムを直接変更します。
全てのResourceにおいて作用する特殊なAttributeも存在します。 それらはメタパラメータと呼ばれ、Resourceごとのログレベルやnoopモードの設定や、Resource間のリレーションを制御するものです。
Resource間のリレーション
PuppetがサポートするResource間のリレーションは鍵となる特長の一つです。Resource間にリレーションが定義されると、各Resourceの依存性を考慮した順序で設定変更を実行するようになります。
サービスであるResourceを他のResourceに依存している場合、そのように設定ファイルに記述すれば、必要なResourceの設定が先に完了していることをPuppetは保証します。これによって、正しい設定ファイルが置かれる前にサービスを起動されることはなくなります。
設定ファイルが変更された時に、サービスを再起動するように記述することもできます。これにより、Puppetはサービスとその依存性を管理し、サービスが常に最新の設定で稼働していることを保証できます。
ExecResource
時には既存のResourceTypeに該当しないようなResourceを扱いたくなるかもしれません。そういった場合のために、ExecResource?という型が用意されています。ExecResource?は外部コマンドを実行することができます。こういった操作もResourceとして扱うことで、他のResourceとの整合性が保たれます。 例えば、Subversionリポジトリがまだ作成されていない時だけsvnadminコマンドでリポジトリを作成する処理などが考えられます。 puppet言語はこれらのResourceをラッピングして他のResourceと同等に扱うことで再利用の可能性を高めています。 ただ実はこのケースではsvnrepoResourceを用いることができるので、コマンドを記述するよりはそのほうがいいでしょう。
言語
Resource
puppetはとにかくResourceを管理するものですので、それらのResourceを指定することに注意を払っています。 ほとんどの基本的な構文は、対象となる一つのResourceを指定して書かれます。
# Make sure the modes on the sudoers file are correct
file { "/etc/sudoers": owner => root, group => root, mode => 644 }
このように、Resourceの要素を記述します。 Type(file), タイトル(/etc/sudoers)、その他のAttribute。 注意点としては、ここではファイルパスがクオートされていますが、英数字とハイフンのみであればクオートする必要はありません。あと、半角空白は全くの問題外であり、使用できません。
中括弧の中で複数のResourceを記述することもできます。その場合にはResourceの間をセミコロンで区切ります。
file {
"/etc/sudoers": owner => root, group => root, mode => 644;
"/usr/sbin/sudo": owner => root, group => root, mode => 4111
}
このようにカンマとセミコロンによって区切られます。
重複を防止する仕組み
Puppetインタプリタは、同じResourceに対する設定記述を複数に分けることを許容しません。 二つの異なるクラスが同一のResourceを指定している場合は、構文解析エラーが発生します。
Puppetはこのようにして二つのクラスが重複しないことを保証します。協同作業者とコードを共有したり、外部からダウンロードしたコードを利用する時でも、システム管理者は衝突が起きないことを確信できるでしょう。
Puppetは、Typeとタイトルの比較によって重複を判断します。 同じTypeとタイトルを持つ二つのResourceが存在する時、それらは重複していると判定されます。
クラス
Resourceの結合の説明に入りましょう。 実際のケースでは、sudoパッケージをインストールする際には、sudoersファイルも一緒に扱いたいところです。
class sudo {
package { sudo: ensure => installed }
file {
"/etc/sudoers": owner => root, group => root, mode => 644;
"/usr/sbin/sudo": owner => root, group => root, mode => 4111
}
}
こうすると"include sudo"と記述するだけで、二つのResourceが適用されます。
継承
Puppetはクラスの継承を限定的にサポートします。継承が有用な点は限られています。 すなわちサブクラスではオーバーライドの記法を用いて、親クラスで定義されたResourceをオーバーライドできるという点です。 ここに例を示します。
class base {
file { "/my/file": content => template("base.erb") }
}
class sub inherits base {
# override the content
File["/my/file"] { content => template("other.erb") }
}
上記のサブクラスでは、Resource Typeの先頭一文字を大文字にして記述しています(この例ではFile)。これは定義済みのTypeを参照することを意味します。もしfileと書いてしまったら、Resourceの重複と判定されてエラーになるでしょう。
組み合わせの実例:SSH
人生がいつもこんなに簡単にいくとは限りません。 ほとんどのパッケージには関連するサービスがあり、しばしばそれらの名前はプラットホーム間で一貫していません。 例えば、UNIXライクOSでインストールされるセキュアシェルは、パッケージ名もサービス名もばらばらです。 また、ただインストールするだけでなく、サービスの起動も指示したいところです。
class ssh {
package { ssh: ensure => installed }
file { sshd_config:
name => $operatingsystem ? {
Darwin => "/etc/sshd_config",
Solaris => "/opt/csw/etc/ssh/sshd_config",
default => "/etc/ssh/sshd_config"
},
source => "puppet://server.domain.com/files/ssh/sshd_config"
}
service { ssh:
name => $operatingsystem ? {
Solaris => openssh,
default => ssh
},
ensure => running,
subscribe => [Package[ssh], File[sshd_config]]
}
}
Facter変数
上記の例では、二つの新要素が登場しました。 一つめはFacter変数である$operatingsystemです。 Facterはインタプリタによってあらかじめ代入されており、グローバルのスコープを持ちます。 現時点で使用可能な全てのFacterを参照できます。$ipaddressや$hostnameがこれに含まれています。
セレクタ
もう一つの新要素は "? { ... }"記法です。 これは3つの部分からなる演算子で、セレクタと呼びます。 セレクタは?の前の値を評価して、与えられたリストの中でマッチした場合には、それに対応する値が返されます。マッチしなければdefaultの値が返されます。 ある項目に対して、OSやホスト名によって異なる値を設定したい時に有用です。 注意すべき点は、もしマッチする値がなかった時には構文解析エラーが発生することです。 また、セレクタはマッチングにおいて大文字・小文字を区別しません。
タイトル vs 名前
運命の導きによって私たちはいくつかのResourceに二つめの名前(訳注:$nameで参照可能)を与えることになりました。 Resourceのタイトルのコロンの手前に書かれているのがResourceのタイトルです。あなたはそれをResourceの関連性の定義において参照することができます。 同じタイトルが意味するコマンド名やパスが異なる場合は、上記の例のようにシステム構成に応じて別の名前を付けることができます。 名前はデフォルトではタイトルと同じになっています。
Resource間のリレーション
ついにリレーションの設定です。 リレーションはサービスとパッケージ、設定ファイルの間で設定できます。 上記の例のFile[sshd_config]はResourceから参照され呼び出されます。そしてResourceを一意に指定しています。 サービスは設定ファイルのフルパスではなくタイトルだけを記述することができます。これは優れています。そうでなければ、あなたはここにまたセレクタを書かなくてはならないでしょう。
その他の言語仕様
真偽値
Puppetは、"true"や"false"や数値を含むほぼ全ての値を文字列として扱います。しかしクオートせずにtrueまたはfalseと記述した際には、puppetはそれを真偽値として扱います。(文字列として扱いたい時にはクオートしてください) この機能はデフォルト値にとして有効ですが、Resourceの値として用いられることもあります。
変数
変数の用法は既に説明しましたが、もちろん下記のようにも代入することができます。
$myvar = value
一つのクラスまたは定義の中で同じ変数を使うことはできません。なぜなら二度目の変数が一度目の変数の値との整合性を保てないからです。 全ての変数の割り当ては、Resourceの評価の前に行われます。
他の制御構造
値を選択するのに有用なセレクタを既に見てきましたが、Puppetは他にもcase構文をサポートしています。 case構文を使うと、Resourceの指定全体を条件分岐させることができます。
case $operatingsystem {
Darwin: { file { "/some/file": ensure => present } }
default: { file { "/other/file": ensure => present } }
}
セレクタと同様に、case構文も大文字小文字を区別しません。
シンプルなif/else構文もサポートされています。これはcase構文から機能を削ったものと言えます。
if $should {
file { "/some/file": ensure => present }
} else {
file { "/other/file": ensure => present }
}
puppetは現時点では比較演算子をサポートしていません。if/else構文は真偽値の比較においてのみ利用可能です。
配列
puppetは配列を極めて限定的にサポートします。 配列を作成し、値を代入することはできますが、値を変更することはできません。 上記のSSHのサンプルにあるように、複数の値を属性に入れるのに利用できます。
user { [bin, adm]: ensure => present }
これは二人のユーザーを作成するResourceを短縮記法で書いたものです。
関数
puppetはシンプルな関数記法をサポートしています。 全ての関数は、値を返さない組込みの命令文か、式の右辺値として値を返す関数です。 値を返す関数は、値を返す以外のことをしません。
notice("This is a log message") # a statement
$content = template("mytemplate.erb") # an rvalue
収録されているビルトイン関数は少なく、それらは全てfunction referenceに記述されています。 最もよく使われる関数の一つがinclude`です。これはクラス名、テンプレート名を評価し、[ERb]を使ってファイルコンテンツを生成します。 あなた自身でcustom functionsを書くことも時には有用です。Puppetでそれを行うのはとても簡単です。
定義文
puppetの設定記述において最もよく使われる文法要素は定義文です。 定義文は複数のResourceをそれぞれ異なるモデルで単一のResourceとして一括して扱えるように設定できます。 たとえば、apache2のバーチャルホストはDebianにおいては極めてシンプルに管理されます。/etc/apache2/sites-availableに設定ファイルを置いて、/etc/apache2/sites-enabledにリンクさせるだけです。 そのコードを各バーチャルホストごとに書いていくことも可能ですが、それよりも下記のように記述するほうが簡単です。
define virtual_host($docroot, $ip, $order = 500, $ensure = "enabled") {
$file = "/etc/sites-available/$name.conf"
# The template fills in the docroot, ip, and name.
file { $file:
content => template("virtual_host.erb"),
notify => Service[apache]
}
file { "/etc/sites-enabled/$order-$name.conf":
ensure => $ensure ? {
enabled => $file,
disabled => absent
}
}
}
これにより、以下のように書くだけでバーチャルホストを設定できるようになりました。
virtual_host { "reductivelabs.com":
order => 100,
ip => "192.168.0.100",
docroot => "/var/www/reductivelabs.com/htdocs"
}
あなたはこの定義文を各バーチャルホストに再利用できます。
この方法は、一つまたは複数の実行可能Resourceをラップして、その挙動を明確化するためによく使われます。 たとえは、これはSubversionリポジトリを作成するシンプルな定義文です。
# Create a new subversion repository.
define svnrepo($path) {
exec { "create-svn-$name":
command => "/usr/bin/svnadmin create $path/$name",
creates => "$path/$name" # only run if this file does not exist
}
}
この定義を用いて、下記のように非常にシンプルかつ直接的に記述できます。
svnrepo { puppet: path => "/var/lib/svn" }
また、定義文は、あなたがこれまで毎回リポジトリを作成していたようなプラットホームの差分を制御します。
重複するResourceをラップすべきもう一つの理由は、同じResourceを複数の場所に置いてしまった時にそれを検証し、意味がわかるなエラーメッセージを出すためです。 たとえは異なるSubversionのリポジトリをpuppetに作らせ、それらが異なるパスを持っていることを知らずにいたような場合でも、それらをラップしていれば構文解析エラーが出されるので、実際にサーバを動かす前にそれを知ることができるでしょう。
ノード
重要な言語構成要素の最後の一つがノードです。 これはちょうどクラス定義のように機能し、継承もサポートしています。ただしPuppetインタプリタは明示的にノードを読み込む代わりに、ノードとの接続が確立した時に読み込みを行います。 Pupeptインタプリタは、ノード定義のリストを見て、クライアント名にマッチする定義文を探し、見つかればその定義文を評価します。
デフォルトのノードを定義しておくこともできます。これは特定のノードにマッチしなかった時に利用されます。
注意点としては、ノード名はシングルクオートで囲む必要があります。 ノード名は人間が理解しやすいように修飾することができます。
Puppetサーバを起動する時に --no-nodesオプションをつけると、ノード定義を無効にして、インタプリタに最上位層のコードだけを評価させることもできます。ノードに与えるクラスをcase構文で決定する場合などに有用でしょう。
レポート機能
レポート機能はデフォルトでは有効になっていませんが、puppetd.confに”report = true”という行を追加するだけで、簡単に有効にすることができます。 クライアントが送信するレポートには、トランザクションごとに生成されたログメッセージと、管理されているResource、変更されたResource、変更に失敗したResource、再起動回数のそれぞれの数が含まれています。
デフォルトでは、レポートはYAMLで保存され、Rubyスクリプトから用意に利用したりウェブページで表示することができるでしょう。 それ以外のレポートの説明はreport referenceにあります。RRDデータベース用のファイル形式などが利用可能です。
スタンドアローン
Puppetは通常、クライアント/サーバで使われますが、スタンドアローンのインタプリタ言語として動作させることもできます。 ローカルで完結した動作をさせることでcfengineと同様に利用することができます。 この機能はPuppetサーバの初期設定の際に非常に有用です。その後、--use-nodesオプションを利用して通常運用に入れるでしょう。 あなたはPuppetサーバを全体的に設定するためのPuppetのコードを書いて、Puppetに自分自身を設定させることができます。 うーむ、これは自己参照だね。
発展性
クライアント/サーバのPuppetは、どこからでもあなたが書いた新しいコードを読み込んで実行できるでしょう。 あなたは独自の関数やResourceタイプ、Provider、レポート、などを作成できます。それをRubyの検索パスに置いて起動時に用いれば、puppetは自動的にそれらを読み込みます。
まとめ
puppetの機能のほんのさわりの部分を駆け足で紹介してきました。 包括的なdocumentationは他に用意されています。またあなたの目的に役立つであろう多くのexample recipesもあります。 私たちは日常的にドキュメントの整備を行っています。何か要望があれば、docs atmark reductivelabs.comまでご連絡ください。
Attachments
- Puppet_Layers.png (45.0 kB) -
use inline
, added by puppet on 04/27/07 16:53:13. - Puppet_Star.png (31.7 kB) -
use inline
, added by puppet on 04/27/07 16:53:45.


