そこはかとなく書くよん。

Python、Sphinx、Mercurial、PostgreSQL、翻訳、などをだらだらと

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を登録すればいいということが分かると思います。

登録できるハンドラが多すぎてここには一部だけしか載せていません。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という実行時間が入ってるのがいいですね。もちろんstdoutという標準出力もとれています。

ちなみに実行したtaskです。

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

導入方法

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

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

  3. ansible.cfgで指定する

    callback_plugins = /usr/share/ansible_plugins/callback_plugins
    

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

まとめ

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

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