Jinja2 — 从数据到配置文件
一句话总结
template 把“各环境不同的配置文件”拆分为一个框架 + 各环境数据。
为什么需要它
为 dev、stage、prod 分别维护 nginx 配置,三个文件很快会出现差异。每增加一台后端,就要修改三处;漏掉一处,该环境就会发生流量倾斜。template 把服务器清单变成数据,框架只编写一次。增加后端时,只需增加一行数据。
如何工作
Jinja2 常用语法只有三种。
{{ 값 }}——输出值{% 제어 %}——if / for 等控制语句{# 주석 #}——不会渲染的注释
在此基础上可以使用过滤器。例如 {{ name | upper }}、{{ timeout | default(30) }}、{{ data | to_nice_json }},以管道连接。default 尤其重要:它为可能未定义的值提供安全替代,避免变量缺失时输出 <no value> 或报错。
最常出问题的是空白。{% for %} 等控制语句本身占据一行,渲染结果会留下空行;生成 YAML 配置时,空行甚至可能破坏文件。有两种解决方法:在语句中使用 {%- / -%} 删除前后空白,或在 template 模块中启用 trim_blocks/lstrip_blocks。
掌握 template 模块的两个选项也能避免事故。validate 用指定命令检查渲染结果,只有通过后才部署;backup: true 在覆盖前保留副本。从自动部署配置文件的那一刻起,就打开了错误配置杀死服务的路径。 validate 是阻断这条路径成本最低的装置。
在实际项目中
第一,每次渲染结果都不同的 template。 在注释中写入时间戳,会让文件每次都发生变化,handler 每次执行,服务也每次重启。仅这一行就可能破坏零停机部署。
第二,使用 facts 的 template。 直接使用 ansible_facts,可以让每台服务器自动生成不同配置。但特殊环境中的 fact 可能出现意料外值,因此应使用 default 建立防线。
第三,在 template 中写入过多逻辑。 五层 if 已经不是 template,而是程序。应把分支提升到变量计算阶段,让 template 保持简单,更利于维护。
template 中常用的过滤器
Jinja2 的价值主要来自过滤器,掌握下面这些已经足够处理多数情况。
{{ port | default(8080) }} 값이 없으면 기본값
{{ name | mandatory }} 없으면 에러 — 조용한 빈 값을 막는다
{{ items | join(',') }} 목록을 문자열로
{{ config | to_nice_yaml(indent=2) }} 딕셔너리를 YAML 블록으로
{{ secret | b64encode }} 쿠버네티스 시크릿용
{{ path | basename }} 경로 조각
{{ hosts | map(attribute='ip') | list }} 목록에서 필드만 뽑기
养成使用 mandatory 的习惯很重要。变量缺失时,Jinja2 默认可能填入空字符串,生成 listen ; 这类损坏配置,而问题直到服务重启才暴露。
在 ansible.cfg 中加入以下配置,可以让未定义变量立即报错。
[defaults]
error_on_undefined_vars = True
如何处理空白
生成文件出现大量空行,通常是控制结构造成的。
{% for h in hosts %}
server {{ h }};
{% endfor %}
这样写时,每个 {% %} 行都会留下换行。加上连字符可以删除。
{% for h in hosts -%}
server {{ h }};
{% endfor -%}
Ansible 的 template 模块默认不启用 trim_blocks,需要时可在 template 第一行声明。
#jinja2: trim_blocks: True, lstrip_blocks: True
如何测试 template
部署前先查看结果。
# 렌더링 결과만 보기 (파일을 쓰지 않는다)
ansible -i inv web -m template -a "src=nginx.conf.j2 dest=/tmp/out.conf" --check --diff
# 문법 검사
ansible-playbook site.yml --syntax-check
# 실제로 무엇이 바뀌는지
ansible-playbook site.yml --check --diff
--check --diff 的组合最有用:它能在不修改的情况下展示将要改变什么。生产部署前不运行它,一行错误配置就可能让服务停止。
还要加入一步,用目标程序自身的检查器验证生成配置。
- name: nginx 설정
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
validate: 'nginx -t -c %s' # ← 실패하면 파일을 바꾸지 않는다
notify: reload nginx
validate 会先检查临时文件,只有通过后才移到目标位置,损坏配置不会接触真实磁盘路径。
下一实验要做什么
从基础渲染开始,依次使用过滤、循环与条件,再通过空白控制整理结果。把 fact 与 inventory_hostname 用在 template 中,并用 validate 阻止错误配置部署。最后从 group variable 中的后端列表生成完整 nginx 配置。