saltstack結合Elasticsearch來做salt運行結果展現
salt盡管好用可是機器管理的越來越多,通過cli的結果輸出方式查看運行結果越來越多不能滿足我的需求。並且作為一個推動運維自己主動化的攻城獅,使用這樣的人眼查看運行結果的方式簡直土到掉渣。盡管別人看起來逼格非常高。但誰累誰知道。。。因為以上原因,給各位推薦一種逼格更高的結果查看方式:
salt returners
先來看一下官方結構圖:
Send data returned by Salt Minions to another system, such as a database. Returners can run on the Salt Minion or on the Salt Master.
這個圖上畫的是redis,事實上官方給出的returners有非常多非常多。這次為了裝逼。哦不正確。為了更好地篩選和過濾salt的運行結果,我選用了elasticsearch+kibana這樣的展現方式,優點是es提供一個準實時的全文檢索引擎。這樣我們能夠實時的想搜什麽搜什麽,多維度過濾,而kibana提供的是便捷的用戶界面和帥的一逼的繪圖界面!
可是。這兩者是怎麽結合起來的呢,非常easy,用一個es支持salt也支持的數據中轉站即可了。我選擇的是kafka。當然你也能夠用rsyslog。redis,等等,任君選擇
以下就以 salt-minion -> kafka -> logstash -> elasticseach <- kibana <- 你的瀏覽器 這條主線進行說明,有紕漏的地方歡迎在評論裏指正,謝謝!
如今張成果圖鎮樓:
以下正時開始:
1. salt-minion端的配置:
1.1 改動minion配置文件,這對於會用salt的我們來說改1w臺機器就是分分鐘的事兒嘛。但註意。改完要重新啟動minion進程。
/etc/salt/minion:
#kafka broker主機,能夠配置多個 returner.kafka.hostnames: - "10.64.0.1" - "10.64.0.2" - "10.64.0.3" #kafka topic名稱 returner.kafka.topic: 'saltstack-topic'
1.2 改動官方returner文件源碼,此處高深莫測。先按下不表
2.kafka 差點兒不用配置。正常能用就可以。順便推薦一下kafka manager真好用。從此再也不用記命令了。
https://github.com/yahoo/kafka-manager
3.logstash 作為剛才minion發送到kafka數據的消費者,拿到kafka的數據後按json格式存到es中
3.1 logstash.conf:
input { kafka { #zk 連接串 zk_connect => '10.64.0.1:2181,10.64.0.2:2181,10.64.0.3:2181,10.64.1.174:2181,10.64.1.175:2181' #與剛才配置的topic一致 group_id => 'logstash-saltstack' topic_id => 'saltstack-topic' consumer_id => 'logstash-saltstack-consumer-{{ grains["id"] }}' consumer_threads => 1 queue_size => 200 codec => plain } } filter { mutate { add_field => { "fromAgent" => "logstash-saltstack-{{ grains["id"] }}" } } json { source => "message" } mutate { remove_field => [ "message" ] convert => { "fun_args" => "string" } rename => { "__run_num__" => "run_num" } } } output { elasticsearch { #輸出到es hosts => ["10.64.0.1:9200","10.64.0.2:9200","10.64.0.3:9200"] index => "saltstack-%{+YYYY.MM}" flush_size => 3000 idle_flush_time => 2 workers => 2 } #stdout { codec => rubydebug } }
這裏有個小坑,順便解釋一下剛才按下不表的為啥非要改源代碼:es收數據的時候會把json全都拆成嵌套的kv格式存儲,啥意思呢,請看圖:
這是兩個命令運行的不同返回,問題在哪兒呢?問題就在10.64.0.1這個key的value。一會兒是boolen型一會兒是nested型,這樣es就沒法辦了。一個字段的類型實在mapping或者第一次創建數據時確定的。不能隨便更改,這樣就造成了es不能接收數據的問題。
怎麽解決?開始我嘗試了改動logstash的配置,只是那個實在不靈活。並且你註意看第二個salt運行的結果,是在一個大json裏面的,這樣我搜索起來匹配上的話也是一整條數據。還是不方便我看結果。
針對以上兩點我還是認為小改一下salt的returners,改動的地方例如以下(每臺minion都要改。只是這次不用重新啟動了):
/usr/lib/python2.6/site-packages/salt/returners/kafka_return.py:
def returner(ret): ''' Return information to a Kafka server ''' if __salt__['config.option']('returner.kafka.topic'): topic = __salt__['config.option']('returner.kafka.topic') conn = _get_conn(ret) producer = SimpleProducer(conn) if ret["return"] == True or ret["return"] == False: producer.send_messages(topic, json.dumps(ret)) else: for retKey in ret["return"]: myRet = {} myRet["id"] = ret["id"] myRet["fun"] = ret["fun"] myRet["fun_args"] = ret["fun_args"] myRet["jid"] = ret["jid"] myRet["retcode"] = ret["retcode"] for key in ret["return"][retKey]: myRet[key] = ret["return"][retKey][key] retKeys = retKey.split("_|-") for i in range(0,len(retKeys)): myRet["key"+str(i)] = retKeys[i] producer.send_messages(topic, json.dumps(myRet)) _close_conn(conn) else: log.error('Unable to find kafka returner config option: topic')
至此我們就應該能在es裏看到每一個動作一條記錄的salt結果了。
4. es 還是沒啥可配置的,正常運轉就能夠
5. kibana
首先。我用的是kibana 3,這個須要es <2.0版本號的配合,不要趕新潮裝es2,這樣就用不了我的模板啦
5.1 加載kibana模板
把以下的內容存成一個文件,然後在kibana中選擇加載。然後趕緊跑個salt任務試試
神馬?沒有,是這樣。我忘了跟你說以後跑任務的時候要加個參數才幹在es裏看到哦:
salt 你要運行的命令 --return kafka
這樣你就得到了開始看到的那個激動人心的逼格界面。 我有啥沒說清楚的歡迎在以下評論裏寫明,我會盡力解答。謝謝!
{ "title": "Saltstack returns", "services": { "query": { "idQueue": [ 1 ], "list": { "0": { "query": "result:\"true\"", "alias": "成功", "color": "#7EB26D", "id": 0, "pin": false, "type": "lucene", "enable": true }, "4": { "id": 4, "color": "#E24D42", "alias": "失敗", "pin": false, "type": "lucene", "enable": true, "query": "result:\"false\"" } }, "ids": [ 0, 4 ] }, "filter": { "idQueue": [ 1 ], "list": { "0": { "type": "time", "field": "@timestamp", "from": "now-1h", "to": "now", "mandate": "must", "active": true, "alias": "", "id": 0 } }, "ids": [ 0 ] } }, "rows": [ { "title": "分類統計", "height": "367px", "editable": true, "collapse": true, "collapsable": true, "panels": [ { "error": false, "span": 4, "editable": true, "type": "terms", "loadingEditor": false, "field": "type", "exclude": [], "missing": false, "other": false, "size": 50, "order": "count", "style": { "font-size": "10pt" }, "donut": false, "tilt": false, "labels": true, "arrangement": "horizontal", "chart": "table", "counter_pos": "below", "spyable": true, "queries": { "mode": "all", "ids": [ 0, 1, 2 ] }, "tmode": "terms", "tstat": "total", "valuefield": "", "title": "type" }, { "error": false, "span": 4, "editable": true, "type": "terms", "loadingEditor": false, "field": "host.raw", "exclude": [], "missing": false, "other": false, "size": 11, "order": "count", "style": { "font-size": "10pt" }, "donut": false, "tilt": false, "labels": true, "arrangement": "horizontal", "chart": "table", "counter_pos": "none", "spyable": true, "queries": { "mode": "all", "ids": [ 0, 1, 2 ] }, "tmode": "terms", "tstat": "total", "valuefield": "", "title": "host" }, { "error": false, "span": 4, "editable": true, "type": "terms", "loadingEditor": false, "field": "tags.raw", "exclude": [], "missing": false, "other": false, "size": 50, "order": "count", "style": { "font-size": "10pt" }, "donut": false, "tilt": false, "labels": true, "arrangement": "horizontal", "chart": "pie", "counter_pos": "none", "spyable": true, "queries": { "mode": "all", "ids": [ 0, 1, 2 ] }, "tmode": "terms", "tstat": "total", "valuefield": "", "title": "tags" } ], "notice": false }, { "title": "Graph", "height": "150px", "editable": true, "collapse": false, "collapsable": true, "panels": [ { "span": 12, "editable": true, "group": [ "default" ], "type": "histogram", "mode": "count", "time_field": "@timestamp", "value_field": null, "auto_int": true, "resolution": 200, "interval": "30s", "fill": 3, "linewidth": 3, "timezone": "browser", "spyable": true, "zoomlinks": true, "bars": true, "stack": true, "points": false, "lines": false, "legend": true, "x-axis": true, "y-axis": true, "percentage": false, "interactive": true, "queries": { "mode": "all", "ids": [ 0, 4 ] }, "title": "Events over time", "intervals": [ "auto", "1s", "1m", "5m", "10m", "30m", "1h", "3h", "12h", "1d", "1w", "1M", "1y" ], "options": true, "tooltip": { "value_type": "cumulative", "query_as_alias": true }, "annotate": { "enable": false, "query": "*", "size": 20, "field": "_type", "sort": [ "_score", "desc" ] }, "pointradius": 5, "show_query": true, "legend_counts": true, "zerofill": true, "derivative": false, "scale": 1, "grid": { "max": null, "min": 0 }, "y_format": "none" } ], "notice": false }, { "title": "Events", "height": "350px", "editable": true, "collapse": false, "collapsable": true, "panels": [ { "title": "All events", "error": false, "span": 12, "editable": true, "group": [ "default" ], "type": "table", "size": 100, "pages": 5, "offset": 0, "sort": [ "@timestamp", "desc" ], "style": { "font-size": "9pt" }, "overflow": "min-height", "fields": [ "@timestamp", "id", "fun", "fun_args", "key0", "key1", "key2", "key3", "result" ], "highlight": [], "sortable": true, "header": true, "paging": true, "spyable": true, "queries": { "mode": "all", "ids": [ 0, 4 ] }, "field_list": true, "status": "Stable", "trimFactor": 300, "normTimes": true, "all_fields": false, "localTime": true, "timeField": "@timestamp" } ], "notice": false } ], "editable": true, "failover": false, "index": { "interval": "month", "pattern": "[saltstack-]YYYY.MM", "default": "NO_TIME_FILTER_OR_INDEX_PATTERN_NOT_MATCHED", "warm_fields": true }, "style": "dark", "panel_hints": true, "pulldowns": [ { "type": "query", "collapse": true, "notice": false, "query": "*", "pinned": true, "remember": 10, "enable": true }, { "type": "filtering", "collapse": true, "notice": false, "enable": true } ], "nav": [ { "type": "timepicker", "collapse": false, "notice": false, "status": "Stable", "time_options": [ "5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d" ], "refresh_intervals": [ "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d" ], "timefield": "@timestamp", "now": true, "filter_id": 0, "enable": true } ], "loader": { "save_gist": false, "save_elasticsearch": true, "save_local": true, "save_default": true, "save_temp": true, "save_temp_ttl_enable": true, "save_temp_ttl": "30d", "load_gist": true, "load_elasticsearch": true, "load_elasticsearch_size": 20, "load_local": true, "hide": false }, "refresh": false }
saltstack結合Elasticsearch來做salt運行結果展現