乐于分享
好东西不私藏

Ansible(八):用Jinja2模板+文件模块,搞定环境差异化配置

Ansible(八):用Jinja2模板+文件模块,搞定环境差异化配置

对于Linux运维来说,Ansible不是“可选工具”,而是“必备神器”——它能帮你摆脱重复劳动,把时间花在更有价值的故障排查、架构优化上。

在 Ansible 自动化中,管理配置文件是核心任务之一。根据场景不同,我们需要把文件原样分发,或是动态生成后再推送,也可能只修改文件中的某一行

下面围绕 Jinja2 模板常用文件模块,系统地拆解它们的用法与适用场景。


4.1 Jinja2 模板:动态生成配置文件

Jinja2 是 Python 的模板引擎,Ansible 内置了对它的支持。通过模板,我们可以将变量、各主机相关的真实变量(facts)、循环和条件语句写入配置文件模板,推到远端时自动渲染成最终内容。

基本语法

  • 变量: {{ variable_name }}输出变量的值,可配合 default 过滤器预防未定义错误:{{ some_var | default('fallback') }}
  • 控制结构: {% ... %}
    • 条件:{% if ... %} ... {% elif ... %} ... {% else %} ... {% endif %}
    • 循环:{% for item in list %} ... {% endfor %}
  • 注释: {# 这是注释 #}
  • 过滤器: 通过管道 | 修饰变量,如 {{ name | upper }}{{ list | join(',') }}

模板文件中可用的数据

  • Playbook 中定义的变量(varsvars_files
  • Inventory 中的主机变量和组变量(host_varsgroup_vars
  • 由 setup 模块采集的各主机上的情况的变量(如 ansible_facts['os_family']
  • 魔法变量(如 inventory_hostnamegroups

示例:动态生成 Nginx 虚拟主机配置

模板文件nginx_vhost.conf.j2:

server {    listen {{ nginx_listen_port | default(80) }};    server_name {{ server_name }};    root {{ document_root }};    index index.html index.htm;    # 根据环境开启访问日志    {% if env == 'production' %}    access_log /var/log/nginx/{{ server_name }}.log main;    {% else %}    access_log off;    {% endif %}    # 循环添加允许访问的IP    {% for ip in allow_ips %}    allow {{ ip }};    {% endfor %}    deny all;}

Playbook 调用template 模块:

- name: 部署 Nginx 虚拟主机配置  template:    src: nginx_vhost.conf.j2    dest: /etc/nginx/conf.d/{{ server_name }}.conf    owner: root    group: root    mode: '0644'  vars:    server_name: www.example.com    document_root: /var/www/example    env: production    allow_ips:      - 192.168.1.0/24      - 10.0.0.1

推送后远端文件会渲染成完整的 server 块,变量被替换,条件、循环都已执行。


4.2 常用文件模块

Ansible 提供了多个模块来处理“文件”类操作,按功能和使用场景归纳如下。

1. copy – 原样复制文件

功能:把控制节点上的一个文件(或内容)复制到受管节点的指定位置。

特点:不做变量替换,适合静态文件(如二进制、脚本、已写死的配置文件)。

常用参数src(本地源文件路径,或 content 提供字符串)、dest(远端目标路径)、owner/group/modebackup(覆盖前备份原文件)。

- name: 分发静态脚本  copy:    src: files/cleanup.sh    dest: /usr/local/bin/cleanup.sh    owner: root    group: root    mode: '0755'- name: 通过内容创建文件  copy:    content: |      # This is a static config      debug=true    dest: /etc/app/static.conf

2. template – 动态模板渲染后复制

功能:将 Jinja2 模板渲染后,写入远端文件。

与 copy 模块的区别template 处理的是 src 模板文件(.j2 后缀只是约定),它会先执行 Jinja2 引擎替换变量,再像 copy 一样设置属性。这是生成各环境差异化配置的核心方法

额外参数validate(某些程序支持配置文件语法校验,如 nginx -t)、force(是否强制覆盖,默认 yes)等。

- name: 动态生成 Redis 配置  template:    src: redis.conf.j2    dest: /etc/redis/redis.conf    validate: 'redis-server %s --test-memory 2'  # 示例校验  notify: restart redis

3. lineinfile – 按行管理文件内容

功能:确保某一行“存在且内容正确”,或删除匹配特定正则的行。常用于修改已有的配置文件中的某个键值,而不影响文件其余部分。

关键参数

  • path: 要操作的文件。
  • regexp: 用于定位该行的正则表达式(通常匹配配置的关键字)。
  • line: 要写入的完整行内容。
  • state: present(保证存在)或 absent(删除匹配行)。
  • backrefs: 启用反向引用,配合正则捕获组时可重构行内容。
  • insertafter/insertbefore
    :在匹配行之后或之前插入。

示例:确保 SSH 禁止 root 登录

- name: 确保 PermitRootLogin 设为 no  lineinfile:    path: /etc/ssh/sshd_config    regexp: '^#?PermitRootLogin\s'    line: 'PermitRootLogin no'    state: present  notify: restart sshd

解释:正则匹配以 PermitRootLogin 开头(可能有注释 #)的行,然后用正确的行替换。如果文件里没有匹配行,lineinfile 会在末尾追加。

示例:删除某行

- name: 删除日志中的 DEBUG 行(可选)  lineinfile:    path: /etc/app/logging.conf    regexp: '^DEBUG='    state: absent

4. 其他常用模块

  • blockinfile: 若需插入或管理一个多行的文本块,用标记包围,比 lineinfile 更安全可控。
  • fetch: 从受管节点拉取文件回控制节点(与 copy 方向相反)。
  • file: 设置文件/目录属性(创建目录、软链接、权限等),不负责内容。常配合 copy/template 前使用。

    - name: 确保应用目录存在  file:    path: /opt/myapp/config    state: directory    owner: app    mode: '0755'

模块选择速查

场景
推荐模块
分发完全不变的静态文件
copy
基于环境变量/主机事实动态生成配置
template
只修改配置文件中某个已存在的键值
lineinfile
在文件中插入或管理多行段落
blockinfile
保证目录、权限、链接存在
file
从远程取回文件到本地
fetch

所有这些模块都具备幂等性:多次执行只会让系统达到期望状态,不会重复修改。lineinfile 会根据正则检查是否已经是要写入的行,copy/template 会比对校验和,只有真正变化时才执行变更并返回 changed 状态。这种设计保证了 Ansible 自动化的安全与可靠。