จาก 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 คือ 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 นั้นจะต้องสร้างไฟล์ชื่ออะไรก็ได้ (แต่ควรจะตั้งชื่อให้สื่อกับสิ่งที่จะทำ เพื่อลดควาามสับสนของผู้ใช้เอง) โดยบันทึกเป็นไฟล์ .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
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
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 แต่อย่างใด
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
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
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
Note:
with_items
หมายถึง การประกาศใช้งาน Loop เวลาเรียกใช้งานคล้ายๆ กับ variables แต่ชื่อตัวแปรต้องเป็น item เท่านั้น! (ตัวอย่าง playbooks ด้านบนถ้าไม่มีการใช้งาน loop เราจะต้องเขียน task เพื่อติดตั้ง packages เพิ่ม 3 tasks เป็นสิ่งที่ไม่ควรทำอย่างยิ่ง)
จะเห็น Playbooks นั้นเข้ามาช่วยทำให้การใช้งาน Ansible ง่ายขึ้นเยอะ นั่นก็เพราะ Playbooks มันเป็น Core Feature ของ Ansible นั่นเอง ในตัวอย่าง Playbooks ด้านบนก็เป็นแค่ พื้นฐาน เพื่อให้เห็นภาพการใช้งานเท่านั้น ส่วนใครที่อยากจะเป็นเทพอันนี้คงต้องไปศึกษาเพิ่มเติมกันเอาเอง (ในชีวิตจริง Project ใหญ่ๆ เขาใช้โหดกว่านี้เยอะ) ส่วนเรื่อง Roles ที่ติดค้างเดี๋ยวจะมาเล่าให้ฟังกันใน Ep. หน้าครับ และขอบอกเลยว่า Roles เป็นสิ่งที่ควรค่าแก่การศึกษาอย่างยิ่ง
กราบขออภัยอย่างยิ่งครับ ผมไม่ได้แวะมาดูใน Gist เลย ปกติ Published บน Medium ไม่คิดว่าจะมีคนมาอ่านในนี้ด้วย แฮร่ ๆ
ปล. ถ้ายังติดค้างเรื่องใด สามารถสอบถามได้ครับ เดี๋ยวจิแวะมาตอบให้ ^^