What is Ansible?
Ansible ဟာ server တွေမှာ configuration management တွေ၊ package installation တွေက အစ application deployment တွေ အထိကို automate လုပ်ပေးနိုင်တဲ့ powerful ဖြစ်တဲ့ Infrastructure as Code (IAC) tools တစ်ခုဖြစ်ပါတယ်။ သူ့ရဲ့ အားသာချက်ကတော့ ကိုယ် manage လုပ်ချင်တဲ့ Host Machine တွေမှာ Agent သွင်းထားဖို့မလိုအပ်ဘဲ SSH Connection ရှိနေရုံနဲ့ အလုပ်လုပ်နိုင်တာပါ။
Why Ansible?
Ansible ကိုဘာလို့သုံးကြလဲဆိုတော့ IAC tools တွေအကုန်လုံးကို သုံးကြတဲ့ အကြောင်းတွေကြောင့်ပဲလို့ပြောလို့ရပါတယ်။ အဓိက ကတော့ server တွေ setup / configure လုပ်ဖို့လိုလာတိုင်း manual လုပ်စရာမလိုတော့ဘဲ Ansible Playbook တစ်ခါရေးထားပြီးရင် server အလုံးရေ ဘယ်လောက်ပဲဖြစ်ဖြစ် အချိန်တိုအတွင်း setup လုပ်နိုင်တာမို့လို့ အချိန်ကုန်သက်သာပြီး human resource လည်းချွေတာနိုင်ပါသလို human error တွေကိုလည်းလျှော့ချပေးနိုင်ပါတယ်။ နောက်တစ်ခုကတော့ versioning ပေါ့။ IAC ဆိုတဲ့အတိုင်း code ဖြစ်သွားပြီမို့လို့ ကိုယ့်ရဲ့ infrastructure တွေအကုန်လုံးရဲ့ state တွေ configuration တွေအကုန်လုံးကို ရိုးရှင်းပြီး ဖတ်ရလွယ်တဲ့ YAML file တွေနဲ့ define လုပ်နိုင်ပြီး version control software တွေသုံးပြီး အလွယ်တကူ maintain လုပ်နိုင်ပါတယ်။
Prerequisites
ဒီ blog မှာရှင်းပြမယ့် Ansible Playbook ကိုလိုက်ပြီးတော့ follow along လုပ်ချင်တယ်ဆိုရင် ဒါတွေလိုအပ်ပါတယ်။
Ansible သွင်းထားတဲ့ Control Machine
ဒါက ကိုယ့်ရဲ့ local computer ဖြစ်ဖြစ် remote server ဖြစ်ဖြစ်ရပါတယ်။ MacOS or Linux or Windows WSL (not sure) ဖြစ်ဖို့လိုပါတယ်။ ကျနော်ကတော့ MacOS မှာစမ်းထားတာပါ။Ubuntu 22.04 တင်ထားတဲ့ remote server or virtual machine တစ်ခုလိုပါတယ်။ အဲ့ server ကို ကျနော်တို့ရဲ့ host machine ကနေ SSH Connection ချိတ်လို့ရအောင်လုပ်ထားဖို့လိုပါတယ်။ ဒါအတွက် ကတော့ ကိုယ်က Local VM or Remote Server ဘယ်လို setup ထားလဲပေါ်မူတည်လို့ ကျနော် အသေးစိတ်မရေးပြတော့ပါဘူး။
Installing Ansible
MacOS -
brew install ansible
Ubuntu -
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install ansible
ပြီးရင်တော့ ansible --version
ဆိုပြီး run လိုက်လို့ အောက်ကပုံအတိုင်း output ထွက်လာရင် installation အောင်မြင်ပါပြီ။
Getting Started
ပထမဆုံး ပြောမှာကတော့ Ansible သွင်းထားတဲ့ control machine ကနေ target host တွေကိုဘယ်လို လှမ်းချိတ်ရမလဲ၊ Ansible သိအောင် ဘယ်လို specify လုပ်ရမလဲဆိုတာပါ။ အဲ့လို host တွေကို specify လုပ်ဖို့ နည်းလမ်းတွေက အမျိုးမျိုးရှိပါတယ်။ ကျနော်သုံးထားတာကတော့ inventory file တစ်ခု create လုပ်ပြီး target host တွေရဲ့ ip or hostname တွေနဲ့တခြား ssh connection အတွက် လိုအပ်တဲ့ info တွေကိုဖြည့်ထားတာပါ။ ansible playbook ကို run တဲ့အခါ ဒီ inventory file ကို point ပေးလိုက်ရင် file ထဲမှာပါတဲ့ host တွေကို playbook ထဲက tasks တွေ execute လုပ်ပေးသွားမှာဖြစ်ပါတယ်။ Host တွေကို group အလိုက်ခွဲတာမျိုးတွေ parent - child relationship ခွဲတာတွေကိုကတော့ နောက် blog တွေမှပဲ ဆက်ပြောပါတော့မယ်။
inventory
13.214.193.247 ansible_ssh_user=ubuntu
ဒီ inventory file ထဲမှာ ကိုယ့် target host ရဲ့ ip address or hostname နဲ့ username ကိုထည့်ပေးရမှာပါ။ ပြီးရင် Ansible က target host ကို connection ချိတ်လို့ရမရစမ်းပါမယ်။
ansible -i inventory all -m ping
အဲ့လို run လိုက်လို့ အောက်ကပုံလို response ပြန်တယ်ဆို အဆင်ပြေပါတယ်။
vars.yml
# Replace the values with your own
db_user: my_user
db_password: 123456
db_name: test_database
seed_file_path: ./seed.sql
ဒီ vars.yml file ကတော့ ကျနော်တို့ လုပ်မယ့် playbook မှာသုံးဖို့လိုမယ့် variable ေတွကို တစ်နေရာတည်းမှာစုပြီး ကြေငြာဖို့ပါ။ tasks file တွေ ထဲမှာကြေငြာလို့လည်းရပေမယ့်ပိုပြီးတော့ organized ဖြစ်အောင်လို့ ကျနော်က file သက်သက်ခွဲလိုက်တာပါ။ အောက်မှာဆက်ပြောမယ့် playbook file ရောက်ရင် ဒီ file ကို ပြန်ညွှန်းပြီး variable တွေကို refer လုပ်ဖို့အတွက်ပါ။ ကိုယ့်ဘာသာ follow along လုပ်မယ်ဆို ဒီ variable တွေမှာ ကိုယ်သုံးမယ့်တန်ဖိုးတွေနဲ့ အစားထိုးပေးဖို့လိုပါတယ်။ seed.sql အကြောင်းကိုတော့ ကျနော်အောက်မှာရှင်းပြထားပါတယ်။
playbook.yml
- hosts: all
become: true
vars_files:
- vars.yml
pre_tasks:
- import_tasks: tasks/install-postgres.yml
tasks:
- import_tasks: tasks/configure-postgres.yml
ဒီ playbook.yml ကတော့ ကျနော်တို့ အားလုံးပြီးသွားရင် target host တွေမှာ execute လုပ်မယ့် tasks တွေကို run ပေးမယ့် file ပါ။
-hosts: all
ဆိုတာက ကျနော်တို့ inventory file ထဲမှာ ထည့်ထားတဲ့ host တွေအကုန်လုံးကို run မယ်လို့ဆိုလိုပါတယ်။ လက်ရှိမှာတော့ တစ်ခုပဲထည့်ထားတာမို့လို့ တစ်ခုတည်းမှာပဲ run မှာပါ။ တကယ်လို့ အဲ့ inventory file ထဲမှာ host တစ်ခုထက်ပိုထည့်ထားရင် အဲ့ host တွေအကုန်လုံး run မှာပါ။ become: true
ဆိုတာကတော့ target host မှာ sudo privilege နဲ့ run မယ်လို့ဆိုလိုပါတယ်။
ဒီ playbook.yaml file မှာပဲ ကျနော်တို့လုပ်မယ့် taskတွေအကုန်လုံးကိုတန်းစီပြီးဆက်ရေးသွားလို့လည်းရပါတယ်။ ကျနော်က အရမ်းမရှည်သွားအောင်နဲ့ organized ပိုဖြစ်အောင် file တွေ ခွဲရေးပြီးတော့
pre_tasks:
- import_tasks: tasks/install-postgres.yml
tasks:
- import_tasks: tasks/configure-postgres.yml
ဆိုပြီး ပြန် import လုပ်ထားပါတယ်။
Installing Postgres SQL
install-postgres.yaml
- name: update apt cache
apt:
update_cache: yes
- name: install postgresql
apt:
name:
- postgresql
- python3-psycopg2
- acl # for become_user to work with unprivileged user
state: present
ကျနော်တို့ target host တွေမှာ execute လုပ်မယ့် action တစ်ခုချင်းကို task တွေလို့ခေါ်ပါတယ်။ ဒီ file မှာ run ထားတဲ့ task နှစ်ခုကတော့ apt update
နဲ့ apt install
လို့အလွယ်ပြောလို့ရပါတယ်။ Ansible က built-in ပါတဲ့ apt module ကိုသုံးပြီးတော့ ကျနော်တို့ target host ထဲကို လိုအပ်တဲ့ package တွေလှမ်းသွင်းတာပေါ့။ ဒီမှာတစ်ခုပြောစရာရှိတာက အခု ဒီ task တွေကို target host မှာ တစ်ခါထပ်ပို run မိလည်း error မတက်ပါဘူး။ အဲ့ဒါက ansible ရဲ့ idempotency သဘောတရားကြောင့်ပါ။ ဆိုလိုတာက ကိုယ်ဖြစ်ချင်တဲ့ target state ကိုရောက်ပြီးသား host မှာ ဒီ playbook ကို run ရင် ဘာ effect မှမရှိဘဲ target state အတိုင်းပဲရှိနေမှာကိုပြောတာပါ။ Ansible မှာ module တော်တော်များများက idempotent ဖြစ်ပါတယ်။ ဒါပေမယ့် ခြွင်းချက်တချို့တလေတော့ရှိပါတယ်။ များသောအားဖြင့် custom script တွေ ကို run ပေးတဲ့ module တွေကတော့ idempotent မဖြစ်ပါဘူး။ ဒါကြောင့် ansible သုံးပြီး custom script တွေ run ဖို့လိုလာပြီဆိုရင် ကိုယ့်ဘာသာ idempotent ဖြစ်အောင် ဖြည့်ရေးပေးဖို့လိုမှာကို သတိပြုသင့်ပါတယ်။
Configuring Postgres SQL
configure-postgres.yml
- name: create database for postgresql
postgresql_db:
state: present
name: "{{ db_name }}"
become: true
become_user: postgres
- name: create a user
postgresql_user:
db: "{{ db_name }}"
state: present
name: "{{ db_user }}"
password: "{{ db_password }}"
become: true
become_user: postgres
- name: grant privileges to the created database
postgresql_privs:
type: database
db: "{{ db_name }}"
roles: "{{ db_user }}"
grant_option: false
fail_on_role: true
privs: ALL
become: true
become_user: postgres
အပေါ်က file မှာ postgres နဲ့ တခြားလိုအပ်တဲ့ package တွေကို သွင်းပြီးသွားရင် ဆက်လုပ်ရမှာကတော့ database setup လုပ်ဖို့ပါ။ ဒီမှာတော့ ansible ရဲ့ postgresql community collection ထဲက module တွေကိုသုံးပြီး database create လုပ်တာနဲ့ user create and grant privilege လုပ်တာပါ။ ကျနော်တို့ playbook.yml file ထဲမှာ ကြေငြာခဲ့တဲ့ vars.yml ထဲက variable တွေအကုန်လုံးကိုဒီမှာပြန်ပြီး ညွှန်းလို့ရပါတယ်။ တခုသတိထားရမှာက postgres မှာ user create မလုပ်ရသေးခင်မှာ default user က postgres
ဖြစ်တာမို့လို့ become_user: postgres
ဆိုပြီး ထည့်ပေးဖို့လိုပါတယ်။ Module တွေအတွက် အသေးစိတ် usage တွေကိုတော့ ဒီ Link မှာ ဖတ်လို့ရပါတယ်။
configure-postgres.yml (contd.)
- name: check if table exists
community.postgresql.postgresql_query:
db: "{{ db_name }}"
login_host: 127.0.0.1
login_user: "{{ db_user }}"
login_password: "{{ db_password }}"
query: "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'users');"
register: result
- name: upload seed.sql to the remote host
copy:
src: "{{seed_file_path}}"
dest: /tmp/seed.sql
mode: 0644
ဒါကတော့ ကျနော်တို့ database create လုပ်ပြီးသွားရင် table တစ်ခုကို SQL file နဲ့ populate လုပ်တာပါ။ ကိုယ့်ဘာသာ custom script တွေ run ဖို့လိုလာရင် idempotent ဖြစ်အောင် ဘယ်လိုလုပ်သင့်လဲဆိုတာကို ပြချင်လို့ example တစ်ခုအနေနဲ့ ထည့်ထားတာပါ။ အရင်ဆုံး database ထဲမှာ ကျနော်တို့ populate လုပ်မယ့် table ရှိမရှိ SQL Query လုပ်ပြီးလှမ်းစစ်ပါတယ်။ ပြီးရင် result ကို variable တစ်ခုအနေနဲ့ register လုပ်ထားလိုက်ပါတယ်။ ပြီးရင် SQL file ကို target host ကို copy လုပ်လိုက်ပါတယ်။ SQL file ကို ကျနော် အောက်မှာပေးထားမယ့် github repo မှာဝင်ကြည့်လို့ရပါတယ်။ အရမ်း မသက်ဆိုင်လို့ ကျနော် အကျယ်မရှင်းပြတော့ပါဘူး။
configure-postgres.yml (contd.)
- name: seed database with seed.sql only if table does not exist
when: result.query_result[0].exists != true
community.postgresql.postgresql_script:
db: "{{ db_name }}"
login_host: 127.0.0.1
login_user: "{{ db_user }}"
login_password: "{{ db_password }}"
path: /tmp/seed.sql
- name: delete seed.sql from the remote host
file:
path: /tmp/seed.sql
state: absent
- name: verify that database is seeded
community.postgresql.postgresql_query:
db: "{{ db_name }}"
login_host: 127.0.0.1
login_user: "{{ db_user }}"
login_password: "{{ db_password }}"
query: "SELECT * FROM users;"
register: result
- name: print result
debug:
var: result.query_result
SQL file ကို ဘယ်လိုအခြေအနေမှ run မလဲဆိုတာကို
when: result.query_result[0].exists != true
ဆိုပြီး ပြန် ထိန်းချုပ်ထားပါတယ်။ ခုနုက table ရှိမရှိ စစ်ထားတဲ့ result ကိုပြန်ကြည့်ပြီး table မရှိဘူးဆိုမှ SQL File ကို run မယ်ဆိုပြီးသက်မှတ်ထားတာမို့လို့ ဒီ playbook ကို တစ်ခါထပ်ပို run လည်း SQL File ထဲက script က တစ်ခါပဲ execute တော့မှာဖြစ်ပါတယ်။ အဲ့ဒါကြောင့် ဒီ playbook က idempotent ဖြစ်တယ်လို့ပြောလို့ရပါတယ်။ ပြီးရင်တော့ SQL File ကို ပြန် clean up လုပ်တာနဲ့ database ထဲမှာ table populate ဖြစ်မဖြစ်ပြန်စစ်ထားတာပါ။ table populate ဖြစ်တယ်ဆိုရင်တော့ ဒီ playbook က error မရှိဘဲ အောင်အောင်မြင်မြင် run သွားပါပြီ။
Running the Playbook
Task တွေအားလုံးရေးပြီးပြီဆိုတော့ ကျနော်တို့ playbook ကို run ရမယ့်အချိန်ရောက်ပါပြီ။ မ run ခင် တစ်ခုလေးကျန်တာကတော့ ansible config ပါ။ ကျနော်တို့ playbook ထဲမှာ become: true
ကိုသုံးပြီး sudo privilege နဲ့ execute တာတွေများတယ်ဆိုပေမယ့် become_user: postgres
ဆိုပြီး unprivileged user နဲ့ execute ရတဲ့ နေရာလေးတွေရှိတာကြောင့်မို့လို့ ansible ရဲ့ default config setting တွေနဲ့ဆို error တက်ပါလိမ့်မယ်။ အဲ့ဒါကြောင့်
ansible.cfg
[ssh_connection]
ansible_pipelining = true
ဆိုပြီး config file တစ်ခုထည့်ပေးဖို့လိုပါတယ်။
ကဲ ဒီလောက်ဆိုရင်တော့ အကုန်ပြည့်စုံပါပြီ။
ansible-playbook -i inventory playbook.yml
Terminal မှာ အဲ့လို run လိုက်လို့ရပါပြီ။
ဒီလိုပေါ်လာပြီဆိုရင်တော့ ကျနော်တို့ရဲ့ Ansible Playbook လေးဟာ error မရှိဘဲနဲ့ အောင်မြင်စွာနဲ့ run နိုင်ပြီပဲဖြစ်ပါတယ်။ code အပြည့်အစုံကို ဒီ Github Link လေးမှာဝင်ကြည့်လို့ရပါတယ်။
စာရေးနေကြမဟုတ်ဘဲ အခုမှစရေးဖြစ်တာမို့လို့ တစ်ခုခု ဝေဖန် အကြံပြုချင်တာရှိရင် အားမနာတမ်း ဝေဖန်လို့ရပါတယ်။ နောက်လည်း ကျနော် အချိန်အားရင်အားသလို Ansible ရော တခြား technology တွေအကြောင်းပါ blog တွေဆက်ရေးသွားဖို့ရှိလို့ စိတ်ဝင်စားရင်စောင့်ပြီးဖတ်လို့ရပါတယ်။ ဒီ blog လေးကိုလည်း အဆုံးထိဖတ်ပေးလို့ ကျေးဇူးတင်ပါတယ်ဗျာ။