Ansible Role Dependencies ช่วยให้ role หนึ่งสามารถกำหนดว่าต้องการ role อื่นทำงานก่อนโดยอัตโนมัติ — แทนที่จะ list roles ทุกตัวใน Playbook เอง role ที่มี dependency จะดึง role ที่จำเป็นมารันให้เองผ่าน meta/main.yml เทคนิคนี้ทำให้สร้าง role architecture แบบ layered ได้ โดยแต่ละ layer ทำหน้าที่เฉพาะและ reuse กันได้
บทความนี้ครอบคลุม role dependency ใน meta/main.yml, การส่ง variables ไปยัง dependency roles, allow_duplicates, การใช้ include_role สำหรับ dynamic dependencies, dependency chain หลายชั้น และ pattern สำหรับ layered role architecture
Role Dependency พื้นฐาน — meta/main.yml
กำหนด dependencies ใน meta/main.yml ของ role — เมื่อ Ansible รัน role A ที่มี dependency ต่อ role B, Ansible จะรัน role B ก่อนโดยอัตโนมัติ ไม่ต้องระบุ role B ใน Playbook
# roles/webapp/meta/main.yml
---
galaxy_info:
author: myteam
description: Deploy web application
license: MIT
min_ansible_version: "2.12"
platforms:
- name: Ubuntu
versions: [focal, jammy]
dependencies:
# Ansible รัน dependency roles เหล่านี้ก่อน role webapp เสมอ
- role: common # local role — setup OS baseline
- role: geerlingguy.nginx # Galaxy role — install nginx
- role: geerlingguy.postgresql # Galaxy role — install postgresql
# Playbook ที่ใช้ role webapp — ไม่ต้องระบุ common, nginx, postgresql
---
- name: Deploy webapp
hosts: appservers
become: true
roles:
- role: webapp # Ansible รัน common → nginx → postgresql → webapp อัตโนมัติ
ลำดับการรัน: Ansible จะ resolve dependency tree ก่อน แล้วรัน roles ตามลำดับ dependency ก่อนเสมอ — ถ้า role C depend on B และ B depend on A ลำดับจะเป็น A → B → C
ส่ง Variables ไปยัง Dependency Role
กำหนด vars ใน dependency entry เพื่อ override defaults ของ role นั้น — ทำให้ parent role ควบคุม configuration ของ dependency ได้โดยตรง
# roles/django_app/meta/main.yml
---
dependencies:
# ส่ง nginx config สำหรับ Django reverse proxy
- role: geerlingguy.nginx
vars:
nginx_worker_processes: auto
nginx_vhosts:
- listen: "80"
server_name: "{{ django_domain }}"
extra_parameters: |
location / {
proxy_pass http://127.0.0.1:{{ django_port }};
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static/ {
alias {{ django_static_root }}/;
expires 30d;
}
# ส่ง postgresql config สำหรับ Django database
- role: geerlingguy.postgresql
vars:
postgresql_version: "15"
postgresql_databases:
- name: "{{ django_db_name }}"
encoding: UTF-8
postgresql_users:
- name: "{{ django_db_user }}"
password: "{{ django_db_password }}"
db: "{{ django_db_name }}"
priv: ALL
role_attr_flags: "NOSUPERUSER,NOCREATEDB"
# ส่ง redis config สำหรับ Django caching
- role: geerlingguy.redis
vars:
redis_bind_interface: "127.0.0.1"
redis_maxmemory: "{{ django_cache_maxmemory | default('256mb') }}"
redis_maxmemory_policy: allkeys-lru
allow_duplicates — รัน Role เดิมหลายครั้ง
ตามค่า default Ansible จะรัน role เดิมเพียงครั้งเดียวต่อ play แม้จะมีหลาย roles depend on role เดียวกัน — ถ้าต้องการให้ role รันซ้ำด้วย parameters ต่างกัน ใช้ allow_duplicates: true ใน meta/main.yml ของ role นั้น
# roles/create_vhost/meta/main.yml — role ที่อนุญาตให้รันซ้ำ
---
galaxy_info:
author: myteam
description: Create a single nginx virtual host
allow_duplicates: true # อนุญาตให้รันหลายครั้งด้วย params ต่างกัน
dependencies: []
# Playbook — รัน create_vhost หลายครั้งสำหรับ vhosts ต่างกัน
---
- name: Setup multiple virtual hosts
hosts: webservers
become: true
roles:
- role: create_vhost
vars:
vhost_name: api
vhost_domain: api.example.com
vhost_port: 8080
- role: create_vhost # รันซ้ำได้เพราะ allow_duplicates: true
vars:
vhost_name: admin
vhost_domain: admin.example.com
vhost_port: 8081
- role: create_vhost
vars:
vhost_name: static
vhost_domain: static.example.com
vhost_port: 8082
include_role — Dynamic Dependency ใน Tasks
include_role ใน tasks ช่วยให้เรียก role แบบ dynamic ได้ตาม condition — ต่างจาก meta/main.yml dependencies ที่รันเสมอ, include_role รองรับ when, loop และ dynamic role names
# roles/webapp/tasks/configure.yml
---
# เรียก role ตาม condition
- name: Configure SSL if enabled
include_role:
name: ssl_cert
vars:
ssl_domain: "{{ webapp_domain }}"
ssl_email: "{{ webapp_admin_email }}"
when: webapp_enable_ssl | bool
# เรียก role สำหรับ monitoring ถ้า enabled
- name: Setup monitoring agent
include_role:
name: "{{ webapp_monitoring_agent }}" # dynamic role name
vars:
monitoring_tags:
- webapp
- "{{ webapp_name }}"
when: webapp_enable_monitoring | bool
# เรียก role หลายครั้งด้วย loop
- name: Create database schemas
include_role:
name: create_schema
vars:
schema_name: "{{ item.name }}"
schema_owner: "{{ item.owner }}"
loop: "{{ webapp_db_schemas }}"
when: webapp_db_schemas is defined
import_role — Static Role ใน Tasks
import_role ต่างจาก include_role ตรงที่ Ansible parse ไว้ตั้งแต่ต้น (static) — ทำให้รองรับ tags และ handlers ของ role นั้น แต่ไม่รองรับ dynamic variables ใน role name
# roles/full_stack/tasks/main.yml
---
# import_role — static, รองรับ tags ของ role ที่เรียก
- name: Setup database layer
import_role:
name: postgresql_server
vars:
pg_version: "15"
pg_data_dir: /var/lib/postgresql/15/main
- name: Setup cache layer
import_role:
name: redis_server
vars:
redis_port: 6379
redis_maxmemory: 512mb
# include_role — dynamic, รองรับ when/loop
- name: Setup app layer
include_role:
name: "{{ item.role }}"
vars:
app_port: "{{ item.port }}"
loop:
- { role: api_service, port: 8080 }
- { role: worker_service, port: 8081 }
- { role: scheduler, port: 8082 }
Dependency Chain — Role ซ้อน Role หลายชั้น
Ansible resolve dependency chain โดยอัตโนมัติ — ถ้า role C depend on B และ B depend on A, Ansible รัน A → B → C เสมอ แม้จะเรียกแค่ role C ใน Playbook
# Layer 1: roles/base_os/meta/main.yml — ไม่มี dependencies
---
galaxy_info:
description: OS baseline setup
dependencies: []
# Layer 2: roles/web_server/meta/main.yml — depend on base_os
---
galaxy_info:
description: Web server setup
dependencies:
- role: base_os
vars:
base_packages:
- curl
- openssl
- ca-certificates
# Layer 3: roles/app_platform/meta/main.yml — depend on web_server
---
galaxy_info:
description: Application platform (Python/Node runtime)
dependencies:
- role: web_server
vars:
web_server_type: nginx
web_server_version: "1.24"
# Layer 4: roles/my_app/meta/main.yml — depend on app_platform
---
galaxy_info:
description: My specific application
dependencies:
- role: app_platform
vars:
platform_runtime: python
platform_version: "3.11"
# Playbook ที่ใช้แค่ my_app — Ansible รัน chain อัตโนมัติ:
# base_os → web_server → app_platform → my_app
Pattern: Layered Role Architecture สำหรับ Multi-Tier Application
ตัวอย่าง architecture ของ role ที่ออกแบบเป็น layers ชัดเจน — แต่ละ layer reusable ข้าม project และ override ได้ด้วย variables
# โครงสร้าง role layers สำหรับ Django application stack
roles/
# Layer 1: Infrastructure baseline
common/
defaults/main.yml # packages, users, timezone, etc.
tasks/main.yml
meta/main.yml # dependencies: []
# Layer 2: Database server
postgresql/
defaults/main.yml # pg_version, pg_port, pg_data_dir
tasks/main.yml
meta/main.yml # dependencies: [common]
# Layer 3: Web server
nginx/
defaults/main.yml # nginx_version, worker settings
tasks/main.yml
meta/main.yml # dependencies: [common]
# Layer 4: Application platform
python_app/
defaults/main.yml # python_version, venv settings
tasks/main.yml
meta/main.yml # dependencies: [common]
# Layer 5: Specific application (depends on layers 2-4)
django_blog/
defaults/main.yml # app-specific settings
tasks/main.yml
meta/main.yml # dependencies: [postgresql, nginx, python_app]
# roles/django_blog/meta/main.yml — complete dependency definition
---
galaxy_info:
author: myteam
description: Deploy Django blog application
min_ansible_version: "2.12"
platforms:
- name: Ubuntu
versions: [focal, jammy]
dependencies:
- role: postgresql
vars:
pg_version: "15"
pg_databases:
- name: "{{ blog_db_name }}"
pg_users:
- name: "{{ blog_db_user }}"
password: "{{ blog_db_password }}"
db: "{{ blog_db_name }}"
priv: ALL
- role: nginx
vars:
nginx_vhosts:
- server_name: "{{ blog_domain }}"
root: "{{ blog_static_root }}"
extra_parameters: |
location / {
proxy_pass http://unix:{{ blog_socket }};
proxy_set_header Host $host;
}
- role: python_app
vars:
python_version: "3.11"
python_venv_path: "{{ blog_venv_path }}"
python_requirements: "{{ blog_install_dir }}/requirements.txt"
# roles/django_blog/defaults/main.yml
---
blog_name: myblog
blog_domain: blog.example.com
blog_port: 8000
blog_install_dir: "/opt/{{ blog_name }}"
blog_static_root: "{{ blog_install_dir }}/staticfiles"
blog_venv_path: "{{ blog_install_dir }}/venv"
blog_socket: "/var/run/{{ blog_name }}/gunicorn.sock"
blog_db_name: "{{ blog_name }}_db"
blog_db_user: "{{ blog_name }}_user"
blog_db_password: "" # set ใน vault
blog_gunicorn_workers: "{{ ansible_processor_vcpus * 2 + 1 }}"
blog_debug: false
# Playbook — เรียกแค่ django_blog, Ansible handle dependency chain เอง
---
- name: Deploy Django blog
hosts: blogservers
become: true
vars:
blog_domain: myblog.example.com
blog_db_password: "{{ vault_blog_db_password }}"
blog_debug: false
roles:
- role: django_blog
# Ansible รัน: postgresql → nginx → python_app → django_blog
# common รันก่อนทุก role เพราะ postgresql/nginx/python_app ต่างก็ depend on common
สรุป
Role dependencies ใน meta/main.yml ช่วยให้สร้าง self-contained roles ที่จัดการ dependencies เองได้ — Ansible resolve chain อัตโนมัติทำให้ Playbook กระชับและไม่ต้องระบุทุก role เอง การออกแบบเป็น layers ทำให้แต่ละ role reusable และ test แยกกันได้
Pattern ที่ควรจำ: ใช้ meta/main.yml dependencies สำหรับ static dependencies ที่รันทุกครั้ง, ใช้ include_role สำหรับ dynamic dependencies ที่รันตาม condition, ใช้ allow_duplicates: true เมื่อต้องการรัน role เดิมหลายครั้งด้วย parameters ต่างกัน และออกแบบ role layers จาก infrastructure baseline ขึ้นไปสู่ application layer เพื่อ reuse สูงสุด

