如何用Ansible或yq一键生成{{}}变量替换后的YAML文件?
问题描述
我有多个类似如下内容的serverlist_xzy.yml文件:
all: children: web: hosts: srv-proxy-01: isWeb: true vars: zf_env: 'preprod' zf_zone: 'global' project_path: api: "proj_api_{{ zf_zone }}_{{ zf_env }}" data: "proj_data_{{ zf_zone }}_{{ zf_env }}" web: "proj_web_{{ zf_zone }}_{{ zf_env }}" flower: "proj_flower_{{ zf_zone }}_{{ zf_env }}" root_path: api: "/opt/{{ project_path['api'] }}" web: "/opt/{{ project_path['web'] }}" data: "/opt/{{ project_path['data'] }}" lazy_table: "{{ root_path['data'] }}/data_lazy_table" logrotate_logpath: api: "/var/log/{{ project_path['api'] }}_app" web: "/var/log/{{ project_path['web'] }}_app" worker: "/var/log/aaa_worker_{{ zf_zone }}_{{ zf_env }}_app" flower: "/var/log/{{ project_path['flower'] }}_app" htpasswd: path: api: "/etc/nginx/passwords/{{ project_path['api'] }}.passwdfile" web: "/etc/nginx/passwords/{{ project_path['web'] }}.passwdfile" flower: "/etc/nginx/passwords/{{ project_path['flower'] }}.passwfile"
需要将文件中所有{{}}形式的变量替换后输出到out.yml,最终效果示例:
$ grep worker out.yml worker: "/var/log/aaa_worker_global_preprod_app"
可以用Python实现,但想知道是否能用Ansible或yq一键完成该操作。
解决方案
一、用Ansible实现
Ansible原生支持Jinja2模板渲染,完美匹配需求中的{{}}变量语法,操作步骤如下:
- 编写Playbook文件
创建render_vars.yml文件:
- name: 渲染YAML变量到输出文件 hosts: localhost gather_facts: no tasks: - name: 读取原YAML文件内容 slurp: src: serverlist_xzy.yml register: yaml_raw - name: 解析并渲染变量,写入目标文件 copy: content: "{{ yaml_raw.content | b64decode | from_yaml | to_nice_yaml }}" dest: out.yml
- 执行Playbook
直接运行命令:
ansible-playbook render_vars.yml
该方法会自动解析文件内的所有Jinja2变量,包括嵌套引用的变量,最终输出格式化后的完整YAML到out.yml。
二、用yq实现
yq支持自定义字符串替换逻辑,需手动处理变量的嵌套关系,步骤如下:
- 提取基础变量值
先从原文件中获取核心变量:
zf_env=$(yq '.all.vars.zf_env' serverlist_xzy.yml | tr -d "'") zf_zone=$(yq '.all.vars.zf_zone' serverlist_xzy.yml | tr -d "'")
- 递归替换所有变量
使用yq的eval命令完成全量替换:
yq eval ' .all.vars.project_path.api |= sub("{{ zf_zone }}_{{ zf_env }}"; env(zf_zone) + "_" + env(zf_env)) | .all.vars.project_path.data |= sub("{{ zf_zone }}_{{ zf_env }}"; env(zf_zone) + "_" + env(zf_env)) | .all.vars.project_path.web |= sub("{{ zf_zone }}_{{ zf_env }}"; env(zf_zone) + "_" + env(zf_env)) | .all.vars.project_path.flower |= sub("{{ zf_zone }}_{{ zf_env }}"; env(zf_zone) + "_" + env(zf_env)) | .all.vars.root_path.api |= sub("{{ project_path['api'] }}"; .all.vars.project_path.api) | .all.vars.root_path.web |= sub("{{ project_path['web'] }}"; .all.vars.project_path.web) | .all.vars.root_path.data |= sub("{{ project_path['data'] }}"; .all.vars.project_path.data) | .all.vars.lazy_table |= sub("{{ root_path['data'] }}"; .all.vars.root_path.data) | .all.vars.logrotate_logpath.api |= sub("{{ project_path['api'] }}"; .all.vars.project_path.api) | .all.vars.logrotate_logpath.web |= sub("{{ project_path['web'] }}"; .all.vars.project_path.web) | .all.vars.logrotate_logpath.worker |= sub("{{ zf_zone }}_{{ zf_env }}"; env(zf_zone) + "_" + env(zf_env)) | .all.vars.logrotate_logpath.flower |= sub("{{ project_path['flower'] }}"; .all.vars.project_path.flower) | .all.vars.htpasswd.path.api |= sub("{{ project_path['api'] }}"; .all.vars.project_path.api) | .all.vars.htpasswd.path.web |= sub("{{ project_path['web'] }}"; .all.vars.project_path.web) | .all.vars.htpasswd.path.flower |= sub("{{ project_path['flower'] }}"; .all.vars.project_path.flower) ' serverlist_xzy.yml > out.yml
该方法适合变量结构固定的场景,若嵌套层次多,Ansible方案会更高效省心。
内容的提问来源于stack exchange,提问作者user3313834
相关产品推荐
相关产品推荐

