Ansible Playbooks เจาะลึก: Handlers, Variables, Conditionals และ Loops
บทความนี้อธิบาย Handlers, Variables ในระดับ play, Conditionals, และ Loops พร้อมตัวอย่างที่นำไปใช้ได้ทันที
Playbook เบื้องต้นที่รัน task ตามลำดับใช้งานได้ดี แต่ระบบจริงต้องการความยืดหยุ่นมากกว่านั้น บางครั้งต้องรีสตาร์ต service เฉพาะเมื่อ config เปลี่ยน บางครั้งต้องรัน task เฉพาะบาง server หรือต้องสร้างไฟล์หลายชุดจากรายการข้อมูล features เหล่านี้คือหัวใจของ playbook ที่ทำงานจริงในสภาพแวดล้อม production
Handlers — รัน Task เฉพาะเมื่อมีการเปลี่ยนแปลง
Handler คือ task พิเศษที่จะรันก็ต่อเมื่อถูก notify เท่านั้น ใช้สำหรับ action ที่ต้องการเฉพาะเมื่อ config หรือไฟล์เปลี่ยนแปลง เช่น restart service หรือ reload config โดย handler จะรันเพียงครั้งเดียวต่อ play ไม่ว่าจะถูก notify กี่ครั้ง
- name: Configure nginx
hosts: webservers
tasks:
- name: Copy nginx config
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: Enable nginx site
file:
src: /etc/nginx/sites-available/mysite
dest: /etc/nginx/sites-enabled/mysite
state: link
notify: reload nginx
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
- name: reload nginx
service:
name: nginx
state: reloaded
ถ้า config file ไม่เปลี่ยนแปลง (task status = ok แทน changed) handler จะไม่รัน ทำให้ไม่มีการ restart service โดยไม่จำเป็น
Variables — กำหนดค่าระดับ Play
นอกจาก host_vars และ group_vars แล้ว ยังกำหนด variable ในระดับ play ได้โดยตรงด้วย vars: หรือโหลดจากไฟล์ด้วย vars_files:
- name: Deploy application
hosts: webservers
vars:
app_name: myapp
app_port: 8080
deploy_dir: /opt/apps/myapp
vars_files:
- vars/common.yml
- vars/{{ env }}.yml # โหลดตาม environment
tasks:
- name: Create app directory
file:
path: "{{ deploy_dir }}"
state: directory
- name: Configure app
template:
src: app.conf.j2
dest: "{{ deploy_dir }}/config.conf"
Conditionals — รัน Task เฉพาะเมื่อตรงเงื่อนไข
when: ใช้กรอง task ให้รันเฉพาะ host หรือสถานการณ์ที่ตรงเงื่อนไข ช่วยให้ playbook เดียวรองรับได้หลาย OS หรือหลาย environment
- name: Install packages
hosts: all
tasks:
# รันเฉพาะบน Ubuntu/Debian
- name: Install nginx (apt)
apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
# รันเฉพาะบน RHEL/CentOS
- name: Install nginx (yum)
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"
# ตรวจสอบ variable ก่อนรัน
- name: Enable SSL
include_tasks: ssl.yml
when: ssl_enabled | bool
# ตรวจสอบหลายเงื่อนไข
- name: Run in production only
command: /opt/scripts/backup.sh
when:
- env == "production"
- backup_enabled | default(true)
Loops — ทำซ้ำ Task กับหลาย Items
loop: ใช้เมื่อต้องการรัน task เดิมหลายครั้งกับข้อมูลต่างกัน แทนการเขียน task ซ้ำหลายบล็อก
- name: Setup users and packages
hosts: all
tasks:
# สร้าง user หลายคนจาก list
- name: Create system users
user:
name: "{{ item }}"
state: present
shell: /bin/bash
loop:
- deploy
- monitor
- backup
# ติดตั้ง packages หลายตัว
- name: Install required packages
apt:
name: "{{ item }}"
state: present
loop:
- git
- curl
- vim
- htop
# loop กับ dict items
- name: Create directories with permissions
file:
path: "{{ item.path }}"
mode: "{{ item.mode }}"
state: directory
loop:
- { path: /opt/app, mode: "0755" }
- { path: /var/log/app, mode: "0750" }
- { path: /etc/app, mode: "0640" }
ใช้ register เก็บผลลัพธ์จาก Task
register: เก็บ output จาก task ไว้ใน variable เพื่อใช้ใน task ถัดไป มีประโยชน์มากเมื่อต้องตัดสินใจตามผลลัพธ์ของ command
- name: Check and conditionally run
hosts: all
tasks:
- name: Check if app is running
command: systemctl is-active myapp
register: app_status
ignore_errors: true
- name: Start app if not running
service:
name: myapp
state: started
when: app_status.rc != 0
- name: Show status
debug:
msg: "App status: {{ app_status.stdout }}"
รวม Features ใน Playbook จริง
ตัวอย่าง playbook ที่ใช้ทุก feature ร่วมกัน เพื่อ deploy และ configure web server
- name: Deploy web application
hosts: webservers
vars:
app_version: "1.2.0"
app_port: 8080
handlers:
- name: restart app
service:
name: myapp
state: restarted
tasks:
- name: Install dependencies
apt:
name: "{{ item }}"
state: present
loop:
- python3
- python3-pip
- name: Deploy application config
template:
src: app.conf.j2
dest: /etc/myapp/config.conf
notify: restart app
when: env == "production"
- name: Check deployment
uri:
url: "http://localhost:{{ app_port }}/health"
status_code: 200
register: health_check
retries: 3
delay: 10
สรุป
Handlers ช่วยลดการ restart service ที่ไม่จำเป็น Conditionals ทำให้ playbook รองรับได้หลาย OS และ environment Loops ลดการเขียน task ซ้ำ และ register เปิดให้ logic ใน playbook ตัดสินใจตามผลลัพธ์จริง ความเข้าใจ feature เหล่านี้เป็นรากฐานของ playbook ที่ใช้ได้จริงในงาน DevOps