本シリーズの目次
- 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)
Resource - Puppetによる構成管理の最小単位
Puppetでは構成管理する対象をResourceと呼ぶ。
Resourceの種類(Resource Type)としては、
- file(ファイル、ディレクトリ、シンボリックリンク)
- user(rootや一般ユーザアカウントなどのUID情報)
- service(OSが管理している初期起動サービス候補)
等様々なものがある。(Docs: Type Reference)
puppet resource による現行システムのリソース定義の確認
puppetがインストールされたサーバでは、各リソースの現在の状態をpuppet resouce
コマンドで表示することが可能である。
$ sudo puppet resource file /etc/motd
file { '/etc/motd':
ensure => 'file',
content => '{md5}d41d8cd98f00b204e9800998ecf8427e',
ctime => 'Thu Jan 16 20:06:18 +0000 2014',
group => '0',
mode => '644',
mtime => 'Tue Jan 12 13:28:22 +0000 2010',
owner => '0',
type => 'file',
}
$ sudo puppet resource user vagrant
user { 'vagrant':
ensure => 'present',
gid => '500',
groups => ['wheel'],
home => '/home/vagrant',
password => 'masked by takeuchikzm',
password_max_age => '99999',
password_min_age => '0',
shell => '/bin/bash',
uid => '500',
}
$ sudo puppet resource service crond
service { 'crond':
ensure => 'running',
enable => 'true',
}
なお、Resource定義の各部分には以下の名称がある。
-
Resource Type:
service
,user
,file
等の{
の前に来る部分。 -
title: そのResourceのタイトル。全てのmanifest中で一意である必要がある。
なおtitleがResource適用時のパラメタとして使われる場合がある。
(たとえば、user
Resourceのtitleは作成するユーザ名として使われる。) -
parameter: 定義中の
ensure
やuid
等。Resource定義の反映時に必要となる各種情報を渡すのに使われる。Resource毎に必須のparameterとそうでないものがある。 -
value: Resource定義中の
=>
から,
の間の値。=>
の左辺のparameterに設定される。
特にResource TypeとParameterで同じ名前のものがあることが多いので正しく言い訳られるようになっておくと良い。
puppet apply によるResource定義の反映
前節では現在のシステムのResource定義を表示したが、逆にあるべきResource定義を記述し、puppet apply
コマンドを実行することでシステムの状態を変化させることができる。
例として、testuserというuser Resource定義を作成し、適用してみる。
まず、以下のようなtestuserのリソース定義もつファイルを作成する。
user { 'testuser':
ensure => 'present',
gid => 'puppet',
groups => ['wheel'],
home => '/home/testuser',
password => '!!',
password_max_age => '99999',
password_min_age => '0',
shell => '/bin/bash',
uid => '1500',
managehome => true,
}
(なお、Resource定義を書いたファイルのことをPuppetではマニフェストと呼ぶ。)
次に、puppet apply
コマンドによりマニフェストをシステムに状態を反映する。
$ sudo puppet apply testuser.pp
Notice: Compiled catalog for vagrant-centos65 in environment production in 0.61 seconds
Notice: /Stage[main]/Main/User[testuser]/ensure: created
Notice: Finished catalog run in 0.22 seconds
これでtestuserがpuppetにより作成された。idコマンドで確認してみるとちゃんとユーザができていることが確認できる。
$ id testuser
uid=1500(testuser) gid=52(puppet) groups=52(puppet),10(wheel)
Resourceのもっと細かい話
さて、ここで反映用のResource定義中にmangehome => true,
という見慣れない行があることに気がついただろうか。
実は、puppet resource
が出力以外に様々なパラメータを指定して、Resource定義をシステムに反映する際の挙動を調整することが可能である。
(むしろ、puppet resource
定義の出力はあくまで参考程度としたほうが良い。)
このmanagehome
はuser
Resource特有のparameterであり、ユーザ作成時やユーザ削除時にそのユーザのホームディレクトリの作成や削除をすべきかどうかを制御するものである。
puppetのResource Typeについての詳細は、puppet doc
コマンドもしくはPuppet公式のDocs: Type Referenceで詳細を確認できる。
システムとはすなわちResourceの集合である
勘の良い方は気づいたと思うが、Puppetによるシステムの構成管理とは前節のようなResource定義を多数持つマニフェストを記述し、システム全体のあるべき状態を定義することである。
システム全体のあるべき状態を定義することである。
もちろん、Resourceをひたすら1つのファイルに列記するのではあんまりなので、それらを意味的にmoduleやClass等に分割して管理する事がPuppetでは可能である。
Ordering - Resourceの適用順序
Resourceをひたすら記述していく前に、Puppetの思想とResourceの適用順序について述べておく必要がある。
Puppetとはあるべき状態を記述するものであるため、マニフェスト中の複数のResourceがどのような順序で適用されるかは基本的には不定である。
構成管理をしていると、
- 全てのパッケージインストールを実行するまえに、yumのリポジトリ設定をしたい。
-
httpd
パッケージをインストールする前に、nobody
ユーザを特定のUIDで作っておきたい。 -
/etc/httpd/conf/httpd.conf
を編集したら、httpd
サービスを再起動したい。
のように順序関係を定義したいことが良く出てくる。
このような場合、Puppetではリソースの適用順序を明確に記述する必要がある。
Puppetを使い始めた頃は「何でよ?書いた順番に適用してよ」と思ったのだが、もし書いたとおりの順番で適用が進むとしたら、それは暗黙に順序関係を定義しているに過ぎないのだ、と最近は思えるようになった。
むしろ、書かれたとおりに実行していたのでタマタマうまく行っていたということが無くなるため、順序の意図が明確に記述されることになりマニフェストの設計意図が汲み易くなるのだ。
Resource参照による順序関係の定義
Resource定義間の順序関係はbefore
,require
パラメータで指定できる。
見るは言うより易し。
group { 'testgroup':
ensure => 'present',
gid => '1500',
}
user { 'testuser':
ensure => 'present',
gid => 'testgroup',
home => '/home/testuser',
shell => '/bin/bash',
uid => '1500',
require => Group['testgroup']
}
上記のように指定するとtestgroupのResourceがtestuserのResourceより先に適用されるようになる。
group { 'testgroup':
ensure => 'present',
gid => '1500',
before => User['testuser'],
}
user { 'testuser':
ensure => 'present',
gid => 'testgroup',
home => '/home/testuser',
shell => '/bin/bash',
uid => '1500',
}
この書き方でも意味は同じである。
なお、複数のResourceに対して順序関係をつけたいときは以下のように書く。
before => [User['testuser'],File['testfile']]
なお、Resource参照Rは先頭を大文字にしたResourceType名に続く四角カッコ内にtitleを入れることで行う。(ex, User['testuser']
、File['testfile']
)
arrowオペレーター
Resource定義の外部でarrowオペレータを使って順序関係を定義することもできる。
Group['testgroup'] -> User['testuser']
arrowオペレータを使うと、Resourceの順序関係を各Resource定義とは別のファイルに記述することができるため、複数のファイルに分かれたResourceの定義を後から調整する際などの威力を発揮する。
また、arrowオペレータを使うResource collecterという特殊記法を使うことで、複数のResource間の関係を効率的に記述することも可能である。
Resourceのリフレッシュ動作の実行
「/etc/httpd/conf/httpd.conf
を編集したら、httpd
サービスを再起動したい。」という例を先に述べたが、リフレッシュ動作をすることが可能な以下のようなResourceTypeが存在する。
- service: 各種デーモンなどの起動サービスの状態を管理
- exec: 任意のコマンドを実行
- mount: ファイルシステムのマウント状態を管理
これらのサービスはnotify
やsubscribe
パラメータを用いることで別のResourceに変化が発生したらリフレッシュ動作をさせるように関係を持たせることができる。
service { 'httpd':
ensure => 'running',
enable => 'true',
subscribe => File['/etc/httpd/conf/httpd.conf']
}
file { '/etc/httpd/conf/httpd.conf':
ensure => 'file',
mode => '644',
owner => 'root',
group => 'root',
}
上記のようにしておくと、もし/etc/httpd/conf/httpd.conf
のmode
、owner
、group
に変更が起きるとhttpdサービスが再起動されるようになる。
自動的な適用順序付け
ある程度暗黙的に適用順序が自動的に付与されるパターンが存在する。
たとえば、入れ子関係にあるディレクトリ/ファイルのfile Resourceが存在する場合、必ず上側のディレクトリのfile Resourceが先に適用される。
自動的な順序付けはfile Resourceの例の以外にもあるらしいが、ユーザが細かく意識しないとならないような複雑だったり副作用があるようなもの無いらしい。
どうしてもマニフェスト記述順序どおりに実行したい人は..
ordering = manifest
とPuppet設定ファイルpuppet.conf
に記述すると、明確に順序関係を書いていない部分は必ずマニフェスト記述順に実行される。
Class - 複数Resourceをまとめて管理
Puppetでは、Resourceを複数まとめてClassとして管理ができる。
たとえば、先に書いたGroup['testgroup']とUser['testuser']リソースをClass['testaccount']にまとめて呼び出す際の記法は以下となる。
class testaccount {
group { 'testgroup':
ensure => 'present',
gid => '1500',
before => User['testuser'],
}
user { 'testuser':
ensure => 'present',
gid => 'testgroup',
home => '/home/testuser',
shell => '/bin/bash',
uid => '1500',
}
}
include testaccount
一点、class定義をするだけでは、Class内のResource定義は有効とならない点に注意が必要である。
include testaccout
もしくはclass { 'testaccout': }
と記述してClassを**宣言(declare)**して始めてClassに書かれたResource定義が適用される。
今後、定義と宣言を明確に区別して書くので覚えよう。
Parameterized Class
Class定義を変数を取るように設計し、Classの宣言時にその変数を渡すことでClassの振る舞いを変更させることができる。
このようにすることで、Class定義時に決定できない部分をClass宣言時に渡すことができるようになるため、Classの再利用性を高めることができる。
class testdirectory (
$testdir = '/tmp/testdir',
){
file { $testdir:
ensure => directory,
mode => '755',
owner => 'root',
group => 'root',
}
}
class { 'testdirectory':
testdir => '/tmp/testdir2',
}
上記の例では、$testdir
という変数(デフォルト値は/tmp/testdir
)を取れるようにtestdirectory
Classを設計している。
この例では宣言時に$testdir
を設定することで、/tmp/testdir2
というディレクトリを作成している。
もし、デフォルト値をそのまま使うのであれば、include testdirectory
と書くこともできる。
ClassとDefined Type
ここで、先ほどのParameterized Classの利用例の最後に以下を足して実行してみよう。
class { 'testdirectory':
testdir => '/tmp/testdir3',
}
こうすれば、/tmp/testdir2と/tmp/testdir3が両方作れるだろうか?残念ながらそうはならず、以下のようなエラーが帰るはずである。
$ sudo puppet apply testdirectory.pp
Error: Duplicate declaration: Class[Testdirectory] is already declared in file testdirectory,pp:13; cannot redeclare at /tmp/testdirectory,pp:16 on node vagrant-centos65
これは、Class Resourceのtitleであるtestdirectory
がマニフェスト中で重複してしまったためである。
Classはそもそもシステムで重複させたくないようなリソース定義をまとめる際に使うものなので、これはPuppetが間違えて重複して宣言されてしまったクラス宣言を検出してくれたとも言える。
同一マニフェストに何度も登場する似たようなResourceをまとめたい場合は、ClassではなくDefined Typeを使ってResourceをまとめるのがPuppe的に正しい方法である。
define definedirectory (
$testdir = '/tmp/testdir',
){
file { $testdir:
ensure => directory,
mode => '755',
owner => 'root',
group => 'root',
}
}
definedirectory { 'define_testdir2':
testdir => '/tmp/testdir2',
}
definedirectory { 'define_testdir3':
testdir => '/tmp/testdir3',
}
Defined TypeもClassと同様に定義しただけではResourceは有効にならず、宣言をして始めて有効になる。
Defined Typeの定義はClassとほぼ同じであるが、宣言の仕方が若干違う点に注意が必要である。
また、Defined Type宣言および定義内のResourceのtitleはマニフェスト中で一意でないとエラーとなる。
Node - サーバ単位でResource定義をまとめる
ここまでの例では、puppet apply に渡したマニフェストの中身は全て実行されていた。
マニフェスト中にnode定義をすると、node定義にマッチしたホストのみしか実行しないResourceを定義することができる。
ホスト名vagrant-centos65
用の設定とそれ以外のホストのものを分けて書く例を以下に記述する。
class testdirectory (
$testdir = '/tmp/testdir',
){
file { $testdir:
ensure => directory,
mode => '755',
owner => 'root',
group => 'root',
}
}
node 'vagrant-centos65' {
# このシングルクォートの中に自ホスト名を入れる
class { 'testdirectory':
testdir => '/tmp/testdir_mynode',
}
}
node default {
# クォート無しのdefaultは予約語でこれ以外のnode定義に該当しない全てのnodeに対応する。
class { 'testdirectory':
testdir => '/tmp/testdir_othernode',
}
}
ここで、vagrant-centos65
NodeとデフォルトNodeでtestdirectory
Classが重複しているが、node定義のvagrant-centos65
側のみしかマニフェストが読み込まれないため、先ほどのようなエラーは起きない。
node定義でのホスト名部分には正規表現を使うこともできる。なお、複数のnode定義がホスト名に適合した場合、以下の上位から優先される。
(ホスト名のFQDNがwww01.example.com
と仮定。)
- 正規表現ではなく、FQDNで一致するnode定義(ex.
node 'www01.example.com' {
) - 正規表現で、FQDNで一致するnode定義(ex.
node /^www01.example.com$/ {
) - 正規表現ではなく、FQDNの先頭側のサブドメインに一致するnode定義(ex.
node 'www01.example' {
) - 正規表現で、FQDNの先頭側のサブドメインに一致するnode定義(ex.
node /^www01.example$/ {
) - 正規表現ではなく、hostnameが一致するnode定義(ex.
node 'www01' {
) - 正規表現で、hostnameが一致するnode定義(ex.
node /^www01$/ {
) - クォート無しの
default
node定義(ex.node default {
)
詳しくはPuppetLabs - Docs: Language: Node Definitionsを見ると良い。
Puppetの動作モード
Puppetには、
- 構成管理をマスターサーバが一元的に管理するMaster/Agentモード
- ローカルのみで動作させるApplyモード
が存在する。
まずは、Puppetの基本的な使い方やコンセプトの理解を目的とするため、利用コストの低いApplyモードを前提とする。
(Applyモードでは、マスターサーバに全Agentの情報を集約して設定を決定するタイプのいくつかの機能が動作しない。)
Puppetの学習に便利なリンク
-
- Puppet開発元のページ。
- 順を追って書かれており初学者が体系的な学習にお勧め。
- ぶっちゃけこのページはこのリンク先を圧縮したようなものである。
- このチュートリアル用のVMも無料ダウンロードできる。(ただし登録が必要)