前回、1.2 系と 1.3 系で変数の優先順位が変わったという記事を書きましたが、template 周りもちょっと変わってるみたいです。 1.2 では次のような loop 処理で、リストが未定義だった場合もエラーになることなく動作していたのですが、1.3.2 で動かしてみたらエラーになってしまいました。
{% for filter in iptables_input_filters %}
-A INPUT {{filter}}
{% endfor %}
ということで次のように対応しました。
{% if iptables_input_filters is defined %}
{% for filter in iptables_input_filters %}
-A INPUT {{filter}}
{% endfor %}
{% endif %}
それでは今日の本題です。
サーバーの情報集めて利用したい
Ansible は Server とか Agent が不要なのが売りなわけで、そこに惹かれて選んでいるのですが Chef Server とかにもちょっと憧れるわけです。情報を一元管理できたらいいなぁって。 そうしたら、なんだか setup モジュールが使えそうだったんですね。 https://github.com/ansible/ansible/blob/devel/library/system/setup EXAMPLES の中に次のような例がありました。
# Display facts from all hosts and store them indexed by I(hostname) at C(/tmp/facts).
ansible all -m setup --tree /tmp/facts
これで /tmp/facts にホスト名をファイル名にした JSON ファイルが収集されるんです。よし、じゃあこれを DB に入れちゃおう。(例では使われていませんが ansible に -s オプションをつけてあげると sudo を使うっていう意味で、root じゃないと取得できないデータがあるのでつけた方が良いです) Ansible の Fact って何?って場合はこちらをどうぞ http://yteraoka.github.io/ansible-tutorial/ansible-in-detail.html#gathering-facts そして、ohai とか factor の情報も取得できるみたいです。 Ansibleを支えるfact: プラットフォームの情報を取得
DB っていってもどれにいれようかな?
ってことで最初に試したのは PostgreSQL の JSON 型です。PostgreSQL には慣れてるし、9.3 で JSON 機能が強化されたみたいだしと、早速試したら… ん?エラーで入らない… どうやら 4kB 制限があるっぽい。 4kB 超えたらエラーで INSERT できない。 勘違いでした、別の環境でやり直したら INSERT できました なんだったんだろう?再現テストしてみよう。
ダメだったのは Linux Mint (Cinnamon) の gnome-terminal で JSON を psql でコピペした場合。Windows から PuTTY で同じ事をしたら問題なかった。gnome-terminal でもファイルにコピペして SQL ファイルとして psql -f したら問題なかった。
他に JSON 突っ込める DB といえば MongoDB!! (今日初めて触る) 今日(もう昨日か)もまた Twitter で dis られてた MongoDB、ガチで使うなら渋谷で人さらいしてこないとダメらしいけど、今回のようなゆるふわ利用なら問題ないんじゃないかなと。 EPEL から yum で入るので簡単に起動までも簡単。
$ sudo yum -y install mongodb mongodb-server
$ sudo /sbin/service mongod start
なんか mongoimport なるコマンドがあるのでこれ使えるのかな?
$ mongoimport -d ansible -c facts20131022 --type json --file /tmp/facts/hostname
exception:BSON representation of supplied JSON is too large: code FailedToParse: FailedToParse: Field name expected: offset:1
ダメポ… 1ドキュメントは1行にしないとダメらしい。 つーことで、こんな感じでまるっとインポート。
ansible -s -m setup -i hosts -t /tmp/facts all
for file in `ls /tmp/facts/*`
do
cat $file | tr -d "\n"
echo
done | mongoimport -d ansible -c facts$(date +%Y%m%d) --type json --file -
(最後の「-」が抜けてることに気づいたので追記 2013-10-24)
$ mongo
MongoDB shell version: 2.4.6
connecting to: test
> show dbs
ansible 0.203125GB
local 0.078125GB
> use ansible
switched to db ansible
> show collections
facts
facts20131022
system.indexes
> db.facts20131022.find().count()
21
インポートできたっぽい。コレクション名に日付を入れておいたので過去のも見れるし、不要ならまるっと削除しちゃおう。(update とか面倒) コレクションの削除はこんな感じ。
$ mongo
MongoDB shell version: 2.4.6
connecting to: test
> show dbs
ansible 0.203125GB
local 0.078125GB
> use ansible
switched to db ansible
> show collections
facts
facts20131022
system.indexes
> db.facts20131022.drop()
true
> show collections
facts
system.indexes
>
検索してみる。Xeon のサーバー一覧とOpteron のサーバー一覧
> db.facts20131022.find({"ansible_facts.ansible_processor": /Xeon/},{"_id":0,"ansible_facts.ansible_hostname":1})
{ "ansible_facts" : { "ansible_hostname" : "vm03" } }
{ "ansible_facts" : { "ansible_hostname" : "vm04" } }
{ "ansible_facts" : { "ansible_hostname" : "vm05" } }
> db.facts20131022.find({"ansible_facts.ansible_processor": /Opteron/},{"_id":0,"ansible_facts.ansible_hostname":1})
{ "ansible_facts" : { "ansible_hostname" : "vm01" } }
{ "ansible_facts" : { "ansible_hostname" : "vm02" } }
超やっつけですが、一応出来ました。 MAC アドレスからサーバーを探したり、「あの壊れたサーバーのシリアル/サービスタグってなんだっけ?」という場合にも使える。
カスタム Facts
「あの仮想ゲストはどのホストに載ってたっけなぁ?」を解決するために、VM ホストで /etc/ansible/facts.d/guests.fact
を次のようなスクリプトにして作成し、実行権限をつけておけば setup module が実行して収集してくれます。 動的でないものは JSON 書いたテキストファイルで OK。拡張子を .fact にする必要があるのと、ファイル名が JSON のキーになるという仕様。
$ cat /etc/ansible/facts.d/guests.fact
#!/bin/sh
guests=( $(virsh list --name) )
list=""
for name in ${guests[@]}
do
let i=i+1
list="${list}\"${name}\""
if [ $i -ne ${#guests[@]} ] ; then
list="${list}, "
fi
done
echo "[ $list ]"
こんな感じで収集されます。
{
"ansible_facts": {
"ansible_local": {
"guests": [
"guest1",
"guest2",
"guest3"
]
}
}
}
ホスト名に kibana を含む Guest の Host を探す
> db.facts20131022.find({"ansible_facts.ansible_local.guests": /kibana/},{"_id":0,"ansible_facts.ansible_hostname":1})
{ "ansible_facts" : { "ansible_hostname" : "vm05" } }
setup module の filter 機能
setup module は filter 機能もあるので、必要な情報だけに絞って取り出すこともできます。
$ ansible all -s -i hosts -m setup -a 'filter=ansible_product_*'
testdb1 | success >> {
"ansible_facts": {
"ansible_product_name": "KVM",
"ansible_product_serial": "NA",
"ansible_product_uuid": "CFB8DD36-FD8A-3FE5-D8AA-57B60878AD0A",
"ansible_product_version": "RHEL 6.3.0 PC"
},
"changed": false
}
MongoDB 以外の選択肢?
ElasticSearch って使えるのかな? CouchDB ってのも JSON らしいな。 Ansible のインベントリファイル(-i で指定するやつ)はテキストファイルでなくて、スクリプトなどを、指定してDBから引っ張って来させたりできるので、いろいろ発展の余地はありますね。