我创建了Chef::Handler::Elasticsearch – 用于按时间顺序可视化Chef报告的工具

这篇文章距离最后更新已经过去了一年以上的时间了。请小心。

继之前将Ohai的数据放入Elasticsearch并在Kibana中进行配置管理之后,我们将尝试使用类似logstash的工具,将Chef的活动报告按时间顺序进行可视化。

不是日志而是报告吗?

由于Chef也生成日志文件,因此您可能会认为可以通过fluentd或logstash的tail插件来收集日志。
然而,Chef的日志是连续流式的,不像Apache HTTP服务器访问日志等每行都是完整的,因此直接传输可能很难使用。
相比之下,报告以事件为单位,因此收集它们是有意义的。

厨师::处理器::Elasticsearch

这里是 Cookbook。

higanworks-cookbooks/chef_handler_elasticsearch:新经厨师,处理程序 Elasticsearch。

以下是默认行为。

    • index template chef_handler_templateが無かったら作成する、対象はchef_handler-*

logstash風にするため@timestampを付与
Chefの成功レポートは/chef_handler-YYYY.MM.DD/success/ID にPUT
Chefの例外レポートは/chef_handler-YYYY.MM.DD/failure/ID にPUT
PUTのタイムアウトは3秒

似乎为Enterprise Chef准备了ID,使用生成的唯一ID来执行Chef命令(可在DEBUG日志中确认)。由于此处理程序还将其输出至常规日志中,因此可以与事件相关联并进行引用。

以下是处理程序部分的源代码(v1.0.0版本)。

# Cookbook Name:: chef_handler_elasticsearch
# Library:: default
#
# Copyright 2014, HiganWorks LLC.
#

class Chef::Handler::Elasticsearch < ::Chef::Handler
  require 'timeout'
  attr_reader :opts, :config

  def initialize(opts = {})
    @config = {}
    @default = {
      url: 'http://localhost:9200',
      timeout: 3,
      prefix: 'chef_handler',
      prepare_template: true,
      template_order: 10,
      index_use_utc: true,
      index_date_format: "%Y.%m.%d",
      mappings: default_mapping
    }
    @opts = opts
    @opts
  end

  def default_mapping
'{
"_default_" : {
"numeric_detection" : true,
"dynamic_date_formats" : ["yyyy-MM-dd HH:mm:ss Z", "date_optional_time"]
}
}'
  end

  def report
    @default.merge!(node[:chef_handler_elasticsearch].symbolize_keys) if node[:chef_handler_elasticsearch]
    @config= @default.merge(@opts)
    Chef::Log.debug @config.to_s

    client = Chef::HTTP.new(@config[:url])
    index = "/#{@config[:prefix]}-#{build_logstash_date(data)}"

    if exception
      type = 'failure'
    else
      type = 'success'
    end

    prepare_template(client) if @config[:prepare_template]

    body = data.merge({'@timestamp' => Time.at(data[:end_time]).to_datetime.to_s})

    Chef::Log.debug "===== Puts to es following..."
    Chef::Log.debug body.to_s

    begin
      res = timeout(@config[:timeout]) {
        client.put([index, type, Chef::RequestID.instance.request_id].join('/'), body.to_json)
      }
      Chef::Log.debug "===== Response from es following..."
      Chef::Log.debug res.to_s
      Chef::Log.info "== Chef::Handler::Elasticsearch request_id: #{JSON.parse(res)['_id']}"
    rescue => e
      Chef::Log.error "== #{e.class}: Status report could not put to Elasticsearch."
    end
  end

  def build_logstash_date(data)
    if config[:index_use_utc]
      Time.at(data[:end_time]).getutc.strftime(@config[:index_date_format])
    else
      Time.at(data[:end_time]).strftime(@config[:index_date_format])
    end
  end

  def prepare_template(client)
    begin
      res = timeout(@config[:timeout]) {
        client.get("/_template/#{@config[:prefix]}_template")
      }
    rescue Net::HTTPServerException
      put_template(client)
      return
    rescue => e
      Chef::Log.error "== #{e.class}: Status report could not put to Elasticsearch."
      raise e.class, e.message
    end

    unless JSON.parse(@config[:mappings]) == JSON.parse(res)["#{@config[:prefix]}_template"]["mappings"]
      put_template(client)
    end
  end

  def put_template(client)
    begin
      Chef::Log.info "== create mapping template to Elasticsearch."
      res = timeout(@config[:timeout]) {
        client.put("/_template/#{@config[:prefix]}_template", build_template_body)
      }
    rescue => e
      Chef::Log.warn "== #{e.class}: mapping template could not put to Elasticsearch. Exiting..."
      raise e.class, e.message
    end
  end

  def build_template_body
    body = Hash.new
    body["template"] = "#{@config[:prefix]}-*"
    body["order"] = @config[:template_order]
    body["mappings"] = JSON.parse(@config[:mappings])
    Chef::Log.debug "===== Template for index following..."
    Chef::Log.debug body.to_json
    body.to_json
  end
end

使用方法

可以使用以下任何一种方式。请在属性或初始化时进行设置更改。

将recipe[chef_handler_elasticsearch::default]添加到run_list中。

在这种情况下,可以通过属性改写设置。

在自己的Chef::Config[:report_handlers]和Chef::Config[:exception_handlers]中添加自定义选项。

在我们自己的Cookbook中,可以通过使用recipe或者library来实现以下操作。
在metadata中需要加入depends ‘chef_handler_elasticsearch’。

厨师::配置[:报告处理程序] << 厨师::处理程序::Elasticsearch.new

要覆盖默认设置,可以在初始化时传递选项信息。

Chef::Config[:report_handlers] << Chef::Handler::Elasticsearch.new(
  url: 'http://test.example.com:9200',
  timeout: 10,
)

在自己的Cookbook或Role中使用优先级较高的属性。

override[:chef_handler_elasticsearch][:url] = 'http://test.example.com:9200'
override[:chef_handler_elasticsearch][:timeout] = 10

可以在solo.rb或client.rb中进行指定。

如果你不喜欢将整个 Cookbook 安装到环境中,或者不想增加额外的属性,你可以将处理程序的源代码复制到适当的位置,并进行 require。

require 'path_to_lib/chef_handler_elasticsearch.rb'

Chef::Config[:report_handlers] << Chef::Handler::Elasticsearch.new(
  url: 'http://test.example.com:9200',
  timeout: 10,
)

执行处理程序

如果添加处理程序成功,则将报告发送到Elasticsearch。仅在第一次创建模板。

[2014-05-22T03:30:15+00:00] INFO: Chef Run complete in 0.060295361 seconds  

Running handlers:       
[2014-05-22T03:30:15+00:00] INFO: Running report handlers       
[2014-05-22T03:30:15+00:00] INFO: HTTP Request Returned 404 Not Found:        
[2014-05-22T03:30:15+00:00] INFO: == create mapping template to Elasticsearch.       
[2014-05-22T03:30:19+00:00] INFO: == Chef::Handler::Elasticsearch request_id: 9368d16f-1f09-487e-9701-8acb001737fc       
  - Chef::Handler::Elasticsearch       
Running handlers complete       

在Kibana上进行显示调整

让我们先试试使用标准的 Logstash 仪表板显示。

可以在”Index Settings”中,将logstash的字符串更改为chef_handler。也可以是”_all”。
指定的索引名称可以在处理程序初始化时自行选择。

スクリーンショット_5_22_14__11_48_AM.png

好了,现在我们来看一下常见的时间序列数据吧。
不过,默认情况下成功和失败都没有区分,所以我们来举一个调整的例子。

スクリーンショット_5_22_14__12_34_PM.png

我试着将查询分成以下两个部分。

    • _type:success

 

    _type:failure
スクリーンショット_5_22_14__12_36_PM.png

颜色分离清晰。

学科

索引模板似乎仍有许多改进的空间。

广告
将在 10 秒后关闭
bannerAds