第七章:Handlers 处理器
学习如何使用 Handlers 实现配置变更后的服务重启。
最后更新: 2024-01-21
页面目录
Handlers 处理器
Handlers 是 Ansible 中用于响应任务执行结果的特殊任务,只有在被任务通知时才会执行。
Handler 基础
基本概念
┌─────────────────────────────────────────────────────────────────┐
│ Handler 工作原理 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Task ──[ changed ]──► notify ──► Handler │
│ │ │
│ Task ──[ ok ]────► (no notify) │
│ │ │
│ Handler 执行时机: │
│ 1. 所有 tasks 执行完成后 │
│ 2. 按定义的顺序执行(不是 notify 顺序) │
│ 3. 每个 handler 只执行一次(无论被 notify 多少次) │
│ │
└─────────────────────────────────────────────────────────────────┘
简单示例
# handlers-basic.yml
---
- name: Configure Nginx
hosts: webservers
become: yes
tasks:
- name: Copy Nginx configuration
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify: Reload Nginx
- name: Start Nginx
service:
name: nginx
state: started
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloaded
Handler 详细配置
基本结构
handlers:
- name: Handler name
module_name:
module_argument: value
listen: "handler topic"
listen: "multiple topics"
delay: 5
retries: 3
Handler 参数
| 参数 | 说明 |
|---|---|
name |
Handler 名称,用于 notify 引用 |
listen |
主题名称,多个 handler 可监听同一主题 |
listen |
支持列表形式定义多个主题 |
delay |
失败重试延迟(秒) |
retries |
失败重试次数 |
notify 通知机制
基本用法
tasks:
# 单个 notify
- name: Update config
copy:
src: app.conf
dest: /etc/myapp/app.conf
notify: Restart app
# 多个 notify(都会被执行)
- name: Update multiple configs
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
loop:
- { src: 'nginx.conf', dest: '/etc/nginx/nginx.conf' }
- { src: 'app.conf', dest: '/etc/myapp/app.conf' }
notify:
- Reload Nginx
- Restart app
条件通知
tasks:
- name: Update config
copy:
src: app.conf
dest: /etc/myapp/app.conf
notify: Restart app
when: app_state == "running"
使用 listen 主题
tasks:
# 使用主题名称通知
- name: Update Nginx config
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify: "Web services"
- name: Update PHP config
copy:
src: php.ini
dest: /etc/php/8.1/fpm/php.ini
notify: "Web services"
handlers:
# 监听主题的 handler
- name: Reload Nginx
service:
name: nginx
state: reloaded
listen: "Web services"
- name: Reload PHP-FPM
service:
name: php8.1-fpm
state: reloaded
listen: "Web services"
Handler 执行时机
执行顺序
---
- name: Handler execution order
hosts: localhost
connection: local
tasks:
- name: Task 1
command: echo "Task 1"
notify: Handler 1
- name: Task 2
command: echo "Task 2"
notify: Handler 2
- name: Task 3
command: echo "Task 3"
notify: Handler 1
handlers:
- name: Handler 1
command: echo "Handler 1"
- name: Handler 2
command: echo "Handler 2"
执行结果:Handler 按定义顺序执行,而非 notify 顺序
- Handler 1 将在 Handler 2 之前执行
- Handler 1 只执行一次(即使被 notify 两次)
flush_handlers
---
- name: Flush handlers example
hosts: localhost
connection: local
tasks:
- name: Task 1
command: echo "Task 1"
notify: Handler 1
- name: Flush handlers immediately
meta: flush_handlers
- name: Task 2
command: echo "Task 2"
notify: Handler 2
handlers:
- name: Handler 1
command: echo "Handler 1"
- name: Handler 2
command: echo "Handler 2"
完整示例
多服务配置
# multi-service.yml
---
- name: Configure LAMP Stack
hosts: webservers
become: yes
tasks:
# Nginx 配置
- name: Install Nginx
apt:
name: nginx
state: present
notify: Start Nginx
- name: Copy Nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/default
notify: Reload Nginx
# PHP-FPM 配置
- name: Install PHP-FPM
apt:
name:
- php8.1-fpm
- php8.1-mysql
state: present
notify: Start PHP-FPM
- name: Copy PHP-FPM config
template:
src: php-fpm.conf.j2
dest: /etc/php/8.1/fpm/pool.d/www.conf
notify: Restart PHP-FPM
# Application 配置
- name: Create app directory
file:
path: /var/www/html
state: directory
owner: www-data
group: www-data
- name: Deploy application
copy:
src: app/
dest: /var/www/html/
owner: www-data
group: www-data
notify:
- Reload Nginx
- Restart PHP-FPM
handlers:
- name: Start Nginx
service:
name: nginx
state: started
enabled: yes
- name: Reload Nginx
service:
name: nginx
state: reloaded
- name: Start PHP-FPM
service:
name: php8.1-fpm
state: started
enabled: yes
- name: Restart PHP-FPM
service:
name: php8.1-fpm
state: restarted
数据库主从配置
# database-cluster.yml
---
- name: Configure MySQL Cluster
hosts: dbservers
become: yes
serial: 1 # 逐台执行
tasks:
- name: Install MySQL
apt:
name: mysql-server
state: present
- name: Copy MySQL config
template:
src: my.cnf.j2
dest: /etc/mysql/my.cnf
notify: Restart MySQL
- name: Setup replication
include_tasks: setup_replication.yml
when: inventory_hostname in groups['dbservers_slave']
notify:
- Start slave
- Check replication
handlers:
- name: Restart MySQL
service:
name: mysql
state: restarted
- name: Start slave
command: mysql -e "START SLAVE;"
- name: Check replication
command: mysql -e "SHOW SLAVE STATUS\G"
register: slave_status
- name: Notify monitoring
debug:
msg: "Replication status: {{ slave_status.stdout }}"
listen: "Monitoring alerts"
Handler 高级特性
Handler 条件执行
handlers:
- name: Restart app
service:
name: myapp
state: restarted
when: app_state == "running"
Handler 变量
vars:
nginx_notify_topic: "Reload services"
tasks:
- name: Update config
copy:
src: app.conf
dest: /etc/myapp/app.conf
notify: "{{ nginx_notify_topic }}"
handlers:
- name: Reload services
service:
name: "{{ item }}"
state: reloaded
loop:
- nginx
- php-fpm
listen: "Reload services"
Handler 中的错误处理
handlers:
- name: Restart service
service:
name: nginx
state: restarted
listen: Restart service
- name: Rollback on failure
command: /opt/rollback.sh
listen: Restart service
failed_when: false
when: ansible_facts['ansible_distribution'] == "Ubuntu"
Handler 最佳实践
1. 使用清晰的命名
# 推荐
handlers:
- name: Restart nginx
- name: Reload php-fpm
- name: Restart mysql
# 不推荐
handlers:
- name: handler1
- name: restart
2. 使用 listen 分组
tasks:
- name: Update nginx config
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify: "Web server reload"
- name: Update php config
copy:
src: php.ini
dest: /etc/php/8.1/fpm/php.ini
notify: "Web server reload"
handlers:
- name: Reload nginx
service:
name: nginx
state: reloaded
listen: "Web server reload"
- name: Reload php-fpm
service:
name: php8.1-fpm
state: reloaded
listen: "Web server reload"
3. Handler 放在 Play 底部
---
- name: Recommended structure
hosts: webservers
tasks:
- name: Task 1
copy:
src: file1.conf
dest: /etc/
notify: Handler 1
handlers:
- name: Handler 1
service:
name: myapp
state: restarted
4. 使用 force_handlers 确保执行
---
- name: Ensure handlers run even on failure
hosts: webservers
force_handlers: yes
tasks:
- name: Install package
apt:
name: custom-package
state: present
- name: This will fail
command: /bin/false
- name: Config change
copy:
src: app.conf
dest: /etc/myapp/app.conf
notify: Restart myapp
handlers:
- name: Restart myapp
service:
name: myapp
state: restarted
常见问题
Handler 不执行
- 检查任务是否真正改变了状态
- 检查 notify 名称是否与 handler name 完全匹配
- 使用
-v查看详细输出
Handler 执行顺序不对
Handler 按定义顺序执行,而非 notify 顺序。使用 listen 可以创建逻辑分组。
Handler 执行多次
每个 handler 只执行一次,无论被 notify 多少次。
下一步
现在你已经掌握了 Handlers 的使用。接下来让我们学习变量和 Facts。