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

发布于:2025-08-18 ⋅ 阅读:(16) ⋅ 点赞:(0)

4 - 部署文件到受管主机

实验环境

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

# 在控制节点(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. 登录受管节点,检查文件是否创建成功,以及属性是否符合预期。
---
- 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. 登录受管节点,验证目录是否存在,以及所有者、权限是否正确。
---
- 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. 登录受管节点,确认文件已被删除。
---
- 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命令,使新添加的规则立即生效。
---
- 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. 查看文件内容,验证行是否添加成功。
---
- 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. 查看文件内容,验证行是否插入到正确位置。
# 在"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. 查看文件内容,验证匹配的行是否被正确替换。
# 替换包含"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. 查看文件内容,验证所有匹配的内容是否被替换。
---
- 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. 查看文件内容,验证文本块是否添加,及是否包含自动生成的标记。
---
- 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               # 确保文本块存在(若不存在则添加;若已存在则更新内容)
...

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

# 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. 查看输出结果,验证是否正确获取校验和。
---
- 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. 在受管节点查看目标路径,验证文件是否复制成功。
---
- 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. 在受管节点查看文件内容,验证是否正确。
---
- 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. 在受管节点验证文件是否同步成功。
---
- 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. 在受管节点查看目标路径,验证目录内容是否同步成功。
---
- 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. 在控制节点查看保存路径,验证文件是否按预期结构保存。
---
- 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
---
- 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,内容如下:

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

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

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

实验流程

  1. 创建含多个变量的 Jinja2 模板(如{{ ssh_port }}{{ root_allowed }});
  2. 在 playbook 中定义模板变量的值;
  3. 部署模板到受管节点,验证生成的配置文件是否正确替换变量。
步骤 1:编写模板(sshd_config.j2
# {{ 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
---
- 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 中定义变量:

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

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

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

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

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

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

  • 主机清单(inventory):

    [controllers]
    controller
    
    [dev]
    node1
    
    [test]
    node2
    
    [prod]
    node3
    node4
    
  • playbook(deploy_hosts.yml):

    ---
    - 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):

    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:判断变量是否存在
{% 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:循环中过滤元素
{% for num in [7,1,5,3,9] if num>3 %}  # 只遍历大于3的数字(过滤条件)
{{ num }}
{% endfor %}

生成的文件内容:

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

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

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

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

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

在模板中引用:

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

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


网站公告

今日签到

点亮在社区的每一天
去签到