一、基础文件部署:静态文件的传输与管理
对于内容固定、不需要针对主机做调整的静态文件,Ansible 的基础文件模块已经可以满足大部分需求,这些模块都集成在 Ansible 的核心集合中,无需额外安装即可使用:
-
copy模块:将控制节点的静态文件直接传输到受管主机,传输过程中可同步设置文件的权限、所有者、SELinux 上下文等属性,适合传输固定的脚本、二进制文件等内容。 -
fetch模块:与copy作用相反,用于将受管主机的文件拉取到控制节点,拉取的文件会自动按主机名组织目录结构,避免多主机文件重名覆盖,适合批量收集主机的日志、密钥等信息。 -
synchronize模块:基于 rsync 的增量同步工具,针对大目录、多文件的同步场景做了优化,传输效率远高于copy模块,适合应用目录的批量发布。 -
file模块:负责文件的基础属性管理,可完成文件 / 目录的创建、删除,以及权限、SELinux 标签的修改,是文件管理的基础模块。
这些模块可以快速完成静态文件的部署,但在面对差异化配置的场景时,静态传输的局限性就会显现:如果我们需要给上百台主机部署 nginx 配置,每台主机的监听端口、域名都有差异,用静态文件就需要维护上百份不同的配置文件,维护成本极高。这时候,Jinja2 模板就成了更合适的选择。
二、Jinja2 模板:动态自定义配置的核心方案
Jinja2 是一款面向 Python 的模板引擎,Ansible 将其集成进来,实现了动态配置的生成能力:运维人员只需要编写一份模板文件,在其中定义变量和逻辑,Ansible 在部署时会自动根据当前受管主机的信息,渲染生成适配该主机的最终配置文件,一份模板即可适配所有主机,大幅降低了配置的维护成本。
2.1 模板的基础语法
Jinja2 通过特定的标记区分不同类型的内容,Ansible 完整支持 Jinja2 的标准语法,三类核心标记覆盖了所有的模板编写需求:
-
{``{ 内容 }}:输出标记 这是最常用的标记,标记内的变量或表达式会被计算,结果会输出到最终的配置文件中。比如模板中的{``{ ansible_facts.hostname }},在部署到 host1 主机时,会被自动替换为host1,部署到 host2 时则会替换为host2。 -
{% 内容 %}:逻辑标记 这个标记用来编写逻辑控制代码,比如循环、条件判断,这些内容不会出现在最终的配置文件中,仅用来控制模板的渲染逻辑。 -
{# 内容 #}:注释标记 用来编写模板的注释,渲染时会被直接忽略,不会出现在最终的文件中,用来给模板加说明,方便后续维护。
2.2 模板中的变量来源
模板中的变量有两个核心来源,覆盖了大部分的配置场景:
-
自定义变量 :运维人员在 playbook 的
vars部分、 inventory 文件中自定义的变量,比如业务端口、服务域名这些配置,都可以自定义后在模板中引用。 -
主机 Facts :Ansible 在连接到受管主机时,会自动收集主机的系统信息,包括主机名、IP 地址、系统版本、内存信息等,这些信息会被整理成
ansible_facts变量,直接在模板中引用即可,不需要手动定义。你也可以通过ansible 主机名 -i 清单 -m setup命令,手动查看某台主机的所有 Facts 信息。
另外需要说明的是,模板文件不需要特定的后缀名,.j2只是行业内的习惯命名,用来标记这是一个模板文件,你可以使用任意后缀,Ansible 都可以正常处理。
2.3 模板的部署方式
编写好模板之后,通过ansible.builtin.template模块即可完成部署,该模块会自动完成模板的渲染,再将渲染后的文件传输到受管主机,用法和copy模块非常接近:
tasks:
- name: 部署自定义配置模板
ansible.builtin.template:
# 控制节点上的模板文件路径
src: templates/nginx.conf.j2
# 受管主机上的目标文件路径
dest: /etc/nginx/nginx.conf
# 同步设置文件的权限、所有者等属性,和copy模块一致
owner: root
group: root
mode: 0644
2.4 模板的控制逻辑:灵活生成动态内容
Jinja2 模板支持在模板中编写循环和条件判断,用来动态生成配置内容,不需要手动编写每一行配置,这也是模板的核心能力之一。
循环:批量生成重复配置
通过for循环,我们可以遍历列表变量,批量生成重复的配置段,最典型的场景就是动态生成/etc/hosts文件:
{% for host in groups['all']%}
{{ hostvars[host]['ansible_facts']['default_ipv4']['address'] }} {{ hostvars[host]['ansible_facts']['fqdn'] }} {{ hostvars[host]['ansible_facts']['hostname'] }}
{% endfor %}
这段模板会遍历你 Ansible 清单中所有的主机,自动取出每个主机的 IP、域名、主机名,拼写成/etc/hosts的标准格式,不管你有多少台主机,这个模板都可以自动生成完整的 hosts 文件,不需要手动维护。
你也可以在循环中添加过滤条件,比如跳过 root 用户,同时通过循环自带的loop.index变量实现计数:
{% for myuser in users if not myuser == "root" %}
User number {{ loop.index}} - {{ myuser }}
{% endfor %}
条件判断:按需生成配置
通过if条件判断,我们可以根据变量的值,决定是否输出某一段配置,比如仅当主机是生产环境主机的时候,才添加对应的监控配置:
{% if env == "prod" %}
monitor_server 192.168.1.100
{%endif%}
2.5 变量过滤器:统一配置的输出格式
Jinja2 提供了过滤器工具,用来修改变量的输出格式,方便生成不同类型的配置文件,常用的过滤器包括:
-
格式转换类:
to_json、to_yaml可以将变量转成对应的格式,to_nice_json、to_nice_yaml则会生成带缩进、换行的人类可读格式,方便调试。 -
解析类:
from_json、from_yaml可以将 JSON/YAML 格式的字符串,解析成 Ansible 的字典 / 列表,方便你处理接口返回的配置数据。
过滤器的用法非常简单,通过|将变量和过滤器连接即可:
{{ output | to_nice_json }}
{{ output | from_yaml }}
三、注意事项
-
静态文件优先使用 copy 模块 :对于内容固定的文件,不需要用模板,直接用
copy模块即可,避免不必要的模板渲染开销,同时也能提升任务的执行效率。 -
避免在模板中编写过于复杂的逻辑:虽然模板支持循环和条件判断,但过于复杂的逻辑会大幅降低模板的可读性和可维护性,复杂的逻辑建议放到 playbook 中处理,模板仅负责渲染。
-
模板的调试 :如果模板渲染出错,可以先在本地通过
ansible localhost -m template -a "src=你的模板 dest=/tmp/test"命令做本地测试,也可以通过 debug 模块输出变量,排查变量是否正确。