Skip to content

Instantly share code, notes, and snippets.

@maprangzth
Last active January 23, 2024 04:27
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maprangzth/128875f633b9690b74e81491d8e20f97 to your computer and use it in GitHub Desktop.
Save maprangzth/128875f633b9690b74e81491d8e20f97 to your computer and use it in GitHub Desktop.
Ansible พื้นฐานที่ (ตัวเอง) ควรรู้ Ep.05 — Playbooks

Ansible พื้นฐานที่ (ตัวเอง) ควรรู้ Ep.05 - Playbooks

Ansible Logo

จาก Ep. ก่อนหน้านี้ที่พูดเกี่ยวกับการใช้งาน Ansible แบบ Ad-Hoc Commands ก็คงพอที่จะทำให้เห็นประโยชน์การนำ Ansible มาใช้งานกันแล้วคร่าวๆ แต่...จะเห็นได้ว่าการเรียกใช้งาน Module บางตัวนั้นจะต้องระบุ Opstions เยอะแยะมากมายวุ่นวายไปหมด ยิ่งไปกว่านั้นสมมุติถ้าเรามี Task ที่ต้องทำหลายๆ Tasks ยกตัวอย่าง เช่น ติดตั้ง Web Server, คอนฟิก Web Server, ติดตั้ง Database Server ทำ Hardening ฯลฯ คำถามคือ...เราต้องมานั่งพิมพ์คำสั่งยาวๆ ทีละ Task งั้นรึ? ดูมันวุ่นวายเนอะ -_-

Ansible จึงมีวิธีจัดการกับเรื่องยุ่งยากซับซ้อนจากที่ต้องรัน Manual แต่ละ Task ก็เกิดวิธีการเขียนเป็น Ansible Script และมีชื่อเรียกอย่างเป็นทางการว่า Playbooks ซึ่งในหัวข้อนี้เราก็จะดูกันว่ามันคืออะไร? และจะมาช่วยให้มันง่ายขึ้นกว่าการใช้งานแบบ Ad-Hoc Commands จริงๆ หรือเปล่า? ชักช้าอยู่ใยไปเริ่มกันเลยดีกว่าครับ

Playbooks คืออะไร?

Playbooks คือ Configuration Management ที่จะเป็นตัวบอก Ansible ว่าต้องทำอะไรบ้าง (คล้ายๆ กับ To-do lists) ซึ่ง Config ข้างในก็จะมี List ของ Tasks ซึ่งแต่ละ Task ก็จะไปเรียกใช้งาน Modules อีกที (จริงๆ มี Config ย่อยๆ อีกเดี๋ยวจะค่อยๆ อธิบายในตัวอย่าง playbooks)

Playbooks นั้นใช้ YAML ในการเขียน Config จึงทำให้การใช้งาน Playbooks กลายเป็นเรื่องกล้วยๆ ไปเลยทีเดียว เพราะ Syntax YAML นั้นถูกออกแบบมาให้ ทั้งการเขียนและอ่าน มีความ friendly กับมนุษย์ที่สุดแล้ว

Friendly ถึงขั้นที่บางคนแม้ไม่เคยใช้งาน Ansible (แต่คลุกคลีกับ UNIX หรือ Linux) มาก่อนอาจจะเดาได้เลยว่า Code หรือ Config ใน Playbooks มันทำจะอะไรบ้าง

ตัวอย่าง Playbooks การติดตั้ง Apache Web Server:

--- 
- name: Install Apache Web Server
  hosts: all 
  user: root
  tasks: 
  - name: Ensure Apache package is installed 
    yum: 
      name: httpd 
      state: present 
  - name: Ensure Apache service is running and enabled 
    service: 
      name: httpd 
      state: started 
      enabled: True

Note:
การจะใช้งาน Playbooks ให้ไหลลื่นนั้นจำเป็นต้องเข้าใจ Syntax YAML ด้วยนะ ซึ่งคงต้องไปศึกษากันเอาเอง แต่ถ้าผมขยัน อาจจะเขียนสรุปมาให้ได้อ่านกันครับ

คำศัพท์ที่(อาจจะ)ต้องพบเจอ

  • Playbooks - เป็นตัวบอก Ansible ว่าต้องทำอะไร
  • Plays - เอาไว้แยกการทำงานใน Playbooks ออกเป็นส่วนๆ ในหนึ่ง Playbooks ก็สามารถมีได้หลาย Plays
  • Tasks - Tasks คือ งานที่จะถูกทำใน remote host ใน tasks ก็จะต้องระบุ Modules ด้วยเสมอ
  • Roles - ทำหน้าที่รวบรวม หรือแยก Tasks ออกเป็นส่วนๆ (เดี๋ยวเขียนอธิบายเรื่องนี้แยกอีกที)
  • Handler - Handler คือ Tasks ชนิดหนึ่งที่เรียกใช้เพื่อให้เกิดการปลี่ยนแปลงบางอย่าง และ Handler จะทำงานก็ต่อเมื่อ notify ที่ระบุไว้ใน Task ทำงานเท่านั้น
  • Inventory - Inventory คือ ที่ที่เก็บ List ของ hosts หรืออาจะเรียกว่า hosts file ก็ได้นะ
  • Modules - Modules คือ ชุดคำสั่งที่ถูกเขียนไว้ให้เรียกใช้งาน
  • Variables - แปลตรงตัวเลยมันก็คือ "ตัวแปร" ซึ่งจะช่วยทำให้การใช้งาน Ansible ยืดหยุ่นยิ่งขึ้น

เริ่มใช้งาน Playbooks...

ในการเรียกใช้งาน Playbooks นั้นจะต้องสร้างไฟล์ชื่ออะไรก็ได้ (แต่ควรจะตั้งชื่อให้สื่อกับสิ่งที่จะทำ เพื่อลดควาามสับสนของผู้ใช้เอง) โดยบันทึกเป็นไฟล์ .yaml หรือ .yml เช่น install_apache.yml และจะต้องใช้ command "ansible-playbook" น่ะจ๊ะ ซึ่งเวลาพิมพ์คำสั่งใน terminal ก็จะมีความแตกต่างจาก command "ansible" อยู่นิดหน่อย ดังนี้

ansible:

ansible -i <inventory_file> <host patterns> -m <module>

ansible-playbook:

ansible-playbook -i <inventory_file> <playbook_file> 

จะเห็นว่าในการใช้เรียกงาน command ansible-playbook นั้นไม่จำเป็นต้องระบุในส่วนของ <host patterns> และ <module> เข้าไปแต่อย่างใด จริงๆ มันไม่ได้หายไปไหนนะ แต่เนื่องจากว่าเราได้เขียนระบุไว้ใน Playbooks file แล้วนั่นเอง

มาดูตัวอย่างการใช้งาน Playbooks เพื่อให้เข้าใจถึงการทำงานกันดีกว่าครับ

Inventory:

[db-servers]
db-01  ansible_host=192.168.124.243
db-02  ansible_host=192.168.124.182

[web-servers]
web-01  ansible_host=192.168.124.158
web-02  ansible_host=192.168.124.153
web-03  ansible_host=192.168.124.92

1. Create Ansible user

file: create_ansible_user.yml

---
- name: Create Ansible user
  hosts: all
  remote_user: root
  vars:
    user_name: maprangzth
    user_keys: https://github.com/maprangzth.keys
  tasks:
  - name: Ensure ansible user exists
    user:
      name: "{{ user_name }}"
      state: present
      comment: "Ansible user"
  - name: Ensure ansible user accepts the SSH key
    authorized_key:
      user: "{{ user_name }}"
      key: "{{ user_keys }}"
      validate_certs: False
      state: present
  - name: Ensure sudo package is installed (RHEL Base)
    yum:
      name: sudo
      state: present
    when: ansible_os_family == 'RedHat'
  - name: Ensure sudo package is installed (Debian Base)
    apt:
      name: sudo
      state: present
    when: ansible_os_family == 'Debian'
  - name: Ensure the ansible user is sudoer with no password required
    lineinfile:
      dest: /etc/sudoers
      state: present
      regexp: '^{{ user_name }} ALL\='
      line: '{{ user_name }} ALL=(ALL) NOPASSWD:ALL'
      validate: 'visudo -cf %s'

ต้นฉบับ firstrun.yaml, Learning Ansible 2 - Second Edition

command:

ansible-playbook -i inventories create_ansible_user.yml
  • Output: create_user

Note:
vars หมายถึง การกำหนดตัวแปร เวลาเรียกใช้งานตัวแปรจะต้องอยู่ภายในปีกกาสองชั้น "{{ variable_name }}"
when หมายถึง การกำหนดเงื่อนไข ในตัวอย่างตอนที่เรียกใช้งาน Task "Ensure sudo package is installed (Debian Base)" จะเห็นว่า Task นั้นถูก Skip หรือถูกข้ามไป นั่นเพราะว่าไม่ตรงกับเงื่อนไข Task นั้นจึงไม่ทำงาน
TASK [Gathering Facts] - เป็น Task ที่ ansible-playbook เรียกใช้งานโดย default เพื่อ ssh เข้าไปตรวจสอบ และดึงข้อมูลที่จำเป็นออกมาแล้วเก็บไว้ในตัวแปรระบบของ Ansible เอง และเราไม่ต้อง ระบุ task นี้ตอนที่เขียน playbooks แต่อย่างใด

2. Install Apache Web Server

file: install_apache.yml

---
- name: Install Apache Web Server
  hosts: web-server 
  remote_user: maprangzth
  tasks: 
  - name: Ensure httpd package is latest 
    yum: 
      name: httpd 
      state: latest
    notify: Start and enabled httpd service
    become: True 

  handlers:
   - name: Start and enabled httpd service
     service: 
      name: httpd 
      state: started 
      enabled: True
     become: True

command-1:

ansible-playbook -i inventories install_apache.yml
  • Output-1: install_apache_with_handler_first

Note:
become: True หมายถึง ให้ user ใช้ sudo ในการรันคำสั่งนั้นๆ
handlers - Handler คือ Tasks ชนิดหนึ่งที่เรียกใช้เพื่อให้เกิดการปลี่ยนแปลงบางอย่าง และ Handler จะทำงานก็ต่อเมื่อ notify ที่ระบุไว้ใน Task ทำงานเท่านั้น ที่สำคัญชื่อของ hendler ควรตั้งให้สั้นและสื่อความหมาย เพราะ notify ต้องอ้างอิงชื่อตามที่ตั้งให้กับ handler (ในตัวอย่าง playbooks ด้านบนมีการกำหนด handler เพื่อ start httpd แต่ถ้า httpd ถูก started อยู่แล้ว handler ที่กำหนดก็จะไม่ทำงาน ดัง Output-2)

command-2:

ansible-playbook -i inventories install_apache.yml
  • Output-2: install_apache_with_handler_second

3. Install Multiple Packages Using Loop

file: install_reqiured_package.yml

---
- name: Install reqiured packages
  hosts: all
  remote_user: maprangzth
  tasks:
  - name: Ensure EPEL Repo is present
    yum:
      name: epel-release
      state: present
    become: True
  - name: Ensure reqiured packages in present
    yum :
      name: "{{ item }}"
      state: present
      update_cache: yes
    become: True
    with_items:
     - ntp
     - iotop
     - htop

command:

ansible-playbook -i inventories install_reqiured_package.yml
  • Output: install_apache_with_handler_second

Note:
with_items หมายถึง การประกาศใช้งาน Loop เวลาเรียกใช้งานคล้ายๆ กับ variables แต่ชื่อตัวแปรต้องเป็น item เท่านั้น! (ตัวอย่าง playbooks ด้านบนถ้าไม่มีการใช้งาน loop เราจะต้องเขียน task เพื่อติดตั้ง packages เพิ่ม 3 tasks เป็นสิ่งที่ไม่ควรทำอย่างยิ่ง)

สรุป

จะเห็น Playbooks นั้นเข้ามาช่วยทำให้การใช้งาน Ansible ง่ายขึ้นเยอะ นั่นก็เพราะ Playbooks มันเป็น Core Feature ของ Ansible นั่นเอง ในตัวอย่าง Playbooks ด้านบนก็เป็นแค่ พื้นฐาน เพื่อให้เห็นภาพการใช้งานเท่านั้น ส่วนใครที่อยากจะเป็นเทพอันนี้คงต้องไปศึกษาเพิ่มเติมกันเอาเอง (ในชีวิตจริง Project ใหญ่ๆ เขาใช้โหดกว่านี้เยอะ) ส่วนเรื่อง Roles ที่ติดค้างเดี๋ยวจะมาเล่าให้ฟังกันใน Ep. หน้าครับ และขอบอกเลยว่า Roles เป็นสิ่งที่ควรค่าแก่การศึกษาอย่างยิ่ง

@imrockyim
Copy link

รบกวนขอลิ้ง EP 1-4 ได้ไหมครับผม

@patzidane
Copy link

เขียนเรื่อง roles ยังเอ่ย กำลังศึกษาอยู่เลย ทั้งการสร้าง และเวลาสร้าง จาก ansible galaxy แล้ว มี โฟลเดอร์ขี้นมาเยอะแยะ ใช้ทำอะไรบ้าง แล้ว ตัวไฟล์ hosts กับ deploy.yml ที่เวลาเราสร้าง roles จาก ansible galaxy เป็น default มาให้ นี่เอาไว้ทำอะไรบ้าง ใช้ยังไงหรอ hosts ตรงนี้ ต่างกับ hosts ตัวที่เรียกใช้ roles ยังไง

@maprangzth
Copy link
Author

maprangzth commented Jan 19, 2020

เขียนเรื่อง roles ยังเอ่ย กำลังศึกษาอยู่เลย ทั้งการสร้าง และเวลาสร้าง จาก ansible galaxy แล้ว มี โฟลเดอร์ขี้นมาเยอะแยะ ใช้ทำอะไรบ้าง แล้ว ตัวไฟล์ hosts กับ deploy.yml ที่เวลาเราสร้าง roles จาก ansible galaxy เป็น default มาให้ นี่เอาไว้ทำอะไรบ้าง ใช้ยังไงหรอ hosts ตรงนี้ ต่างกับ hosts ตัวที่เรียกใช้ roles ยังไง

กราบขออภัยอย่างยิ่งครับ ผมไม่ได้แวะมาดูใน Gist เลย ปกติ Published บน Medium ไม่คิดว่าจะมีคนมาอ่านในนี้ด้วย แฮร่ ๆ
ปล. ถ้ายังติดค้างเรื่องใด สามารถสอบถามได้ครับ เดี๋ยวจิแวะมาตอบให้ ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment