共计 4142 个字符,预计需要花费 11 分钟才能阅读完成。
这个问题出现有一段时间了,最开始的时候从一天 3 - 5 次左右到最近的一天出现 10 多次的告警邮件 …
因为 Puppet 同步采取了主动触发和定时同步两种策略,几乎每次的报错都是在定时同步时出现 …
Puppet Server 采用双主结构,Web ui 使用 Foreman,为了确定这个报错是出现在那台服务器上, 通过对源代码的 log 增加主机标记最终定位到了这个错误只是出现在一台服务器上 …,出现的很偶然,但所有的错误标记中,都是它 ….
Level Resource message
err Puppet Could not retrieve catalog from remote server: Error 400 on SERVER: Failed when searching for node xxx: 001。,Could not load external node results for xxx: undefined method `inject’ for false:FalseClass ::— false
notice Puppet Using cached catalog
err Puppet Could not retrieve catalog; skipping run
最后面的 :: — false 其中:: 是在 log 中追加的分解符,方便区分,— false 是返回的 output 的信息..
在 Puppet 源代码中,通过 indirector 与 enc 相关的 find 方法中可以看到这个 find 方法接受一个参数 request
indirector/node/exec.rb
def find(request)
output = super or return nil
# Translate the output to ruby.
result = translate(request.key, output)
create_node(request.key, result)
end
output 是调用父方法的 find
父方法的 find 会调用 enc 脚本获取返回值,如果失败或调用不成功则为 Nil..
这时会继续通过 translate 方法,将 yaml 输出转为 ruby 的对象
如果 output 为 nil,这时 yaml 在读取这个数据的时候就会抛出异常,异常就是收到的 Puppet 邮件告警的内容了。
def translate(name, output)
YAML.load(output).inject({}) do |hash, data|
case data[0]
when String
hash[data[0].intern] = data[1]
when Symbol
hash[data[0]] = data[1]
else
raise Puppet::Error, “key is a #{data[0].class}, not a string or symbol”
end
hash
end
rescue => detail
raise Puppet::Error, “001,Could not load external node results for #{name}: #{detail} ::#{output} “
end
罗嗦了一大堆,其实就是 node.rb 的脚本在通过 api 取参数的时候,没有获得 200… 导致的。
通过指向一个错误的 WEB 服务器地址,可以看到 开头 — false。。。。
[root@test puppet]# ruby node1.rb test
— false
Error retrieving node test: Net::HTTPNotFound
分析 node.rb
def enc(certname)
foreman_url = “#{url}/node/#{certname}?format=yml”
uri = URI.parse(foreman_url)
req = Net::HTTP::Get.new(uri.request_uri)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == ‘https’
if http.use_ssl?
if SETTINGS[:ssl_ca] && !SETTINGS[:ssl_ca].empty?
http.ca_file = SETTINGS[:ssl_ca]
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
else
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
if SETTINGS[:ssl_cert] && !SETTINGS[:ssl_cert].empty? && SETTINGS[:ssl_key] && !SETTINGS[:ssl_key].empty?
http.cert = OpenSSL::X509::Certificate.new(File.read(SETTINGS[:ssl_cert]))
http.key = OpenSSL::PKey::RSA.new(File.read(SETTINGS[:ssl_key]), nil)
end
end
res = http.start {|http| http.request(req) }
raise “Error retrieving node #{certname}: #{res.class}” unless res.code == “200”
res.body
end
脚本的前面都是在构造一个 http 的对象 …,直接看倒数第三行
可以清楚的看到一个判断,然后抛出异常,没有任何的重试机制 ….,为此我很确信我的 web,它如果能有一次重试的机会,那么下一次一定能正常获得返回值,然后我就给了它很多次的机会。。。
#raise “Error retrieving node #{certname}: #{res.class}” unless res.code == “200”
while res.code != “200”
res = http.start {|http| http.request(req) }
puts “Error retrieving node #{certname}: #{res.class}” sleep 3
end
这时有些人可能会想,while 循环,加 3 秒重试,,如果一直不成功怎么办?
在脚本最开头会有配置 timeout 的地方,在 timeout 到了之后,会关闭 http 连接,然后读取 cache。
# query External node
begin
result = “”
timeout(tsecs) do
result = enc(certname)
cache(certname, result)
end
rescue TimeoutError, SocketError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED
# Read from cache, we got some sort of an error.
result = read_cache(certname)
这段代码可以很清晰的看出,在 timeout 没超时时会调用 enc 这个方法返回结果,然后在调用 cache 方法写入到 cache 文件
如果超时或 http 错误,则读取 cache,但是这里的异常不包括 …,HTTP 的 …,如果如果是 4XX 的错误,不会触发读取 cache 的异常..
Puppet 学习系列:
Puppet 学习一:安装及简单实例应用 http://www.linuxidc.com/Linux/2013-08/88710.htm
Puppet 学习二: 简单模块配置和应用 http://www.linuxidc.com/Linux/2013-08/88711.htm
相关阅读:
有关 Puppet agent 端三种备份恢复方案探讨研究 http://www.linuxidc.com/Linux/2013-07/87885.htm
选择更安全的方式注册你的 Puppet 节点 http://www.linuxidc.com/Linux/2013-07/87884.htm
通过配置 SSH 深刻理解 Puppet 的语法及工作机制 http://www.linuxidc.com/Linux/2013-07/87882.htm
Puppet 利用 Nginx 多端口实现负载均衡 http://www.linuxidc.com/Linux/2013-02/79794.htm
CentOS(5 和 6)下 Puppet 的 C / S 模式实例 http://www.linuxidc.com/Linux/2011-12/50502.htm
Puppet 的详细介绍 :请点这里
Puppet 的下载地址 :请点这里