Puppetの基本その2について記載する。
本シリーズの目次
- Infrastructure as Code 概念の理解
- Puppet環境の準備
- Puppetの基本の理解その1 Resource,Ordering,Class,Define,Node
- Puppetの基本の理解その2 Module,File,Templates,Variable
- Puppetベストプラクティスの理解その1 ディレクトリ構成とその役割
- Puppetベストプラクティスの理解その2 多種多様なサーバの構成管理
- [Puppetベストプラクティスの理解その3 Hieraで世界が変わる?] (http://qiita.com/takeuchikzm/items/e04e6cbe84a185eb1f26)
Module - マニフェストの機能単位での分割管理
Puppetの基本の理解その1にて説明した、
- Resource - 構成管理するResource状態の記述
- Ordering - Resourceの適用順序の指定
- Class/Define - Resourceのまとめ込みによる再利用性の向上
- Node - サーバ単位でのResoure定義のまとめ込み
を使えば複数のサーバに適用できるマニフェストは一通り書く事ができるようになった。
しかし、ここまででは全てのマニフェストを1つのファイルに全て詰め込む方法しか紹介していなかった。
VCSによるバージョン管理や共同開発等を考えると不便である。
Puppetでは、意味的にマニフェストファイルを分割して管理するためにModule機能を用意している。
-
Moduleは、任意の機能(mysqlやapache等)毎の構成管理に必要なマニフェストや配布用のファイル等をのディレクトリにまとめられる。
-
puppet.conf
で設定できるmodulepath
にModuleを置いておけば、どのマニフェストからでも宣言してModuleは利用できる。- またModule内部のResourceやファイル等を外部から参照する際に省略記法を使うことができる。
-
世の中の人々が作ったModuleを利用することも可能である。
modulepathの設定
modulepathの設定はpuppet config
コマンドで確認できる。
$ sudo puppet config print
:(コロン)
で区切られたディレクトリのどれかにModuleを置けば、その環境のマニフェストから参照することができる。
Moduleのディレクトリ構成
Moduleの標準的なディレクトリ構成は以下となる。
- manifests/: マニフェストを置く。
- templates/: そのModuleの設定ファイルの雛形ファイルを置く。(変数等による内容変更が必要なものを置く。)
- files/: そのModuleの動作に必要なファイルを置く。(変数等による内容変更が必要のないものを置く。)
- lib/: Custom facter(システム状態等から自律的に決定される変数)やCustom Resource Typeの定義を置く。
- tests/ or exapmles/: Moduleのマニフェストの利用方法の説明などを置く。
- spec/: rspec-puppetで書かれたテストファイルを置く。
中身が空なディレクトリは作成しなくても動作上問題は無い。
manifests/の書き方
Moduleのmanifestsディレクトリの中には複数のマニフェストファイルを用意したり、サブディレクトリを作ったりすることができる。
ただし、Module内のマニフェストのclass名付けにはいくつかのルールがある。
init.ppの書き方のルール
-
init.ppを必ず用意し、Module名と同名のclassを用意しなくてはならない。また、
<Module名>::init
というclassは持てない。-
${modulepath}/apache/manifests/init.pp
にclass apache { ..; }
が存在する必要がある。
-
-
init.pp中には
<Module名>::<サブクラス名>
という名前のclassが定義できる。-
${modulepath}/apache/manifests/init.pp
にはclass apache::class1 { ..; }
やclass apache::class2 { ..; }
等を定義する。
-
init.pp以外のマニフェストの書き方のルール
-
init.pp以外のマニフェストファイルを作成したい場合、
<モジュール名>::<ファイル名>
という名のclassを用意しなくてはならない。-
${modulepath}/apache/manifests/install.pp
にclass apache::install { ..; }
が存在する必要がある。
-
-
init.pp以外のマニフェストファイルには、
<Module名>::<ファイル名>::<サブクラス名>
という名前のclassが定義できる。-
${modulepath}/apache/manifests/install.pp
にはclass apache::install::class1 { ..; }
やclass apache::install::class2 { ..; }
等を定義する。
-
manifestsディレクトリにサブディレクトリを作る場合のルール
-
manifessディレクトリにサブディレクトリを作る場合、その中のマニフェストファイルには
<Module名>::<サブディレクトリ名>::<ファイル名>
というclassを用意しなくてはならない。-
${modulepath}/apache/manifests/site1/install.pp
にはclass apache::site1::install { ..; }
が存在する必要がある。
-
-
manifestsディレクトリのサブディレクトリのマニフェストファイルには
<Module名>::<サブディレクトリ名>::<ファイル名>::<サブクラス名>
という名前のclassが定義できる。-
${modulepath}/apache/manifests/site1/install.pp
にはclass apache::site1::install::class1 { ..; }
やclass apache::site1::install::class2 { ..; }
等を定義する。
-
基本的にclass名をModule名やマニフェストファイル/サブディレクトリ名とマッピングさせるように記載する必要がある。
面倒であるが、class定義の探しやすさのために必要と言える。(perlやruby等のプログラム経験があるとさほど違和感を感じないのではないだろうか。)
上記のルールにしたがって記述したModuleをmodulepath
に配備すれば、そのマシンのどのマニフェストからも、
include apache::install
include apache::install::class1
include apache::install::class2
include apache::site1::install
include apache::site1::install::class1
include apache::site1::install::class2
もしくは、
class { 'apache::install': }
class { 'apache::install::class1': }
class { 'apache::install::class2': }
class { 'apache::site1::install': }
class { 'apache::site1::install::class1': }
class { 'apache::site1::install::class2': }
のようにすることで呼び出しが可能である。
より詳細な解説は、PuppetLabs - Language: Namespaces and Autoloadingを参考されたい。
Files - 静的ファイルの配備
さて、ここまで騙し騙し説明をしてきたが、Puppetでの構成管理のほとんどはシステムへのファイルの配備と言える。
Modulesの中のfiles/
ディレクトリは配布用のファイルを置くために利用するものである。
file Resourceのsource
parameterを使えば、files
以下のファイルを実システムに配備することができる。
testmodule_file
モジュールを使った例を示す。(なお、例ではmodulepath
としては/etc/puppet/modules
を利用する。)
まず、配布したいファイルfile1
をモジュールディレクトリに以下のように配備する。
$ tree /etc/puppet/modules/testmodule_file/
/etc/puppet/modules/testmodule_file/
|-- files
| `-- file1
`-- manifests
`-- init.pp
file1の中には適当な内容を入れておく。
$ cat /etc/puppet/modules/testmodule_file/files/file1
testmodule_file_file1
init.ppの記述例は以下。
class testmodule_file {
file { '/tmp/testmodule_file_file1':
ensure => file,
mode => '644',
owner => 'root',
group => 'root',
source => "puppet:///modules/testmodule_file/file1",
}
}
source parameterのvalueのファイルPATHの指定
source
parameterのvalueでのModuleのfiles
化のファイルの指定は若干特殊でpuppet:///modules
というprefixをつける必要がある。
具体的には、
puppet:///modules/<Module名>/<filesディレクトリからのファイルの相対Path>
となる。files
以下にサブディレクトリを用意することも可能である。
Templates - 動的ファイルの配備
file Resourceのcontent
parameterとtemplate
関数を組み合わせると、雛形ファイルの中身を配布時に変数を使って変更して配布することができる。
説明の都合上、files
を使った静的ファイルの配布から説明したが、設定ファイルの配備等ではほとんどの場合template
を使う。
具体的には、Module配下のtemplates
ディレクトリは雛形ファイルを置き、file
Resourceのcontent
parameterのvalueでtemplate関数を使い雛形ファイルの場所を指定することで行う。
以下、testmodule_temlate
を使った例を示す。
$ tree /etc/puppet/modules/testmodule_template/
/etc/puppet/modules/testmodule_template/
|-- manifests
| `-- init.pp
`-- templates
`-- template1
雛形ファイルであるtemplate1の中は以下のようにしておく。
ファイル中の<%= @var1 %>
は雛形ファイルの特殊表記でvar1変数を参照した内容を入れるという意味である。
$ cat /etc/puppet/modules/testmodule_template/templates/template1
This template file test.
<%= @var1 %>
init.ppには以下のように記述して、雛形ファイルを指定する。
class testmodule_template (
$var1 = "default var1"
) {
file { '/tmp/file_from_testmodule_template':
ensure => file,
mode => '644',
owner => 'root',
group => 'root',
content => template('testmodule_template/template1'),
}
}
試しに以下のような宣言を用意して実行してみる。雛形ファイル中の変数も変更してみる。
class { 'testmodule_template':
var1 => 'set by class declare var1',
}
$ sudo puppet apply /tmp/puppet_and_me/testmodule_template.pp
Notice: Compiled catalog for vagrant-centos65 in environment production in 0.25 seconds
Notice: /Stage[main]/Testmodule_template/File[/tmp/file_from_testmodule_template]/content: content changed '{md5}60a685a6103212f8347d88031cbd7867' to '{md5}e8915467f3fbc34cb902d6bab4ef0609'
Notice: Finished catalog run in 0.04 seconds
$ cat /tmp/file_from_testmodule_template
This template file test.
set by class declare var1
template関数による雛形ファイルの指定
template関数の引数にはファイルPATHのようなものを書くが、絶対PATHで書いた場合と相対PATH指定で記述した場合でルックアップの挙動が変わる。
- 絶対PATHの場合: ファイルシステムの絶対PATHから雛形ファイルを探す。
- 相対PATHの場合: 1つ目の
/
(スラッシュまではModule名、1つ目のスラッシュより後ろは対応するModule名のtemplatesディレクトリ直下からの相対PATHとして雛形ファイルを探す。
content => template('/tmp/puppet_and_me/templates/template1'),
と書くと文字通り/tmp/puppet_and_me/templates/template1
が雛形ファイルとなり、
content => template('testmodule_template/template1'),
と書くと、/etc/puppet/modules/testmodule_template/templates/template1
が雛形ファイルとなる。
雛形ファイルの書式
雛形ファイルはRuby on Rails等で使われているERB記法で記述する。
ERBでは<% %>
というTag内部でほとんどのRuby言語の記載が可能である。
<%= @var %>
<% if @var -%>
var is <%= @var %>
<% end -%>
<% @array_var.each do |v| -%>
<%= v %>
<% end -%>
等がよく使われる。
というか、ERBのタグ内では上記パターン以外の複雑な事をしないほうが良い。
極力雛形ファイルをシンプルかつ実際に適用された際の内容が想像しやすくしないと再利用性が下がる。
詳細なERB表記に関しては、PuppetLabs - Docs: Using Puppet Templatesを参照されたい。
Variable
Puppetでは、通常のプログラミング言語のように変数を使うことができる。
変数の参照、代入部ともに文字列の先頭に$
(ドルマーク)をつけることでその文字列が変数として認識される。
$var = "this is a pen"
ただし、Parameterized Classの宣言部分({}で囲まれる内部)でのparameter代入式の左辺のparameter名には$
はつけない点に注意されたい。
class param_class (
$param_var = '',
) {
file { '/tmp/param_class':
ensure => file,
content => inline_template($param_var),
}
}
$var = "This is a pen"
class { 'param_class':
param_var => $var,
}
また、Templateファイル外で定義された変数にアクセスする際は、<%= @var %>
のように変数名の先頭に@をつける。
Array, Map
整数値や文字列等のprimitiveな型以外にも配列(array)や連想配列(map)型も使うことができる。
$array_var = ["apple", "banana"]
$var = ${array_var[0]} # apple
$map_var = {"name" => "takuechikzm", "address" => "Japan"}
$var = ${map_var['name']} # takekuchikzm
主にarrayやmapはTemplateファイルのERBに対しての値渡しに使う場合が多い。
Facter変数
Puppetでは特に自分で指定しなくても、自動的に設定される変数が存在する。それらの変数はfacter変数(もしくはfacter)と呼ばれる。
例えば
- hostname: その環境のホスト名が代入される。
- virtual: 仮想マシンの場合
kvm
等のハイパーバイザ名が、物理マシンの場合physical
が代入される。 - operatingsystem: OSの種類(RedHat, CentOs, Debian等)が代入される。
等がある。
現在の環境で利用可能なfacter変数は、
$ facter
と実行することで確認できる。
Puppetマニフェスト内でfacter変数を参照する場合は、$::operatingsystem
のように$と変数名の間に::
を入れることが推奨される。
(これはローカルスコープのfacterと同名の変数を参照しないようにするため。)
変数のスコープ
classから特にスコープを指定せずに変数を参照した場合の、変数名の解決は以下の順に行われる。
-
Localスコープ(同一class定義内)からの解決
-
親クラススコープからの解決
- (あまり使わないので詳しく説明しないがpuppetではクラスの継承をしてクラスの共通部分の記述を削減することができる。)
-
Nodeスコープからの解決
-
Topスコープからの解決
classで定義されいてる変数に関しては、Puppetマニフェスト中では<class名>::<変数名>
というフルネームで参照することもできる。
class other_class (
$other_param = "other class var",
) {
}
include other_class
file { '/tmp/other_class_var':
ensure => file,
content => "$other_class::other_param",
}
この場合、/tmp/other_class_var
の内容はother class var
になる。
また、Resource定義と違い、変数は同一スコープ内では先に変数定義が無いとならないので注意。
(これは、状態記述言語であるPuppetとしてはイマイチな制約であるが、まだ残存してしまっている問題とのこと。)
Puppetマニフェストでは同一スコープ内で同一変数に再代入することができない点にも注意が必要である。
$var = "first file content"
file { '/tmp/first_file':
ensure => file,
content => "$var",
}
$var = "second file content"
file { '/tmp/second_file':
ensure => file,
content => "$var"
}
#上記を実行すると、Error: Cannot reassign variable in .. というエラーが出る。
変数の使い方のコツ
変数はとても便利なのであるが使い方を間違えると動かすとどのような構成になるのかがわからない恐怖のマニフェストが出来上がるため注意が必要である。
万人が読んでわかりやすいマニフェストを書く際は以下に留意すると良い。
-
Nodeスコープの変数をNodeスコープ以外から使わない。
-
スコープを指定しない変数参照の場合、必ずローカルスコープからの参照となるようにする。
- 同一モジュール配下の変数管理用のクラスを継承する場合のみ親クラススコープから解決は可。
- トップスコープやfacter変数の参照の場合は必ず
$::top_scope_var
のようにトップ or facter変数からの参照とわかるようにする。
Puppetの学習に便利なリンク
-
- Puppet開発元のページ。
- 順を追って書かれており初学者が体系的な学習にお勧め。
- ぶっちゃけこのページはこのリンク先を圧縮したようなものである。
- このチュートリアル用のVMも無料ダウンロードできる。(ただし登録が必要)