Chef执行顺序异常排查:为何脚本未先于execute资源运行?
Chef执行顺序异常:execute资源提前运行导致monit未安装报错的解决方案
你这个问题其实不是Chef跳过了script 'install monit'资源,而是你的execute 'monit reload'写法完全错了——你把本该在Chef执行阶段运行的命令,直接写在了资源的定义块里,导致这些代码在编译阶段就提前跑了。
问题根源:Chef的编译阶段vs执行阶段
Chef运行分两个核心阶段:
- 编译阶段:Chef会从头到尾读取所有recipe代码,解析并构建要执行的资源列表,但这时候所有资源都还没真正运行(比如你的install monit脚本还没执行,monit根本没装)。
- 执行阶段:Chef按照资源定义的顺序(或者依赖关系),逐个运行这些资源,完成实际的安装、配置操作。
你现在的execute 'monit reload'资源里,直接在do...end块中调用了cmnd.run_command,这部分代码会在编译阶段就立即执行,而不是等到execute资源该运行的执行阶段。这时候monit还没被安装,自然会抛出"找不到monit命令"的错误。
为什么之前正常?
大概率是之前你的目标环境里已经提前安装过monit了,所以编译阶段跑monit start all不会报错;或者你之前的代码写法是正确的,后来修改时不小心把逻辑移到了编译阶段。
修正方案
我们需要把monit的操作逻辑移到执行阶段,同时明确依赖关系,确保它在script 'install monit'之后运行。这里给你两种符合Chef规范的写法:
方案1:用原生execute资源(推荐)
直接利用execute资源的特性,把命令逻辑写在command属性里,同时通过subscribes或者notifies控制执行顺序:
# 先定义execute资源,默认不运行 execute 'monit reload or start' do command <<-EOH # 先检查monit是否已在运行 if monit status >/dev/null 2>&1; then monit reload else monit start all fi EOH # 指定PATH,避免找不到命令 path '/usr/bin:/usr/sbin' # 当install monit脚本执行完成,或者monitrc模板更新时,触发这个execute subscribes :run, 'script[install monit]', :immediately subscribes :run, 'template[/etc/monitrc]', :immediately action :nothing end # 给install monit脚本添加通知,确保执行完触发上面的execute script 'install monit' do interpreter "bash" code <<-EOH git clone https://tildeslash@bitbucket.org/tildeslash/monit.git monit cd monit/ ./bootstrap ./configure --without-pam --without-ssl --sysconfdir=/etc --prefix=/usr make && make install EOH notifies :run, 'execute[monit reload or start]', :immediately end
方案2:用ruby_block封装Mixlib逻辑(如果一定要用ShellOut)
如果你坚持要用Mixlib::ShellOut,必须把逻辑放在ruby_block里——因为ruby_block的代码是在执行阶段运行的:
ruby_block 'monit reload or start' do block do require 'mixlib/shellout' # 指定PATH,确保能找到monit命令 cmnd = Mixlib::ShellOut.new('monit start all', path: '/usr/bin:/usr/sbin') cmnd.run_command if cmnd.error? cmndd = Mixlib::ShellOut.new('monit reload', path: '/usr/bin:/usr/sbin') cmndd.run_command Chef::Log.info("Monit reload executed: #{cmndd.stdout}") else Chef::Log.info("Monit started all services: #{cmnd.stdout}") end end # 明确依赖,在install monit之后运行 subscribes :run, 'script[install monit]', :immediately subscribes :run, 'template[/etc/monitrc]', :immediately action :run end
额外注意事项
- 不要在资源的
do...end定义块里直接执行需要在运行阶段完成的操作,除非用ruby_block或者Chef的回调机制。 - 优先使用Chef原生资源(比如execute、package),而不是自己用ShellOut,这样更符合Chef的收敛性和可维护性。
- 用
Chef::Log替代puts输出日志,这样日志会被记录到Chef客户端的日志文件中,方便后续排查问题。
内容的提问来源于stack exchange,提问作者Bernard2324
相关产品推荐
相关产品推荐

