Ansible Handlers คือ task พิเศษที่ถูก trigger โดย notify เมื่อ task อื่นมีการเปลี่ยนแปลง (changed) เท่านั้น — เหมาะสำหรับ action ที่ควรทำเฉพาะเมื่อจำเป็น เช่น restart service หลังแก้ไข config, reload Nginx หลังเพิ่ม virtual host หรือ clear cache หลัง deploy code
บทความนี้ครอบคลุมการประกาศ handlers, การใช้ notify, ลำดับการ execute, การ flush handlers, การใช้ listen เพื่อ group notifications และ pattern สำหรับ service management ใน deployment workflow
Handlers พื้นฐาน
ประกาศ handlers ใน handlers section ของ Play และ trigger ด้วย notify ใน task หลัก Handler จะถูกรันเพียงครั้งเดียวหลัง tasks ทั้งหมดของ Play เสร็จ แม้จะมี notify หลายครั้ง
---
- name: Basic handlers usage
hosts: all
become: true
tasks:
# เมื่อ task นี้ changed จะ notify handler
- name: Deploy Nginx configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: Reload Nginx
# notify หลาย handler พร้อมกัน
- name: Deploy application config
template:
src: app.conf.j2
dest: /etc/myapp/app.conf
notify:
- Restart myapp
- Clear app cache
handlers:
# handler ถูกรันเฉพาะเมื่อมี task notify มา
- name: Reload Nginx
service:
name: nginx
state: reloaded
- name: Restart myapp
service:
name: myapp
state: restarted
- name: Clear app cache
command: /opt/myapp/bin/clear-cache
ลำดับการ Execute — Handlers รันเมื่อไร
Handlers ไม่รันทันทีหลัง notify — รันหลัง tasks ทั้งหมดของ Play เสร็จ และรันเพียงครั้งเดียวต่อ Play แม้จะ notify หลายครั้ง ลำดับการรันตาม order ที่ประกาศใน handlers section
---
- name: Handler execution order
hosts: all
become: true
tasks:
# task 1: แก้ไข nginx.conf → notify Reload Nginx
- name: Update nginx main config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Reload Nginx
# task 2: เพิ่ม virtual host → notify Reload Nginx อีกครั้ง
- name: Add virtual host config
template:
src: vhost.conf.j2
dest: /etc/nginx/conf.d/mysite.conf
notify: Reload Nginx
# task 3: copy SSL cert → notify Reload Nginx
- name: Copy SSL certificate
copy:
src: files/ssl/mysite.crt
dest: /etc/ssl/certs/mysite.crt
notify: Reload Nginx
# Reload Nginx จะถูกรันแค่ 1 ครั้งหลัง tasks 1-3 เสร็จ
# ไม่ใช่ 3 ครั้ง
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloaded
Flush Handlers — บังคับรัน Handler ทันที
ใช้ meta: flush_handlers เพื่อบังคับให้ handlers ที่ pending ทั้งหมดรันทันที ณ จุดนั้น ก่อน tasks ที่เหลือจะดำเนินต่อ เหมาะเมื่อ task ถัดไปต้องการ service ที่ restart แล้ว
---
- name: Flush handlers example
hosts: all
become: true
tasks:
# deploy database config
- name: Update database configuration
template:
src: postgresql.conf.j2
dest: /etc/postgresql/14/main/postgresql.conf
notify: Restart PostgreSQL
# flush handlers — บังคับ restart PostgreSQL ก่อนทำ migration
- name: Flush handlers before migration
meta: flush_handlers
# task นี้ต้องการ PostgreSQL ที่ restart แล้ว
- name: Run database migrations
command: /opt/myapp/bin/migrate
become_user: appuser
handlers:
- name: Restart PostgreSQL
service:
name: postgresql
state: restarted
listen — Group Notifications
ใช้ listen เพื่อให้หลาย handlers ตอบสนองต่อ notification เดียวกัน เหมาะสำหรับกรณีที่ event หนึ่งต้องการ trigger หลาย action โดยไม่ต้อง notify แต่ละ handler ทีละชื่อ
---
- name: listen for grouped handlers
hosts: all
become: true
tasks:
# notify ด้วยชื่อ event แทนชื่อ handler
- name: Deploy application code
git:
repo: "{{ app_repo }}"
dest: /opt/myapp/current
version: "{{ app_version }}"
notify: app deployed
- name: Update application config
template:
src: app.conf.j2
dest: /etc/myapp/app.conf
notify: app deployed
handlers:
# handlers ที่ listen "app deployed" จะทำงานทั้งหมด
- name: Restart application
listen: app deployed
service:
name: myapp
state: restarted
- name: Clear application cache
listen: app deployed
command: /opt/myapp/bin/clear-cache
become_user: appuser
- name: Notify monitoring
listen: app deployed
uri:
url: https://monitoring.example.com/webhook/deploy
method: POST
body_format: json
body:
app: myapp
version: "{{ app_version }}"
status: deployed
Handlers ใน Role
เมื่อใช้ Ansible Roles ให้วาง handlers ในไฟล์ handlers/main.yml ใน role directory โครงสร้างนี้ทำให้ handlers เป็น reusable และแยก concerns ออกจาก tasks หลักได้ชัดเจน
# roles/nginx/handlers/main.yml
---
- name: Reload Nginx
service:
name: nginx
state: reloaded
listen: nginx config changed
- name: Restart Nginx
service:
name: nginx
state: restarted
listen: nginx restart required
# roles/nginx/tasks/main.yml
---
- name: Install Nginx
apt:
name: nginx
state: present
- name: Deploy main Nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: nginx config changed # trigger handler จาก handlers/main.yml
- name: Deploy site config
template:
src: site.conf.j2
dest: /etc/nginx/conf.d/mysite.conf
notify: nginx config changed
- name: Deploy SSL config (requires full restart)
template:
src: ssl.conf.j2
dest: /etc/nginx/conf.d/ssl.conf
notify: nginx restart required
Pattern: Service Management ใน Deployment
ตัวอย่าง Playbook deploy application stack โดยใช้ handlers จัดการ service restart อย่างมีประสิทธิภาพ — service จะถูก restart เฉพาะเมื่อ config หรือ binary เปลี่ยนจริง ๆ เท่านั้น
---
- name: Deploy application with proper handler usage
hosts: appservers
become: true
vars:
app_name: myapp
app_version: "{{ deploy_version }}"
tasks:
# deploy config files
- name: Deploy main application config
template:
src: app.conf.j2
dest: "/etc/{{ app_name }}/app.conf"
owner: root
group: appgroup
mode: '0640'
notify: Restart application
- name: Deploy Nginx virtual host
template:
src: nginx-vhost.conf.j2
dest: "/etc/nginx/conf.d/{{ app_name }}.conf"
notify: Reload Nginx
# deploy binary
- name: Deploy application binary
copy:
src: "dist/{{ app_name }}-{{ app_version }}"
dest: "/opt/{{ app_name }}/bin/{{ app_name }}"
owner: appuser
group: appgroup
mode: '0755'
notify: Restart application
# flush handlers หลัง deploy ทุกอย่าง
# เพื่อให้ restart เสร็จก่อนตรวจสอบ
- name: Apply all pending service restarts
meta: flush_handlers
# ตรวจสอบ service หลัง restart
- name: Verify application is running
wait_for:
host: localhost
port: 8080
timeout: 60
handlers:
- name: Restart application
service:
name: "{{ app_name }}"
state: restarted
- name: Reload Nginx
service:
name: nginx
state: reloaded
สรุป
Handlers ทำให้ Playbook idempotent มากขึ้นโดย trigger service restart หรือ action อื่น เฉพาะเมื่อมีการเปลี่ยนแปลงจริง ๆ รันเพียงครั้งเดียวต่อ Play แม้มี notify หลายครั้ง และสามารถ group ด้วย listen ให้จัดการ multi-action events ได้สะดวก
Pattern ที่ควรจำ: ใช้ meta: flush_handlers เมื่อ task ถัดไปต้องการผลจาก handler ที่ pending, ใช้ listen แทนการ notify ชื่อ handler โดยตรงเมื่อต้องการ trigger หลาย handler จาก event เดียว, วาง handlers ใน handlers/main.yml ใน role เสมอ และตั้งชื่อ handler ให้อ่านเข้าใจง่ายเพราะชื่อคือ interface ที่ tasks ใช้ notify

