SSD Nodes Learn 🎉 VPS เริ่ม $5.50/เดือน
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-07

Ansible playbook ต่างจาก role อย่างไร เลือกใช้อันไหนดี

เรียนรู้ความแตกต่างระหว่าง Ansible playbook และ role พร้อมเกณฑ์การตัดสินใจเลือกใช้ตามขนาดโปรเจกต์ โครงสร้างไดเรกทอรี การใช้ ansible-galaxy init และลำดับความสำคัญของตัวแปร

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

ความแตกต่างระหว่าง Ansible playbook และ role

Ansible playbook คือไฟล์ที่คุณเรียกใช้งานด้วย ansible-playbook โดยทำหน้าที่จับคู่กลุ่มโฮสต์เข้ากับงานที่ต้องทำ ส่วน Ansible role คือไดเรกทอรีที่มีโครงสร้างตายตัว ซึ่งเก็บ task, template, handler และตัวแปรเริ่มต้นไว้ โดยที่ playbook จะเรียกใช้งาน role ผ่านชื่อ ไวยากรณ์ของ task ในทั้งสองรูปแบบนั้นเหมือนกัน ดังนั้นจึงไม่ใช่คำถามว่าคุณสามารถเขียนอะไรได้บ้าง แต่เป็นคำถามเรื่องการนำกลับมาใช้ใหม่ (reuse)

ให้เริ่มต้นด้วย playbook แบบแบนราบ การใช้ site.yml หนึ่งรายการที่เก็บรายการ tasks: คือรูปแบบที่เหมาะสมสำหรับการทำระบบอัตโนมัติครั้งแรกของคุณ และมันยังคงใช้งานได้ดีนานกว่าที่คนส่วนใหญ่คาดคิด ให้เปลี่ยนไปใช้ role เมื่อชุดของ task เดิมต้องถูกนำไปรันให้กับกลุ่มโฮสต์กลุ่มที่สอง หรือเมื่อไฟล์มีขนาดใหญ่เกินกว่า 100 บรรทัดจนคุณไม่สามารถหา task ที่ต้องการได้จากการเลื่อนหน้าจอ

หากคุณยังไม่เคยเขียนมาก่อน ให้ เริ่มต้นด้วย playbook แรกของคุณกับ VPS หนึ่งเครื่อง แล้วค่อยกลับมาอ่านหน้านี้เมื่อ playbook ของคุณเริ่มมีขนาดใหญ่ขึ้น

เมื่อ playbook แบบแบนคือคำตอบที่เหมาะสม

playbook แบบแบน (flat playbook) เหมาะสมเมื่อต้องทำงานเพียงครั้งเดียว บนโฮสต์เดียว หรือเมื่อไม่มีผู้อื่นต้องอ่านโค้ดนั้น การจัดเตรียมเซิร์ฟเวอร์แอปพลิเคชันเพียงเครื่องเดียว หรือการแพตช์เซิร์ฟเวอร์ก่อนช่วงเวลาบำรุงรักษา ไม่จำเป็นต้องสร้างโครงสร้างไดเรกทอรีที่ซับซ้อน การใช้ role จะเพิ่มไดเรกทอรีอีก 7 แห่งและเพิ่มชั้นการอ้างอิงทางอ้อม หากผู้เรียกใช้มีเพียง playbook ที่วางอยู่ข้างๆ การอ้างอิงทางอ้อมนั้นก็ไม่มีประโยชน์และทำให้คุณต้องเสียเวลาคลิกเข้าไปดูทุกครั้งที่ต้องการตรวจสอบการทำงานจริง

playbook แบบแบนจะเริ่มไม่เหมาะสม ณ จุดหนึ่ง ซึ่งสังเกตได้ง่าย คือเมื่อคุณคัดลอกบล็อกของ task ไปไว้ใน playbook ที่สอง การคัดลอกนั้นคือสัญญาณเตือน จากนั้นเป็นต้นไป การแก้ไขทุกอย่างจะต้องทำซ้ำสองครั้ง และวันหนึ่งคุณจะลืมแก้ไขในที่ใดที่หนึ่งไปอย่างแน่นอน

สิ่งที่อยู่ในไดเรกทอรีของ role จริงๆ

roles/common/
  defaults/main.yml
  vars/main.yml
  tasks/main.yml
  handlers/main.yml
  templates/99-hardening.conf.j2
  files/
  meta/main.yml
  • tasks/main.yml คือจุดเริ่มต้น Ansible จะรันไฟล์นี้เมื่อมีการเรียกใช้ role และไดเรกทอรีอื่นๆ ทั้งหมดเป็นเพียงส่วนเสริม
  • defaults/main.yml เก็บตัวแปรที่คาดหวังให้ผู้เรียกทำการ override ซึ่งเป็นแหล่งข้อมูลที่มีลำดับความสำคัญต่ำที่สุดใน Ansible ดังนั้นเกือบทุกอย่างจึงมีความสำคัญเหนือกว่าตัวแปรในส่วนนี้
  • vars/main.yml เก็บตัวแปรที่ไม่ได้คาดหวังให้ผู้เรียกทำการ override โดยมีลำดับความสำคัญสูงกว่า inventory ซึ่งถือเป็นข้อกำหนดที่เข้มงวดมาก ควรใช้ด้วยความระมัดระวัง
  • handlers/main.yml เก็บ task ที่ถูกเรียกใช้งานโดย notify โดย handler จะทำงานที่ส่วนท้ายของ play เพียงหนึ่งครั้งเท่านั้น ไม่ว่าจะมีกี่ task ที่ส่งสัญญาณแจ้งเตือนมาก็ตาม
  • files/ เก็บไฟล์ที่ถูกคัดลอกแบบตรงตัวโดยโมดูล copy และ templates/ เก็บ Jinja2 template ที่ถูกประมวลผลโดยโมดูล template ภายใน role คุณสามารถอ้างอิงไฟล์ทั้งสองประเภทได้ด้วยชื่อไฟล์เปล่าๆ โดยไม่ต้องระบุ path เนื่องจาก Ansible จะค้นหาในไดเรกทอรีของ role นั้นๆ ก่อนเสมอ
  • meta/main.yml ประกาศ dependency ของ role และ metadata ที่ Ansible Galaxy จะอ่านไปใช้งาน

โครงสร้างนี้ไม่ใช่เรื่องของความชอบส่วนบุคคล Ansible ค้นหาไฟล์ใน path เหล่านี้โดยเฉพาะ ดังนั้น template ที่คุณวางไว้ใน roles/common/template/ (รูปเอกพจน์) จะไม่ถูกค้นพบอย่างแน่นอน

สร้าง role ทั่วไปด้วย ansible-galaxy init

mkdir -p ~/infra/roles
cd ~/infra
ansible-galaxy init --init-path roles common

คำสั่งนี้จะสร้างโครงสร้างไฟล์ทั้งหมดไว้ภายใต้ roles/common รวมถึงไดเรกทอรีที่คุณไม่ได้ใช้งานและไฟล์ stub main.yml ที่มีเพียง --- เท่านั้น ให้ลบไฟล์ที่คุณไม่ได้ใช้งานทิ้งเสีย ไฟล์ vars/main.yml ที่ว่างเปล่าไม่มีผลเสียต่อ Ansible แต่จะทำให้ไม่ทราบว่าไฟล์ใดใน role ที่มีความสำคัญจริง

จากนั้นให้เติมเนื้อหาลงในไฟล์ที่ทำหน้าที่ประมวลผล โดยเริ่มจาก defaults ก่อน เนื่องจากเป็นอินเทอร์เฟซสาธารณะของ role

# roles/common/defaults/main.yml
---
common_packages:
  - ufw
  - fail2ban
  - unattended-upgrades
common_admin_group: admins
common_permit_root_login: "no"
common_password_authentication: "no"

ให้ใส่เครื่องหมายคำพูดครอบ "no" และ "yes" เนื่องจาก Ansible ประมวลผล YAML ด้วย PyYAML ซึ่งจะอ่านค่า no เปล่าๆ ว่าเป็นค่า boolean false ส่งผลให้บรรทัด config ที่ถูกเรนเดอร์กลายเป็น PermitRootLogin False และ sshd จะปฏิเสธค่าดังกล่าว การใส่เครื่องหมายคำพูดจะช่วยรักษาค่าให้เป็นสตริง

# roles/common/tasks/main.yml
---
- name: Install the base packages
  ansible.builtin.apt:
    name: "{{ common_packages }}"
    state: present
    update_cache: true
    cache_valid_time: 3600

- name: Create the admin group
  ansible.builtin.group:
    name: "{{ common_admin_group }}"
    state: present

- name: Install the sshd hardening drop-in
  ansible.builtin.template:
    src: 99-hardening.conf.j2
    dest: /etc/ssh/sshd_config.d/99-hardening.conf
    owner: root
    group: root
    mode: "0644"
    validate: /usr/sbin/sshd -t -f %s
  notify: Restart sshd
# roles/common/handlers/main.yml
---
- name: Restart sshd
  ansible.builtin.service:
    name: ssh
    state: restarted
# roles/common/templates/99-hardening.conf.j2
# Managed by Ansible. Local edits are overwritten on the next run.
PermitRootLogin {{ common_permit_root_login }}
PasswordAuthentication {{ common_password_authentication }}

บน Debian และ Ubuntu นั้น systemd unit จะใช้ชื่อว่า ssh ส่วนบนระบบตระกูล RHEL จะใช้ชื่อว่า sshd handler ที่ระบุชื่อ unit ผิดจะแสดงข้อผิดพลาดก็ต่อเมื่อมีการเปลี่ยนแปลงใน template เท่านั้น ซึ่งเป็นสาเหตุว่าทำไมปัญหาประเภทนี้มักจะถูกตรวจพบหลังจากผ่านไปหลายสัปดาห์

บรรทัด validate เป็นส่วนที่มีประโยชน์ที่สุดใน task นั้น Ansible จะเรนเดอร์ template ลงในไฟล์ชั่วคราว จากนั้นจะแทนที่ path ของไฟล์นั้นด้วย %s แล้วจึงรันคำสั่ง ไฟล์ปลายทางจะถูกแทนที่ก็ต่อเมื่อคำสั่งนั้น exit code เป็น 0 เท่านั้น หากคุณใส่ directive ที่ไม่มีอยู่จริงลงใน template แล้วรันอีกครั้ง task จะล้มเหลวด้วย failed to validate โดยที่ไฟล์ /etc/ssh/sshd_config.d/99-hardening.conf จริงจะยังคงไม่ถูกแก้ไข และคุณจะยังคงสามารถล็อกอินเข้าเซิร์ฟเวอร์ได้ โปรดทราบว่าการตรวจสอบนี้ทดสอบมากกว่าแค่ไวยากรณ์ของคุณ หาก sshd -t ไม่สามารถอ่าน host keys ได้ มันจะ exit ด้วย sshd: no hostkeys available -- exiting. และ Ansible จะรายงานเป็น failed to validate เช่นกัน ดังนั้นโปรดอ่าน msg ของโมดูลก่อนที่จะสรุปว่าปัญหาเกิดจาก template

วิธีที่ playbook เรียกใช้งาน role

# site.yml
---
- name: Base configuration for every server
  hosts: all
  become: true
  roles:
    - common
# inventory.ini
[local]
localhost ansible_connection=local
ansible-playbook -i inventory.ini site.yml

Play ควรจบลงด้วย failed=0 ในส่วนสรุป ให้ส่งพารามิเตอร์ที่จุดเรียกใช้งานด้วยรูปแบบขยาย ซึ่งเป็นวิธีที่ role เดียวสามารถรองรับโฮสต์สองกลุ่มได้:

  roles:
    - role: common
      common_admin_group: ops
      common_permit_root_login: prohibit-password

มีกฎการเรียงลำดับหนึ่งข้อที่มักทำให้ทุกคนประหลาดใจ Play หนึ่งสามารถประกอบด้วย pre_tasks, roles, tasks และ post_tasks โดย Ansible จะรันตามลำดับดังกล่าวเสมอไม่ว่าคุณจะเขียนไว้ในไฟล์ด้วยลำดับใดก็ตาม แม้คุณจะวาง tasks: ไว้เหนือ roles: แต่ role ก็ยังคงรันก่อนอยู่ดี ดังนั้นหากมีสิ่งใดที่ต้องเกิดขึ้นก่อน role สิ่งนั้นต้องอยู่ใน pre_tasks: ไม่ใช่ที่ด้านบนของ tasks:

- name: Ordering demonstration
  hosts: local
  gather_facts: false
  pre_tasks:
    - name: Runs first
      ansible.builtin.debug:
        msg: pre
  roles:
    - common
  tasks:
    - name: Runs after the role
      ansible.builtin.debug:
        msg: task
  post_tasks:
    - name: Runs last
      ansible.builtin.debug:
        msg: post

หากต้องการเรียกใช้งาน role จากภายในรายการ task แทนการใช้คีย์ roles: ให้ใช้ import_role หรือ include_role

  tasks:
    - name: Static, read when the playbook is parsed
      ansible.builtin.import_role:
        name: common

    - name: Dynamic, resolved when the task runs
      ansible.builtin.include_role:
        name: postgres
      when: "'db' in group_names"

import_role เป็นแบบ static โดย Ansible จะอ่าน role ในช่วงเวลา parse และ task ของ role นั้นจะกลายเป็นส่วนหนึ่งของ play ทำให้ ansible-playbook --list-tasks site.yml สามารถแสดงรายการ task เหล่านั้นได้ และ tag ที่กำหนดบน import จะมีผลกับทุก task ที่อยู่ภายใน ส่วน include_role เป็นแบบ dynamic ซึ่งจะไม่มีการอ่านข้อมูลใดๆ จนกว่า task จะทำงานจริง ทำให้คุณสามารถกำหนดชื่อ role จากตัวแปรหรือ loop ได้ แต่ข้อเสียคือ task เหล่านั้นจะไม่ปรากฏให้ --list-tasks และ --start-at-task มองเห็น

มีกับดักหนึ่งจุดที่ต้องระวัง คือ when: บน task แบบ include_role จะถูกประเมินก่อนที่ defaults/main.yml ของ role ที่ถูก include จะอยู่ในขอบเขต (scope) หากคุณเขียน when: common_packages | length > 0 ไว้บน include การทำงานจะหยุดลงพร้อมกับข้อผิดพลาด 'common_packages' is undefined แม้ว่าตัวแปรนั้นจะถูกกำหนดไว้ใน role ที่คุณกำลัง include อยู่ก็ตาม วิธีแก้ไขคือให้ย้ายตัวแปรควบคุมนั้นออกมาจาก role โดยนำไปไว้ใน group_vars/all.yml ซึ่งจะทำให้ตัวแปรอยู่ในขอบเขตที่เข้าถึงได้จากทุกที่ และให้คงค่า default ของ role ไว้สำหรับค่าที่ role นั้นใช้งานเองเท่านั้น

ตัวแปรใดมีลำดับความสำคัญสูงสุด: defaults, group_vars, vars, extra vars

Ansible มีลำดับความสำคัญของตัวแปรมากกว่า 20 ระดับ แต่มี 4 ระดับที่ใช้ตัดสินข้อโต้แย้งส่วนใหญ่ในการใช้งานจริง โดยเรียงจากลำดับความสำคัญต่ำสุดไปสูงสุดดังนี้:

  • roles/<name>/defaults/main.yml อยู่ในลำดับเกือบล่างสุด ค่าเกือบทุกอย่างที่คุณกำหนดไว้ที่อื่นจะมีความสำคัญเหนือกว่า ซึ่งเป็นเหตุผลว่าทำไมตำแหน่งนี้จึงเหมาะสำหรับค่าที่ปรับแต่งได้ของ role
  • group_vars/ และ host_vars/ อยู่ในระดับกลาง นี่คือตำแหน่งที่ควรเก็บค่าเฉพาะสำหรับไซต์ของคุณ ซึ่งจะเขียนทับค่าเริ่มต้นของ role ได้อย่างชัดเจน
  • roles/<name>/vars/main.yml อยู่เหนือกว่า host_vars ค่าที่คุณกำหนดไว้ที่นี่จะไม่สามารถถูกเขียนทับจาก inventory ได้ ควรสำรองไว้สำหรับสิ่งที่ role จำเป็นต้องใช้เพื่อรักษาความสอดคล้องภายใน เช่น ชื่อแพ็กเกจที่ต้องตรงกับชื่อบริการ
  • พารามิเตอร์ของ role ที่ส่งผ่าน ณ จุดเรียกใช้งานจะมีความสำคัญเหนือกว่า vars/main.yml และ -e ที่กำหนดผ่าน command line จะมีความสำคัญเหนือกว่าทุกอย่าง รวมถึงพารามิเตอร์ของ role ด้วย

คุณสามารถสังเกตการแก้ไขลำดับความสำคัญนี้ได้ภายในเวลาประมาณหนึ่งนาที โดยกำหนดค่าเริ่มต้นหนึ่งค่าและตัวแปร role หนึ่งค่าให้กับ role ขนาดเล็ก จากนั้นกำหนดชื่อเดียวกันใน host_vars

# roles/prec/defaults/main.yml
---
prec_tunable: from-defaults
prec_internal: from-defaults
# roles/prec/vars/main.yml
---
prec_internal: from-rolevars
# host_vars/localhost.yml
---
prec_tunable: from-hostvars
prec_internal: from-hostvars
# roles/prec/tasks/main.yml
---
- name: Show which value survived
  ansible.builtin.debug:
    msg: "tunable={{ prec_tunable }} internal={{ prec_internal }}"
ansible-playbook -i inventory.ini prec.yml
ansible-playbook -i inventory.ini prec.yml -e prec_internal=from-cli

การรันครั้งแรกจะแสดงผลเป็น tunable=from-hostvars internal=from-rolevars เนื่องจาก inventory มีความสำคัญเหนือกว่าค่าเริ่มต้นของ role แต่ยังแพ้ให้กับตัวแปรของ role ส่วนการรันครั้งที่สองจะแสดงผลเป็น internal=from-cli เพราะ extra vars อยู่ในลำดับสูงสุดและไม่มีค่าใดที่อยู่ต่ำกว่าสามารถเขียนทับได้ นี่คือเหตุผลว่าทำไม -e จึงเหมาะสำหรับการรันแบบครั้งเดียว แต่เป็นวิธีที่ผิดหากใช้ในสคริปต์ที่คุณต้องเก็บไว้ใช้งานระยะยาว เพราะมันจะข้ามขั้นตอนการตัดสินใจทั้งหมดที่คุณได้วางแผนไว้ใน repository ของคุณโดยไม่แจ้งเตือน

กฎการทำงานคือ: หากคุณต้องการให้ค่าสามารถปรับเปลี่ยนได้ ให้ใส่ไว้ใน defaults/ การใส่ค่าไว้ใน vars/ เป็นการบอกผู้ใช้ role ในอนาคตทุกคนว่า inventory ไม่สามารถเปลี่ยนแปลงค่านี้ได้ ซึ่งบางครั้งอาจเป็นสิ่งที่คุณต้องการ แต่โดยส่วนใหญ่แล้วมักเป็นความผิดพลาดโดยไม่ตั้งใจ

พิสูจน์ว่า role มีคุณสมบัติ idempotent: รันซ้ำสองรอบ

การรัน Ansible ที่เชื่อถือได้จะต้องให้ผลลัพธ์เหมือนเดิมในการรันครั้งที่สอง และรายงานว่าไม่มีการเปลี่ยนแปลงใดๆ ให้รัน playbook สองรอบแล้วอ่านสรุปผล

ansible-playbook -i inventory.ini site.yml
ansible-playbook -i inventory.ini site.yml

สรุปผลรอบที่สองควรมีลักษณะดังนี้:

PLAY RECAP *********************************************************************
localhost   : ok=4  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

changed=0 หมายความว่าทุก module ได้ตรวจสอบสถานะปัจจุบันแล้วพบว่างานเสร็จสิ้นไปแล้ว หากพบ changed=2 ในการรันครั้งที่สอง แสดงว่ามีสอง task ที่ไม่สามารถแยกแยะความแตกต่างได้ จึงพยายามเขียนไฟล์และรีสตาร์ท service ซ้ำไปเรื่อยๆ สาเหตุที่พบบ่อยคือการใช้ command หรือ shell เนื่องจาก Ansible ไม่มีทางทราบได้ว่าคำสั่งเหล่านั้นทำอะไรลงไปบ้าง

# traps.yml
---
- name: Command modules do not know what they changed
  hosts: local
  gather_facts: false
  tasks:
    - name: This appends a line on every run
      ansible.builtin.shell: "echo run >> /tmp/grow.txt"

    - name: This appends a line only once
      ansible.builtin.shell: "echo run >> /tmp/guarded.txt"
      args:
        creates: /tmp/guarded.txt

รัน playbook นั้นสองรอบ จากนั้นนับจำนวนบรรทัดที่มี wc -l /tmp/grow.txt /tmp/guarded.txt โดย /tmp/grow.txt จะมีสองบรรทัดและ /tmp/guarded.txt จะมีหนึ่งบรรทัด ในการรันครั้งที่สอง task ที่มีการป้องกันไว้จะไม่ทำงานเลย และผลลัพธ์จะแสดงข้อความ skipped, since /tmp/guarded.txt exists เนื่องจาก creates ทำให้ module มีผลลัพธ์ที่มองเห็นได้ให้ตรวจสอบก่อน หากคำสั่งไม่มีผลลัพธ์ดังกล่าว ให้เก็บค่า output ไว้แล้วตัดสินใจด้วยตนเองโดยใช้ changed_when

ansible-playbook --check --diff site.yml ใช้คาดการณ์การเปลี่ยนแปลงโดยไม่ลงมือทำจริง และ --diff จะแสดงบรรทัดที่ template จะเขียนทับจริง ให้อ่านผลลัพธ์โดยคำนึงถึงข้อควรระวังประการหนึ่งคือ task ประเภท shell และ command จะถูกข้ามไปในโหมด check ดังนั้นแผนที่ดูเรียบร้อยอาจยังมีการทำงานที่ซ่อนอยู่ได้

เหตุใด Ansible จึงแจ้งว่าไม่พบ role

Ansible จะค้นหาไดเรกทอรี roles/ ที่อยู่ถัดจากไฟล์ playbook จากนั้นจึงค้นหาใน roles_path โดยลำดับการค้นหาจะอ้างอิงตามตำแหน่งของ playbook ไม่ใช่ตำแหน่งของ shell ที่คุณใช้งานอยู่

ERROR! the role 'common' was not found in /home/deploy/lonely/roles:/home/deploy/lonely

ข้อความดังกล่าวหมายความว่า site.yml และ roles/ ไม่ตรงกัน ซึ่งระบบจะแสดง path ที่พยายามค้นหาให้เห็นอย่างชัดเจน ควรเก็บไฟล์ทั้งสองไว้ในไดเรกทอรีเดียวกัน การรันคำสั่งจากไดเรกทอรีแม่สามารถทำได้ตามปกติ เนื่องจากตำแหน่งของ playbook คือสิ่งที่ Ansible ใช้ในการอ้างอิง:

ansible-playbook -i infra/inventory.ini infra/site.yml

ปัญหาในลักษณะเดียวกันนี้อาจเกิดขึ้นแบบเงียบๆ ได้เช่นกัน Ansible จะเพิกเฉยต่อไฟล์ ansible.cfg ในไดเรกทอรีปัจจุบันหากไดเรกทอรีนั้นอนุญาตให้ผู้ใช้ทุกคนเขียนไฟล์ได้ (world writable) เนื่องจากผู้ใช้รายอื่นบนเครื่องอาจวางไฟล์ config ที่ไม่พึงประสงค์เพื่อเปลี่ยนพฤติกรรมการรันคำสั่งของคุณได้

[WARNING]: Ansible is being run in a world writable directory (/tmp/infra), ignoring it as an ansible.cfg source.

ในกรณีนี้ การตั้งค่า roles_path และ inventory ของคุณจะถูกละเว้นไปโดยไม่มีการแจ้งเตือน ส่งผลให้การค้นหา role ล้มเหลวโดยที่สาเหตุไม่ได้เกี่ยวข้องกับตัว role เอง คุณสามารถใช้ ansible --version เพื่อตรวจสอบ config file ที่ถูกโหลดใช้งานจริง และใช้ ansible-config dump --only-changed เพื่อดูการตั้งค่าทั้งหมดที่แตกต่างไปจากค่าเริ่มต้นของระบบ ควรตรวจสอบทั้งสองคำสั่งนี้ทุกครั้งที่การรันคำสั่งมีพฤติกรรมเหมือนกับว่าไม่มีไฟล์ config ของคุณอยู่จริง

การแชร์บทบาท: requirements.yml และการระบุเวอร์ชันแบบเจาะจง

บทบาทที่ผู้อื่นเขียนขึ้นควรใช้วิธีติดตั้งแทนการคัดลอกไฟล์ ให้ประกาศไว้ในไฟล์เดียวดังนี้:

# requirements.yml
---
roles:
  - name: postgres
    src: https://github.com/example/ansible-role-postgres
    scm: git
    version: v1.4.0
ansible-galaxy install -r requirements.yml -p galaxy_roles

ต้องกำหนดค่า version เสมอ หากไม่มีการกำหนดค่านี้ คุณจะได้รับเวอร์ชันล่าสุดจาก branch เริ่มต้น ณ วันที่รันคำสั่ง ซึ่งจะส่งผลให้การปรับใช้ (deployment) ที่เคยทำงานได้ในเดือนที่แล้วเกิดข้อผิดพลาดขึ้นได้แม้คุณจะไม่ได้แก้ไข repository ของตนเองเลยก็ตาม ให้ชี้ roles_path ไปยังไดเรกทอรีสำหรับดาวน์โหลด และเก็บไดเรกทอรีดังกล่าวไว้นอก git:

# ansible.cfg
[defaults]
inventory = inventory.ini
roles_path = ./galaxy_roles

บทบาทที่อยู่ใน roles/ ซึ่งอยู่ถัดจาก playbook จะยังคงถูกค้นพบตามปกติ เนื่องจากเส้นทางดังกล่าวจะถูกค้นหาเพิ่มเติมจาก roles_path เสมอ ดังนั้นบทบาทที่คุณเขียนเองจะยังคงถูก commit และตรวจสอบได้ ในขณะที่บทบาทจากบุคคลที่สามจะเป็นไฟล์ที่ดาวน์โหลดมาอย่างชัดเจนและระบุเวอร์ชันไว้ด้วย tag อย่างถูกต้อง

จุดที่ role ไม่ใช่คำตอบอีกต่อไป

Role คือหน่วยสำหรับการนำกลับมาใช้ใหม่ภายใน Ansible run หนึ่งครั้ง มันไม่ได้ทำหน้าที่สร้างเซิร์ฟเวอร์หรือบันทึก DNS ที่ผู้ให้บริการของคุณ และการพยายามทำให้มันทำหน้าที่ดังกล่าวจะทำให้ playbook กลายเป็นสิ่งที่ไม่มีใครอยากดูแลรักษา การอ่านเรื่อง การแบ่งงานระหว่าง Ansible และ Terraform เป็นสิ่งที่ควรทำก่อนเริ่มต้น นอกจากนี้ role ยังไม่สามารถทดแทนการออกแบบ inventory ได้ เมื่อคุณมีเครื่องจำนวนมาก วิธีการจัดกลุ่มและเข้าถึงเซิร์ฟเวอร์เหล่านั้น จะมีความสำคัญมากกว่าวิธีการจัดเก็บไฟล์ task

การเพิ่มความปลอดภัยที่ role common นี้ติดตั้งให้ ก็เป็นสิ่งที่ต้องตัดสินใจด้วยตนเองเช่นกัน การตั้งค่าแบบ drop-in ด้านบนกำหนด directive ไว้เพียงสองรายการเท่านั้น ดังนั้นโปรดอ่าน การตั้งค่า SSH ใดที่ควรเปลี่ยนจริง และ วิธีการตั้งค่าให้ Ubuntu อัปเดตความปลอดภัยด้วยตนเอง ก่อนที่คุณจะตัดสินใจว่าสิ่งใดควรอยู่ใน role สำหรับทุกโฮสต์ที่คุณเป็นเจ้าของ

FAQ

เมื่อใดที่ควรเปลี่ยน Ansible playbook ให้เป็น role?

เมื่อชุดของ task เดิมต้องถูกนำไปรันใน play ที่สอง หรือรันกับกลุ่ม host กลุ่มที่สอง การคัดลอก task ระหว่าง playbook คือสัญญาณเตือน เพราะนับจากจุดนั้น การแก้ไขทุกครั้งจะต้องทำซ้ำสองจุด และวันหนึ่งคุณจะลืมแก้ไขจุดใดจุดหนึ่งไป Playbook เดียวที่มีความยาวไม่เกิน 100 บรรทัดและกำหนดเป้าหมายเพียงกลุ่มเดียวไม่ได้รับประโยชน์จากการทำเป็น role และโครงสร้างไดเรกทอรีที่เพิ่มขึ้นจะทำให้การอ่านทำความเข้าใจยากขึ้น

role ทำงานก่อน task อื่นๆ ใน play เดียวกันหรือไม่?

ใช่ Ansible จะรัน pre_tasks ก่อน จากนั้นจึงรันทุกอย่างที่ระบุไว้ภายใต้ roles:, tasks: และ post_tasks: โดย Ansible จะไม่สนใจลำดับของ key เหล่านี้ที่ปรากฏในไฟล์ของคุณ การเขียน tasks: ไว้เหนือ roles: ไม่ได้ทำให้ task เหล่านั้นทำงานก่อน หากมีสิ่งใดที่จำเป็นต้องเกิดขึ้นก่อน role ให้ใส่ไว้ใน pre_tasks:

ทำไมค่าใน group_vars ของฉันถึงไม่ทับค่าใน role?

ให้ตรวจสอบว่าตัวแปรนั้นถูกตั้งค่าไว้ใน vars/main.yml ของ role แทนที่จะเป็น defaults/main.yml หรือไม่ vars/ มีลำดับความสำคัญสูงกว่า group_vars และ host_vars ในลำดับการประมวลผลของ Ansible ดังนั้น inventory จึงไม่สามารถทับค่าได้ ให้ย้ายตัวแปรไปไว้ที่ defaults/main.yml ซึ่งอยู่เกือบล่างสุดของลำดับความสำคัญ และเป็นตำแหน่งที่ถูกต้องสำหรับค่าใดๆ ที่ผู้เรียกใช้ควรจะสามารถเปลี่ยนแปลงได้ หากต้องการยืนยันว่าปัญหาเกิดจากลำดับความสำคัญไม่ใช่การพิมพ์ผิด ให้รันด้วย -e name=value ซึ่งมีลำดับความสำคัญสูงกว่าแหล่งข้อมูลอื่นทั้งหมด

ทำไม Ansible ถึงแจ้งว่าไม่พบ role?

การค้นหาจะเริ่มจากตำแหน่งที่อยู่ข้างไฟล์ playbook ดังนั้น site.yml และ roles/ จะต้องอยู่ในไดเรกทอรีเดียวกัน ข้อผิดพลาดจะแสดง path ที่พยายามค้นหา เช่นใน the role 'common' was not found in /home/deploy/lonely/roles:/home/deploy/lonely การรัน playbook จากไดเรกทอรีแม่สามารถทำได้ปกติ เพราะการค้นหาจะอ้างอิงตาม path ของ playbook ไม่ใช่ working directory ของ shell หากคุณพึ่งพา roles_path จาก ansible.cfg ให้ตรวจสอบว่าไฟล์นั้นถูกโหลดด้วย ansible --version แล้ว เนื่องจาก Ansible จะเพิกเฉยต่อไฟล์ดังกล่าวหาก working directory นั้นอนุญาตให้ทุกคนเขียนไฟล์ได้ (world writable)

ฉันจำเป็นต้องใช้ ansible-galaxy init เพื่อสร้าง role หรือไม่?

ไม่จำเป็น role เป็นเพียงไดเรกทอรีที่มีชื่อตามที่กำหนด ดังนั้น mkdir -p roles/common/tasks ร่วมกับ tasks/main.yml ก็ถือเป็น role ที่ใช้งานได้แล้ว ansible-galaxy init --init-path roles common ช่วยประหยัดเวลาในการพิมพ์และให้โครงสร้างพื้นฐานที่ครบถ้วน รวมถึง meta/main.yml และไฟล์ README เบื้องต้น ให้ลบไดเรกทอรีที่คุณไม่ได้ใช้งานทิ้ง เพราะ vars/main.yml ที่ว่างเปล่าจะทำให้ดูไม่ออกว่าไฟล์ใดใน role ที่มีการทำงานจริง

#ansible#roles#playbook#structure#automation