Ansible Handlers #

什么是 Handlers? #

Handlers 是特殊的任务,只在被通知(notify)且产生变更时执行。通常用于配置变更后的服务重启或重载。

text
┌─────────────────────────────────────────────────────────────┐
│                   Handlers 工作流程                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Task 1 (notify: Handler A)                                  │
│  ├── 变更发生 ──────────────────┐                            │
│  │                              │                            │
│  Task 2 (notify: Handler A)    │                            │
│  ├── 变更发生 ──────────────────┤                            │
│  │                              │                            │
│  Task 3 (notify: Handler B)    │                            │
│  └── 无变更                     │                            │
│                                 ▼                            │
│                         执行 Handlers                        │
│                         ────────────                         │
│                         Handler A (只执行一次)               │
│                         Handler B (不执行)                   │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Handler 特点 #

  1. 条件执行:只在被通知且产生变更时执行
  2. 去重执行:即使被多次通知,也只执行一次
  3. 延迟执行:在 Play 中所有任务执行完后执行
  4. 顺序执行:按定义顺序执行,而非通知顺序

基本使用 #

定义 Handler #

yaml
---
- name: Configure web server
  hosts: webservers
  become: yes
  
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
    
    - name: Deploy Nginx configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: Restart Nginx
    
    - name: Deploy site configuration
      template:
        src: site.conf.j2
        dest: /etc/nginx/sites-available/default
      notify: Restart Nginx
  
  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

通知多个 Handler #

yaml
tasks:
  - name: Update configuration
    template:
      src: app.conf.j2
      dest: /etc/app/app.conf
    notify:
      - Restart App
      - Reload Firewall
      - Clear Cache

handlers:
  - name: Restart App
    service:
      name: app
      state: restarted
  
  - name: Reload Firewall
    command: ufw reload
  
  - name: Clear Cache
    command: rm -rf /var/cache/app/*

Handler 执行顺序 #

按定义顺序执行 #

yaml
tasks:
  - name: Task A
    debug:
      msg: "Task A"
    notify: Handler 2    # 第二个通知
  
  - name: Task B
    debug:
      msg: "Task B"
    notify: Handler 1    # 第一个通知

handlers:
  - name: Handler 1
    debug:
      msg: "Handler 1 executed"
  
  - name: Handler 2
    debug:
      msg: "Handler 2 executed"

# 执行顺序:Handler 1 -> Handler 2(按定义顺序,非通知顺序)

多次通知只执行一次 #

yaml
tasks:
  - name: Update config 1
    template:
      src: config1.j2
      dest: /etc/app/config1.conf
    notify: Restart App
  
  - name: Update config 2
    template:
      src: config2.j2
      dest: /etc/app/config2.conf
    notify: Restart App
  
  - name: Update config 3
    template:
      src: config3.j2
      dest: /etc/app/config3.conf
    notify: Restart App

handlers:
  - name: Restart App
    service:
      name: app
      state: restarted

# 即使通知三次,Handler 也只执行一次

Handler 作用域 #

Play 级别 Handler #

yaml
- name: Play 1
  hosts: webservers
  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted
  tasks:
    - name: Configure Nginx
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: Restart Nginx

- name: Play 2
  hosts: databases
  handlers:
    - name: Restart MySQL
      service:
        name: mysql
        state: restarted
  tasks:
    - name: Configure MySQL
      template:
        src: my.cnf.j2
        dest: /etc/mysql/my.cnf
      notify: Restart MySQL

Role 级别 Handler #

text
roles/
└── nginx/
    ├── handlers/
    │   └── main.yml
    └── tasks/
        └── main.yml
yaml
# roles/nginx/handlers/main.yml
---
- name: Restart Nginx
  service:
    name: nginx
    state: restarted

- name: Reload Nginx
  service:
    name: nginx
    state: reloaded

- name: Validate Nginx config
  command: nginx -t
  changed_when: false
yaml
# roles/nginx/tasks/main.yml
---
- name: Deploy Nginx configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: 
    - Validate Nginx config
    - Reload Nginx

强制执行 Handler #

meta: flush_handlers #

立即执行所有待处理的 Handler:

yaml
tasks:
  - name: Update configuration
    template:
      src: app.conf.j2
      dest: /etc/app/app.conf
    notify: Restart App
  
  - name: Force handler execution
    meta: flush_handlers
  
  - name: Check application status
    uri:
      url: http://localhost:8080/health
    register: health_check
    retries: 5
    delay: 5
    until: health_check.status == 200

实际应用场景 #

yaml
tasks:
  - name: Stop application
    service:
      name: app
      state: stopped
  
  - name: Update application code
    git:
      repo: https://github.com/user/app.git
      dest: /var/www/app
      version: main
    notify: Start App
  
  - name: Run database migrations
    command: /var/www/app/migrate.sh
  
  - name: Execute handlers now
    meta: flush_handlers
  
  - name: Verify application is running
    uri:
      url: http://localhost:8080/health
      status_code: 200

Handler 条件 #

条件 Handler #

yaml
handlers:
  - name: Restart Nginx
    service:
      name: nginx
      state: restarted
    when: nginx_enabled | default(true)
  
  - name: Send notification
    mail:
      to: admin@example.com
      subject: "Nginx restarted on {{ inventory_hostname }}"
    when: send_notifications | default(false)

监听 Handler #

yaml
handlers:
  - name: Restart Nginx
    service:
      name: nginx
      state: restarted
    listen: "restart web services"
  
  - name: Restart PHP-FPM
    service:
      name: php-fpm
      state: restarted
    listen: "restart web services"

tasks:
  - name: Update Nginx config
    template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: "restart web services"
  
  - name: Update PHP config
    template:
      src: php.ini.j2
      dest: /etc/php.ini
    notify: "restart web services"

完整示例 #

Web 服务器配置 #

yaml
---
- name: Configure web server
  hosts: webservers
  become: yes
  
  vars:
    nginx_port: 80
    nginx_user: www-data
  
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
        update_cache: yes
      notify: Enable Nginx
    
    - name: Deploy main configuration
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
        validate: nginx -t -c %s
      notify:
        - Validate Nginx config
        - Reload Nginx
    
    - name: Deploy site configuration
      template:
        src: templates/site.conf.j2
        dest: /etc/nginx/sites-available/default
        owner: root
        group: root
        mode: '0644'
      notify: Reload Nginx
    
    - name: Deploy SSL certificate
      copy:
        src: files/ssl/
        dest: /etc/nginx/ssl/
        owner: root
        group: root
        mode: '0600'
      notify: Reload Nginx
      when: ssl_enabled | default(false)
    
    - name: Create document root
      file:
        path: /var/www/html
        state: directory
        owner: "{{ nginx_user }}"
        group: "{{ nginx_user }}"
        mode: '0755'
    
    - name: Deploy index page
      template:
        src: templates/index.html.j2
        dest: /var/www/html/index.html
        owner: "{{ nginx_user }}"
        group: "{{ nginx_user }}"
        mode: '0644'
  
  handlers:
    - name: Enable Nginx
      service:
        name: nginx
        enabled: yes
    
    - name: Validate Nginx config
      command: nginx -t
      changed_when: false
    
    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded
    
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

应用部署 #

yaml
---
- name: Deploy application
  hosts: app_servers
  become: yes
  
  vars:
    app_name: myapp
    app_dir: /var/www/myapp
    app_user: appuser
  
  tasks:
    - name: Pull latest code
      git:
        repo: "{{ app_repo }}"
        dest: "{{ app_dir }}"
        version: "{{ app_version | default('main') }}"
      notify: Restart Application
    
    - name: Install dependencies
      command: npm install --production
      args:
        chdir: "{{ app_dir }}"
      when: git_result.changed
      register: npm_result
      changed_when: "'added' in npm_result.stdout"
    
    - name: Run database migrations
      command: npm run migrate
      args:
        chdir: "{{ app_dir }}"
      when: git_result.changed
      notify: Clear Cache
    
    - name: Update configuration
      template:
        src: app.env.j2
        dest: "{{ app_dir }}/.env"
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
        mode: '0600'
      notify:
        - Restart Application
        - Clear Cache
    
    - name: Force handler execution
      meta: flush_handlers
    
    - name: Wait for application
      wait_for:
        port: 3000
        timeout: 60
    
    - name: Health check
      uri:
        url: http://localhost:3000/health
        return_content: yes
      register: health
      until: "'healthy' in health.content"
      retries: 5
      delay: 5
    
    - name: Send deployment notification
      slack:
        token: "{{ slack_token }}"
        msg: "Deployment completed on {{ inventory_hostname }}"
      delegate_to: localhost
      when: notify_slack | default(false)
  
  handlers:
    - name: Restart Application
      systemd:
        name: "{{ app_name }}"
        state: restarted
        daemon_reload: yes
    
    - name: Clear Cache
      file:
        path: "{{ app_dir }}/cache"
        state: absent
    
    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded
      listen: "web services changed"

最佳实践 #

1. 使用描述性名称 #

yaml
handlers:
  - name: Restart Nginx web server
    service:
      name: nginx
      state: restarted

2. 分离重启和重载 #

yaml
handlers:
  - name: Reload Nginx
    service:
      name: nginx
      state: reloaded
  
  - name: Restart Nginx
    service:
      name: nginx
      state: restarted

3. 使用 listen 组织 Handler #

yaml
handlers:
  - name: Restart Nginx
    service:
      name: nginx
      state: restarted
    listen: "restart web"
  
  - name: Restart PHP-FPM
    service:
      name: php-fpm
      state: restarted
    listen: "restart web"

tasks:
  - name: Update web config
    template:
      src: config.j2
      dest: /etc/app/config
    notify: "restart web"

4. 验证配置后再重载 #

yaml
handlers:
  - name: Validate and reload Nginx
    shell: nginx -t && systemctl reload nginx

5. 使用 flush_handlers 控制时机 #

yaml
tasks:
  - name: Update config
    template:
      src: config.j2
      dest: /etc/app/config
    notify: Restart App
  
  - name: Run migrations
    command: /app/migrate.sh
  
  - name: Flush handlers
    meta: flush_handlers
  
  - name: Verify app is running
    uri:
      url: http://localhost:8080/health

下一步 #

现在你已经掌握了 Handlers 处理器,接下来学习 Ansible Vault 了解如何保护敏感数据!

最后更新:2026-03-29