执行salt "*" state.apply时跨节点共享命令输出遇问题求方案
问题场景
执行salt "*" state.apply时,想要把master节点的命令输出共享到worker节点,采用mine.send+mine.get方案,配置如下:
配置文件
top.sls:
base: 'master*': - states.master* 'worker*': - states.worker*
states/master.sls:
save: module.run: - mine.send: - foo - mine_function: cmd.shell - name: whoami
states/worker.sls:
load: file.managed: - name: /tmp/a.sh - contents: {{ salt['mine.get']("*", "foo").values() | first }}
首次执行错误
首次运行时worker节点直接报错:
worker-0: Data failed to compile: ---------- Rendering SLS 'base:states.worker' failed: Jinja variable No first item, sequence was empty.; line 7 --- [...] load: file.managed: - name: /tmp/a.sh - contents: {{ salt['mine.get']("*", "foo").values() | first }} ---
第二次运行一切正常,核心原因是:Jinja渲染worker的SLS模板时,master节点的mine.send还没执行完成,mine数据未同步到master,导致mine.get返回空序列,触发渲染失败。
解决建议
方法1:给worker的state加等待逻辑,确保mine数据就绪
修改states/worker.sls,用wait_for模块等待master的mine数据可用,再执行文件生成:
wait_for_mine_data: module.run: - name: wait_for.wait_for - condition: mine.get("master*", "foo") - timeout: 30 - interval: 2 load: file.managed: - name: /tmp/a.sh - contents: {{ salt['mine.get']("master*", "foo").values() | first }} - require: - module: wait_for_mine_data
这里设置等待30秒,每2秒检查一次master节点的foo mine数据,worker的文件生成操作依赖等待完成,确保渲染时已有数据。
方法2:分批次执行state,先同步mine再部署worker
先单独执行master节点的state,确保mine数据发送完成:
salt "master*" state.apply states.master
再执行所有节点的state:
salt "*" state.apply
也可以用SaltStack的batch模式,先跑master组再跑worker组,从根源避免并行执行的时序冲突。
方法3:给Jinja模板加默认值,避免渲染失败
修改worker的SLS,给first过滤器加默认值,即使首次mine为空也不会报错,后续state运行时自动更新文件:
load: file.managed: - name: /tmp/a.sh - contents: {{ salt['mine.get']("master*", "foo").values() | first | default("", true) }} - onchanges: - module: save
添加default("", true)保证空序列时渲染不报错,同时用onchanges关联master的save状态,只有当mine数据更新时才重新生成文件,避免无意义的覆盖。
方法4:缩短mine数据的缓存刷新间隔
修改master配置文件/etc/salt/master,调整mine缓存刷新周期:
mine_interval: 5
默认mine数据缓存是60秒,缩短间隔能让master更快同步节点的mine数据,但会增加master负载,根据实际集群规模调整。
内容的提问来源于stack exchange,提问作者yellowhat

