一、变量:让 Playbook 更灵活
变量是 Playbook 的 "数据容器",用于存储和复用信息(如主机地址、软件版本、文件路径等),避免重复编写固定值。Ansible 中的变量按作用范围可分为五大类,不同类型的变量适用场景不同,且存在明确的优先级规则。
1.1 变量的命名规则
在定义变量前,需遵守以下规则:
- 变量名由字母、数字和下划线组成,必须以字母开头 (如
web_port合法,123_port非法); - 不能使用 Ansible 的保留关键字(如
task、play、vars等)。
1.2 五大变量类型及使用场景
(1)全局变量:临时传递的变量
全局变量通过ansible或ansible-playbook命令的-e参数手动传递,适用于临时修改变量值的场景(如临时指定目标主机、软件版本)。
使用示例:
-
传递单个键值对: bash
# 临时指定user变量的值为"admin",并执行debug模块 ansible all -i localhost, -m debug -a "msg='当前用户:{{ user }}'" -e "user=admin" -
传递 JSON/YAML 文件:先创建
vars.json文件:json
{"name": "nginx", "version": "1.24.0"}再通过
-e @文件路径传递:bash
ansible-playbook -i hosts deploy.yml -e @vars.json
(2)剧本变量:定义在 Playbook 中的变量
剧本变量直接在 Playbook 内定义,作用范围仅限当前 Playbook,适用于固定在任务中的变量(如默认安装路径、服务名)。定义方式有两种:
-
通过
vars属性直接定义 :yaml
--- - name: 安装Nginx hosts: web_servers vars: nginx_pkg: nginx # 软件包名 nginx_conf: /etc/nginx/nginx.conf # 配置文件路径 tasks: - name: 安装Nginx包 yum: name: "{{ nginx_pkg }}" state: present -
通过
vars_files引入外部文件 :当变量较多时,可将变量抽离到单独的 YAML 文件(如vars/nginx.yml):yaml
# vars/nginx.yml nginx_pkg: nginx nginx_conf: /etc/nginx/nginx.conf再在 Playbook 中引入:
yaml
--- - name: 安装Nginx hosts: web_servers vars_files: - vars/nginx.yml # 引入外部变量文件 tasks: - name: 安装Nginx包 yum: name: "{{ nginx_pkg }}" state: present
(3)资产变量:针对主机 / 主机组的变量
资产变量定义在 Ansible 的 inventory(资产清单)中,分为主机变量 (仅对单个主机生效)和主机组变量(对整个主机组生效),适用于不同主机的个性化配置(如不同主机的 SSH 端口、数据库密码)。
使用示例 :编辑 inventory 文件hosts:
ini
# 主机组:web_servers
[web_servers]
192.168.1.101 ssh_port=2222 # 主机变量:该主机的SSH端口为2222
192.168.1.102
# 主机组变量:对web_servers组所有主机生效
[web_servers:vars]
db_host=192.168.1.200 # 数据库地址
db_port=3306 # 数据库端口
验证变量:
bash
# 查看192.168.1.101的ssh_port变量
ansible 192.168.1.101 -i hosts -m debug -a "var=ssh_port"
# 查看web_servers组的db_host变量
ansible web_servers -i hosts -m debug -a "var=db_host"
(4)Facts 变量:自动收集的主机信息
Facts 变量是 Ansible 通过setup模块自动收集的被管理主机信息(如操作系统版本、IP 地址、内存大小),无需手动定义,适用于基于主机状态的动态配置(如根据操作系统选择安装命令)。
使用示例:
-
手动收集 Facts 信息: bash
# 收集localhost的所有Facts信息 ansible localhost -m setup # 过滤内存相关信息(使用filter参数) ansible localhost -m setup -a "filter=*memory*" -
在 Playbook 中使用 Facts: yaml
--- - name: 打印主机信息 hosts: all tasks: - name: 输出操作系统版本和IP地址 debug: msg: "操作系统:{{ ansible_os_family }},IP地址:{{ ansible_default_ipv4.address }}"
若 Playbook 中无需使用 Facts,可关闭收集以提升执行速度:
yaml
---
- name: 无需Facts的任务
hosts: all
gather_facts: no # 关闭Facts收集
tasks:
- name: 安装Nginx
yum: name=nginx state=present
(5)注册变量:保存任务执行结果
注册变量通过register关键字定义,用于保存某个任务的执行结果(如命令输出、模块返回值),适用于基于前序任务结果的后续操作(如判断命令是否执行成功、根据输出内容触发后续任务)。
使用示例:
yaml
---
- name: 检查Nginx服务状态
hosts: web_servers
tasks:
- name: 执行systemctl is-active命令
command: /usr/bin/systemctl is-active nginx
register: nginx_status # 将命令结果保存到nginx_status变量
ignore_errors: yes # 忽略命令执行失败(如Nginx未启动)
- name: 输出Nginx状态
debug:
msg: "Nginx状态:{{ nginx_status.stdout }}" # 打印命令标准输出
1.3 变量优先级规则
当不同类型的变量重名时,优先级从高到低为:全局变量(-e参数) > 剧本变量(vars/vars_files) > 主机变量 > 主机组变量
例如:若全局变量、剧本变量、主机组变量均定义了user,最终生效的是全局变量。
二、条件语句:根据状态执行任务
条件语句用于根据变量、Facts 或前序任务结果,决定是否执行某个任务,核心关键字是when。常见场景包括:基于操作系统选择安装命令、判断服务状态决定是否重启、检查文件是否存在等。
2.1 when关键字的基本使用
when后面跟随 Python 表达式,表达式结果为true时执行任务,false时跳过任务。
示例 1:根据操作系统安装软件
yaml
---
- name: 跨系统安装Nginx
hosts: all
tasks:
# RedHat/CentOS系统使用yum安装
- name: 安装Nginx(RedHat系列)
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat" # 条件:操作系统为RedHat系列
# Debian/Ubuntu系统使用apt安装
- name: 安装Nginx(Debian系列)
apt:
name: nginx
state: present
when: ansible_os_family == "Debian" # 条件:操作系统为Debian系列
示例 2:根据注册变量结果执行任务
yaml
---
- name: 检查并重启Postfix服务
hosts: mail_servers
tasks:
- name: 获取Postfix状态
command: systemctl is-active postfix
register: postfix_status
ignore_errors: yes
# 若Postfix运行中(返回码rc=0),则重启Apache
- name: 重启Apache
service:
name: httpd
state: restarted
when: postfix_status.rc == 0
2.2 常用运算符
在when表达式中,可使用比较运算符和逻辑运算符组合条件:
| 类型 | 运算符 | 说明 | 示例 |
|---|---|---|---|
| 比较运算符 | == |
等于 | ansible_machine == "x86_64" |
!= |
不等于 | ansible_distribution != "Ubuntu" |
|
>/</>=/<= |
大小比较(数字 / 版本号) | ansible_memory_mb.real.total > 2048 |
|
| 逻辑运算符 | and |
逻辑与(同时满足) | a == 1 and b == 2 |
or |
逻辑或(满足一个即可) | a == 1 or b == 2 |
|
not |
逻辑非(取反) | not (a == 1) |
|
() |
组合条件(提升优先级) | (os == "RedHat" and ver == "7") or (os == "Ubuntu" and ver == "20.04") |
示例:组合条件判断
yaml
---
- name: 版本兼容性检查
hosts: web_servers
tasks:
- name: 仅在CentOS 7或Ubuntu 20.04上执行
debug:
msg: "当前系统符合要求"
when:
(ansible_distribution == "CentOS" and ansible_distribution_version == "7") or
(ansible_distribution == "Ubuntu" and ansible_distribution_version == "20.04")
2.3 条件判断与 Tests
Ansible 支持使用 Jinja2 模板的tests功能,实现更丰富的条件判断(如判断变量是否定义、路径是否存在、字符串是否为小写)。
常用 Tests 场景:
| Tests | 说明 | 示例 |
|---|---|---|
defined/undefined |
判断变量是否已定义 / 未定义 | when: nginx_port is defined |
exists |
判断路径是否存在(注意:检查主控端路径) | when: "/etc/nginx" is exists |
file/directory |
判断路径是否为文件 / 目录 | when: "/etc/nginx/nginx.conf" is file |
lower/upper |
判断字符串是否全为小写 / 大写 | when: "hello" is lower |
even/odd |
判断数字是否为偶数 / 奇数 | when: 6 is even |
version |
版本号比较 | when: ansible_distribution_version is version("7.5", "ge") # 大于等于 7.5 |
示例:使用 Tests 判断文件是否存在
yaml
---
- name: 检查Nginx配置文件
hosts: web_servers
vars:
nginx_conf_path: /etc/nginx/nginx.conf
tasks:
- name: 若配置文件存在,则备份
copy:
src: "{{ nginx_conf_path }}"
dest: "{{ nginx_conf_path }}.bak"
remote_src: yes
when: nginx_conf_path is exists # 判断文件是否存在
2.4 条件块:block/rescue/always
当多个任务需要使用相同条件时,可通过block将任务分组,避免重复编写when。此外,block还支持rescue(任务失败时执行)和always(无论成功 / 失败都执行),实现错误处理。
示例 1:批量执行条件任务
yaml
---
- name: 配置Ubuntu 16.04的DNS
hosts: web_servers
tasks:
- name: 通用DNS配置(所有系统)
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
# 仅Ubuntu 16.04执行以下两个任务
- block:
- name: 配置Ubuntu 16.04的DNS基础文件
template:
src: resolv.conf.j2
dest: /etc/resolvconf/resolv.conf.d/base
- name: 重启resolvconf服务
service:
name: resolvconf
state: restarted
when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version == "16"
示例 2:错误处理(rescue/always)
yaml
---
- name: 检查/testdir目录
hosts: all
tasks:
- block:
# 尝试列出/testdir目录(若目录不存在则失败)
- name: 列出/testdir内容
command: ls /testdir
rescue:
# 若block任务失败,执行此处(如提示目录不存在)
- name: 目录不存在时的提示
debug:
msg: "/testdir目录不存在!"
always:
# 无论block成功/失败,都执行此处(如记录日志)
- name: 记录任务执行时间
debug:
msg: "任务执行时间:{{ ansible_date_time.iso8601 }}"
三、循环语句:批量执行重复任务
循环语句用于批量执行相同操作(如批量安装软件、创建用户、复制文件),核心关键字是loop(Ansible 2.5 + 推荐),替代了旧版的with_*语法(如with_items、with_dict)。
3.1 loop关键字的基本使用
loop接收一个列表,通过{``{ item }}引用列表中的每个元素,实现循环执行。
示例 1:批量启动服务
yaml
---
- name: 启动多个服务
hosts: web_servers
tasks:
- name: 启动httpd、postfix、sshd服务
service:
name: "{{ item }}"
state: started
loop:
- httpd
- postfix
- sshd
示例 2:循环使用变量列表 先定义变量列表,再在loop中引用:
yaml
---
- name: 批量创建用户
hosts: web_servers
vars:
# 用户列表:每个元素是字典(包含用户名和所属组)
users:
- { name: "user1", groups: "wheel" }
- { name: "user2", groups: "www" }
- { name: "user3", groups: "ftp" }
tasks:
- name: 创建用户并指定所属组
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop: "{{ users }}" # 循环用户列表
3.2 循环中注册变量
若需保存循环中每个任务的执行结果,可在循环任务中使用register,结果会以列表形式存储在变量中。
示例:批量检查文件是否存在
yaml
---
- name: 检查多个文件
hosts: localhost
tasks:
- name: 检查/etc/hosts、/etc/passwd、/etc/group
stat:
path: "{{ item }}"
loop:
- /etc/hosts
- /etc/passwd
- /etc/group
register: file_check_result # 保存循环结果
- name: 输出文件检查结果
debug:
msg: "文件{{ item.item }}是否存在:{{ item.stat.exists }}"
loop: "{{ file_check_result.results }}" # 遍历循环结果列表
3.3 旧版循环语法(with_*)
Ansible 2.5 以前使用with_*语法实现循环,目前仍兼容但不推荐。以下是常见旧版循环的使用场景:
| 语法 | 说明 | 示例 |
|---|---|---|
with_items |
简单列表循环(等同于loop) |
with_items: [1,2,3] |
with_dict |
循环字典(遍历键值对) | with_dict: { "a":1, "b":2 } |
with_fileglob |
循环指定目录的文件(主控端) | with_fileglob: "/root/*.pub" |
with_lines |
循环命令输出或文件内容(逐行) | with_lines: "cat /tmp/users.txt" |
with_sequence |
生成整数序列(指定起始 / 结束 / 步长) | with_sequence: start=1 end=5 stride=2 |
示例:使用with_dict循环字典
yaml
---
- name: 遍历用户字典
hosts: localhost
vars:
users:
alice:
age: 25
role: developer
bob:
age: 30
role: tester
tasks:
- name: 输出用户信息
debug:
msg: "用户名:{{ item.key }},年龄:{{ item.value.age }},角色:{{ item.value.role }}"
with_dict: "{{ users }}" # 循环字典
3.4 循环与条件结合
可在循环中使用when,实现 "筛选式循环"(仅执行满足条件的循环项)。
示例:仅安装内存大于 2GB 的主机的 Nginx
yaml
---
- name: 按需安装Nginx
hosts: all
tasks:
- name: 内存大于2GB时安装Nginx
yum:
name: nginx
state: present
# 条件:内存总量(MB)> 2048
when: ansible_memory_mb.real.total > 2048
示例:循环中筛选元素
yaml
---
- name: 仅打印大于5的数字
hosts: localhost
tasks:
- name: 输出5以上的数字
command: echo "{{ item }}"
loop: [1,2,3,4,5,6,7,8]
when: item > 5 # 仅循环项大于5时执行
四、总结
变量、条件语句和循环语句是 Ansible Playbook 的核心能力,三者结合可实现复杂的自动化场景:
- 变量:解决数据复用问题,让 Playbook 更灵活;
- 条件语句:解决 "按需执行" 问题,让任务更智能;
- 循环语句:解决 "重复操作" 问题,让代码更简洁。