我创建了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”。
指定的索引名称可以在处理程序初始化时自行选择。
好了,现在我们来看一下常见的时间序列数据吧。
不过,默认情况下成功和失败都没有区分,所以我们来举一个调整的例子。
我试着将查询分成以下两个部分。
-
- _type:success
- _type:failure
颜色分离清晰。
学科
索引模板似乎仍有许多改进的空间。