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: node1gather_facts: notasks:- 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: node1gather_facts: notasks:- 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: node1tasks:- name: 获取SElinux当前状态command: getenforce  # 执行getenforce命令,获取SELinux当前模式(Enforcing/Permissive)register: selinux_running_state  # 将命令结果保存到变量selinux_running_statechanged_when: false  # 标记该任务不会改变主机状态(避免Ansible误判为"已修改")failed_when: false   # 即使命令执行失败也不标记任务失败(兼容SELinux未开启的情况)- name: 仅在Permissive模式下切换为Enforcingcommand: setenforce 1  # 将SELinux切换为强制模式(1表示Enforcing)ignore_errors: no  # 若执行失败则标记任务失败(确保SELinux正常运行)- name: 安装policycoreutils-python依赖yum:  # 使用yum模块安装依赖包(SELinux规则管理需要该工具)name: policycoreutils-pythonstate: 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: node1gather_facts: notasks:- name: 创建文件file:  # 先创建/tmp/testfile文件(若已存在则不做操作)path: /tmp/testfilestate: touchowner: bqgroup: wheelmode: 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: node1gather_facts: notasks:- name: add line before Listen 80lineinfile:path: /etc/httpd/conf/httpd.conf  # 目标文件(httpd服务的配置文件)line: 'Listen 82'                 # 要插入的行(让httpd监听82端口)insertbefore: 'Listen 80'         # 插入位置:在"Listen 80"行之前state: present# 在"Listen 80"行之后插入"Listen 82"
---
- hosts: node1gather_facts: notasks:- name: add line after Listen 80lineinfile:path: /etc/httpd/conf/httpd.confline: 'Listen 82'insertafter: 'Listen 80'  # 插入位置:在"Listen 80"行之后state: present
...
实验 3:替换符合条件的行

实验流程

  1. 编写 playbook,用正则表达式匹配要替换的行,指定新内容;
  2. 执行 playbook,让 Ansible 替换目标行;
  3. 查看文件内容,验证匹配的行是否被正确替换。
# 替换包含"Add"的行
---
- hosts: node1gather_facts: notasks:- name: replace line  # 任务描述:替换行内容lineinfile:path: /tmp/testfileregexp: 'Add'     # 正则表达式:匹配所有包含"Add"的行line: 'replace'   # 替换后的内容(将匹配的行替换为"replace")state: present# 注释掉httpd的80端口监听(在行首加#)
---
- hosts: node1gather_facts: notasks:- name: comment Listen 80lineinfile:path: /etc/httpd/conf/httpd.confline: '#Listen 80'  # 替换后的内容(在原行首加#,实现注释)regexp: '^Listen 80'  # 正则表达式:匹配以"Listen 80"开头的行(^表示行首)state: present
...
replace 模块:批量替换文件中的内容

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

实验:替换所有匹配的行

实验流程

  1. 编写 playbook,定义目标文件、匹配规则(正则表达式)和替换内容;
  2. 执行 playbook,让 Ansible 批量替换文件内容;
  3. 查看文件内容,验证所有匹配的内容是否被替换。
---
- hosts: node1gather_facts: notasks:- 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: node1gather_facts: notasks:- name: add block lines to file  # 任务描述:添加多行文本块blockinfile:path: /tmp/testfile          # 目标文件(node1上的/tmp/testfile)block: |                     # 要添加的多行内容(| 表示保留换行符,保持原格式)line 1 in fileline 2 in fileaaline 3 in file sssstate: 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: node1gather_facts: notasks:- 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  # 可选:设置文件所有者为rootgroup: root  # 可选:设置文件所属组为rootmode: 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_contentwhen: 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: node1tasks:- name: 创建文件并写入内容copy:content: |  # 要写入的内容(| 表示保留换行,支持多行)这是第一行内容这是第二行内容可以包含多行字符串最后一行内容dest: /tmp/demo.txt  # 受管节点上的目标文件路径(会创建该文件)owner: root          # 可选:设置文件所有者为rootgroup: root          # 可选:设置文件所属组为rootmode: 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: rsyncstate: 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: node1gather_facts: noremote_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: node1gather_facts: notasks:- 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: node1tasks:- name: ensure latest version of httpd  # 安装httpd服务(用于提供web服务)yum:name: httpdstate: 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: httpdenabled: 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: node1vars:  # 定义模板中用到的变量(键值对形式)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/myhostshosts: all  # 必须对所有主机执行,才能收集所有主机的信息tasks:- name: generate /etc/myhoststemplate:src: hosts.j2dest: /etc/myhostswhen: 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")

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/93683.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/93683.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/93683.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vuex的使用

Vuex 超详细使用教程&#xff08;从入门到精通&#xff09;一、Vuex 是什么&#xff1f;Vuex 是专门为 Vue.js 设计的状态管理库&#xff0c;它采用集中式存储管理应用的所有组件的状态。简单来说&#xff0c;Vuex 就是一个"全局变量仓库"&#xff0c;所有组件都可以…

pytorch 数据预处理,加载,训练,可视化流程

流程定义自定义数据集类定义训练和验证的数据增强定义模型、损失函数和优化器训练循环&#xff0c;包括验证训练可视化整个流程模型评估高级功能扩展混合精度训练​分布式训练​{:width“50%” height“50%”} 定义自定义数据集类 # #1. 自定义数据集类 # class CustomImageD…

Prompt工程:OCR+LLM文档处理的精准制导系统

在PDF OCR与大模型结合的实际应用中&#xff0c;很多团队会发现一个现象&#xff1a;同样的OCR文本&#xff0c;不同的Prompt设计会产生截然不同的提取效果。有时候准确率能达到95%&#xff0c;有时候却只有60%。这背后的关键就在于Prompt工程的精细化程度。 &#x1f3af; 为什…

RecSys:粗排模型和精排特征体系

粗排 在推荐系统链路中&#xff0c;排序阶段至关重要&#xff0c;通常分为召回、粗排和精排三个环节。粗排作为精排前的预处理阶段&#xff0c;需要在效果和性能之间取得平衡。 双塔模型 后期融合&#xff1a;把用户、物品特征分别输入不同的神经网络&#xff0c;不对用户、…

spring声明式事务,finally 中return对事务回滚的影响

finally 块中使用 return 是一个常见的编程错误&#xff0c;它会&#xff1a; 跳过正常的事务提交流程。吞掉异常&#xff0c;使错误处理失效 导致不可预测的事务行为Java 中 finally 和 return 的执行机制&#xff1a;1. finally 块的基本特性 在 Java 中&#xff0c;finally …

WPF 打印报告图片大小的自适应(含完整示例与详解)

目标&#xff1a;在 FlowDocument 报告里&#xff0c;根据 1~6 张图片的数量&#xff0c; 自动选择 2 行 3 列 的最佳布局&#xff1b;在只有 1、2、4 张时保持“占满感”&#xff0c;打印清晰且不变形。规则一览&#xff1a;1 张 → 占满 23&#xff08;大图居中&#xff09;…

【AI大模型前沿】百度飞桨PaddleOCR 3.0开源发布,支持多语言、手写体识别,赋能智能文档处理

系列篇章&#x1f4a5; No.文章1【AI大模型前沿】深度剖析瑞智病理大模型 RuiPath&#xff1a;如何革新癌症病理诊断技术2【AI大模型前沿】清华大学 CLAMP-3&#xff1a;多模态技术引领音乐检索新潮流3【AI大模型前沿】浙大携手阿里推出HealthGPT&#xff1a;医学视觉语言大模…

迅为RK3588开发板Android12 制作使用系统签名

在 Android 源码 build/make/target/product/security/下存放着签名文件&#xff0c;如下所示&#xff1a;将北京迅为提供的 keytool 工具拷贝到 ubuntu 中&#xff0c;然后将 Android11 或 Android12 源码build/make/target/product/security/下的 platform.pk8 platform.x509…

Day08 Go语言学习

1.安装Go和Goland 2.新建demo项目实践语法并使用git实践版本控制操作 2.1 Goland配置 路径**&#xff1a;** GOPATH workspace GOROOT golang 文件夹&#xff1a; bin 编译后的可执行文件 pkg 编译后的包文件 src 源文件 遇到问题1&#xff1a;运行 ‘go build awesomeProject…

Linux-文件创建拷贝删除剪切

文章目录Linux文件相关命令ls通配符含义touch 创建文件命令示例cp 拷贝文件rm 删除文件mv剪切文件Linux文件相关命令 ls ls是英文单词list的简写&#xff0c;其功能为列出目录的内容&#xff0c;是用户最常用的命令之一&#xff0c;它类似于DOS下的dir命令。 Linux文件或者目…

RabbitMQ:交换机(Exchange)

目录一、概述二、Direct Exchange &#xff08;直连型交换机&#xff09;三、Fanout Exchange&#xff08;扇型交换机&#xff09;四、Topic Exchange&#xff08;主题交换机&#xff09;五、Header Exchange&#xff08;头交换机&#xff09;六、Default Exchange&#xff08;…

【实时Linux实战系列】基于实时Linux的物联网系统设计

随着物联网&#xff08;IoT&#xff09;技术的飞速发展&#xff0c;越来越多的设备被连接到互联网&#xff0c;形成了一个庞大而复杂的网络。这些设备从简单的传感器到复杂的工业控制系统&#xff0c;都在实时地产生和交换数据。实时Linux作为一种强大的操作系统&#xff0c;为…

第五天~提取Arxml中描述信息New_CanCluster--Expert

🔍 ARXML描述信息提取:挖掘汽车电子设计的"知识宝藏" 在AUTOSAR工程中,描述信息如同埋藏在ARXML文件中的金矿,而New_CanCluster--Expert正是打开这座宝藏的密钥。本文将带您深度探索ARXML描述信息的提取艺术,解锁汽车电子设计的核心知识资产! 💎 为什么描述…

开源 C++ QT Widget 开发(一)工程文件结构

文章的目的为了记录使用C 进行QT Widget 开发学习的经历。临时学习&#xff0c;完成app的开发。开发流程和要点有些记忆模糊&#xff0c;赶紧记录&#xff0c;防止忘记。 相关链接&#xff1a; 开源 C QT Widget 开发&#xff08;一&#xff09;工程文件结构-CSDN博客 开源 C…

手写C++ string类实现详解

类定义cppnamespace ym {class string {private:char* _str; // 字符串数据size_t _size; // 当前字符串长度size_t _capacity; // 当前分配的内存容量static const size_t npos -1; // 特殊值&#xff0c;表示最大可能位置public:// 构造函数和析构函数string(…

C++信息学奥赛一本通-第一部分-基础一-第3章-第2节

C信息学奥赛一本通-第一部分-基础一-第3章-第2节 2057 星期几 #include <iostream>using namespace std;int main() {int day; cin >> day;switch (day) {case 1:cout << "Monday";break;case 2:cout << "Tuesday";break;case 3:c…

【leetcode 3】最长连续序列 (Longest Consecutive Sequence) - 解题思路 + Golang实现

最长连续序列 (Longest Consecutive Sequence) - LeetCode 题解 题目描述 给定一个未排序的整数数组 nums&#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。要求设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#x…

矿物分类系统开发笔记(一):数据预处理

目录 一、数据基础与预处理目标 二、具体预处理步骤及代码解析 2.1 数据加载与初步清洗 2.2 标签编码 2.3 缺失值处理 &#xff08;1&#xff09;删除含缺失值的样本 &#xff08;2&#xff09;按类别均值填充 &#xff08;3&#xff09;按类别中位数填充 &#xff08;…

《UE5_C++多人TPS完整教程》学习笔记43 ——《P44 奔跑混合空间(Running Blending Space)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P44 奔跑混合空间&#xff08;Running Blending Space&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&…

TensorRT-LLM.V1.1.0rc1:Dockerfile.multi文件解读

一、TensorRT-LLM有三种安装方式&#xff0c;从简单到难 1.NGC上的预构建发布容器进行部署,见《tensorrt-llm0.20.0离线部署DeepSeek-R1-Distill-Qwen-32B》。 2.通过pip进行部署。 3.从源头构建再部署&#xff0c;《TensorRT-LLM.V1.1.0rc0:在无 GitHub 访问权限的服务器上编…