324232 发表于 2014-11-26 09:09:06

Puppet使用ENC报'Could not load external node results for'

这个问题出现有一段时间了,最开始的时候从一天3-5次左右到最近的一天出现10多次的告警邮件...
因为Puppet同步采取了主动触发和定时同步两种策略,几乎每次的报错都是在定时同步时出现...
Puppet Server采用双主结构,Web ui使用Foreman,为了确定这个报错是出现在那台服务器上, 通过对源代码的log增加主机标记最终定位到了这个错误只是出现在一台服务器上...,出现的很偶然,但所有的错误标记中,都是它....

1
2
3
4
Level   Resource    message
err PuppetCould 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
noticePuppetUsing cached catalog
err PuppetCould not retrieve catalog; skipping run





最后面的 :: --- false    其中::是在log中追加的分解符,方便区分, --- false 是返回的output的信息..

在Puppet源代码中 , 通过indirector与enc相关的find方法中可以看到这个find方法接受一个参数 request

1
2
3
4
5
6
7
8
9
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邮件告警的内容了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def translate(name, output)
    YAML.load(output).inject({}) do |hash, data|                                             
      case data                                                                           
      when String                                                                           
      hash.intern] = data                                                      
      when Symbol                                                                           
      hash] = data                                                            
      else                                                                                 
      raise Puppet::Error, "key is a #{data.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。。。。

1
2
3
# ruby node1.rb test
--- false
Error retrieving node test: Net::HTTPNotFound





分析node.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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,它如果能有一次重试的机会,那么下一次一定能正常获得返回值,然后我就给了它很多次的机会。。。

1
2
3
4
5
#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。


1
2
3
4
5
6
7
8
9
10
      # 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的异常..


页: [1]
查看完整版本: Puppet使用ENC报'Could not load external node results for'