一、循环语句核心价值
Ansible 循环语句用于批量执行重复任务,避免在 Playbook 中重复编写相同逻辑的任务(如批量创建用户、批量安装软件、批量创建目录等),大幅简化 Playbook 编写,提升自动化效率。
二、循环的核心对象
| 循环类型 | 适用场景 | 核心关键字 | 特点 |
|---|---|---|---|
| 列表循环 | 批量处理单一维度的重复任务(如批量创建同名规则的用户/文件) | with_items/loop |
结构简单,仅需遍历列表元素 |
| 字典循环 | 批量处理多维度的重复任务(如创建不同UID、不同家目录的用户) | with_dict/loop + dict2items |
可同时遍历"键-值"对,支持复杂属性配置 |
三、基础循环:列表循环(with_items)
1. 核心概念
with_items 是 Ansible 早期的列表循环关键字,用于遍历列表(List) 类型数据,循环中通过 {``{ item }} 引用当前遍历的列表元素。
2. 语法格式(三种常见写法)
写法1:变量定义列表(推荐,便于维护)
yaml
- hosts: node01
gather_facts: no
vars:
# 定义列表变量:待创建的用户名
user_list:
- alice
- bob
- john
- tom
tasks:
- name: 批量创建用户(列表循环)
user: # user模块用于管理系统用户
name: "{{ item }}" # item代表当前循环的列表元素(依次为alice/bob/john/tom)
state: present # state=present表示创建用户(absent为删除)
with_items: "{{ user_list }}" # 指定循环的列表变量
写法2:直接写列表(简单场景)
yaml
- hosts: node01
gather_facts: no
tasks:
- name: 批量创建用户
user:
name: "{{ item }}"
state: present
with_items: # 直接列出循环元素,无需定义变量
- alice
- bob
- john
- tom
写法3:列表简写(极简场景)
yaml
- hosts: node01
gather_facts: no
tasks:
- name: 打印用户名(仅调试)
debug: # debug模块用于输出信息
msg: "当前遍历的用户:{{ item }}"
with_items: ["alice", "tom", "bob", "john"] # 列表简写形式
3. 关键说明
item是循环的默认变量,代表当前遍历的列表元素,不可自定义名称;- 列表元素可以是字符串、数字等简单类型;
- 适用场景:批量创建用户、批量安装软件、批量创建目录等"单一属性"的重复任务。
4. 扩展示例(批量创建目录)
yaml
- hosts: node01
gather_facts: no
tasks:
- name: 批量创建目录
file: # file模块用于管理文件/目录
path: "/data/{{ item }}" # 目录路径:/data/log、/data/data、/data/backup
state: directory # state=directory表示创建目录
mode: "0755" # 目录权限
with_items:
- log
- data
- backup
四、进阶循环:字典循环(with_dict)
1. 核心概念
当需要批量处理多属性 的重复任务(如创建用户时指定UID、家目录、备注等),列表无法满足需求,需使用字典(Dict) 存储多维度数据,通过 with_dict 遍历字典。
2. 字典结构说明
yaml
# 示例字典:users(键为用户分组,值为用户属性)
users:
dev: # 键(key):dev(开发组)
name: natasha # 值(value):用户属性(名称、UID、家目录、备注)
uid: 6001
home: /data/natasha
comment: "Dev User"
ops: # 键(key):ops(运维组)
name: harry
uid: 6002
home: /data/harry
comment: "Ops User"
with_dict遍历字典时,item.key代表字典的"键"(如dev/ops),item.value代表字典的"值"(如natasha的所有属性);- 如需引用具体属性,需用
item.value.属性名(如item.value.name表示用户名)。
3. 完整示例(批量创建带属性的用户)
步骤1:创建变量文件(user_vars.yml,可选,便于维护)
yaml
# user_vars.yml
users:
dev:
name: natasha
uid: 6001
home: /data/natasha
comment: "Dev User"
ops:
name: harry
uid: 6002
home: /data/harry
comment: "Ops User"
步骤2:编写Playbook
yaml
- hosts: node01
gather_facts: no
vars_files: # 加载外部变量文件
- user_vars.yml
tasks:
- name: 批量创建带属性的用户(字典循环)
user:
name: "{{ item.value.name }}" # 引用用户名(natasha/harry)
uid: "{{ item.value.uid }}" # 引用UID(6001/6002)
home: "{{ item.value.home }}" # 引用家目录
comment: "{{ item.value.comment }}" # 引用备注
state: present
with_dict: "{{ users }}" # 指定循环的字典变量
4. 适用场景
- 批量创建带自定义属性的用户(UID、家目录、备注);
- 批量配置不同参数的服务(如不同端口、不同配置文件的Nginx虚拟主机);
- 批量部署不同版本的应用(如不同目录、不同启动参数的应用程序)。
五、统一循环:loop(Ansible 2.5+ 推荐)
1. 核心概念
Ansible 2.5 后推出 loop 关键字,统一所有循环场景 (列表/字典/文件等),替代旧的 with_* 系列关键字(如 with_items/with_dict),是官方推荐的循环方式。
2. loop 遍历列表(替代 with_items)
loop 天生支持列表遍历,用法与 with_items 几乎一致,仅需将 with_items 替换为 loop。
示例(批量创建用户):
yaml
- hosts: node01
gather_facts: no
vars:
user_list: ["alice", "bob", "tom"]
tasks:
- name: 批量创建用户(loop列表循环)
user:
name: "{{ item }}"
state: present
loop: "{{ user_list }}" # 替代with_items
3. loop 遍历字典(需 dict2items 过滤器)
loop 仅支持列表遍历,遍历字典时需用 dict2items 过滤器将字典转换为列表 ,转换后列表的每个元素是包含 key 和 value 的子字典。
转换原理
yaml
# 原字典
users:
dev: {name: natasha, uid: 6001}
ops: {name: harry, uid: 6002}
# dict2items转换后(列表形式)
[
{"key": "dev", "value": {"name": "natasha", "uid": 6001}},
{"key": "ops", "value": {"name": "harry", "uid": 6002}}
]
完整示例(loop 遍历字典)
yaml
- hosts: node01
gather_facts: no
vars_files:
- user_vars.yml
tasks:
- name: 批量创建用户(loop字典循环)
user:
name: "{{ item.value.name }}"
uid: "{{ item.value.uid }}"
home: "{{ item.value.home }}"
comment: "{{ item.value.comment }}"
state: present
loop: "{{ users | dict2items }}" # 字典转列表后遍历
4. loop 优势
- 语法统一:无需记忆
with_items/with_dict等不同关键字; - 扩展性强:支持更多过滤器(如
flatten展平嵌套列表、shuffle随机排序); - 兼容性好:Ansible 后续版本会优先维护
loop,逐步淘汰with_*。
六、循环进阶技巧(初学者必备)
1. 嵌套列表循环(flatten 过滤器)
若列表包含子列表(嵌套列表),直接遍历会导致 item 是子列表而非元素,需用 flatten 展平。
示例:
yaml
- hosts: node01
gather_facts: no
vars:
nested_list:
- [alice, bob]
- [tom, john]
tasks:
- name: 遍历嵌套列表
debug:
msg: "{{ item }}"
loop: "{{ nested_list | flatten }}" # 展平为[alice, bob, tom, john]
2. 循环结果注册(register + loop)
批量执行任务后,可通过 register 注册所有循环的执行结果,便于后续判断。
示例:
yaml
- hosts: node01
gather_facts: no
tasks:
- name: 批量创建用户并注册结果
user:
name: "{{ item }}"
state: present
loop: ["alice", "bob"]
register: create_result # 注册所有循环的结果
- name: 打印循环结果
debug:
msg: "创建{{ item.item }}的结果:{{ item.changed }}" # item.item是原循环元素,item.changed是执行状态
loop: "{{ create_result.results }}" # 遍历注册的结果列表
3. 循环条件过滤(when + loop)
循环中可结合 when 条件,仅满足条件的元素执行任务。
示例(仅创建UID大于6000的用户):
yaml
- hosts: node01
gather_facts: no
vars:
users:
dev: {name: natasha, uid: 6001}
test: {name: testuser, uid: 5001}
tasks:
- name: 仅创建UID>6000的用户
user:
name: "{{ item.value.name }}"
uid: "{{ item.value.uid }}"
state: present
loop: "{{ users | dict2items }}"
when: item.value.uid > 6000 # 仅UID>6000时执行
七、练习题(从易到难)
基础题
- 编写Playbook:使用
loop批量安装nginx、mariadb-server、vsftpd三个软件(CentOS系统)。 - 编写Playbook:使用
loop批量创建/opt/dev、/opt/ops、/opt/test三个目录,权限均为0755,属主为root。
进阶题
- 编写Playbook:通过字典定义以下用户信息,使用
loop + dict2items批量创建用户:- 销售组(sale):用户名sale01,UID 7001,家目录/opt/sale01,备注"Sale User";
- 财务组(finance):用户名finance01,UID 7002,家目录/opt/finance01,备注"Finance User"。
- 编写Playbook:批量创建用户
user01、user02、user03,仅当主机内存大于4096MB时执行,且注册创建结果,最后打印每个用户的创建状态(是否变更)。
综合题
- 编写Playbook实现:
- 定义字典变量,包含3个Nginx虚拟主机配置:
- web1:域名web1.example.com,端口8080,根目录/var/www/web1;
- web2:域名web2.example.com,端口8081,根目录/var/www/web2;
- 使用
loop遍历字典,批量创建每个虚拟主机的根目录; - 批量生成每个虚拟主机的Nginx配置文件(/etc/nginx/conf.d/{{ item.key }}.conf);
- 仅当配置文件生成成功时,重启Nginx服务。
- 定义字典变量,包含3个Nginx虚拟主机配置:
练习题参考答案(核心片段)
基础题1
yaml
- hosts: node01
gather_facts: no
tasks:
- name: 批量安装软件
yum:
name: "{{ item }}"
state: present
loop: ["nginx", "mariadb-server", "vsftpd"]
基础题2
yaml
- hosts: node01
gather_facts: no
tasks:
- name: 批量创建目录
file:
path: "/opt/{{ item }}"
state: directory
mode: "0755"
owner: root
group: root
loop: ["dev", "ops", "test"]
进阶题3
yaml
- hosts: node01
gather_facts: no
vars:
users:
sale:
name: sale01
uid: 7001
home: /opt/sale01
comment: "Sale User"
finance:
name: finance01
uid: 7002
home: /opt/finance01
comment: "Finance User"
tasks:
- name: 批量创建带属性的用户
user:
name: "{{ item.value.name }}"
uid: "{{ item.value.uid }}"
home: "{{ item.value.home }}"
comment: "{{ item.value.comment }}"
state: present
loop: "{{ users | dict2items }}"
进阶题4
yaml
- hosts: node01
gather_facts: yes # 需收集内存信息
tasks:
- name: 批量创建用户(仅内存>4G)
user:
name: "{{ item }}"
state: present
loop: ["user01", "user02", "user03"]
when: ansible_memtotal_mb > 4096
register: create_result
- name: 打印每个用户的创建状态
debug:
msg: "用户{{ item.item }}创建状态:{{ '已创建/更新' if item.changed else '已存在' }}"
loop: "{{ create_result.results }}"
综合题5(核心片段)
yaml
- hosts: node01
gather_facts: no
vars:
nginx_vhosts:
web1:
domain: web1.example.com
port: 8080
root: /var/www/web1
web2:
domain: web2.example.com
port: 8081
root: /var/www/web2
tasks:
# 1. 批量创建根目录
- name: 创建虚拟主机根目录
file:
path: "{{ item.value.root }}"
state: directory
mode: "0755"
loop: "{{ nginx_vhosts | dict2items }}"
register: dir_result
# 2. 批量生成Nginx配置文件
- name: 生成虚拟主机配置
copy:
content: |
server {
listen {{ item.value.port }};
server_name {{ item.value.domain }};
root {{ item.value.root }};
}
dest: "/etc/nginx/conf.d/{{ item.key }}.conf"
loop: "{{ nginx_vhosts | dict2items }}"
register: conf_result
# 3. 仅配置生成成功时重启Nginx
- name: 重启Nginx
service:
name: nginx
state: restarted
when: conf_result is changed