一、案例一:handlers 触发器怎么用

原始笔记首先演示了“分发配置文件后重启 NFS 服务”的场景。

1.1 不使用 handlers 的问题

如果剧本直接写成下面这样:

- hosts: nfs
  gather_facts: no
  tasks:
    - name: 分发配置文件
      copy:
        src: exports
        dest: /etc/exports
        backup: yes

    - name: 重启服务
      systemd:
        name: nfs
        state: reloaded

那么不管 /etc/exports 是否真的发生变化,NFS 都会被 reload。

这会带来两个问题:

  • 浪费操作
  • 增加不必要的服务扰动

1.2 使用 handlers 的正确写法

原始笔记给出的优化版如下:

- hosts: nfs
  gather_facts: no
  tasks:
    - name: 分发配置文件
      copy:
        src: exports
        dest: /etc/exports
        backup: yes
      notify:
        - 重启服务
  handlers:
    - name: 重启服务
      systemd:
        name: nfs
        state: reloaded

这样只有在 copy 任务真的把配置文件改动了时,handlers 才会触发。

1.3 这个案例的关键结论

原始笔记总结得很清楚:

  • handlers 常用于配置文件发生变化后再重启服务
  • notify 相当于“埋触发器”
  • handlers 需要放在剧本最后

二、案例二:when 根据系统类型执行不同安装逻辑

原始笔记设计了一个非常适合入门的场景:

  • 如果目标系统是 CentOS,就安装 slcowsaytree
  • 如果目标系统是 Ubuntu,就安装 cmatrixlolcat

2.1 剧本示例

- hosts: all
  tasks:
    - name: CentOS系统安装sl,cowsay,tree
      yum:
        name: sl,cowsay,tree
        state: present
      when:
        - ansible_distribution == "CentOS"

    - name: Ubuntu系统安装cmatrix,lolcat
      apt:
        name: cmatrix,lolcat
        state: present
      when:
        - ansible_distribution == "Ubuntu"

2.2 这个案例体现了什么

它体现了 when 最典型的用法:

  • 借助 facts 变量 ansible_distribution
  • 对不同系统执行不同模块和不同包管理逻辑

不满足条件的主机会显示:

  • skipped

这正是条件执行的正常表现。

2.3 原始笔记中的额外提醒

笔记还补充了一点 Ubuntu 环境常见问题:

  • Ubuntu 默认可能禁用了 root 远程登录

如果实验里需要 root 登录,可能需要在 sshd_config 中打开:

PermitRootLogin yes

三、案例三:loop 批量重启服务

原始笔记用一个 NFS 场景演示循环:

  • 批量重启 rpcbind
  • 再重启 nfs

3.1 剧本示例

- hosts: nfs
  gather_facts: no
  tasks:
    - name: 重启服务
      systemd:
        name: "{{ item }}"
        state: restarted
      loop:
        - rpcbind
        - nfs

3.2 为什么这个例子很典型

因为它直接说明:

  • 同一个模块
  • 只是在不同对象上重复执行

这种场景最适合使用 loop

四、案例四:loop 配合多个变量批量创建用户

原始笔记还进一步展示了“列表里放字典”的用法,这是循环进阶里非常实用的能力。

4.1 需求

批量创建用户:

  • oldboy,UID 2020
  • lidao,UID 2021
  • lidao996,UID 2022

4.2 剧本示例

- hosts: all
  gather_facts: no
  tasks:
    - name: add user
      user:
        name: "{{ item.name }}"
        uid: "{{ item.uid }}"
        state: present
      loop:
        - { name: "oldboy", uid: 2020 }
        - { name: "lidao", uid: 2021 }
        - { name: "lidao996", uid: 2022 }

4.3 这个案例的关键价值

它说明 loop 不只是循环字符串列表,还可以循环结构化数据,这对于:

  • 批量创建用户
  • 批量创建目录
  • 批量生成配置项

都非常有用。

五、这三个流程控制能力如何组合使用

实际运维里,经常不是单独使用某一种,而是组合使用。例如:

  • loop 批量分发配置
  • when 控制不同系统差异
  • handlers 在配置变化后统一 reload 服务

一旦这样组合起来,Playbook 的表达能力会明显提升。

六、小结

这篇实战里最值得真正掌握的是:

  • handlers:配置变化后再触发后续动作
  • when:根据系统或变量条件决定是否执行
  • loop:让同一任务批量处理多个对象

如果你已经能看懂并手写这三个案例,Ansible 流程控制的核心就已经基本入门了。