Ansible Roles: จัดระเบียบ Playbook ด้วยโครงสร้าง Role

Roles คือวิธีจัดระเบียบ Playbook ขนาดใหญ่ให้เป็นหน่วยย่อยที่นำกลับมาใช้ใหม่ได้ แทนที่จะเขียน tasks ทั้งหมดในไฟล์เดียว Role แยกงานออกเป็นโครงสร้างไดเรกทอรีมาตรฐานที่ประกอบด้วย tasks, handlers, templates, files, vars และ defaults

บทความนี้อธิบายโครงสร้าง Role, วิธีสร้างและใช้งาน, การส่ง variables เข้า Role, และ pattern ที่ใช้บ่อยสำหรับ Role ที่ดูแลรักษาง่าย

โครงสร้างไดเรกทอรีของ Role

Role มีโครงสร้างไดเรกทอรีมาตรฐานที่กำหนดโดย Ansible ไฟล์ใน main.yml ของแต่ละไดเรกทอรีจะถูกโหลดโดยอัตโนมัติ

roles/
└── nginx/
    ├── tasks/
    │   └── main.yml        # tasks หลักของ Role
    ├── handlers/
    │   └── main.yml        # handlers เช่น reload service
    ├── templates/
    │   └── nginx.conf.j2   # Jinja2 templates
    ├── files/
    │   └── index.html      # ไฟล์ static สำหรับ copy
    ├── vars/
    │   └── main.yml        # variables ที่ override ไม่ได้ (priority สูง)
    ├── defaults/
    │   └── main.yml        # default variables (priority ต่ำ — override ได้)
    └── meta/
        └── main.yml        # metadata เช่น dependencies

ความต่างระหว่าง vars/main.yml และ defaults/main.yml: ค่าใน defaults มี priority ต่ำที่สุดและถูก override ได้จากทุกที่ ส่วน vars มี priority สูงกว่า ใช้สำหรับค่าที่ Role ต้องการบังคับ

สร้าง Role ด้วย ansible-galaxy

คำสั่ง ansible-galaxy role init สร้างโครงสร้างไดเรกทอรีครบถ้วนโดยอัตโนมัติ ไม่ต้องสร้างทีละไดเรกทอรีเอง

# สร้าง Role ใหม่ชื่อ nginx
ansible-galaxy role init roles/nginx

# โครงสร้างที่สร้างมาให้:
# roles/nginx/
# ├── defaults/main.yml
# ├── files/
# ├── handlers/main.yml
# ├── meta/main.yml
# ├── README.md
# ├── tasks/main.yml
# ├── templates/
# ├── tests/
# │   ├── inventory
# │   └── test.yml
# └── vars/main.yml

ตัวอย่าง Role: nginx

ตัวอย่าง Role สำหรับติดตั้งและ configure nginx ประกอบด้วยไฟล์หลัก 4 ไฟล์

# roles/nginx/defaults/main.yml
nginx_port: 80
nginx_worker_processes: "auto"
nginx_server_name: "localhost"
nginx_web_root: /var/www/html
nginx_enable_ssl: false
# roles/nginx/tasks/main.yml
---
- name: Install nginx
  package:
    name: nginx
    state: present

- name: Deploy nginx config
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    validate: nginx -t -c %s
  notify: reload nginx

- name: Ensure web root exists
  file:
    path: "{{ nginx_web_root }}"
    state: directory
    owner: www-data
    group: www-data
    mode: '0755'

- name: Start and enable nginx
  service:
    name: nginx
    state: started
    enabled: yes
# roles/nginx/handlers/main.yml
---
- name: reload nginx
  service:
    name: nginx
    state: reloaded

- name: restart nginx
  service:
    name: nginx
    state: restarted

ใช้งาน Role ใน Playbook

เรียกใช้ Role ด้วย key roles: หรือ ansible.builtin.include_role ใน task

---
# site.yml — ใช้ Role แบบ static
- name: Configure web servers
  hosts: webservers
  become: true
  roles:
    - role: nginx
      vars:
        nginx_port: 8080
        nginx_server_name: "app.example.com"
        nginx_enable_ssl: true

    - role: certbot          # Role อื่นในลำดับถัดไป

---
# ใช้ include_role แบบ dynamic (ใช้ใน task)
- name: Conditionally include role
  hosts: all
  tasks:
    - name: Install nginx on web servers
      include_role:
        name: nginx
      vars:
        nginx_port: "{{ app_port }}"
      when: server_role == "web"

Role Dependencies

Role สามารถกำหนด dependencies กับ Role อื่นได้ใน meta/main.yml โดย Ansible จะรัน dependency Role ก่อนโดยอัตโนมัติ

# roles/wordpress/meta/main.yml
---
dependencies:
  - role: nginx
    vars:
      nginx_port: 80
  - role: mysql
    vars:
      mysql_db_name: wordpress
      mysql_db_user: wp_user
  - role: php
    vars:
      php_version: "8.1"

เมื่อรัน Role wordpress ระบบจะรัน nginx, mysql, และ php ก่อนโดยอัตโนมัติตามลำดับ dependencies ที่กำหนด

แยก tasks ออกเป็นหลายไฟล์

Role ที่ซับซ้อนควรแยก tasks ออกเป็นหลายไฟล์ตาม concern แล้วใช้ include_tasks หรือ import_tasks ใน main.yml

# roles/nginx/tasks/main.yml
---
- name: Install packages
  import_tasks: install.yml

- name: Configure nginx
  import_tasks: configure.yml

- name: Setup SSL (if enabled)
  include_tasks: ssl.yml
  when: nginx_enable_ssl | bool

- name: Configure virtual hosts
  include_tasks: vhosts.yml
  when: nginx_vhosts is defined

ความต่างระหว่าง import_tasks และ include_tasks: import_tasks โหลดแบบ static ตอน parse (conditions ทำงานทีละ task), ส่วน include_tasks โหลดแบบ dynamic ตอน runtime (condition ทำงานกับทั้งไฟล์)

ติดตั้ง Role จาก Galaxy

Ansible Galaxy คือ repository ของ Community Roles ที่นำมาใช้ได้ทันทีโดยไม่ต้องเขียนเอง

# ติดตั้ง Role จาก Galaxy
ansible-galaxy role install geerlingguy.nginx
ansible-galaxy role install geerlingguy.mysql

# ติดตั้งทุก Role จากไฟล์ requirements.yml
ansible-galaxy role install -r requirements.yml
# requirements.yml — กำหนด Roles ที่ต้องการ
---
roles:
  - name: geerlingguy.nginx
    version: "3.2.0"

  - name: geerlingguy.mysql
    version: "4.3.0"

  - name: my_custom_role
    src: https://github.com/myorg/ansible-role-custom.git
    scm: git
    version: main

สรุป

Roles เปลี่ยน Playbook จากสคริปต์ยาวเป็นโครงสร้างที่อ่านง่ายและนำกลับมาใช้ซ้ำได้ Pattern ที่ควรจำ: ใช้ defaults/main.yml เสมอสำหรับ variables ที่ต้องการให้ผู้ใช้ override ได้, แยก tasks ออกเป็นไฟล์ย่อยเมื่อ Role มีมากกว่า 20 tasks, และ pin version ของ Galaxy Roles ใน requirements.yml เพื่อความ reproducible

Role ที่ดีคือ Role ที่ทำงานเดียวได้ดี (single responsibility) มี defaults ที่สมเหตุสมผล และ document ตัวแปรทั้งหมดใน defaults/main.yml ด้วย comment อธิบาย