Puppet4.xではじめるサーバ設定自動化の、ファイルの穴埋めERB編です。
全体的な目次は、ここを参照してください。
主にCentOS 7を例に紹介しているので、その他OSについては読み替えてください。
Embedded Ruby (ERB) テンプレート
PuppetとERB形式のテンプレートファイルを組み合わせると、共通のテンプレートファイルで、サーバごとに設定ファイルを変えることができます。
まず、配布する設定ファイルのテンプレートに、サーバごとに変える部分をRubyコードを埋め込んでおきます。サーバに設定ファイルが配布される際に、Rubyの部分が処理されて設定ファイルが作られます。
ERBを使えば、テンプレートを共通にしつつも、次のことができるようになります。
- 特定のパラメータを変数にして、サーバごとに値を指定する。
- if文で特定のサーバのみだけの設定が記載できる。
- 配列、ハッシュをループさせて、行数の異なる設定ファイルが作れる。
ちなみにERB単独ではなく、テンプレートファイルに適切な変数を渡すのは、Puppet側で対応する必要があります。もしくはfacterでの値取得も使えますが、facterについては別の機会に紹介します。
ERBのタグ
テンプレートにRubyコードを埋め込むためのタグです。わかりにくいので、後で書くサンプルも参考にしてください。
<%= EXPRESSION %> : 変数の値を埋め込む
<%= EXPRESSION -%> : 変数の値を埋め込む。直後の改行を無視する
<% CODE %> : ifやループを書く場合に使用。
<%- CODE %> : ifやループを書く場合に使用。(タグの前の)行頭の空白文字を、ファイルに出力しない。インデントをそろえたいときに。
<% CODE -%> : ifやループを書く場合に使用。直後の改行を無視する
<%- CODE -%> : ifやループを書く場合に使用。行頭の空白文字、直後の改行をファイルに出力しない
<%# COMMENT %> : テンプレートファイルにコメントを書く。ファイルには出力しない
<%# COMMENT -%> : コメントを書きつつ、直後の改行を無視する
<%% : 設定ファイルに<%を出力したい場合、テンプレートファイルには<%%と書く
%%> : 設定ファイルに%>を出力したい場合、テンプレートファイルには%%>と書く
サンプル
サンプルの前に、サンプルのファイル構成と実行方法です。
puppetのマニフェストファイルとerb01.ppと、テンプレートファイルpuppet_erb_sample.txt.erbを次のようにおきます。
puppet_erb_sample.txt.erbについては、今回日本語(2バイト文字)を含んでいるので、UTF8で保存してください。また、実行するサーバの言語設定をきちんとしておかないと動かない場合があります。ふつう設定ファイルに日本語をあまり書かないので、とりあえず適当な英数字に書き換えて試してみるでもよいです。
.
|-- erb01.pp
`-- modules
`-- sample
`-- templates
`-- puppet_erb_sample.txt.erb
applyで実行する場合なので、オプションにmodulepathを指定します。
/opt/puppetlabs/bin/puppet apply erb01.pp --modulepath=modules
以降、サンプルです。サンプルでは、Puppetのマニフェストファイルで、ホスト名ごとに変数の値を設定しています。例では、ホスト名がfrontwebとそれ以外のサーバで、設定される値を変えています。ERB形式のテンプレートファイルから、設定する変数を使うには変数名の前に@をつけます。
変数にする
マニフェストファイル
$myparameter = $hostname ? {
"frontweb" => 100,
default => 20,
}
file { "/tmp/puppet_erb_sample.txt":
ensure => "file",
owner => "root",
group => "root",
mode => "0644",
content => template("sample/puppet_erb_sample.txt.erb"),
}
テンプレートファイル
# パラメータの置き換えの例です。タグの閉じ方で、直後の改行を無視するしないがあります
MaxClients <%= @myparameter %>
MaxClients <%= @myparameter %>
# comment
MaxClients <%= @myparameter -%>
# comment
実行結果例(ホスト名がfrontweb)
# パラメータの置き換えの例です。タグの閉じ方で、直後の改行を無視するしないがあります
MaxClients 100
MaxClients 100
# comment
MaxClients 100 # comment
実行結果例(ホスト名がfrontweb以外)
# パラメータの置き換えの例です。タグの閉じ方で、直後の改行を無視するしないがあります
MaxClients 20
MaxClients 20
# comment
MaxClients 20 # comment
ハッシュを渡す
マニフェストファイル
$myhash = $hostname ? {
"frontweb" => { "server_limit" => 100, "max_clients" => 100 },
default => { "server_limit" => 20, "max_clients" => 20 },
}
file { "/tmp/puppet_erb_sample.txt":
ensure => "file",
owner => "root",
group => "root",
mode => "0644",
content => template("sample/puppet_erb_sample.txt.erb"),
}
テンプレートファイル
# パラメータの置き換えの例です。ハッシュも使えます
ServerLimit <%= @myhash["server_limit"] %>
MaxClients <%= @myhash["max_clients"] %>
実行結果例(ホスト名がfrontweb)
# パラメータの置き換えの例です。ハッシュも使えます
ServerLimit 100
MaxClients 100
実行結果例(ホスト名がfrontweb以外)
# パラメータの置き換えの例です。ハッシュも使えます
ServerLimit 20
MaxClients 20
サーバごとに出力するしない
マニフェストファイル
$mycondition = $hostname ? {
"frontweb" => true,
default => false,
}
file { "/tmp/puppet_erb_sample.txt":
ensure => "file",
owner => "root",
group => "root",
mode => "0644",
content => template("sample/puppet_erb_sample.txt.erb"),
}
テンプレートファイル
# Rubyコードを埋め込む例です
# if sample 1
<% if @mycondition == true %>
if文を満たす場合、出力されます。タグの閉じ方でif,endの行で改行するパターン。
<% end %>
# if sample 2
<% if @mycondition == true -%>
if文を満たす場合、出力されます。タグの閉じ方でif,endの行で改行しないパターン。
<% end -%>
# if sample 3
<% if @mycondition == true %>1行でも書けます<% end %>
# if sample 4
<% if @mycondition == true -%>
if文を満たす場合、出力されます。
<% else -%>
if文を満たすさない場合、出力されます。
<% end -%>
実行結果例(ホスト名がfrontweb)
# Rubyコードを埋め込む例です
# if sample 1
if文を満たす場合、出力されます。タグの閉じ方でif,endの行で改行するパターン。
# if sample 2
if文を満たす場合、出力されます。タグの閉じ方でif,endの行で改行しないパターン。
# if sample 3
1行でも書けます
# if sample 4
if文を満たす場合、出力されます。
実行結果例(ホスト名がfrontweb以外)
# Rubyコードを埋め込む例です
# if sample 1
# if sample 2
# if sample 3
# if sample 4
if文を満たすさない場合、出力されます。
ループ
マニフェストファイル
$myarry = $hostname ? {
"frontweb" => ["192.168.10.0/24", "192.168.20.0/24", "192.168.30.0/24"],
default => ["192.168.1.0/24"],
}
file { "/tmp/puppet_erb_sample.txt":
ensure => "file",
owner => "root",
group => "root",
mode => "0644",
content => template("sample/puppet_erb_sample.txt.erb"),
}
テンプレートファイル
ちなみに、eachで配列の各要素でループしていますが、そのとき各要素の値をitemという名前の変数に入れています(名前はitemでなくてもよいです)。itemはテンプレート内で使っている変数なので、@をつけないのに注意してください。
# loop sample 1
# myarryの要素数分繰り返す例
<% @myarry.each do |item| -%>
allow from <%= item %>
<% end -%>
# loop sample 2
# myarryの要素数分繰り返す例(入れ子の場合もキレイに書きたい場合)
<Directory /usr/local/www/data>
order deny,allow
deny from all
# 読みにくい...
<% @myarry.each do |item| -%>
allow from <%= item %>
<% end -%>
# こうすると無駄な空白ができてしまう
<% @myarry.each do |item| -%>
allow from <%= item %>
<% end -%>
# こうすると無駄な空白ができない
<%- @myarry.each do |item| -%>
allow from <%= item %>
<%- end -%>
</Directory>
実行結果例(ホスト名がfrontweb)
# loop sample 1
# myarryの要素数分繰り返す例
allow from 192.168.10.0/24
allow from 192.168.20.0/24
allow from 192.168.30.0/24
# loop sample 2
# myarryの要素数分繰り返す例(入れ子の場合もキレイに書きたい場合)
<Directory /usr/local/www/data>
order deny,allow
deny from all
# 読みにくい...
allow from 192.168.10.0/24
allow from 192.168.20.0/24
allow from 192.168.30.0/24
# こうすると無駄な空白ができてしまう
allow from 192.168.10.0/24
allow from 192.168.20.0/24
allow from 192.168.30.0/24
# こうすると無駄な空白ができない
allow from 192.168.10.0/24
allow from 192.168.20.0/24
allow from 192.168.30.0/24
</Directory>
実行結果例(ホスト名がfrontweb以外)
# loop sample 1
# myarryの要素数分繰り返す例
allow from 192.168.1.0/24
# loop sample 2
# myarryの要素数分繰り返す例(入れ子の場合もキレイに書きたい場合)
<Directory /usr/local/www/data>
order deny,allow
deny from all
# 読みにくい...
allow from 192.168.1.0/24
# こうすると無駄な空白ができてしまう
allow from 192.168.1.0/24
# こうすると無駄な空白ができない
allow from 192.168.1.0/24
</Directory>
コメントとかエスケープ
マニフェストファイル
file { "/tmp/puppet_erb_sample.txt":
ensure => "file",
owner => "root",
group => "root",
mode => "0644",
content => template("sample/puppet_erb_sample.txt.erb"),
}
テンプレートファイル
# コメント例です。タグの閉じ方で、直後の改行を無視するしないがあります
# comment
<%# これはコメントです %>
直後の改行が有効
<%# これはコメントです -%>
直後に改行が無視される
# タグのエスケープ例です
<%%
%%>
<%= "<%%" %>
<%= "%%>" %>
実行結果例
エスケープについてはわかりにくですが、タグの外で書く場合と、タグの中で書く場合でエスケープされるされないがあります。
# コメント例です。タグの閉じ方で、直後の改行を無視するしないがあります
# comment
直後の改行が有効
直後に改行が無視される
# タグのエスケープ例です
<%
%%>
<%%
%>
穴埋めするべきか、穴埋めしないべきか
本題からそれた内容です。
ERBの動的ファイル作成はうまく使えば便利ですが、デメリットもあります。動的にファイルを作る場合の、メリットとデメリットは次の通りです。
メリット
- サーバ構成が動的に変わる場合でも対応できる(オートスケール、スケールアウトなど)
- 似たような設定ファイルを減らせるため、変更漏れが起きにくい
- サーバごとに変わる部分と、そうでない部分がきわだつ
デメリット
- 期待する設定が実際に入っているか確認(テスト)が必要
- ERBとPuppetを理解していないと、どんな設定がされているか分からない
- 設定変更が多い場合は、テンプレートを丸ごと入れ替えた方が楽な場合がある
- ifやループを埋め込みすぎると、(個人的に)かなり見づらい。
なので、バランスよく取り入れるくらいがよいかと。少なくとも、1つのテンプレートでどのサーバの設定もカバーするようなことはやらない方がよいです。まずは、サーバごとに配布するテンプレートを分けつつ、ここをサーバーごとに変更できれば、テンプレートが減らせそうだと思ったところから穴埋めを使っていくのがベターです。また、設定ファイルを他の人に用意してもらうような運用の場合、穴埋めは最小限にしないとメンテが大変です。
参考
fileリソースの詳しい情報は、下記参照です。
ERB形式のテンプレートファイルのシンタックスについては、下記参照です。
変数スコープとか、他のテンプレートを読み込むとか興味のある方はぜひ。
ERBについて日本語。Puppetでの文法と違う場合もあるかもしれないで、参考程度に。