学习Ansible Playbook 核心语法
一、变量:让 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: 安装Nginxhosts: web_serversvars: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
再在 Playbook 中引入:# vars/nginx.yml nginx_pkg: nginx nginx_conf: /etc/nginx/nginx.confyaml
--- - name: 安装Nginxhosts: web_serversvars_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: alltasks:- name: 输出操作系统版本和IP地址debug:msg: "操作系统:{{ ansible_os_family }},IP地址:{{ ansible_default_ipv4.address }}"
若 Playbook 中无需使用 Facts,可关闭收集以提升执行速度:
yaml
---
- name: 无需Facts的任务hosts: allgather_facts: no # 关闭Facts收集tasks:- name: 安装Nginxyum: name=nginx state=present
(5)注册变量:保存任务执行结果
注册变量通过register关键字定义,用于保存某个任务的执行结果(如命令输出、模块返回值),适用于基于前序任务结果的后续操作(如判断命令是否执行成功、根据输出内容触发后续任务)。
使用示例:
yaml
---
- name: 检查Nginx服务状态hosts: web_serverstasks:- name: 执行systemctl is-active命令command: /usr/bin/systemctl is-active nginxregister: 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: 跨系统安装Nginxhosts: alltasks:# RedHat/CentOS系统使用yum安装- name: 安装Nginx(RedHat系列)yum:name: nginxstate: presentwhen: ansible_os_family == "RedHat" # 条件:操作系统为RedHat系列# Debian/Ubuntu系统使用apt安装- name: 安装Nginx(Debian系列)apt:name: nginxstate: presentwhen: ansible_os_family == "Debian" # 条件:操作系统为Debian系列
示例 2:根据注册变量结果执行任务
yaml
---
- name: 检查并重启Postfix服务hosts: mail_serverstasks:- name: 获取Postfix状态command: systemctl is-active postfixregister: postfix_statusignore_errors: yes# 若Postfix运行中(返回码rc=0),则重启Apache- name: 重启Apacheservice:name: httpdstate: restartedwhen: 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_serverstasks:- 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_serversvars:nginx_conf_path: /etc/nginx/nginx.conftasks:- name: 若配置文件存在,则备份copy:src: "{{ nginx_conf_path }}"dest: "{{ nginx_conf_path }}.bak"remote_src: yeswhen: nginx_conf_path is exists # 判断文件是否存在
2.4 条件块:block/rescue/always
当多个任务需要使用相同条件时,可通过block将任务分组,避免重复编写when。此外,block还支持rescue(任务失败时执行)和always(无论成功 / 失败都执行),实现错误处理。
示例 1:批量执行条件任务
yaml
---
- name: 配置Ubuntu 16.04的DNShosts: web_serverstasks:- name: 通用DNS配置(所有系统)template:src: resolv.conf.j2dest: /etc/resolv.conf# 仅Ubuntu 16.04执行以下两个任务- block:- name: 配置Ubuntu 16.04的DNS基础文件template:src: resolv.conf.j2dest: /etc/resolvconf/resolv.conf.d/base- name: 重启resolvconf服务service:name: resolvconfstate: restartedwhen: ansible_distribution == "Ubuntu" and ansible_distribution_major_version == "16"
示例 2:错误处理(rescue/always)
yaml
---
- name: 检查/testdir目录hosts: alltasks:- block:# 尝试列出/testdir目录(若目录不存在则失败)- name: 列出/testdir内容command: ls /testdirrescue:# 若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_serverstasks:- name: 启动httpd、postfix、sshd服务service:name: "{{ item }}"state: startedloop:- httpd- postfix- sshd
示例 2:循环使用变量列表先定义变量列表,再在loop中引用:
yaml
---
- name: 批量创建用户hosts: web_serversvars:# 用户列表:每个元素是字典(包含用户名和所属组)users:- { name: "user1", groups: "wheel" }- { name: "user2", groups: "www" }- { name: "user3", groups: "ftp" }tasks:- name: 创建用户并指定所属组user:name: "{{ item.name }}"groups: "{{ item.groups }}"state: presentloop: "{{ users }}" # 循环用户列表
3.2 循环中注册变量
若需保存循环中每个任务的执行结果,可在循环任务中使用register,结果会以列表形式存储在变量中。
示例:批量检查文件是否存在
yaml
---
- name: 检查多个文件hosts: localhosttasks:- name: 检查/etc/hosts、/etc/passwd、/etc/groupstat:path: "{{ item }}"loop:- /etc/hosts- /etc/passwd- /etc/groupregister: 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: localhostvars:users:alice:age: 25role: developerbob:age: 30role: testertasks:- name: 输出用户信息debug:msg: "用户名:{{ item.key }},年龄:{{ item.value.age }},角色:{{ item.value.role }}"with_dict: "{{ users }}" # 循环字典
3.4 循环与条件结合
可在循环中使用when,实现 "筛选式循环"(仅执行满足条件的循环项)。
示例:仅安装内存大于 2GB 的主机的 Nginx
yaml
---
- name: 按需安装Nginxhosts: alltasks:- name: 内存大于2GB时安装Nginxyum:name: nginxstate: present# 条件:内存总量(MB)> 2048when: ansible_memory_mb.real.total > 2048
示例:循环中筛选元素
yaml
---
- name: 仅打印大于5的数字hosts: localhosttasks:- name: 输出5以上的数字command: echo "{{ item }}"loop: [1,2,3,4,5,6,7,8]when: item > 5 # 仅循环项大于5时执行
四、总结
变量、条件语句和循环语句是 Ansible Playbook 的核心能力,三者结合可实现复杂的自动化场景:
- 变量:解决数据复用问题,让 Playbook 更灵活;
- 条件语句:解决 "按需执行" 问题,让任务更智能;
- 循环语句:解决 "重复操作" 问题,让代码更简洁。
