Ansible Variables: เบื้องต้น Local, Fact, Extra Variables

Variables คือหัวใจสำคัญของ Playbook ที่ทำให้ automation ยืดหยุ่นและนำกลับมาใช้ซ้ำได้ แทนที่จะเขียน value ตายตัวลงในแต่ละ task ตรง ๆ การใช้ variables ช่วยให้ปรับค่าต่าง ๆ ได้จากจุดเดียวโดยไม่ต้องแก้ไข task ทั้งหมด

บทความนี้อธิบายครบ 3 ประเภทหลักที่ใช้บ่อยที่สุด ได้แก่ Local Variables (กำหนดในไฟล์), Facts (ข้อมูลระบบที่รวบรวมอัตโนมัติ), และ Extra Variables (ส่งผ่าน command line) พร้อม Variable Precedence ที่บอกว่าเมื่อ variable ซ้ำกัน ตัวไหนจะ override

Local Variables: กำหนดค่าในไฟล์ Playbook

Local variables คือค่าที่กำหนดตรงในไฟล์ Playbook หรือไฟล์ vars แยกต่างหาก มี 3 วิธีหลัก

1. vars block ใน Playbook

วิธีที่ง่ายที่สุดคือกำหนดใต้ vars: ในไฟล์ Playbook โดยตรง

---
- name: Deploy web application
  hosts: webservers
  become: true

  vars:
    app_name: myapp
    app_port: 8080
    app_user: www-data
    app_dir: /var/www/myapp

  tasks:
    - name: Create app directory
      ansible.builtin.file:
        path: "{{ app_dir }}"
        owner: "{{ app_user }}"
        state: directory

    - name: Start application on port {{ app_port }}
      ansible.builtin.debug:
        msg: "Starting {{ app_name }} on port {{ app_port }}"

2. vars_files: โหลดจากไฟล์แยก

สำหรับ variables จำนวนมาก หรือที่ต้องแชร์ระหว่างหลาย Playbook ควรแยกออกเป็นไฟล์ YAML ต่างหาก

# vars/app_config.yml
app_name: myapp
app_port: 8080
db_host: localhost
db_name: myapp_db
db_user: app_user

# playbook.yml
---
- name: Configure application
  hosts: webservers
  become: true
  vars_files:
    - vars/app_config.yml

  tasks:
    - name: Configure database connection
      ansible.builtin.template:
        src: templates/db.conf.j2
        dest: /etc/myapp/db.conf

3. host_vars และ group_vars: กำหนดต่อ host/group

สำหรับค่าที่แตกต่างกันในแต่ละ host หรือ group ให้สร้างไดเรกทอรี host_vars/ หรือ group_vars/ โดยชื่อไฟล์ต้องตรงกับชื่อ host หรือ group

# group_vars/webservers.yml  (ใช้กับทุก host ใน group webservers)
nginx_worker_processes: 4
max_connections: 1024

# group_vars/dbservers.yml  (ใช้กับทุก host ใน group dbservers)
mysql_max_connections: 200
innodb_buffer_size: 2G

# host_vars/web01.example.com.yml  (ใช้กับ host นี้เท่านั้น)
nginx_worker_processes: 8   # override ค่าของ group
server_role: primary

ค่าใน host_vars/ จะ override ค่าใน group_vars/ เสมอ ทำให้ตั้งค่า default ที่ group แล้ว override เฉพาะ host ที่ต้องการได้

Ansible Facts: ข้อมูลระบบที่รวบรวมอัตโนมัติ

เมื่อรัน Playbook ระบบจะรวบรวม facts จากแต่ละ host อัตโนมัติก่อน task แรกเสมอ (ผ่าน module setup) ข้อมูลเหล่านี้เข้าถึงได้ผ่าน ansible_facts

---
- name: Show system facts
  hosts: all
  tasks:
    - name: Display OS information
      ansible.builtin.debug:
        msg: >
          Host: {{ ansible_hostname }}
          OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
          Memory: {{ ansible_memtotal_mb }} MB

Facts ที่ใช้บ่อยที่สุดในทางปฏิบัติ:

# ตรวจสอบ OS แล้วใช้ package manager ที่ถูกต้อง
- name: Install nginx (Debian/Ubuntu)
  ansible.builtin.apt:
    name: nginx
    state: present
  when: ansible_os_family == "Debian"

- name: Install nginx (RHEL/CentOS)
  ansible.builtin.dnf:
    name: nginx
    state: present
  when: ansible_os_family == "RedHat"

Custom Facts: กำหนด Facts เอง

นอกจาก built-in facts ยังสร้าง custom facts ได้โดยวางไฟล์ .fact ไว้ที่ /etc/ansible/facts.d/ บน host

# /etc/ansible/facts.d/app.fact  (ไฟล์บน remote host)
[app]
version=2.5.1
env=production
deploy_date=2026-04-16

# ใช้งานใน Playbook
- name: Show custom fact
  ansible.builtin.debug:
    msg: "App version: {{ ansible_local.app.app.version }}"

ปิด Fact Gathering เมื่อไม่จำเป็น

การรวบรวม facts ใช้เวลาประมาณ 1-2 วินาทีต่อ host เมื่อรัน Playbook ที่ไม่ต้องการ facts ควรปิดเพื่อประหยัดเวลา

---
- name: Quick task (no facts needed)
  hosts: all
  gather_facts: false    # ปิด fact gathering

  tasks:
    - name: Create directory
      ansible.builtin.file:
        path: /tmp/mydir
        state: directory

Extra Variables: ส่งค่าผ่าน Command Line

Extra variables หรือ -e / --extra-vars คือวิธีส่งค่า variable จากภายนอกตอนรัน command ซึ่งมี priority สูงสุด — override ทุก variable อื่นในระบบ

# ส่ง variable เดี่ยว
ansible-playbook deploy.yml -e "app_version=2.5.1"

# ส่งหลาย variables พร้อมกัน
ansible-playbook deploy.yml -e "app_version=2.5.1 env=staging"

# ส่งจากไฟล์ YAML (ใส่ @ นำหน้า)
ansible-playbook deploy.yml -e "@vars/override.yml"

Extra variables มีประโยชน์มากใน CI/CD pipeline โดยส่งค่า version หรือ environment ตอนรัน Playbook โดยไม่ต้องแก้ไขไฟล์ใด ๆ

---
- name: Flexible deployment
  hosts: "{{ target_env }}_servers"    # กำหนด host group จาก extra var
  become: true

  vars:
    app_version: "1.0.0"               # default ถ้าไม่ส่ง -e

  tasks:
    - name: Deploy version {{ app_version }}
      ansible.builtin.debug:
        msg: "Deploying {{ app_version }} to {{ target_env }}"

รัน Playbook นี้ด้วย:

ansible-playbook deploy.yml -e "target_env=staging app_version=2.5.1"
ansible-playbook deploy.yml -e "target_env=production app_version=2.5.1"

Variable Precedence: ลำดับความสำคัญ

เมื่อ variable ชื่อเดียวกันถูกกำหนดจากหลายที่พร้อมกัน ระบบจะใช้ลำดับ priority นี้ (จากต่ำไปสูง — ค่าที่มี priority สูงกว่าจะ override)

# ลำดับ priority จากต่ำ → สูง (ค่าสูงกว่า override ค่าต่ำกว่า)
# 1. role defaults
# 2. inventory vars (group_vars/all)
# 3. group_vars/<groupname>
# 4. host_vars/<hostname>
# 5. play vars (vars: ใน Playbook)
# 6. vars_files
# 7. set_fact / registered variables
# 8. extra vars (-e) ← สูงสุด

# ตัวอย่างเชิงปฏิบัติ:
# group_vars/all.yml
app_port: 80

# group_vars/production.yml
app_port: 443    # override ค่าจาก all

# host_vars/web01.yml
app_port: 8443   # override ค่าจาก production group

# command line: -e "app_port=9443"
# → ใช้ 9443 (override ทุกอย่าง)

หลักง่าย ๆ คือ: ยิ่งเฉพาะเจาะจงมากเท่าไร priority ยิ่งสูง และ -e สูงสุดเสมอ

set_fact: สร้าง Variable แบบ Dynamic

บางครั้งต้องคำนวณหรือสร้าง variable ระหว่างรัน Playbook ใช้ module set_fact สำหรับงานนี้

---
- name: Dynamic variables example
  hosts: all
  tasks:
    - name: Set backup filename with timestamp
      ansible.builtin.set_fact:
        backup_file: "backup_{{ ansible_hostname }}_{{ ansible_date_time.date }}.tar.gz"

    - name: Use the dynamic variable
      ansible.builtin.debug:
        msg: "Will create: {{ backup_file }}"

    - name: Compute memory threshold (80% of total)
      ansible.builtin.set_fact:
        memory_threshold: "{{ (ansible_memtotal_mb * 0.8) | int }}"

    - name: Show threshold
      ansible.builtin.debug:
        msg: "Alert when memory > {{ memory_threshold }} MB"

ตัวแปรที่สร้างด้วย set_fact จะคงอยู่ตลอด play และสามารถใช้ใน task ถัด ๆ ไปได้ทันที

สรุป

การเลือกประเภท variable ที่เหมาะสมทำให้ Playbook อ่านง่ายและจัดการได้ดี: ใช้ vars_files + group_vars สำหรับค่าที่แตกต่างกันตาม environment, ใช้ facts สำหรับข้อมูลระบบที่ต้องการ real-time, และใช้ -e สำหรับค่าที่ต้องการ override ตอนรัน เช่น version ใน pipeline

จุดที่ต้องระวัง: Variable Precedence คือสาเหตุหลักของ bug ที่หาสาเหตุยาก ถ้า Playbook ทำงานผิดปกติให้ตรวจสอบว่ามี variable ชื่อเดียวกันถูกกำหนดจากหลายที่หรือไม่ โดยเพิ่ม task - debug: var=ชื่อ_variable เพื่อดูค่าที่ใช้จริง