4.Ansible自动化之-部署文件到主机
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 的touch
、mkdir
、rm
、chmod
等命令的组合)。
实验 1:创建文件并设置权限
实验流程:
- 编写 playbook,定义要创建的文件路径、所有者、权限等信息;
- 执行 playbook,让 Ansible 在目标主机上执行创建操作;
- 登录受管节点,检查文件是否创建成功,以及属性是否符合预期。
---
- 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:创建目录
实验流程:
- 编写 playbook,指定要创建的目录路径、所有者、权限等;
- 执行 playbook,让 Ansible 在目标主机上创建目录;
- 登录受管节点,验证目录是否存在,以及所有者、权限是否正确。
---
- 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:删除文件
实验流程:
- 编写 playbook,指定要删除的文件路径;
- 执行 playbook,让 Ansible 在目标主机上删除文件;
- 登录受管节点,确认文件已被删除。
---
- 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 上下文规则
实验流程:
- 编写 playbook,定义要设置的文件路径和对应的 SELinux 类型;
- 执行 playbook,让 Ansible 在目标主机上添加 SELinux 规则;
- (可选)在受管节点执行
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:向文件添加特定行
实验流程:
- 编写 playbook,先创建目标文件,再指定要添加的行内容;
- 执行 playbook,让 Ansible 在目标文件中添加行;
- 查看文件内容,验证行是否添加成功。
---
- 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:在指定位置插入行
实验流程:
- 编写 playbook,指定目标文件、要插入的行,以及插入位置(某行之前 / 之后);
- 执行 playbook,让 Ansible 在指定位置插入行;
- 查看文件内容,验证行是否插入到正确位置。
# 在"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:替换符合条件的行
实验流程:
- 编写 playbook,用正则表达式匹配要替换的行,指定新内容;
- 执行 playbook,让 Ansible 替换目标行;
- 查看文件内容,验证匹配的行是否被正确替换。
# 替换包含"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'
命令,会替换所有匹配项,而不仅是单行)。
实验:替换所有匹配的行
实验流程:
- 编写 playbook,定义目标文件、匹配规则(正则表达式)和替换内容;
- 执行 playbook,让 Ansible 批量替换文件内容;
- 查看文件内容,验证所有匹配的内容是否被替换。
---
- 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
),方便后续识别和更新(再次执行时会更新块内内容,而不是重复添加)。
实验:向文件添加多行文本块
实验流程:
- 编写 playbook,定义目标文件和要添加的多行内容;
- 执行 playbook,让 Ansible 在目标文件中插入文本块;
- 查看文件内容,验证文本块是否添加,及是否包含自动生成的标记。
---
- 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 校验和
实验流程:
- 编写 playbook,用 stat 模块获取文件信息并保存到变量;
- 执行 playbook,通过 debug 模块输出文件的 MD5 校验和;
- 查看输出结果,验证是否正确获取校验和。
---
- 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:复制控制节点文件到受管节点
实验流程:
- 在控制节点准备好要复制的文件(如
/tmp/testfile
); - 编写 playbook,指定控制节点的源文件路径和受管节点的目标路径;
- 执行 playbook,让 Ansible 完成文件复制;
- 在受管节点查看目标路径,验证文件是否复制成功。
---
- 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:直接写入字符串到文件
实验流程:
- 编写 playbook,用
content
参数定义要写入的文件内容; - 执行 playbook,让 Ansible 在受管节点创建文件并写入内容;
- 在受管节点查看文件内容,验证是否正确。
---
- 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:同步单个文件
实验流程:
- 确保控制节点和受管节点都安装
rsync
(可通过yum install -y rsync
安装); - 编写 playbook,指定控制节点的源文件路径和受管节点的目标路径;
- 执行 playbook,让 Ansible 通过 rsync 同步文件;
- 在受管节点验证文件是否同步成功。
---
- 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:同步目录
实验流程:
- 在控制节点准备要同步的目录(如
/etc/sysconfig
); - 编写 playbook,指定控制节点的源目录和受管节点的目标路径;
- 执行 playbook,让 Ansible 同步目录;
- 在受管节点查看目标路径,验证目录内容是否同步成功。
---
- hosts: node1gather_facts: noremote_user: root # 同步系统目录可能需要root权限tasks:- name: synchronize directory # 任务描述:同步目录synchronize:src: /etc/sysconfig # 控制节点上的源目录(/etc/sysconfig,包含系统服务配置)dest: /tmp/ # 受管节点上的目标路径(同步后会在/tmp下创建sysconfig子目录,即/tmp/sysconfig)
...
fetch 模块:从受管节点获取文件到控制节点
模块作用:将受管节点的文件复制到控制节点,常用于收集日志、配置文件等。文件会按 “控制节点目标路径 / 受管节点名 / 原文件路径” 的结构保存(方便区分不同节点的文件)。
实验:从受管节点获取文件
实验流程:
- 编写 playbook,指定受管节点的源文件路径和控制节点的保存路径;
- 执行 playbook,让 Ansible 从受管节点复制文件到控制节点;
- 在控制节点查看保存路径,验证文件是否按预期结构保存。
---
- 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 会自动替换变量、执行逻辑,生成个性化的配置文件。
基础示例:部署动态网页
实验流程:
- 创建 Jinja2 模板文件(包含变量,如
{{ ansible_fqdn }}
); - 编写 playbook,用
template
模块部署模板到受管节点; - 执行 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 配置文件
实验流程:
- 创建含多个变量的 Jinja2 模板(如
{{ ssh_port }}
、{{ root_allowed }}
); - 在 playbook 中定义模板变量的值;
- 部署模板到受管节点,验证生成的配置文件是否正确替换变量。
步骤 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")
如涉及版权问题请联系作者处理!!!!