Ansible Playbook Structure: Tasks, Handlers, Variables อธิบายละเอียด

โครงสร้าง playbook ที่ดีช่วยให้อ่านเข้าใจง่าย แก้ไขได้คล่อง และทีมใหม่สามารถ contribute ได้โดยไม่ต้องใช้เวลานาน ทุกส่วนใน playbook ตั้งแต่ plays, tasks, handlers, ไปจนถึง variables มีบทบาทและตำแหน่งที่ชัดเจน การเข้าใจโครงสร้างนี้เป็นพื้นฐานสำคัญก่อนเขียน playbook ที่ซับซ้อนขึ้น

บทความนี้อธิบายแต่ละส่วนของ playbook อย่างละเอียด พร้อมแนวทาง structuring ที่นำไปใช้ได้กับงานจริง

โครงสร้างภาพรวมของ Playbook

Playbook คือไฟล์ YAML ที่ประกอบด้วย play หนึ่งหรือหลาย play แต่ละ play กำหนดว่าจะรัน tasks ใดกับ hosts กลุ่มใด โครงสร้างระดับสูงมีดังนี้

---
# playbook.yml
- name: Play 1 — Configure web servers
  hosts: webservers
  become: true
  vars:
    http_port: 80
  tasks:
    # tasks ที่ต้องการรัน
  handlers:
    # handlers ที่รอถูก notify

- name: Play 2 — Configure database servers
  hosts: dbservers
  become: true
  tasks:
    # tasks สำหรับ database

Play-level Options

แต่ละ play มี options ระดับบนที่กำหนด context ของการรัน

- name: Configure application servers
  hosts: appservers          # กลุ่ม hosts เป้าหมาย
  become: true               # ใช้ sudo/privilege escalation
  become_user: root          # เปลี่ยน user เป็น root
  gather_facts: true         # เก็บข้อมูล host (default: true)
  serial: 2                  # รันทีละ 2 hosts (rolling update)
  max_fail_percentage: 20    # หยุดถ้า hosts ล้มเหลวเกิน 20%
  any_errors_fatal: false    # หยุดทุก host ถ้า task fail
  environment:               # กำหนด environment variables
    APP_ENV: production
    LOG_LEVEL: info

Tasks — หน่วยงานพื้นฐาน

Task คือหน่วยงานที่เล็กที่สุด แต่ละ task ใช้ module หนึ่งตัวเพื่อทำงานเฉพาะอย่าง โครงสร้าง task มาตรฐานมีดังนี้

tasks:
  - name: Install nginx web server    # ชื่อ task ที่อ่านเข้าใจ
    apt:                               # module ที่ใช้
      name: nginx
      state: present
      update_cache: true
    become: true                       # override privilege ระดับ task
    when: ansible_os_family == "Debian"  # เงื่อนไขการรัน
    notify: restart nginx              # notify handler
    tags:
      - packages                       # tag สำหรับ selective run
    ignore_errors: false               # ถ้า true จะไม่หยุดเมื่อ fail
    retries: 3                         # ลองใหม่ถ้าล้มเหลว
    delay: 5                           # รอ 5 วินาทีก่อนลองใหม่

Handlers — Task ที่รอถูกเรียก

Handlers เหมือน tasks ทั่วไปแต่รันเฉพาะเมื่อถูก notify และรันเพียงครั้งเดียวหลังจาก tasks ทั้งหมดใน play เสร็จ วางไว้ใน section handlers: ในระดับเดียวกับ tasks:

handlers:
  - name: restart nginx
    service:
      name: nginx
      state: restarted
    listen: "web server restart"    # alias สำหรับ notify หลาย handler พร้อมกัน

  - name: reload nginx config
    service:
      name: nginx
      state: reloaded
    listen: "web server restart"    # handler 2 ตัวนี้จะรันเมื่อ notify "web server restart"

Variables ในระดับต่าง ๆ

สามารถกำหนด variables ได้ในหลายระดับภายใน playbook ลำดับความสำคัญจากต่ำไปสูง ได้แก่ play vars → vars_files → task vars → set_fact → extra_vars

- name: Variable examples
  hosts: all
  vars:
    # inline vars (ต่ำสุดในระดับ play)
    app_port: 8080
    debug_mode: false

  vars_files:
    - vars/common.yml          # โหลดจากไฟล์ภายนอก

  tasks:
    - name: Set computed variable
      set_fact:
        full_app_url: "http://{{ inventory_hostname }}:{{ app_port }}"

    - name: Override for this task only
      debug:
        msg: "Running on port {{ app_port }}"
      vars:
        app_port: 9090          # override เฉพาะ task นี้

Tags — รัน Subset ของ Tasks

Tags ช่วยให้รัน tasks บางส่วนได้โดยไม่ต้องรันทั้ง playbook มีประโยชน์มากในระหว่าง development หรือเมื่อต้องการ apply เฉพาะบางส่วน

tasks:
  - name: Install packages
    apt:
      name: "{{ item }}"
      state: present
    loop: [nginx, git, curl]
    tags: [packages, setup]

  - name: Deploy config
    template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    tags: [config, deploy]

  - name: Start services
    service:
      name: nginx
      state: started
    tags: [services]
# รันเฉพาะ task ที่มี tag "config"
ansible-playbook site.yml --tags config

# ข้าม task ที่มี tag "packages"
ansible-playbook site.yml --skip-tags packages

Include และ Import — แบ่ง Playbook เป็นไฟล์ย่อย

เมื่อ playbook ยาวขึ้น ควรแบ่งออกเป็นไฟล์ย่อย ทำให้แต่ละไฟล์ทำหน้าที่เดียวและนำกลับมาใช้ซ้ำได้

# site.yml — playbook หลัก
- name: Full site deployment
  hosts: all
  tasks:
    - import_tasks: tasks/install.yml      # โหลดก่อนรัน (static)
    - include_tasks: tasks/configure.yml   # โหลดระหว่างรัน (dynamic)
      when: configure_required | bool

# tasks/install.yml
- name: Install nginx
  apt:
    name: nginx
    state: present

- name: Install certbot
  apt:
    name: certbot
    state: present

สรุป

โครงสร้าง playbook ที่ดีประกอบด้วย plays ที่กำหนด scope ชัดเจน, tasks ที่มีชื่ออ่านเข้าใจ, handlers ที่จัดการ service restarts, variables ที่จัดระดับถูกต้อง และ tags ที่ทำให้รัน selective ได้ การแบ่ง tasks ออกเป็นไฟล์ย่อยด้วย include/import เพิ่มความ maintainability ในโปรเจกต์ขนาดใหญ่