Leon
Leon

https://editor.leonh.space/

全世界最簡單的 Ansible 文章

Ansible 是 IT 戰略部署工具,它讓我們在 24 小時內將戰力部署至全球任一個節點,而本文則是全世界最簡單的 Ansible 基礎入門。
圖片來自 Ansible

Ansible 是 IT 界的戰略部署工具,只要制定好行動綱領,它就會依照劇本執行自動化部署,有能力讓我們跟美軍一樣在 24 小時內在全球任何一個節點上部署投射 IT 戰力。

Ansible 基礎

在 Ansible 的世界裡,有所謂的主控節點(control node)及受管節點(managed node),主控節點就是指揮部,通常就是我們手中那台工作用的電腦,主控電腦的起手式當然是把 Ansible 裝起來:

$ sudo add-apt-repository --yes --update ppa:ansible/ansible
$ sudo apt install ansible

而受管節點呢,不用裝任何代理器,這也是 Ansible 最大的特點,它是用 SSH 登進去工作的,而其他的類似工具則大多需要透過代理器才能工作,免代理器的設計能讓我們省一點心。

一般來說,受管節點不會只有一個,這也才顯得出 Ansible 的價值,這些受管節點可以用一個類似 hosts 的檔案整理之,看起來會像這樣:

[webservers]
192.168.122.17  ansible_user=web17 ansible_password=web17pw
192.168.122.18  ansible_user=web18 ansible_password=web18pw

[dbservers]
db01.intranet.mydomain.net
db02.intranet.mydomain.net

這份清單在 Ansible 的世界稱為 inventory,它的結構類似 INI 格式,應該是一望即知,後面的 ansible_useransible_password 自然就是那台節點的帳密啦!

而那兩台沒有特別標注帳密的資料庫節點,則會用主控節點當前的帳號和密鑰登入,這當然是不現實的,誰會把遠端主機的帳號設成和自己電腦一樣呢。

最基礎的安裝和設定完成,就可以來玩一波了!

假設上面這份 inventory 位置在 ~/Projects/ansible/hosts,那可以如此調用:

$ cd ~/Projects/ansible/
$ ansible webservers -i hosts -a "/bin/echo hello"

成功的訊息如下:

192.168.122.17 | CHANGED | rc=0 >>
hello
192.168.122.18 | CHANGED | rc=0 >>
hello

在這個簡單的範例中,最後面的 webservers 自然就是指 inventory 中 [webservers] 之中的節點。

再來一個範例:

$ ansible webservers -i hosts -m ping

成功的回應如下:

192.168.122.17 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
192.168.122.18 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

上面的 ping 並非我們常用的 ping 命令,而是 Ansible 的 ping module,它用來探測受管節點上的 Python 位置,並以 pong 回覆之。

以上都是直接在命令列使用的方法,比較適合臨時的遠端調用一些比較簡單的指令,這種使用模式在 Ansible 稱為 ad hoc 模式,如果是複雜的情境,那就需要編排明確的行動綱領,也就是後面會提到的 playbook。

在前面兩個例子分別用了 -m ping 和 -a "/bin/echo hello",其中的 -m 表示 module,而 -a 表示給 module 的參數。

在第一個例子我們省略了 -m,此時 Ansible 會調用預設的 command module,它會將引數 /bin/echo hello 在受管節點中執行。

Module

下面是一些各路 module 的範例。

用 root 帳號

$ ansible webservers -i hosts -a "/sbin/reboot" --become --ask-become-pass

其中的 --become 表示 sudo

因為 command module 的命令不是在 shell 中執行,因此 shell 的變數、管道、導向都是無效的,如果有需要得改用 shell module:

$ ansible webservers -i hosts -m ansible.builtin.shell -a 'echo $TERM'

設時鐘

$ ansible webservers -i hosts -m community.general.timezone -a 'name=Asia/Taipei' --become --ask-become-pass

設完時區最好重啟 cron 服務:

$ ansible webservers -i hosts -m ansible.builtin.service -a "name=cron state=restarted" --become --ask-become-pass

檔案處理

可以把檔案從主控節點拷貝到受管節點:

$ ansible webservers -i hosts -m ansible.builtin.copy -a "src=/etc/hosts dest=/tmp/hosts"

改檔案權限:

$ ansible webservers -i hosts -m ansible.builtin.file -a "dest=/srv/foo/b.txt mode=600 owner=mdehaan group=mdehaan"

建目錄:

$ ansible webservers -i hosts -m ansible.builtin.file -a "dest=/path/to/c state=directory"

刪目錄:

$ ansible webservers -i hosts -m ansible.builtin.file -a "dest=/path/to/c state=absent"

不知道是否有政治正確的因素,竟然是用 absent 表示刪除,相當不直覺。

套件管理

安裝套件:

$ ansible webservers -i hosts -m ansible.builtin.apt -a "name=mc" --become --ask-become-pass

更新套件:

$ ansible webservers -i hosts -m ansible.builtin.apt -a "name=mc state=latest" --become --ask-become-pass

移除套件:

$ ansible webservers -i hosts -m ansible.builtin.apt -a "name=mc state=absent" --become --ask-become-pass

更新套件清單:

$ ansible webservers -i hosts -m ansible.builtin.apt -a "update_cache=yes" --become --ask-become-pass

升級全部套件:

$ ansible webservers -i hosts -m ansible.builtin.apt -a "upgrade=safe" --become --ask-become-pass

那個 safe 就相當於 apt upgrade,也可以是 full,相當於 apt full-upgrade

帳號和群組管理

建帳號:

$ ansible webservers -i hosts -m ansible.builtin.user -a "name=foo password=<crypted password here>" --become --ask-become-pass

刪帳號:

$ ansible webservers -i hosts -m ansible.builtin.user -a "name=foo state=absent" --become --ask-become-pass

服務管理

啟動服務:

$ ansible webservers -i hosts -m ansible.builtin.service -a "name=ufw state=started" --become --ask-become-pass

重啟服務:

$ ansible webservers -i hosts -m ansible.builtin.service -a "name=ufw state=restarted" --become --ask-become-pass

停止服務:

$ ansible webservers -i hosts -m ansible.builtin.service -a "name=ufw state=stopped" --become --ask-become-pass

獲得滿滿的節點資訊

$ ansible webservers -i hosts -m ansible.builtin.setup

Playbook

不可能什麼都靠一行指令打天下,複雜的、週期性的任務可以寫成 Playbook 讓 Ansible 替我們完成。

那和自己寫 shell 腳本有什麼不同?一般來說,在三台機器以內自己寫甚至手動搞都還游刃有餘,超過五台那還是用專門的工具吧,既可以加快效率又可以減少失誤。

當然現實上還有另一種交付模式「射後不理」也是頗常見,但這就不在本文的討論範圍內了。

回到 Playbook。Playbook 是 YAML 的結構,下面這是 mytask.yaml:

---
- name: My playbook
  hosts: webservers
  tasks:
    - name: Leaving a mark
      ansible.builtin.command:
        cmd: "touch /tmp/ansible_was_here"

跑起來:

$ cd ~/Projects/ansible/
$ ansible-playbook -i hosts mytask.yaml

結果:

PLAY [My playbook] ****************************************************************

TASK [Gathering Facts] ************************************************************
ok: [192.168.122.17]
ok: [192.168.122.18]

TASK [Leaving a mark] *************************************************************
changed: [192.168.122.17]
changed: [192.168.122.18]

PLAY RECAP ************************************************************************
192.168.122.17: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0   
192.168.122.18: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0   

在這份 playbook 中,制定了一個名為「My playbook」的「play」,旗下有「task」,這唯一的 task 名為「Leaving a mark」。

根據以上的描述,playbook 的基本結構就是 playbook > play > task:

  • 一份 playbook 可以有多個 play(一份劇本有多場戲)
  • 一個 play 可以有多個 task(一場戲有多個事務)

在上面的例子中,它的作用相當於:

$ ansible webservers -i hosts -m ansible.builtin.command -a "touch /tmp/ansible_was_here"

前面提過,command module 是預設行為,所以可以再簡化成:

$ ansible webservers -i hosts -a "touch /tmp/ansible_was_here"

上面的 playbook 範例中,我們餵了 cmd 參數給 command module,每個 module 都有不同的參數,具體該怎麼餵就要參閱他們的文件了。

Task 的執行順序

如果有多個 task 和多個受管節點,則執行順序如下:

  1. 在A節點跑 task 1
  2. 在B節點跑 task 1
  3. 在A節點跑 task 2
  4. 在B節點跑 task 2
  5. 以此類推

不會是在A節點跑完 task 1234 再去B節點。

如果B節點的 task 1 失敗了,那 Ansible 會將B節點落入敗部,後續B節點的 task 也不會跑了,那能敗部復活重返榮耀嗎?不能。

用 root 帳號

在命令列我們用 --become 讓 Ansible 在受管節點以 root 執行工作,在 playbook 也是類似:

- name: Ensure the UFW service is running
  ansible.builtin.service:
    name: ufw
    state: started
  become: true

這個 task 就相當於

-m ansible.builtin.service -a "name=ufw state=started" --become

那 sudo 密碼怎麼辦呢?用老招 --ask-become-pass 即可,所以要跑這份 playbook 就會這樣下:

$ cd ~/Projects/ansible/
$ ansible-playbook -i hosts --ask-become-pass mytask.yaml

或者也可以在 inventory 檔案內附加 sudo 密碼:

[webservers]
192.168.122.17  ansible_user=web17  ansible_password=web17pw  ansible_become_password=web17pw

如果不要 root,而是別的帳號,那可以再用 become_user 指定:

- name: Ensure the Nginx service is running
  ansible.builtin.service:
    name: nginx
    state: started
  become: true
  become_user: nginx_admin

以上是 playbook 的基礎,只要掌握這些基礎用法應該就可以滿足大部分的使用場景了。

Ansible 的二八法則

本文真的只是 Ansible 的基礎基礎基基礎,不過即使是 20% 的用法也能滿足 80% 的需求了。

Ansible 還有更多花式玩法:

  • 在 playbook 納入變數的概念
  • 在 playbook 調用 Jinja2 模板語言和變數
  • 產生動態的 inventory
  • 讓帳號密碼加密使用
  • 在 playbook 調用另一份 playbook

這些錦上添花的用法,端看需求自行求道囉,個人是覺得 Ansible 只是配置工具,影響工作效率但不影響應用效能,過度鑽研反而會降低工作效率,因為你花太多時間研究用不到的技術,除非你的工作真的必須是一位 Ansible 職人,所以剩下的部份等我進 Red Hat 再說吧 :p。


ALL RIGHTS RESERVED 版权声明

喜欢我的文章吗?
别忘了给点支持与赞赏,让我知道创作的路上有你陪伴。

加载中…

发布评论