Ansible自动化(九):循环语句

一、循环语句核心价值

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 过滤器将字典转换为列表 ,转换后列表的每个元素是包含 keyvalue 的子字典。

转换原理
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时执行

七、练习题(从易到难)

基础题

  1. 编写Playbook:使用 loop 批量安装 nginxmariadb-servervsftpd 三个软件(CentOS系统)。
  2. 编写Playbook:使用 loop 批量创建 /opt/dev/opt/ops/opt/test 三个目录,权限均为0755,属主为root。

进阶题

  1. 编写Playbook:通过字典定义以下用户信息,使用 loop + dict2items 批量创建用户:
    • 销售组(sale):用户名sale01,UID 7001,家目录/opt/sale01,备注"Sale User";
    • 财务组(finance):用户名finance01,UID 7002,家目录/opt/finance01,备注"Finance User"。
  2. 编写Playbook:批量创建用户 user01user02user03,仅当主机内存大于4096MB时执行,且注册创建结果,最后打印每个用户的创建状态(是否变更)。

综合题

  1. 编写Playbook实现:
    • 定义字典变量,包含3个Nginx虚拟主机配置:
    • 使用 loop 遍历字典,批量创建每个虚拟主机的根目录;
    • 批量生成每个虚拟主机的Nginx配置文件(/etc/nginx/conf.d/{{ item.key }}.conf);
    • 仅当配置文件生成成功时,重启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
相关推荐
Monly2110 小时前
Windows:服务注册
windows
cly110 小时前
Ansible自动化(十):配置文件管理模块(lineinfile / blockinfile)
运维·自动化·ansible
0思必得010 小时前
[Web自动化] Selenium简单使用
前端·python·selenium·自动化·web自动化
深圳市恒讯科技10 小时前
如何从损坏的Windows或Linux VPS中恢复文件
linux·运维·windows
彷徨而立11 小时前
【Windows API】音频 API 对比:wavein/waveout、DirectSound、ASIO、WASAPI
windows·音视频
天空属于哈夫克311 小时前
利用 RPA 实现企业微信外部群自动化的架构逻辑
自动化·企业微信·rpa
汉得数字平台11 小时前
班翎流程平台 | 班翎流程平台定时启动功能上线!流程自动化不用等
运维·自动化
梦想的旅途211 小时前
基于 RPA 的企业微信外部群自动化接口实现方案
自动化·企业微信·rpa
2501_9419820511 小时前
如何通过 RPA 构建企业微信外部群的自动化 SOP 流程?
自动化·企业微信·rpa