1. 程式人生 > >saltstack結合Elasticsearch來做salt運行結果展現

saltstack結合Elasticsearch來做salt運行結果展現

gedit 畫的 sys ide data lap producer factor esc

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運行結果展現