4.Ansible自动化之-部署文件到主机

4 - 部署文件到受管主机

实验环境

先通过以下命令搭建基础环境(创建工作目录、配置 Ansible 环境和主机清单):

bash 复制代码
# 在控制节点(controller)上创建web目录并进入,作为工作目录
[bq@controller ~]$ mkdir web && cd web

# 创建Ansible配置文件ansible.cfg,定义连接和权限提升参数
[bq@controller web]$ cat > ansible.cfg <<'EOF'
[defaults]
remote_user = bq    # 默认远程连接用户为bq
inventory = ./inventory  # 指定主机清单文件路径为当前目录的inventory

[privilege_escalation]
become = True           # 允许提权(切换到其他用户)
become_user = root      # 提权目标用户为root
become_method = sudo    # 提权方式为sudo
become_ask_pass = False # 提权时不询问密码(需提前配置sudo免密)
EOF

# 创建主机清单文件inventory,列出需要管理的主机
[bq@controller web]$ cat > inventory <<'EOF'
controller  # 控制节点自身
node1       # 受管节点1
node2       # 受管节点2
node3       # 受管节点3
node4       # 受管节点4
EOF

修改文件并将其复制到主机

Ansible 的 Files 模块库包含一系列文件管理工具,能帮我们完成创建文件、复制文件、修改权限等日常 Linux 文件操作。下面逐个介绍常用模块的用法。

file 模块:管理文件 / 目录的属性(创建、删除、改权限等)

模块作用 :可以设置文件 / 目录的权限、所有者、SELinux 上下文等属性,也能直接创建空文件或目录,或者删除不需要的文件 / 目录(类似 Linux 的touchmkdirrmchmod等命令的组合)。

实验 1:创建文件并设置权限

实验流程

  1. 编写 playbook,定义要创建的文件路径、所有者、权限等信息;
  2. 执行 playbook,让 Ansible 在目标主机上执行创建操作;
  3. 登录受管节点,检查文件是否创建成功,以及属性是否符合预期。
yaml 复制代码
---
- hosts: node1        # 目标主机为node1(仅在该节点执行)
  gather_facts: no    # 不收集主机信息(减少执行时间,加快操作速度)
  tasks:
    - name: Touch a file and set permissions  # 任务描述:创建文件并设置权限
      file:
        path: /tmp/testfile  # 目标文件的路径(在node1的/tmp目录下创建testfile)
        owner: bq         # 文件的所有者设置为bq用户
        group: wheel         # 文件的所属组设置为wheel组
        mode: 0640           # 文件权限设置为640(所有者可读可写,组内用户可读,其他用户无权限;必须带前导0,否则会解析错误)
        state: touch         # 状态为"创建文件"(类似Linux的touch命令,若文件不存在则创建空文件,存在则更新修改时间)

注意:mode参数必须带前导 0(如0640)或用引号包裹(如'640')。如果直接写640,Ansible 会当作十进制处理,导致权限计算错误(比如变成-w-------T这种不符合预期的权限)。

实验 2:创建目录

实验流程

  1. 编写 playbook,指定要创建的目录路径、所有者、权限等;
  2. 执行 playbook,让 Ansible 在目标主机上创建目录;
  3. 登录受管节点,验证目录是否存在,以及所有者、权限是否正确。
yaml 复制代码
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: create directory  # 任务描述:创建目录
      file:
        path: /webdev         # 目标目录路径(在node1的根目录下创建webdev目录)
        owner: apache         # 目录所有者设置为apache用户(通常用于web服务相关目录)
        group: apache         # 目录所属组设置为apache组
        mode: 0755            # 目录权限为755(所有者可读可写可执行,组和其他用户可读可执行;目录需要执行权限才能进入)
        state: directory      # 状态为"创建目录"(类似Linux的mkdir命令,若目录不存在则创建,存在则不做操作)
...                           # YAML文件结尾的省略符号(实际编写时必须包含)
实验 3:删除文件

实验流程

  1. 编写 playbook,指定要删除的文件路径;
  2. 执行 playbook,让 Ansible 在目标主机上删除文件;
  3. 登录受管节点,确认文件已被删除。
yaml 复制代码
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: delete file  # 任务描述:删除文件
      file:
        path: /tmp/testfile  # 要删除的文件路径(node1上的/tmp/testfile)
        state: absent        # 状态为"不存在"(即删除文件,类似Linux的rm命令;若文件不存在则不做操作)
sefcontext 模块:管理 SELinux 的持久规则

模块作用 :用于在 SELinux 的规则库中添加或删除持久规则(类似 Linux 的semanage fcontext命令)。注意:添加的规则不会立即生效,需要通过restorecon等命令重新标注文件后才会应用。

实验:添加 SELinux 上下文规则

实验流程

  1. 编写 playbook,定义要设置的文件路径和对应的 SELinux 类型;
  2. 执行 playbook,让 Ansible 在目标主机上添加 SELinux 规则;
  3. (可选)在受管节点执行restorecon命令,使新添加的规则立即生效。
yaml 复制代码
---
- name: 添加SELinux上下文规则
  hosts: node1
  tasks:
    - name: 获取SElinux当前状态
      command: getenforce  # 执行getenforce命令,获取SELinux当前模式(Enforcing/Permissive)
      register: selinux_running_state  # 将命令结果保存到变量selinux_running_state
      changed_when: false  # 标记该任务不会改变主机状态(避免Ansible误判为"已修改")
      failed_when: false   # 即使命令执行失败也不标记任务失败(兼容SELinux未开启的情况)

    - name: 仅在Permissive模式下切换为Enforcing
      command: setenforce 1  # 将SELinux切换为强制模式(1表示Enforcing)
      ignore_errors: no  # 若执行失败则标记任务失败(确保SELinux正常运行)

    - name: 安装policycoreutils-python依赖
      yum:  # 使用yum模块安装依赖包(SELinux规则管理需要该工具)
        name: policycoreutils-python
        state: present  # 确保包已安装

    - name: 设置/samba目录的SELinux上下文
      sefcontext:
        target: '/samba(/.*)?'  # 匹配路径:/samba目录及所有子目录和文件(正则表达式)
        setype: samba_share_t   # 设置SELinux类型为samba_share_t(Samba共享目录的专用类型)
        state: present          # 状态为"存在"(即添加该规则到SELinux规则库)

注意:sefcontext模块仅修改 SELinux 的规则库,不会直接改变现有文件的 SELinux 上下文。如果要让规则立即生效,需配合file模块(file模块会实时应用上下文)或手动执行restorecon -R /samba(递归刷新 /samba 目录的上下文)。

lineinfile 模块:管理文件中的单行内容

模块作用 :用于修改已存在的文件,可确保文件中存在特定行、替换符合条件的行,或在指定位置插入行(类似 Linux 的sed命令的单行处理功能)。

实验 1:向文件添加特定行

实验流程

  1. 编写 playbook,先创建目标文件,再指定要添加的行内容;
  2. 执行 playbook,让 Ansible 在目标文件中添加行;
  3. 查看文件内容,验证行是否添加成功。
yaml 复制代码
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: 创建文件
      file:  # 先创建/tmp/testfile文件(若已存在则不做操作)
        path: /tmp/testfile
        state: touch
        owner: bq
        group: wheel
        mode: 0644

    - name: add line  # 任务描述:添加一行内容
      lineinfile:
        path: /tmp/testfile  # 目标文件路径(node1上的/tmp/testfile)
        line: 'Add this line to file'  # 要添加的行内容
        state: present      # 确保该行存在(如果文件中没有,则添加到末尾;如果已有,则不重复添加)
实验 2:在指定位置插入行

实验流程

  1. 编写 playbook,指定目标文件、要插入的行,以及插入位置(某行之前 / 之后);
  2. 执行 playbook,让 Ansible 在指定位置插入行;
  3. 查看文件内容,验证行是否插入到正确位置。
yaml 复制代码
# 在"Listen 80"行之前插入"Listen 82"
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: add line before Listen 80
      lineinfile:
        path: /etc/httpd/conf/httpd.conf  # 目标文件(httpd服务的配置文件)
        line: 'Listen 82'                 # 要插入的行(让httpd监听82端口)
        insertbefore: 'Listen 80'         # 插入位置:在"Listen 80"行之前
        state: present

# 在"Listen 80"行之后插入"Listen 82"
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: add line after Listen 80
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        line: 'Listen 82'
        insertafter: 'Listen 80'  # 插入位置:在"Listen 80"行之后
        state: present
...
实验 3:替换符合条件的行

实验流程

  1. 编写 playbook,用正则表达式匹配要替换的行,指定新内容;
  2. 执行 playbook,让 Ansible 替换目标行;
  3. 查看文件内容,验证匹配的行是否被正确替换。
yaml 复制代码
# 替换包含"Add"的行
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: replace line  # 任务描述:替换行内容
      lineinfile:
        path: /tmp/testfile
        regexp: 'Add'     # 正则表达式:匹配所有包含"Add"的行
        line: 'replace'   # 替换后的内容(将匹配的行替换为"replace")
        state: present

# 注释掉httpd的80端口监听(在行首加#)
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: comment Listen 80
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        line: '#Listen 80'  # 替换后的内容(在原行首加#,实现注释)
        regexp: '^Listen 80'  # 正则表达式:匹配以"Listen 80"开头的行(^表示行首)
        state: present
...
replace 模块:批量替换文件中的内容

模块作用 :用正则表达式匹配文件中所有符合条件的内容,并一次性替换(类似 Linux 的sed -i 's/原内容/新内容/g'命令,会替换所有匹配项,而不仅是单行)。

实验:替换所有匹配的行

实验流程

  1. 编写 playbook,定义目标文件、匹配规则(正则表达式)和替换内容;
  2. 执行 playbook,让 Ansible 批量替换文件内容;
  3. 查看文件内容,验证所有匹配的内容是否被替换。
yaml 复制代码
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: replace multi line  # 任务描述:批量替换行
      replace:
        path: /tmp/testfile    # 目标文件(node1上的/tmp/testfile)
        regexp: '^Hello World.*'  # 正则表达式:匹配所有以"Hello World"开头的行(.*表示任意字符)
        replace: 'Hello bq'    # 替换后的内容(将匹配的行替换为"Hello bq")
...
blockinfile 模块:管理文件中的多行文本块

模块作用 :向文件中插入、更新或删除多行文本块。插入的内容会自动添加标记(# BEGIN ANSIBLE MANAGED BLOCK# END ANSIBLE MANAGED BLOCK),方便后续识别和更新(再次执行时会更新块内内容,而不是重复添加)。

实验:向文件添加多行文本块

实验流程

  1. 编写 playbook,定义目标文件和要添加的多行内容;
  2. 执行 playbook,让 Ansible 在目标文件中插入文本块;
  3. 查看文件内容,验证文本块是否添加,及是否包含自动生成的标记。
yaml 复制代码
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: add block lines to file  # 任务描述:添加多行文本块
      blockinfile:
        path: /tmp/testfile          # 目标文件(node1上的/tmp/testfile)
        block: |                     # 要添加的多行内容(| 表示保留换行符,保持原格式)
           line 1 in file
           line 2 in fileaa
           line 3 in file sss
        state: present               # 确保文本块存在(若不存在则添加;若已存在则更新内容)
...

执行后,文件中会添加以下内容(自动包含标记):

plaintext 复制代码
# BEGIN ANSIBLE MANAGED BLOCK
line 1 in file
line 2 in fileaa
line 3 in file sss
# END ANSIBLE MANAGED BLOCK
stat 模块:获取文件的状态信息

模块作用 :检索文件的详细信息(如权限、校验和、创建时间、大小等),类似 Linux 的stat命令。获取的结果可以保存到变量中,用于后续任务的条件判断(比如根据文件是否存在决定是否执行某操作)。

实验:获取文件的 MD5 校验和

实验流程

  1. 编写 playbook,用 stat 模块获取文件信息并保存到变量;
  2. 执行 playbook,通过 debug 模块输出文件的 MD5 校验和;
  3. 查看输出结果,验证是否正确获取校验和。
yaml 复制代码
---
- hosts: node1
  gather_facts: no
  tasks:
    - stat:  # 调用stat模块获取文件信息
        path: /tmp/testfile        # 目标文件(node1上的/tmp/testfile)
        checksum_algorithm: md5    # 指定校验和算法为MD5(可选值:md5、sha1、sha256等)
      register: result             # 将获取的信息保存到变量result中

    - debug:  # 输出MD5校验和
        msg: "/tmp/testfile md5 is {{ result.stat.checksum }}"  # 通过{{ }}引用变量中的checksum值

    - debug:  # 输出所有文件信息(方便查看详细内容,如权限、大小等)
        var: result  # 直接输出变量result的内容
...
copy 模块:复制文件到受管节点

模块作用 :将控制节点或其他远程节点的文件复制到受管节点,类似 Linux 的scp命令。支持设置文件权限、所有者,以及是否备份原文件、是否强制覆盖等。

实验 1:复制控制节点文件到受管节点

实验流程

  1. 在控制节点准备好要复制的文件(如/tmp/testfile);
  2. 编写 playbook,指定控制节点的源文件路径和受管节点的目标路径;
  3. 执行 playbook,让 Ansible 完成文件复制;
  4. 在受管节点查看目标路径,验证文件是否复制成功。
yaml 复制代码
---
- name: 复制控制节点文件到受管节点
  hosts: node1  # 目标受管节点
  tasks:
    - name: 复制控制节点的/tmp/testfile到受管节点
      copy:
        src: /tmp/testfile  # 控制节点上的源文件路径(可以是绝对路径,或相对于playbook的相对路径)
        dest: /tmp/  # 受管节点上的目标目录(复制后会保持原文件名,即/tmp/testfile)
        owner: root  # 可选:设置文件所有者为root
        group: root  # 可选:设置文件所属组为root
        mode: 0644   # 可选:设置文件权限为644(所有者可读可写,组和其他用户可读)
        backup: yes  # 可选:如果目标文件已存在,先创建.bak备份(如/tmp/testfile.bak)

    - name: 验证文件是否复制成功
      stat:  # 检查受管节点上的目标文件
        path: /tmp/testfile  # 受管节点上的目标文件路径
      register: file_check  # 将检查结果保存到变量file_check

    - name: 输出验证结果
      debug:  # 当文件存在时,输出成功信息
        msg: "文件已成功复制到受管节点,路径为: /tmp/testfile"
      when: file_check.stat.exists  # 条件:仅当file_check.stat.exists为true时执行

    - name: 查看文件内容(可选)
      command: cat /tmp/testfile  # 读取文件内容
      register: file_content  # 保存内容到变量file_content
      when: file_check.stat.exists  # 仅当文件存在时执行

    - name: 显示文件内容
      debug:  # 输出文件内容
        msg: "文件内容:\n{{ file_content.stdout }}"  # stdout存储命令执行结果
      when: file_check.stat.exists  # 仅当文件存在时执行
...

说明:copy模块默认force: yes(强制覆盖目标文件),如果设置force: no,则当目标文件已存在时不会覆盖(保留原文件)。

实验 2:直接写入字符串到文件

实验流程

  1. 编写 playbook,用content参数定义要写入的文件内容;
  2. 执行 playbook,让 Ansible 在受管节点创建文件并写入内容;
  3. 在受管节点查看文件内容,验证是否正确。
yaml 复制代码
---
- name: 直接写入字符串到受管节点的文件
  hosts: node1
  tasks:
    - name: 创建文件并写入内容
      copy:
        content: |  # 要写入的内容(| 表示保留换行,支持多行)
          这是第一行内容
          这是第二行内容
          可以包含多行字符串
          最后一行内容
        dest: /tmp/demo.txt  # 受管节点上的目标文件路径(会创建该文件)
        owner: root          # 可选:设置文件所有者为root
        group: root          # 可选:设置文件所属组为root
        mode: 0644           # 可选:设置文件权限为644
    - name: 验证文件内容
      command: cat /tmp/demo.txt  # 读取文件内容
      register: file_content  # 保存内容到变量

    - name: 显示文件内容
      debug:  # 输出文件内容
        msg: "文件内容如下:\n{{ file_content.stdout }}"
...
synchronize 模块:基于 rsync 同步文件 / 目录

模块作用 :封装了rsync工具,用于高效同步文件 / 目录(速度比copy模块快,适合大量文件或大文件同步)。需要控制节点和受管节点都安装rsync工具。

实验 1:同步单个文件

实验流程

  1. 确保控制节点和受管节点都安装rsync(可通过yum install -y rsync安装);
  2. 编写 playbook,指定控制节点的源文件路径和受管节点的目标路径;
  3. 执行 playbook,让 Ansible 通过 rsync 同步文件;
  4. 在受管节点验证文件是否同步成功。
yaml 复制代码
---
- name: 使用synchronize模块同步单个文件
  hosts: node1  # 受管节点
  tasks:
    # 步骤1:确保控制节点和受管节点都安装rsync
    - name: 安装rsync工具
      yum:
        name: rsync
        state: present  # 确保rsync已安装

    # 步骤2:同步控制节点的文件到受管节点
    - name: 同步单个文件到受管节点
      synchronize:
        src: /path/on/control/node/source.txt  # 控制节点上的源文件路径(替换为实际路径)
        dest: /path/on/remote/node/destination.txt  # 受管节点上的目标路径(替换为实际路径)
        mode: push  # 同步方向:从控制节点推送到受管节点(默认就是push,可省略)

    # 步骤3:验证文件是否同步成功
    - name: 检查受管节点上的目标文件
      stat:
        path: /path/on/remote/node/destination.txt  # 受管节点上的目标文件路径
      register: file_status  # 保存检查结果

    - name: 显示验证结果
      debug:  # 当文件存在时,输出成功信息
        msg: "文件同步成功,路径:{{ file_status.stat.path }}"
      when: file_status.stat.exists  # 仅当文件存在时执行
...
实验 2:同步目录

实验流程

  1. 在控制节点准备要同步的目录(如/etc/sysconfig);
  2. 编写 playbook,指定控制节点的源目录和受管节点的目标路径;
  3. 执行 playbook,让 Ansible 同步目录;
  4. 在受管节点查看目标路径,验证目录内容是否同步成功。
yaml 复制代码
---
- hosts: node1
  gather_facts: no
  remote_user: root  # 同步系统目录可能需要root权限
  tasks:
    - name: synchronize directory  # 任务描述:同步目录
      synchronize:
        src: /etc/sysconfig  # 控制节点上的源目录(/etc/sysconfig,包含系统服务配置)
        dest: /tmp/          # 受管节点上的目标路径(同步后会在/tmp下创建sysconfig子目录,即/tmp/sysconfig)
...
fetch 模块:从受管节点获取文件到控制节点

模块作用:将受管节点的文件复制到控制节点,常用于收集日志、配置文件等。文件会按 "控制节点目标路径 / 受管节点名 / 原文件路径" 的结构保存(方便区分不同节点的文件)。

实验:从受管节点获取文件

实验流程

  1. 编写 playbook,指定受管节点的源文件路径和控制节点的保存路径;
  2. 执行 playbook,让 Ansible 从受管节点复制文件到控制节点;
  3. 在控制节点查看保存路径,验证文件是否按预期结构保存。
yaml 复制代码
---
- hosts: node1
  gather_facts: no
  tasks:
    - name: fetch file from remote node  # 任务描述:获取远程文件
      fetch:
        src: /tmp/testfile  # 受管节点上的源文件路径(node1的/tmp/testfile)
        dest: /tmp          # 控制节点上的保存路径
...

执行后,文件会保存在控制节点的/tmp/node1/tmp/testfile(路径结构:目标路径/受管节点名/原文件绝对路径)。

使用 JINJA2 模板部署文件

Jinja2 模板是一种带变量和逻辑的文件 (比如包含{``{ 变量 }}{% 循环/条件 %}等)。通过 Ansible 的template模块部署到受管节点时,Ansible 会自动替换变量、执行逻辑,生成个性化的配置文件。

基础示例:部署动态网页

实验流程

  1. 创建 Jinja2 模板文件(包含变量,如{``{ ansible_fqdn }});
  2. 编写 playbook,用template模块部署模板到受管节点;
  3. 执行 playbook,在受管节点查看生成的文件,验证变量是否被正确替换。
步骤 1:编写 playbook(deploy_web.yml
yaml 复制代码
---
- name: Enable intranet services  # 部署web服务
  hosts: node1
  tasks:
    - name: ensure latest version of httpd  # 安装httpd服务(用于提供web服务)
      yum:
        name: httpd
        state: latest  # 确保安装最新版本

    - name: deploy test html page  # 部署动态主页
      template:
        src: index.html.j2  # 控制节点上的Jinja2模板文件(与playbook同目录)
        dest: /var/www/html/index.html  # 受管节点上的目标文件(httpd的默认主页路径)

    - name: start and enable httpd  # 启动httpd并设置开机自启
      service:
        name: httpd
        enabled: true  # 开机自启
        state: restarted  # 重启服务(使配置生效)
步骤 2:创建模板文件(index.html.j2

在 playbook 同目录下创建index.html.j2,内容如下:

jinja2 复制代码
Welcome to {{ ansible_fqdn }}  # {{ }} 中的变量会被替换为受管节点的完全主机名(如node1.bq.cloud)
步骤 3:执行与验证

执行 playbook 后,受管节点的/var/www/html/index.html内容会自动替换变量,变成:

plaintext 复制代码
Welcome to node1.bq.cloud  # 假设node1的完全主机名是node1.bq.cloud
进阶示例:部署 SSH 配置文件

实验流程

  1. 创建含多个变量的 Jinja2 模板(如{``{ ssh_port }}{``{ root_allowed }});
  2. 在 playbook 中定义模板变量的值;
  3. 部署模板到受管节点,验证生成的配置文件是否正确替换变量。
步骤 1:编写模板(sshd_config.j2
jinja2 复制代码
# {{ ansible_managed }}  # 会替换为"Ansible managed"(可在ansible.cfg中自定义)
# DO NOT MAKE LOCAL MODIFICATIONS TO THIS FILE AS THEY WILL BE LOST
Port {{ ssh_port }}  # SSH端口(使用变量ssh_port的值)
ListenAddress {{ ansible_facts['default_ipv4']['address'] }}  # 监听IP(使用受管节点的默认IPv4地址)

HostKey /etc/ssh/ssh_host_rsa_key  
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
SyslogFacility AUTHPRIV
PermitRootLogin  {{ root_allowed }}  # 是否允许root登录(使用变量root_allowed的值)
AllowGroups {{ groups_allowed }}  # 允许登录的组(使用变量groups_allowed的值)

AuthorizedKeysFile      /etc/.rht_authorized_keys .ssh/authorized_keys
PasswordAuthentication {{ passwords_allowed }}  # 是否允许密码认证(使用变量passwords_allowed的值)
步骤 2:编写 playbook(deploy_ssh.yml
yaml 复制代码
---
- name: config sshd service  # 配置SSH服务
  hosts: node1
  vars:  # 定义模板中用到的变量(键值对形式)
    ssh_port: 1022  # SSH端口设为1022(非默认端口更安全)
    root_allowed: "yes"  # 允许root登录
    groups_allowed: wheel  # 允许wheel组用户登录
    passwords_allowed: "yes"  # 允许密码认证
    ansible_managed: "Ansible managed: do not edit manually"  # 覆盖默认的ansible_managed变量
  tasks:
    - name: deploy sshd config  # 部署模板
      template:
        src: sshd_config.j2  # 控制节点上的模板文件
        dest: /root/sshd_config  # 先部署到/root目录验证(实际使用时应替换为/etc/ssh/sshd_config)
Jinja2 模板语法详解

模板中通过特殊符号包裹变量和逻辑,常用符号:

  • {``{ 变量/表达式 }}:输出变量或表达式结果(如{``{ 1+1 }}输出2);
  • {% 控制语句 %}:用于循环(for)、条件判断(if)等(如{% for user in users %});
  • {# 注释 #}:模板内的注释,不会出现在生成的文件中(仅用于模板编写者参考)。
1. for 循环:遍历列表 / 字典

作用 :重复输出列表或字典中的每个元素,类似编程语言中的for循环。

示例 1:遍历用户列表
  • playbook 中定义变量:

    yaml 复制代码
    vars:
      users:  # 定义一个用户列表
        - tom
        - jack
        - Snoopy
        - lucy
  • 模板文件(testfile.j2):

    jinja2 复制代码
    {% for user in users %}  # 遍历users列表,每次取一个元素赋值给user
    {{ user }}  # 输出当前user的值
    {% endfor %}  # 结束循环
  • 生成的文件内容:

    plaintext 复制代码
    tom
    jack
    Snoopy
    lucy
示例 2:带索引的循环
  • 模板文件(testfile.j2):

    jinja2 复制代码
    {% for user in users %}
    {{ loop.index }} - {{ user }}  # loop.index是循环的序号(从1开始)
    {% endfor %}
  • 生成的文件内容:

    plaintext 复制代码
    1 - tom
    2 - jack
    3 - Snoopy
    4 - lucy
示例 3:生成主机列表文件

需求 :在/etc/myhosts中添加所有主机的 IP、完全主机名和主机名。

  • 主机清单(inventory):

    ini 复制代码
    [controllers]
    controller
    
    [dev]
    node1
    
    [test]
    node2
    
    [prod]
    node3
    node4
  • playbook(deploy_hosts.yml):

    yaml 复制代码
    ---
    - name: deploy /etc/myhosts
      hosts: all  # 必须对所有主机执行,才能收集所有主机的信息
      tasks:
        - name: generate /etc/myhosts
          template:
            src: hosts.j2
            dest: /etc/myhosts
          when: inventory_hostname in groups.dev  # 只部署到dev组主机(这里指node1)
    ...
  • 模板文件(hosts.j2):

    jinja2 复制代码
    127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
    
    {% for server in groups.all %}   # 遍历所有主机(groups.all是包含所有主机的列表)
    # hostvars[server]               # 用于获取其他主机的信息(如IP、主机名)
    {{ hostvars[server].ansible_default_ipv4.address }} {{ hostvars[server].ansible_fqdn }} {{ hostvars[server].ansible_hostname }}
    {% endfor %}
2. if 条件判断:根据条件输出内容

作用 :满足条件时才输出特定内容,类似if-else语句。

示例 1:判断变量是否存在
jinja2 复制代码
{% if PORT is defined %}  # 如果PORT变量已定义(在playbook中设置过)
bind-address=0.0.0.0:{{ PORT }}  # 使用PORT变量的值(如0.0.0.0:3307)
{% else %}  # 否则(PORT未定义)
bind-address=0.0.0.0:3306  # 使用默认端口3306
{% endif %}
示例 2:循环中过滤元素
jinja2 复制代码
{% for num in [7,1,5,3,9] if num>3 %}  # 只遍历大于3的数字(过滤条件)
{{ num }}
{% endfor %}

生成的文件内容:

plaintext 复制代码
7
5
9
3. 表达式:支持运算和比较
比较运算
jinja2 复制代码
{{ 1 == 1 }}  # 等于 → True
{{ 2 != 2 }}  # 不等于 → False
{{ 2 > 1 }}   # 大于 → True
{{ 2 <= 1 }}  # 小于等于 → False
逻辑运算
jinja2 复制代码
{{ (2 > 1) or (1 > 2) }}  # 或 → True(只要一个条件成立)
{{ (2 > 1) and (1 > 2) }} # 与 → False(两个条件都成立才为True)
{{ not true }}  # 非 → False(取反)
算术运算
jinja2 复制代码
{{ 3 + 2 }}   # 加 → 5
{{ 3 * 5 }}   # 乘 → 15
{{ 2 **3 }}  # 幂 → 8(2的3次方)
{{ 7 // 5 }}  # 整除 → 1(只保留整数部分)
{{ 17 % 5 }}  # 取余 → 2(17除以5的余数)
4. 过滤器:格式化输出结果

过滤器用于对变量或表达式的结果进行处理(如转换大小写、排序等),格式为{``{ 变量 | 过滤器 }}

字符串处理
jinja2 复制代码
{{ 'hello' | upper }}  # 转大写 → HELLO
{{ 'HELLO' | lower }}  # 转小写 → hello
{{ 'hello' | capitalize }}  # 首字母大写 → Hello
{{ '  hello  ' | trim }}  # 去除首尾空格 → hello
列表处理
jinja2 复制代码
{{ [3,1,7] | sort }}  # 升序排序 → [1, 3, 7]
{{ [3,1,7] | max }}   # 最大值 → 7
{{ [3,1,7] | sum }}   # 求和 → 11
{{ [3,1,7] | join(',') }}  # 用逗号连接 → 3,1,7
数字处理
jinja2 复制代码
{{ '123' | int }}  # 转整数 → 123(将字符串转为整数)
{{ 'abc' | int(default=0) }}  # 转换失败用默认值 → 0(字符串无法转整数时返回0)
{{ 12.5 | round }}  # 四舍五入 → 13
{{ -5 | abs }}  # 绝对值 → 5
ansible_managed 变量

用于在模板中添加 "此文件由 Ansible 管理" 的注释,提醒用户不要手动修改(否则下次执行 playbook 可能被覆盖)。默认值在ansible.cfg中定义:

ini 复制代码
[defaults]
ansible_managed = Ansible managed  # 可自定义,如"Ansible managed: do not edit manually"

在模板中引用:

jinja2 复制代码
# {{ ansible_managed }}  # 会替换为上述配置的值(如"Ansible managed: do not edit manually")

如涉及版权问题请联系作者处理!!!!

相关推荐
悠然南风9 天前
Ansible常见模块总结及LDAP Role 编写与调试
ansible
祺风挽楠18 天前
ansible编辑
网络·ansible
芳心粽伙饭18 天前
Ansible课后作业
ansible
烁34720 天前
Ansible初识
ansible
烁34720 天前
Ansible安装部署调试
ansible
烁34720 天前
Ansible命令
ansible
小义_20 天前
【Ansible】(三)基础配置与连接设置
云原生·ansible
炸炸鱼.24 天前
Ansible 企业级实战:Playbook 与 Roles 完全指南
网络·ansible
风曦Kisaki25 天前
# 自动化运维Day03:Ansible模块进阶(setup,debug),四种常用变量,进阶语法;Ansible Roles(角色)
运维·自动化·ansible
炸炸鱼.1 个月前
Ansible 部署应用:从入门到精通
ansible