Ansible之Playbook(四):循环与判断

Ansible之Playbook:循环与判断

一、循环 (Loops)

循环允许你对一组数据(如列表、字典)重复执行同一个任务。Ansible 提供了多种循环方式。

1. 标准循环 (loopwith_items)
  • 用途: 对一组简单项(通常是列表)执行任务。

  • 语法 (推荐 loop):

    yaml 复制代码
    - name: Install multiple packages on OpenEuler
      dnf:
        name: "{{ item }}"
        state: present
      loop:
        - htop
        - tmux
        - lsof
  • 说明:

    • loop: 关键字后面跟一个列表。
    • 在任务内部,使用 {``{ item }} 来引用当前循环到的元素。
    • 此例在 OpenEuler 上使用 dnf 模块安装了三个软件包 (htop, tmux, lsof)。Ansible 会为列表中的每个包名执行一次安装任务。
  • with_items (旧语法,仍可用):

    yaml 复制代码
      with_items:
        - htop
        - tmux
        - lsof
2. 字典循环 (with_dictloop + dict2items)
  • 用途: 当你的数据是键值对(字典)时,循环处理每个键值对。

  • 语法 (with_dict):

    yaml 复制代码
    - name: Create users with specific UIDs on OpenEuler
      user:
        name: "{{ item.key }}"
        uid: "{{ item.value }}"
      with_dict:
        alice: 1001
        bob: 1002
        charlie: 1003
  • 语法 (loop + dict2items 过滤器 - 更现代):

    yaml 复制代码
    - name: Create users with specific UIDs on OpenEuler (modern)
      user:
        name: "{{ item.key }}"
        uid: "{{ item.value }}"
      loop: "{{ {'alice': 1001, 'bob': 1002, 'charlie': 1003} | dict2items }}"
  • 说明:

    • 循环到的 item 是一个包含 .key.value 的对象。
    • 此例在 OpenEuler 上创建了三个用户,并分别指定了 UID。
3. 文件循环 (with_fileglob)
  • 用途: 基于文件模式(通配符)循环处理匹配的文件。

  • 语法:

    yaml 复制代码
    - name: Copy all .conf files from local to OpenEuler
      copy:
        src: "{{ item }}"
        dest: /etc/conf.d/
      with_fileglob:
        - "files/*.conf" # 匹配本地 files/ 目录下的所有 .conf 文件
4. 条件循环 (until)
  • 用途: 重复执行一个任务,直到满足某个条件为止(通常用于等待某个服务启动或端口可用)。

  • 语法:

    yaml 复制代码
    - name: Wait for SSH port 22 to be open on OpenEuler
      wait_for:
        port: 22
        host: "{{ inventory_hostname }}"
        timeout: 30
      register: result
      until: result is success
      retries: 5
      delay: 3
  • 说明:

    • wait_for 模块检查端口 22 是否开放。
    • register: result 保存检查结果。
    • until: result is success 定义成功条件。
    • retries: 5 最多重试 5 次。
    • delay: 3 每次重试间隔 3 秒。

二、判断 (Conditionals)

判断语句 (when) 允许你根据条件决定是否执行某个任务、角色或包含的文件。

1. 基本条件判断 (when)
  • 用途: 基于变量、事实(facts)或其他条件控制任务执行。

  • 语法:

    yaml 复制代码
    - name: Install GUI packages only if OpenEuler is a desktop machine
      dnf:
        name:
          - gnome-session
          - firefox
        state: present
      when: ansible_facts['distribution'] == 'openEuler' and ansible_facts['desktop'] == 'gnome'
  • 说明:

    • when: 语句后面的表达式结果为 True 时,任务才会执行。
    • 此例检查系统是否是 OpenEuler (ansible_facts['distribution']) 并且桌面环境是否是 GNOME (ansible_facts['desktop']),只有同时满足才安装 GUI 包。
  • 常见条件来源:

    • 变量 (vars): when: my_variable == 'some_value'

    • 主机事实 (ansible_facts): when: ansible_facts['os_family'] == 'RedHat' (OpenEuler 属于此家族)

    • 任务执行结果 (register):

      yaml 复制代码
      - name: Check if a service exists
        command: systemctl status some-service
        register: service_check
        ignore_errors: true
      - name: Start the service only if it exists
        systemd:
          name: some-service
          state: started
        when: service_check.rc == 0 # 上一条命令成功退出 (rc=0) 表示服务存在
2. 结合循环的条件判断
  • 用途: 在循环中对每个项目进行条件过滤。

  • 语法:

    yaml 复制代码
    - name: Ensure critical services are running on OpenEuler
      systemd:
        name: "{{ item }}"
        state: started
      loop:
        - sshd
        - chronyd
        - firewalld
      when: ansible_facts['services'][item]['state'] != 'running'
  • 说明:

    • 循环启动 sshd, chronyd, firewalld 服务。
    • when 条件检查了当前服务的状态(通过 ansible_facts['services'][item]['state'])。只有当服务不是 running 状态时,才执行启动任务。避免了不必要的重启。
3. 条件导入/包含
  • include_tasks / import_tasks with when:

    yaml 复制代码
    - name: Include firewall configuration tasks for OpenEuler if needed
      include_tasks: configure_firewall.yml
      when: configure_firewall # 当变量 configure_firewall 为 True 时才包含
  • include_role / import_role with when:

    yaml 复制代码
    - name: Apply the hardening role only to production OpenEuler servers
      include_role:
        name: os_hardening
      when: inventory_hostname in groups['production']

三、在 OpenEuler 上的典型应用场景示例

  1. 批量软件包管理:

    yaml 复制代码
    - name: Install required development tools on OpenEuler
      dnf:
        name: "{{ item }}"
        state: present
      loop: "{{ dev_tools_packages }}"
      vars:
        dev_tools_packages:
          - gcc
          - make
          - kernel-devel
          - git
  2. 配置文件管理 (带备份):

    yaml 复制代码
    - name: Backup existing config files before modification
      copy:
        src: "{{ item }}"
        dest: "{{ item }}.backup-{{ ansible_date_time.epoch }}"
        remote_src: yes # 源文件在目标主机 (OpenEuler) 上
      loop:
        - /etc/ssh/sshd_config
        - /etc/sysctl.conf
      when: backup_configs | default(true) | bool # 默认开启备份,可通过变量控制
  3. 服务状态检查与恢复:

    yaml 复制代码
    - name: Check status of essential services on OpenEuler
      systemd:
        name: "{{ item }}"
        state: started
        enabled: yes
      loop:
        - crond
        - rsyslog
        - auditd
      register: service_results
      ignore_errors: yes # 即使某个服务启动失败,也继续检查下一个
    
    - name: Report any failed service startups
      debug:
        msg: "Service {{ item.item }} failed to start! Check journalctl."
      loop: "{{ service_results.results }}" # 遍历之前注册的每个服务的结果
      when: item is failed # 仅当对应服务任务失败时才打印消息

四、最佳实践

  1. 命名清晰: 为循环变量或注册结果 (register) 使用有意义的名称。
  2. 优先使用 loop 它是现代 Ansible 推荐的标准循环语法。
  3. 条件前置: 尽可能在 when 中使用事实 (ansible_facts) 或预定义的变量,减少在目标主机上运行额外命令的需要。
  4. 避免深层嵌套: 过于复杂的循环嵌套条件判断会降低 Playbook 的可读性和可维护性。考虑拆分成多个任务或使用角色。
  5. 测试: 使用 --check (dry-run) 和 --diff 模式测试你的 Playbook,特别是复杂的循环和条件逻辑。
  6. OpenEuler 特性: 熟悉 OpenEuler 特有的服务名 (如 firewalld 而非 iptables)、包管理 (dnf)、路径等,确保条件判断准确。
相关推荐
cui_ruicheng2 小时前
Linux IO入门(二):重定向与缓冲区机制
linux·运维·服务器
希望永不加班2 小时前
SpringBoot 依赖管理:BOM 与版本控制
java·spring boot·后端·spring
落木萧萧8252 小时前
MyBatis、MyBatis-Plus、JPA、MyBatisGX 写法比较:同一个需求,四种解法
java·后端
彳亍走的猪2 小时前
Android 全局防抖/防重复点击
android·java·开发语言
Harvy_没救了2 小时前
Ansible 学习指南
linux·运维·服务器·ansible
lhbian2 小时前
30分钟搭建PHP+Java全栈Web应用
java·前端·php
有谁看见我的剑了?2 小时前
Linux 内存巨页与透明巨页学习
java·linux·学习
勿忘,瞬间2 小时前
Spring Boot
java·数据库·spring boot
SimonKing2 小时前
AI大模型中转平台,无需科学上网就可以使用国外模型
java·后端·程序员