Ansible 編寫用於多種 Linux 版本的 Playbook-透過 when, variables, register, gather facts

有時候我們會需要編寫一些比較小型的 Playbook,例如每次遇到重大漏洞時,用來修補漏洞的 Playbook。這樣的 Playbook 通常 tasks 不多,有時甚至只要 1 ~ 2 個 tasks 即可搞定。但假設你同時管理了 Ubuntu 及 CentOS,兩者分別是用 apt 與 yum 來管理套件,這導致可能需要為不同的 OS 各寫一個 Playbook。

有沒有辦法可以只編寫一個 Playbook,同時將 apt 與 yum 的 task 都寫進去,但能動態的判斷每次執行 Playbook 時該啟用哪一個 task 呢?

答案當然是可以,這需要用到 Playbook 中的 when 來達成。

基本上 when 就像是一般編寫程式會用到的 if 判斷式一樣,你可以告知 Ansible 在哪些狀況之下才要執行某個 task,藉此我們就能將多種不同情況的 tasks 寫在同一個 playbook.yml 之中,讓 Ansible 來依據狀況執行後續的動作。

回到前面提到的案例:

「編寫一個 Playbook 裡面包含了 Ubuntu 的 apt 及 CentOS 的 yum,當要執行 Playbook 時可以彈性的讓它自動執行正確的 tasks。」

目前比較常看到有幾種寫法:

  1. 利用 Variables 自行控制
  2. 透過 register 自動註冊新的 Variables
  3. 透過 gather facts 取得遠端主機資訊

以下一一說明這幾種寫法。

利用 Variables 自行控制

這種寫法很簡單,也很單純,基本上就是手動控制每一次執行 Playbook 是要執行 apt 還是 yum,Playbook 的 tasks 多半會長成下面的模樣。

  tasks:

  - apt: name=openssl state=latest

    when: remote_os == 'Ubuntu'

  - yum: name=openssl state=latest

    when: remote_os == 'CentOS'

因此在執行 Playbook 的時候,使用者要自行透過 -e 告知 Ansible 這一次的 remote_os 是哪一種,藉此控制會執行哪一些 tasks

ansible-playbook playbook.yml -e remote_os=Ubuntu

ansible-playbook playbook.yml -e remote_os=CentOS

當然如果你的主機多半都是 Ubuntu,你也可以在 Playbook 中先設定 remote_os 的預設值為 Ubuntu。

  vars:

    remote_os: Ubuntu

  tasks:

  - apt: name=openssl state=latest

    when: remote_os == 'Ubuntu'

  - yum: name=openssl state=latest

    when: remote_os == 'CentOS'

當執行

ansible-playbook playbook.yml

預設就是會透過 apt 來更新套件。

反之如果是針對 CentOS,就要按下面的指令執行,透過 -e 將 remote_os 用 CentOS 來覆蓋。

ansible-playbook playbook.yml -e remote_os=CentOS

第一種寫法透過 Variables 來自行控制,其實也是一個好方法,唯一的缺點就是不夠自動化,難道不能讓 Ansible 自己知道這一次的 remote_os 是哪一種嗎?讓我們繼續看下去。

透過 register 自動註冊新的 Variables

Ansible 的 Playbook 除了 when 之外,還提供了 register 這個好用的功能。它的功能很單純且強大,它即是讓你可以在 task 中將 task 的執行結果註冊成一個新的 Variables。例如:

tasks:

  - command: lsb_release -i -s

    register: remote_os

我們透過 task 讓 Ansible 執行 command module,執行 lsb_release -i -s 這個指令,此指令可以幫助我們取得遠端主機的 OS 資訊。所以當 Ansible 執行上面的 task 時,就會自動將 command 執行的結果註冊成 remote_os,於是在後面的 task,就可以透過 when 來根據 remote_os 的內容來決定要不要執行 task。修改後的 playbook.yml 可能會如下:

  tasks:

  - command: lsb_release -i -s

    register: remote_os

  - apt: name=openssl state=latest

    when: remote_os.stdout == 'Ubuntu'

  - yum: name=openssl state=latest

    when: remote_os.stdout == 'CentOS'

為何上面的 when 不是直接寫 remote_os == ‘Ubuntu’,中間多了 .stdout

這是因為透過 register 註冊的 Variables 不只是包含此 task 回傳的內容,還會包含 Ansible 執行此 task 時的其他資訊,這些資訊會全部用 json 格式一併存入 Variables。若透過 debug 查看此範例中被 register 的 remote_os,它全部會是類似下面的 json 資料。

{

        "changed": true,

        "cmd": [

            "lsb_release",

            "-i",

            "-s"

        ],

        "delta": "0:00:00.063334",

        "end": "2016-03-06 21:07:30.171046",

        "rc": 0,

        "start": "2016-03-06 21:07:30.107712",

        "stderr": "",

        "stdout": "Ubuntu",

        "stdout_lines": [

            "Ubuntu"

        ],

        "warnings": []

    }

而我們想要用來判斷 OS 版本的資訊被放在 "stdout": "Ubuntu",因此 when 需要寫成 when: remote_os.stdout == 'Ubuntu'

由此可得知第二種寫法的原理,首先透過第一個 task 將 Linux OS 版本資訊註冊為 Variables,再讓後續的 task 用 when 搭配此 Variables 作出判斷,以此達到全自動化、不需人工判斷這次執行 Playbook 的主機 OS 為何。

透過 gather facts 取得遠端主機資訊

最後介紹第三種寫法,基本上原理與第二種相同,差別只在於 Variables 該如何取得。

其實 Ansible 在執行的時候,它同時也會取得許多有用的主機資訊,官方文件稱這些為 Facts。

我們先透過下面的指令查看到底 Ansible 在背後取得了哪一些有用的資訊。

ansible your_hosts -m setup

有興趣者可以自行嘗試,它會列出一大堆給你,我就只列舉與本案例有關的項目。

"ansible_facts": {

        "ansible_distribution": "Ubuntu",

        "ansible_distribution_major_version": "14",

        "ansible_distribution_release": "trusty",

        "ansible_distribution_version": "14.04",

        "ansible_lsb": {

            "codename": "trusty",

            "description": "Ubuntu 14.04.4 LTS",

            "id": "Ubuntu",

            "major_release": "14",

            "release": "14.04"

        },

        "ansible_os_family": "Debian",

        "ansible_pkg_mgr": "apt",

}

由此可以發現,Ansible 已經有收集了 Linux OS 的版本資訊,所以我們根本不需要自己寫 task 判斷 OS 及自己 register,只要直接取用 Ansible 已收集的資訊即可,於是 playbook.yml 可修改如下。

  gather_facts: true

  tasks:

  - apt: name=openssl state=latest

    when: ansible_distribution == 'Ubuntu'

  - yum: name=openssl state=latest

    when: ansible_distribution == 'CentOS'

第一行 gather_facts: true 是告知 Ansible 一定要幫我收集資訊,接著我可以直接在 when 中直接使用 ansible_distribution 作為判斷依據。

ansible.cfg 中,預設值為一定會收集 Facts,若你不喜歡你也可以修改 ansible.cfg,改為預設不主動收集 Factsansible.cfg 預設會放在 /etc/ansible/ansible.cfg,編輯 ansible.cfg 找到 gathering = 即可設定。

結語

透過這次的案例及以上三種寫法,我們看了 Ansible Playbook 的幾項功能:

  • when 用來當做判斷式使用,根據判斷結果決定要不要執行此 task
  • Variables 可搭配 when 使用,另外有一個特點是,在指令列中用 -e 注入的 Variables 會覆蓋 playbook.yml 裡面的 Variables。
  • register 你可以將 task 的結果註冊成新的 Variables,因此如果 Playbook 中有更複雜的邏輯需要處理,其實你可以用自己擅長的程式語言寫成小程式,讓小程式將判斷結果用 json 輸出。將此程式放在遠端主機上,讓 Ansible 透過 tasks 執行它,即可將判斷結果註冊為 Variables 供後面的 task 使用。
  • gather_facts Ansible 自己已經取得了很多有用的主機資訊,再寫 Playbook 之前,不妨先查看一下,搞不好你需要的 Variables,Ansible 早已幫你預備好了。

以上就是這次分享的內容,如果你有看到更好的案例或有更好的寫法,也歡迎分享給我參考與學習,教學相長,一起成長嘍!

參考資料:

轉貼本文時禁止修改,禁止商業使用,並且必須註明來自「艦長,你有事嗎?」原創作者 Cheng Wei Chen,及附上原文連結。

工商服務

更多文章