このエントリーをはてなブックマークに追加

ansibleのログをfluentdに流す

Chef のログを Fluentd に流すという記事を読みました。Ansibleにもそのための機能があるにも関わらず、あまり情報がないのでまとめてみました。

callback plugin

Ansibleにはmoduleとは別にpluginという機構があります。例えば"{{lookup('file', '/etc/foo.txt') }}"というように使うlookup pluginがあります。他にもconnectionやvarsのpluginがありますが、そのうちの一つに、callback pluginがあります。

callback pluginはその名の通り、いろいろなイベントが発生した時に自動的に呼び出されるcallbackを登録するためのpluginです。イベントの例としては、

  • playbookの開始時
  • taskの開始時
  • task成功時
  • task失敗時
  • playbook終了時

などがあります。

例: fluentdへと送るplugin

moduleは各種言語で実装できるansibleなのですが、pluginは残念ながら現状pythonでしか実装できません。

ということで、callback pluginを実装してみた結果が以下のコードです。callbackの関数を実装すればいいということが分かると思います。

ただ、登録できるハンドラが多すぎてここには一部だけしか載せていません。gistにあげておきましたので、そちらもご覧ください。どういう時に呼び出されるかは関数名から雰囲気で察してください。(このあたり公式にもドキュメントがないので、コードを読むしかないのです)

import json
import urllib
import urllib2

url = 'http://localhost:8888/ansible'

def post(category, data):
    data['category'] = category

    invocation = data.pop('invocation', None)
    if invocation:
        data['module_name'] = invocation['module_name']
        data['module_args'] = invocation['module_args']

    values = {'json': json.dumps(data)}

    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    urllib2.urlopen(req)

class CallbackModule(object):
    def on_any(self, *args, **kwargs):
        pass

    def runner_on_failed(self, host, res, ignore_errors=False):
        res["host"] = host
        post(host, 'FAILED', res)

    def runner_on_ok(self, host, res):
        res["host"] = host
        post('OK', res)

    def runner_on_error(self, host, msg):
        post('ERROR', {"host": host, 'error': msg})

    def runner_on_skipped(self, host, item=""):
        post('SKIPPED', {"host": host, "item": item})

これをin_http経由でfluentdに流し、out_fileで書きだした結果が以下の通りです。

2014-02-16T00:18:04+09:00    ansible {"category":"Play_on_start"}
2014-02-16T00:18:05+09:00    ansible {"category":"Play_start"}
2014-02-16T00:18:10+09:00    ansible {"category":"OK","changed":true,"end":"2014-02-1515:18:10.810734","stdout":"Sat Feb 15 15:18:10 UTC 2014","cmd":["date"],"rc":0,"start":"2014-02-1515:18:10.808307","host":"docker","stderr":"","delta":"0:00:00.002427","module_name":"command","module_args":"date"}
2014-02-16T00:18:11+09:00    ansible {"verbose_always":true,"category":"OK","host":"docker","msg":"Sat Feb 15 15:18:10 UTC 2014","module_name":"debug","module_args":"msg=\"Sat Feb 15 15:18:10 UTC 2014\""}

ちょっと分かりにくいですが、3行目のtaskの結果("OK")に、start(開始時刻)、end(終了時刻)、そしてdeltaという実行時間が入っています。ログに取るのであれば、deltaはかなり有用ではないでしょうか。もちろんstdoutという標準出力もとれています。

ちなみに実行したtaskは以下の通りです。

tasks:
  - name: get date
    command: date
    register: date
  - name: debug
    debug: msg="{{ date.stdout }}"

導入方法

callback pluginを使うには以下の3つの方法があります。

  1. inventoryファイルと同じ階層にcallback_pluginsというディレクトリを作成し、その中にpluginファイルを置く。

  2. デフォルトでは /usr/share/ansible/plugins 以下にある、callback_pluginの下に置く

  3. ansible.cfgでcallback_pluginを置いてあるディレクトリを指定する

    callback_plugins = /usr/share/ansible_plugins/callback_plugins
    

いずれも単にpythonファイルを置くだけで使えるようになります。逆に言うと、不用意に置くと勝手に実行されてしまう点には気をつけてください。

なお、複数のcallback pluginがディレクトリにあるとそれらすべてが実行されます。

まとめ

ansibleの実行結果を得るcallback pluginを紹介し、一例としてfluentdへと送るpluginを実装しました。

fluentdに限らず例えばfailした時にnagiosに飛ばしたりhipchatに飛ばしたりと、いろいろ役に立つと思います。