chef-clientが処理の途中で403 Forbiddenエラーになる時の対処

Chef

認証に問題が無いのに404 Forbiddenになる

chef-clientを走らせていて、以下のようなエラーにたまに出くわします:

198.xx.xxx.xxx ================================================================================
198.xx.xxx.xxx Error executing action `create` on resource 'template[/var/www/mysite.com/shared/config/database.yml]'
198.xx.xxx.xxx ================================================================================
198.xx.xxx.xxx
198.xx.xxx.xxx Net::HTTPServerException
198.xx.xxx.xxx ------------------------
198.xx.xxx.xxx
198.xx.xxx.xxx 403 "Forbidden"
198.xx.xxx.xxx
198.xx.xxx.xxx
198.xx.xxx.xxx Resource Declaration:
198.xx.xxx.xxx
198.xx.xxx.xxx ---------------------
198.xx.xxx.xxx # In /var/chef/cache/cookbooks/rails/recipes/production.rb
198.xx.xxx.xxx
198.xx.xxx.xxx  40: template node[:rails][:app_root]+"/shared/config/database.yml" do
198.xx.xxx.xxx  41:     owner "root"
198.xx.xxx.xxx  42:     group "xx-dev"
198.xx.xxx.xxx  43:     mode 0775
198.xx.xxx.xxx  44: end
198.xx.xxx.xxx  45:
198.xx.xxx.xxx
198.xx.xxx.xxx Compiled Resource:
198.xx.xxx.xxx
198.xx.xxx.xxx ------------------
198.xx.xxx.xxx
198.xx.xxx.xxx # Declared in /var/chef/cache/cookbooks/rails/recipes/production.rb:40:in `from_file'
198.xx.xxx.xxx
198.xx.xxx.xxx template("/var/www/mysite.com/shared/config/database.yml") do
198.xx.xxx.xxx   provider Chef::Provider::Template
198.xx.xxx.xxx   action "create"
198.xx.xxx.xxx   retries 0
198.xx.xxx.xxx   retry_delay 2
198.xx.xxx.xxx   path "/var/www/mysite.com/shared/config/database.yml"
198.xx.xxx.xxx   backup 5
198.xx.xxx.xxx   source "database.yml.erb"
198.xx.xxx.xxx   cookbook_name "rails"
198.xx.xxx.xxx   recipe_name "production"
198.xx.xxx.xxx   mode 509
198.xx.xxx.xxx   owner "root"
198.xx.xxx.xxx   group "xx-dev"
198.xx.xxx.xxx end
198.xx.xxx.xxx
198.xx.xxx.xxx [2014-05-29T20:16:34+00:00] ERROR: Running exception handlers
198.xx.xxx.xxx [2014-05-29T20:16:34+00:00] FATAL: Saving node information to /var/chef/cache/failed-run-data.json
198.xx.xxx.xxx [2014-05-29T20:16:34+00:00] ERROR: Exception handlers complete
198.xx.xxx.xxx [2014-05-29T20:16:34+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
198.xx.xxx.xxx [2014-05-29T20:16:34+00:00] FATAL: Net::HTTPServerException: template[/var/www/mysite.com/shared/config/database.yml] (rails::production line 40) had an error: Net::HTTPServerException: 403 "Forbidden"

しかし、最初はうまく走っていたので、認証周りで何かおかしいとは考え難い現象です。

no_lazy_loadを指定してみる

もしあなたのchef-clientが、十数分とか長い時間動作しているとしたら、以下のフラグをclient.rbに追記すると改善するかもしれません:

no_lazy_load true

chef-clientは必要に応じてcookbook_filetemplateを取得します(lazy-load)。
しかし、長い時間走らせていると、認証がタイムアウトするようです。
これを防ぐため、no_lazy_load trueを指定することでlazy-loadを無効にします。
これによって、はじめに必要なデータをすべて取得するようになります。

参考

Make logrotate cookbook for Chef possible to work on Ruby1.8

There is a great cookbook posted on GitHub to configure logrotation with Chef though, it may work on Ruby 1.9 or greater. My server Ruby1.8 installed couldn’t run its recipes.
I fixed a part of the cookbook so that it can also work on Ruby1.8:

https://gist.github.com/noradaiko/5219337.js

Diff is as follows:

--- a/cookbooks/logrotate/libraries/logrotate_config.rb
+++ b/cookbooks/logrotate/libraries/logrotate_config.rb
@@ -28,36 +28,46 @@ module CookbookLogrotate
       end
 
       def directives_from hash
-        hash.select { |k, v| DIRECTIVES.include?(k) && v }.keys
+                 Hash[ hash.select { |k, v| DIRECTIVES.include?(k) && v } ].keys
       end
 
       def values_from hash
-        hash.select { |k| VALUES.include? k }
+                 Hash[ hash.select { |k, v|
+                                 VALUES.include? k
+                         }
+                 ]
       end
 
       def paths_from hash
-        hash.select { |k| !(DIRECTIVES_AND_VALUES.include? k) }.inject({}) do | accum_paths, (path, config) |
-          accum_paths[path] = {
-            'directives' => directives_from(config),
-            'values' => values_from(config),
-            'scripts' => scripts_from(config)
-          }
+               pp hash
+        Hash[ hash.select { |k| !(DIRECTIVES_AND_VALUES.include? k) } ].inject({}) do | accum_paths, (path, config) |
+                 pp "----"
+          if config.instance_of?(Mash)
+            accum_paths[path] = {
+              'directives' => directives_from(config),
+              'values' => values_from(config),
+              'scripts' => scripts_from(config)
+            }
 
-          accum_paths
+                       pp accum_paths[path]
+            accum_paths
+          else
+            accum_paths
+                 end
         end
       end
 
       def scripts_from hash
-        defined_scripts = hash.select { |k| SCRIPTS.include? k }
-        defined_scripts.inject({}) do | accum_scripts, (script, lines) |
-          if lines.respond_to? :join
-            accum_scripts[script] = lines.join "n"
-          else
-            accum_scripts[script] = lines
-          end
+          defined_scripts = hash.select { |k| SCRIPTS.include? k }
+          defined_scripts.inject({}) do | accum_scripts, (script, lines) |
+            if lines.respond_to? :join
+              accum_scripts[script] = lines.join "n"
+            else
+              accum_scripts[script] = lines
+            end
 
-          accum_scripts
-        end
+            accum_scripts
+          end
       end
     end

Hope it helps.