cookbook目录结构必须规范,否则chef-client在收敛阶段会因缺失metadata.rb或recipes/default.rb报NoMethodError;需确保metadata.rb、recipes/目录及.rb文件存在且权限正确,推荐用chef generate cookbook创建。

cookbook目录结构不规范,chef-client直接报错
新建的 cookbook 如果没按 Chef 要求组织目录,chef-client 在 converging 阶段会找不到 recipes/default.rb 或读取不到 metadata.rb,直接退出并提示 NoMethodError: undefined method `recipe' for nil:NilClass 这类模糊错误。
必须确保以下最小结构存在:
-
metadata.rb文件(哪怕只写name 'myapp'和version '0.1.0') -
recipes/目录下至少一个.rb文件(如default.rb) - 所有文件权限为普通用户可读(避免
chef-client因权限拒绝加载)
用 chef generate cookbook myapp 创建是最稳妥的方式;手动 mkdir + touch 容易漏掉 metadata.rb 或写错格式(比如用单引号包住版本号但没加空格)。
package资源在Ubuntu和CentOS上包名不一致
写一个部署 Redis 的 recipe,如果直接写 package 'redis',在 Ubuntu 上能装上 redis-server,但在 CentOS 上会失败——因为默认源里叫 redis 的包不存在,实际是 redis(RHEL 8+)或 redis(需启用 EPEL),而旧版 CentOS 7 是 redis。
正确做法是用平台判断分支:
case node['platform_family']
when 'debian'
package 'redis-server' do
action :install
end
when 'rhel', 'fedora'
package 'redis' do
action :install
end
end
更健壮的写法是配合 node['platform_version'] 细分 RHEL 大版本,或用 include_recipe 引入社区 cookbook(如 redisio),避免自己维护多平台逻辑。
template资源路径错配导致服务启动失败
中间件(如 Nginx、Tomcat)常需模板生成配置文件。常见错误是:template 指定了 source 'nginx.conf.erb',但没把 templates/default/nginx.conf.erb 文件放对位置,chef-client 不报错但静默跳过渲染,最终服务因配置缺失无法启动。
验证方式很简单:
- 检查
templates/default/下是否存在对应.erb文件 - 运行
chef-client --local-mode --log-level debug,搜索template[/etc/nginx/nginx.conf]日志行,确认是否显示up to date或updated content - 模板里不要硬编码绝对路径,用
node['nginx']['dir']这类 attribute 替代,方便跨环境复用
另外注意:ERB 模板中 Ruby 代码若抛异常(如调用未定义变量),chef-client 会中断整个 recipe,错误信息藏在 debug 日志末尾,不是报在终端第一屏。
knife upload后节点不执行新recipe
执行 knife cookbook upload myapp 成功,但 knife ssh 'role:web' 'sudo chef-client' 后发现中间件没更新——大概率是 run_list 没生效。
先确认三点:
-
knife node show nodename | grep 'run_list'看输出是否含myapp::default -
knife search node 'recipes:myapp\:\:default'是否返回该节点 - 节点上
/var/chef/cache/cookbooks/myapp/是否存在且时间戳最新(旧缓存可能被复用)
最常踩的坑是:上传 cookbook 后忘了用 knife node run_list add nodename "recipe[myapp::default]",或者用了 set 而非 add 导致清空了原有 run_list。Chef 不会自动把新 cookbook 加进任何节点的 run_list,这一步必须显式操作。
中间件自动化最难的不是写 recipe,而是把平台差异、配置热加载、服务依赖顺序、以及 Chef 自身的缓存机制全串起来。哪怕一个 service 资源漏写了 supports :status => true,chef-client 就可能误判服务已运行而跳过重启,导致配置变更不生效。











