Ansible Host Variables และ Group Variables: ตั้งค่า Default ต่อ Server

ตัวแปรใน Ansible ช่วยให้ playbook เดียวทำงานต่างกันในแต่ละ server ได้ ไม่ว่าจะเป็น port, path, หรือค่า config ที่แตกต่างกันระหว่าง production กับ staging การเข้าใจวิธีกำหนด host_vars และ group_vars อย่างถูกต้องช่วยให้ playbook อ่านง่าย maintain ได้ดี และขยายระบบได้โดยไม่ต้องแก้ logic หลัก

บทความนี้อธิบายการใช้งาน host_vars และ group_vars ทั้งแบบ inline ในไฟล์ inventory และแบบแยกโฟลเดอร์ พร้อมลำดับความสำคัญของตัวแปรที่ต้องเข้าใจก่อนออกแบบโครงสร้างโปรเจกต์

ประเภทของ Variables

ตัวแปรใน Ansible แบ่งตามขอบเขตการใช้งานได้ 2 ระดับหลัก ได้แก่ host variables ที่ใช้เฉพาะกับ server เครื่องนั้น และ group variables ที่ใช้กับทุก server ในกลุ่มนั้น การเลือกระดับที่ถูกต้องช่วยลดการซ้ำซ้อนและทำให้ config อ่านเข้าใจง่ายขึ้น

Inline Variables ใน Inventory File

วิธีที่ง่ายที่สุดคือกำหนด variable ตรงใน inventory file ตามหลัง hostname ในรูปแบบ key=value

# inventory.ini

[webservers]
web1.example.com http_port=80 max_connections=200
web2.example.com http_port=8080 max_connections=100

[dbservers]
db1.example.com db_port=5432

# group variables สำหรับทุก host ใน webservers
[webservers:vars]
app_user=www-data
document_root=/var/www/html

# variables สำหรับทุก host ในระบบ
[all:vars]
ntp_server=time.google.com
ansible_user=ubuntu

วิธีนี้เหมาะสำหรับโปรเจกต์เล็กที่มี host ไม่มาก เพราะทุกอย่างอยู่ในไฟล์เดียว แต่เมื่อ inventory ขยายใหญ่ขึ้น การแยกออกมาเป็นโฟลเดอร์แยกจะ maintain ได้ง่ายกว่า

โครงสร้างโฟลเดอร์ host_vars และ group_vars

วิธีมาตรฐานที่แนะนำสำหรับโปรเจกต์ขนาดกลางขึ้นไปคือสร้างโฟลเดอร์ host_vars/ และ group_vars/ ในโฟลเดอร์โปรเจกต์ โดยระบบจะค้นหาไฟล์ใน path เหล่านี้อัตโนมัติ

project/
├── inventory.ini
├── group_vars/
│   ├── all.yml           # ตัวแปรสำหรับทุก host
│   ├── webservers.yml    # ตัวแปรสำหรับ group webservers
│   └── dbservers.yml     # ตัวแปรสำหรับ group dbservers
├── host_vars/
│   ├── web1.example.com.yml   # ตัวแปรเฉพาะของ web1
│   └── db1.example.com.yml    # ตัวแปรเฉพาะของ db1
└── site.yml

ตัวอย่างไฟล์ group_vars

แต่ละไฟล์ใน group_vars/ ใช้รูปแบบ YAML ธรรมดา โดยชื่อไฟล์ต้องตรงกับชื่อ group ใน inventory

# group_vars/all.yml — ใช้กับทุก host
ntp_server: time.google.com
timezone: Asia/Bangkok
ansible_user: ubuntu

# group_vars/webservers.yml — ใช้เฉพาะ webservers group
http_port: 80
document_root: /var/www/html
app_user: www-data
worker_processes: auto

# group_vars/dbservers.yml — ใช้เฉพาะ dbservers group
db_port: 5432
db_max_connections: 100
backup_dir: /var/backups/postgres

ตัวอย่างไฟล์ host_vars

ไฟล์ใน host_vars/ ชื่อต้องตรงกับ hostname หรือ alias ที่กำหนดใน inventory

# host_vars/web1.example.com.yml
http_port: 80
server_alias: www.example.com
ssl_enabled: true

# host_vars/web2.example.com.yml
http_port: 8080
server_alias: staging.example.com
ssl_enabled: false

แยกโฟลเดอร์ย่อยสำหรับ Group ที่ซับซ้อน

สำหรับ group ที่มีตัวแปรจำนวนมาก สามารถสร้างโฟลเดอร์ย่อยแทนไฟล์เดียว ระบบจะอ่านไฟล์ทุกไฟล์ใน path นั้นอัตโนมัติ

group_vars/
└── webservers/
    ├── main.yml      # ค่าพื้นฐาน
    ├── ssl.yml       # ค่า SSL
    └── firewall.yml  # ค่า firewall rules

วิธีนี้ทำให้แบ่ง config ตามหัวข้อได้ชัดเจนขึ้น และแต่ละไฟล์มีขนาดเล็กลง ง่ายต่อการ review และ merge ใน version control

ลำดับความสำคัญของ Variables (Precedence)

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

  • group_vars/all — ความสำคัญต่ำสุด ใช้สำหรับ default ของทั้งระบบ
  • group_vars/[groupname] — ใช้กับ host ในกลุ่มนั้น
  • host_vars/[hostname] — ใช้เฉพาะ host นั้น override ค่า group ได้
  • vars ใน play/task — กำหนดใน playbook โดยตรง
  • extra_vars (-e) — ส่งผ่าน command line ความสำคัญสูงสุด override ทุกอย่าง
# ตัวอย่าง: http_port ถูกกำหนดในหลายที่
# group_vars/all.yml       → http_port: 80   (default)
# group_vars/webservers.yml → http_port: 443  (override all)
# host_vars/web2.yml       → http_port: 8080 (override group)

# ผลลัพธ์: web2 จะใช้ port 8080, web อื่นในกลุ่มใช้ 443

ใช้ Variables ใน Playbook

ตัวแปรที่กำหนดใน host_vars หรือ group_vars พร้อมใช้งานใน playbook ผ่าน Jinja2 template syntax โดยอัตโนมัติ

# site.yml
- name: Configure web server
  hosts: webservers
  tasks:
    - name: Set nginx port
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      vars:
        listen_port: "{{ http_port }}"

    - name: Create document root
      file:
        path: "{{ document_root }}"
        state: directory
        owner: "{{ app_user }}"

ตรวจสอบ Variables ที่ Host รับ

ใช้คำสั่งนี้เพื่อดูตัวแปรทั้งหมดที่ host แต่ละเครื่องจะได้รับ รวมถึง source ที่มาของแต่ละค่า

# ดู variables ทั้งหมดของ host
ansible-inventory -i inventory.ini --host web1.example.com

# รัน debug module เพื่อดู variables จาก playbook
ansible webservers -m debug -a "var=http_port" -i inventory.ini

สรุป

host_vars และ group_vars เป็นกลไกหลักในการจัดการ configuration ที่แตกต่างกันในแต่ละ server โดยไม่ต้องแก้ playbook หลัก การใช้โฟลเดอร์แยกแทน inline variables ทำให้โปรเจกต์ scale ได้ดีกว่าในระยะยาว สิ่งสำคัญคือต้องเข้าใจลำดับ precedence เพื่อออกแบบโครงสร้างตัวแปรได้ถูกต้องตั้งแต่ต้น