Hướng dẫn viết Ansible Module bằng Bash Shell – Cuongquach.com | Chào các bạn, nếu bạn đọc bài viết này hẳn bạn đã biết sử dụng Ansible với các module Ansible để tự động hoá các tác vụ cần thiết trên các máy chủ remote như Linux, Windows, Cisco Router,… Hôm nay chúng ta sẽ cùng tìm hiểu cơ bản cách thức viết một Ansible Module bằng ngôn ngữ lập trình Bash Shell chạy trên các máy chủ remote Linux như thế nào nhé.
Có thể bạn quan tâm chủ đề khác
– Sự khác biệt giữa Ansible Module Command và Module Shell
– Tìm hiểu cấu hình Handler trong Ansible Playbook
– Cấu hình Ansible Retry Task trong playbook
– Các mẹo tăng tốc độ thực thi của Ansible
Contents
- 1. Ansible Module với Bash Shell
- 2. Một Ansible Module Bash Shell tuỳ biến hoạt động sao ?
- 3. Chương trình chạy Ansible Module phía máy chủ remote
- 4. Thư mục chứa Ansible Module Bash Shell tuỳ biến
- 5. Tên Ansible Module tuỳ biến
- 6. Nhược điểm khi viết Anssible Module tuỳ biến bằng Bash Shell
- 7. Cấu trúc cần nắm khi code ansible modules với Bash Shell
- Thực hành 1 – Ansible Module Bash Shell Inventory
- Thực hành 2 – Ansible Module Bash Shell Backup
- Tổng kết
1. Ansible Module với Bash Shell
Một trong những điểm mạnh của Ansible đó là ngoài các module đang có, Ansible cho phép bạn lập trình viết thêm các Module Ansible tuỳ biến theo nhu cầu cá nhân với các ngôn ngữ phổ biến khác nhau như: python, go, bash shell, perl,… giúp bạn đạt được nội dung tự động hoá tác vụ riêng biệt của mình.
Vì vậy trong hôm nay chúng ta sẽ thử làm quen với việc lập trình code ansible module bằng Bash Shell thử xem nhé. Mình khuyến khích các bạn sử dụng Python để lập trình Ansible Module vì sự tích hợp tương thích với các Ansible Plugins. Nhưng khi làm quen hoặc thích đơn giản, không quan tâm nhiều xử lý output trả về,.. thì bạn có thể làm quen với Ansible Module Bash Shell.
(Chúng ta sẽ tìm hiểu cách lập trình code ansible module bằng Python ở bài viết khác)
Bash Shell ?
Bash Shell (shell script) là ngôn ngữ lập trình kịch bản phổ biến trên các hệ điều hành Unix/Linux. Một ngôn ngữ đơn giản để chạy các tác vụ Linux tự động mà bạn mong muốn. Mặc định khi đọc bài viết này là bạn đã biết lập trình Bash Shell rồi nhé.
Yêu cầu code ansible module bằng Bash Shell
- Biết lập trình Bash Shell trên Linux.
- Biết kiến thức về JSON.
- Biết sử dụng Ansible.
2. Một Ansible Module Bash Shell tuỳ biến hoạt động sao ?
Ở phần này ta sẽ lưu ý cách mà Ansible hoạt động với các module (ví dụ với module bằng code bash shell chạy trên máy chủ remote Linux) như thế nào nhé :
- Ansible khởi tạo file bash shell (python/go/perl/…) với module chỉ định và các tham số biến truyền vào. File bash shell này sẽ sử dụng cho tác vụ chạy trên máy chủ remote.
- Kết nối SSH đến máy chủ remote khởi tạo thư mục tạm ở thư mục $HOME user kết nối.
- Upload file code bash shell lên thư mục tạm.
- Chạy file bash shell tác vụ và khi hoàn tất thì xoá thư mục tạm.
- Lấy thông tin kết quả thực thi tác vụ với output định dạng json.
3. Chương trình chạy Ansible Module phía máy chủ remote
Ở phần trên như bạn đã biết thì Ansible sẽ copy file code module với các option truyền biến vào qua bên máy chủ remote để khởi chạy. Thì tất nhiên yêu cầu phía máy chủ phải có các chương trình cần thiết để chạy được code đó như ‘bash’, ‘python’, ‘go’,…
4. Thư mục chứa Ansible Module Bash Shell tuỳ biến
Chúng ta cần khai báo thư mục chứa các file code module đơn lẻ, sử dụng như một module Ansible tuỳ biến. Nếu bạn cài Ansible bằng package hoặc repository thì file cấu hình Ansible sẽ nằm ở đường dẫn như dưới :
/etc/ansible/ansible.cfg
Bạn cấu hình giá trị ‘library’ để chỉ định Ansible thư mục sẽ load các module tuỳ biến của bạn. Ví dụ mình dùng thư mục ‘/etc/ansible/custom_modules’ để chứa các file code Ansible Module tuỳ biến của mình.
# vi /etc/ansible/ansible.cfg library = /etc/ansible/custom_modules/ # mkdir -p /etc/ansible/custom_modules/
5. Tên Ansible Module tuỳ biến
Một Ansible Module tuỳ biến nên có tên đơn giản, tránh trùng với các tên module hiện hành của Ansible, không nên để đuôi extension bất kì. Ví dụ:
cqinventory cqbackup
6. Nhược điểm khi viết Anssible Module tuỳ biến bằng Bash Shell
- Khá là khó khăn khi muốn tận dụng nguồn tài nguyên plugin của Ansible, vì Ansible core được viết chính bằng Python nên không thể tương thích hoặc tích hợp chung được.
- Yêu cầu output trả về phải là định dạng JSON, bash shell xử lý định dạng JSON khá là phức tạp không linh động được.
7. Cấu trúc cần nắm khi code ansible modules với Bash Shell
Input Ansible Module
Thường khi cấu hình Ansible Playbook bạn sẽ thấy các biến như sau được truyền vào như dưới :
--- - hosts: collectors_init gather_facts: false become: yes tasks: - name: "Create User Apps - Cuongquach" user: name: "apps" state: present shell: "/sbin/nologin" comment: “Cuongquach-user" createhome: no
Các biến này có dạng ‘key=value’, trong code bash shell thì bạn sẽ dùng option dưới trong file module code bash shell để load tất cả các biến truyền vào bash shell trong suốt quá trình chạy Ansible Module tuỳ biến bash shell của bạn.
source $1
Ví dụ, bạn có 2 option khi sử dụng module:
src=/home/cuongquach/ action=backup
thì khi sử dụng ‘source $1’, nó sẽ tạo ra 2 biến
src="/home/cuongquach/“ action=“backup"
Bạn sẽ gọi các biến này trong script như bình thường khi code bash shell :
echo $src echo $action
Output Ansible Module
Output trả về cho Ansible từ module của bạn sau khi chạy xong ở máy chủ remote phải là định dạng JSON. Nếu bạn trả về bất kì thứ gì không phải dịnh dạng JSON hoặc Ansible không thể phân tích được output JSON của bạn thì Ansible sẽ coi Module đó đã chạy thất bại. Điều này có nghĩa trong code Bash Shell của bạn cần in ra nội dung json cho Ansible. (Bạn có thể tìm hiểu trước về JSON trên Google nếu chưa biết)
Trong nội dung output JSON thì bạn cần quan tâm tồn tại ít nhất hai key như sau:
- changed: giá trị này thể hiện việc thực thi các tác vụ module của bạn đã thành công. Set giá trị ‘true‘ nếu các thay đổi thành công, set giá trị ‘false‘ nếu không có gì thay đổi.
- msg: trả về bất kì chuỗi thông tin nào mà bạn muốn đính kèm.
Bên cạnh đó bạn có thể chèn thêm rất nhiều giá trị key=value mà bạn mong muốn khác vào output JSON cho Ansible từ Module tuỳ biến của bạn. Giúp bạn kiểm soát được các thông tin bạn muốn.
Tham khảo một số giá trị biến trả về phổ biến khác của Ansible: LINK
Ví dụ, code bạn sau cùng nên in ra JSON Output như sau. Các tiến trình trong khi chạy không nên để in ra màn hình làm hư định dạng output, có thể redirect stdout stderr vào /dev/null .
echo '{"changed": true, "msg": "Backup thanh cong"}'
hoặc
echo "{\"changed\": true, \"msg\": \"Backup thanh cong\"}"
hoặc
msg="Backup thanh cong" printf '{"changed": true, "msg": "%s"}' "$msg"
Lưu ý:
- Nếu bạn đã tìm hiểu về JSON rồi thì luôn luôn nhớ là giá trị key hay tên biến luôn phải nằm trong dấu ngoặc kép “..” .
- Value của key nếu là chuỗi thì phải nằm trong dấu ngoặc kép luôn; còn nếu là số hoặc boolean (true hoặc false), lists, dictionaries thì không cần dấu ngoặc kép.
Thực hành 1 – Ansible Module Bash Shell Inventory
Với phần này chúng ta sẽ đến với việc code một file Ansible Module Bash Shell với nội dung đơn giản là đi thống kê thông tin của máy chủ remote Linux mà bạn đang chạy.
Chúng ta sẽ truyền một tham số theo yêu cầu ví dụ code thứ 1:
- option: nếu bạn có xử lý nhiều function tác vụ khác nhau thì truyền vào đây.
File code mẫu Ansible Module Bash Shell Inventory sẽ nằm ở :
/etc/ansible/custom_modules/cqinventory
Code mẫu Ansible Module Bash Shell Inventory :
#!/bin/bash #Author: CuongQuach #Homepage: https://cuongquach.com/ #Module ansible bash shell - lab function guess_distro_os(){ [ -f /etc/redhat-release ] && awk '{print ($1,$3~/^[0-9]/?$3:$4)}' /etc/redhat-release && return [ -f /etc/os-release ] && awk -F'[= "]' '/PRETTY_NAME/{print $3,$4,$5}' /etc/os-release && return [ -f /etc/lsb-release ] && awk -F'[="]+' '/DESCRIPTION/{print $2}' /etc/lsb-release && return } function get_info_os(){ cpu_name="$(awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//' | awk '$1=$1')" cpu_cores="$(awk -F: '/model name/ {core++} END {print core}' /proc/cpuinfo)" mem_total="$(free -m | awk '/Mem/ {print $2}')" swap_total=$( free -m | awk '/Swap/ {print $2}' ) os_distro=$(guess_distro_os) os_arch=$(uname -m) os_lbit=$(getconf LONG_BIT ) os_kern=$(uname -r) os_hostname="$(hostname)" date=$(date) #Change result variable changed_result="false" msg_result="Get information about this host $os_hostname successful." #Json result summary info_result="{ \"cpu_name\": \"${cpu_name}\", \"cpu_cores\": \"${cpu_cores}\", \"mem_total\": \"${mem_total}\", \"swap_total\": \"${swap_total}\", \"os_distro\": \"${os_distro}\", \"os_arch\": \"${os_arch}\", \"os_lbit\": \"${os_lbit}\", \"os_kern\": \"${os_kern}\", \"os_hostname\": \"${os_hostname}\", \"date\": \"${date}\" }" info_result="$(echo $info_result | tr -d '\n')" } ################# # Main function # ################# #Load agruments source $1 if [ -z "${option}" ];then printf '{"failed": true, "msg": "missing required arguments: option"}' exit 1 fi #Default variables changed_result="false" msg_result="" info_result="" case $option in os) get_info_os ;; *) printf '{"failed": true, "msg": "invalid option: %s"}' "$option" exit 1 ;; esac #Return final result to ansible printf '{"changed": %s, "msg": "%s", "info": %s}' "$changed_result" "$msg_result" "$info_result" exit 0
Chạy thử Ansible Module Bash Shell Inventory
Khai báo host cho Ansible trước nhé (còn setup ssh key thì bạn tự làm nhé).
# vi /etc/ansible/hosts [servers] demo-host-cuongquach ansible_host=192.168.1.1
+ Ansible Ad-hoc Command
Giờ chạy thử Ansible Module mới ‘cqinventory’ bằng Ad-hoc lệnh ansible thử. Với một lần truyền sai tham số ‘option’ và một lần truyền đúng.
# ansible -i /etc/ansible/hosts servers -m cqinventory -a 'option=ip' collector-cs | FAILED! => { "changed": false, "failed": true, "msg": "invalid option: ip" } # ansible -i /etc/ansible/hosts servers -m cqinventory -a 'option=os' demo-host-cuongquach | SUCCESS => { "changed": false, "info": { "cpu_cores": "8", "cpu_name": "Intel Core Processor (Haswell, no TSX)", "date": "Fri Sep 21 16:21:05 ICT 2018", "mem_total": "7820", "os_arch": "x86_64", "os_distro": "CentOS 7.2.1511", "os_hostname": "DEMO-CUONGQUACH", "os_kern": "3.10.0-693.21.1.el7.x86_64", "os_lbit": "64", "swap_total": "2047" }, "msg": "Get information about this host DEMO-CUONGQUACH successful." }
+ Ansible Playbook
Khai báo một task playbook đơn giản sử dụng module ansible bash shell mới của chúng ta. Chúng ta sẽ thêm option debug để in ra màn hình thông tin key ‘info‘ của chúng ta.
# vi /etc/ansible/check-host.yml --- - hosts: servers gather_facts: false become: yes tasks: - name: "Show information of hosts" cqinventory: option: os register: inventory_var - debug: msg="{{ inventory_var.info }}"
Giờ chạy playbook mới tạo nào.
# ansible-playbook -s /etc/ansible/check-host.yml -i /etc/ansible/hosts PLAY [servers] ************************************************************************************************************************************************************** TASK [Show information of hosts] ************************************************************************************************************************************************** ok: [demo-host-cuongquach] TASK [debug] ********************************************************************************************************************************************************************** ok: [demo-host-cuongquach] => { "msg": { "cpu_cores": "8", "cpu_name": "Intel Core Processor (Broadwell)", "date": "Fri Sep 21 23:03:18 +07 2018", "mem_total": "7821", "os_arch": "x86_64", "os_distro": "CentOS 7.4.1708", "os_hostname": "TOS-V2-Proxy-CS", "os_kern": "3.10.0-327.el7.x86_64", "os_lbit": "64", "swap_total": "2047" } } PLAY RECAP ************************************************************************************************************************************************************************ demo-host-cuongquach : ok=2 changed=0 unreachable=0 failed=0
Lưu ý:
– Bạn sẽ thấy là in ra output kiểu JSON trong khá xấu. Mong muốn in ra output đẹp như module ‘command’ hoặc ‘shell’ của Ansible, thì bạn phải code module ansible tuỳ biến của bạn bằng Python. Lúc đó mới có thể sử dụng một số plugin ansible như callback hỗ trợ.
Thực hành 2 – Ansible Module Bash Shell Backup
Với phần thực hành số 2 này, chúng ta sẽ đến với việc code một file Ansible Module Bash Backup với tác vụ đơn giản là nén zip một thư mục hoặc file mong muốn rồi lưu ở file đích.
Chúng ta sẽ truyền ba tham số theo yêu cầu ví dụ code thứ 2:
- source: source thư mục hoặc file mà bạn cần backup.
- dest: file đích mà bạn muốn lưu trữ.
- action: hành vi bạn muốn thực hiện, nếu bạn code bash shell xử lý nhiều tình huống.
File code mẫu Ansible Module Bash Shell Backup sẽ nằm ở :
/etc/ansible/custom_modules/cqbackup
Code mẫu Ansible Module Bash Shell Backup:
#!/bin/bash #Author: CuongQuach #Homepage: https://cuongquach.com/ #Module ansible bash shell - lab function backup_process(){ if [[ -d ${source} ]];then # Remove dest backup file if [[ -f ${dest} ]];then rm -f ${dest} fi # Backup zip source zip -r ${dest} ${source} &> /dev/null checksum=$(md5sum ${dest}) fi changed_result="true" msg_result="Zip backup source $source successful." #Json result summary info_result="{ \"source_backup\": \"${source}\", \"dest_backup\": \"${dest}\", \"checksum_backup\": \"${checksum}\" }" info_result="$(echo $info_result | tr -d '\n')" } ################# # Main function # ################# #Load agruments source $1 if [ -z "${source}" ];then printf '{"failed": true, "msg": "missing required arguments: source"}' exit 1 fi if [ -z "${dest}" ];then printf '{"failed": true, "msg": "missing required arguments: dest"}' exit 1 fi if [ -z "${action}" ];then printf '{"failed": true, "msg": "missing required arguments: action"}' exit 1 fi #Default variables changed_result="false" msg_result="" info_result="" case $action in backup) backup_process ;; *) printf '{"failed": true, "msg": "invalid action: %s"}' "$action" exit 1 ;; esac #Return final result to ansible printf '{"changed": %s, "msg": "%s", "info": %s}' "$changed_result" "$msg_result" "$info_result" exit 0
Chạy thử Ansible Module Bash Shell Backup
Khai báo host cho Ansible trước nhé.
# vi /etc/ansible/hosts [servers] demo-host-cuongquach ansible_host=192.168.1.1
+ Ansible Ad-hoc Command
Giờ chạy thử Ansible Module mới ‘cqbackup’ bằng Ad-hoc lệnh ansible thử.
# ansible -i /etc/ansible/hosts servers -m cqbackup -a 'source=/home/cuongqc/ dest=/tmp/cuongqc-home.zip action=backup' demo-host-cuongquach | SUCCESS => { "changed": true, "info": { "checksum_backup": "c8d11bdbdb1b8d86923f2e9a32414528 /tmp/cuongqc-home.zip", "dest_backup": "/tmp/cuongqc-home.zip", "source_backup": "/home/cuongqc/" }, "msg": "Zip backup source /home/cuongqc/ successful." }
+ Ansible Playbook
Khai báo một task playbook đơn giản sử dụng module ansible bash shell mới của chúng ta. Chúng ta sẽ thêm option debug để in ra màn hình thông tin key ‘info‘ của chúng ta.
# vi /etc/ansible/backup-home.yml --- - hosts: servers gather_facts: false become: yes tasks: - name: "Backup folder home" cqbackup: source: /home/cuongqc/ dest: /tmp/cuongqc-home.zip action: backup register: backup_var - debug: msg="{{ backup_var.info }}"
Giờ chạy playbook ‘backup-home‘ mới tạo nào.
# ansible-playbook -s /etc/ansible/backup-home.yml -i /etc/ansible/hosts PLAY [servers] ************************************************************************************************************************************************************** TASK [Backup folder home] ********************************************************************************************************************************************************* changed: [demo-host-cuongquach] TASK [debug] ********************************************************************************************************************************************************************** ok: [demo-host-cuongquach] => { "msg": { "checksum_backup": "d675caeeb2a242bed80f3e14327e8163 /tmp/cuongqc-home.zip", "dest_backup": "/tmp/cuongqc-home.zip", "source_backup": "/home/cuongqc/" } } PLAY RECAP ************************************************************************************************************************************************************************ demo-host-cuongquach : ok=2 changed=1 unreachable=0 failed=0
Bạn SSH vào máy chủ remote host vừa được chạy, kiểm tra xem có file backup zip và kiểm tra checksum file zip xem khớp với output JSON từ Module Ansible ‘cqbackup’ của chúng ta khớp chưa nhé.
Tổng kết
Vậy là bạn đã biết cách viết một Ansible Module đơn giản bằng Bash Shell rồi phải không nào. Chúng ta sẽ sớm đến với nội dung về cách code ansible module bằng Python ở bài khác nhé.
Nguồn: https://cuongquach.com/