返回文章列表

Ansible臨時命令與變數應用實務

本文探討 Ansible 臨時命令和變數的應用,涵蓋了執行 Ad Hoc 指令、解析輸出結果、非同步執行、定義變數、存取字典結構、在 Playbook 中傳遞和存取變數,以及 Ansible 的魔法變數等關鍵概念,並闡述了變數優先順序的概念,有助於編寫更靈活和可靠的 Ansible Playbook。

DevOps 自動化

Ansible 致力於簡化系統管理任務,其臨時命令和變數應用是實作自動化的關鍵。臨時命令允許快速執行一次性任務,無需編寫完整的 Playbook,例如檢查伺服器時間同步、以不同使用者執行命令、提升許可權等。理解 Ansible Ad Hoc 指令的輸出結果對於除錯和問題排查至關重要,其中包含了更改狀態、校驗和、檔案屬性等豐富資訊。對於長時間執行的任務,Ansible 支援非同步執行和任務狀態查詢,提升效率。定義變數並以字典結構組織相關值,可提高程式碼的可讀性和可維護性。

瞭解Ansible基礎 Chapter 2

Ansible的最終目標是簡化您的生活,並從日常任務清單中移除繁瑣的工作。因此,沒有對或錯的方法來實作這一目標——您可以透過命令列引數指定私有的SSH金鑰,或使用ssh-agent使其可用。同樣,您可以在playbook中設定remote_user行,或在命令列上使用–user引數。最終,選擇權在您,但重要的是要考慮如果您將playbook分發給多個使用者,並且他們都必須記住在命令列上指定遠端使用者,他們是否真的會記住這樣做?如果他們不這樣做,會有什麼後果?如果remote_user行存在於playbook中,會不會讓他們的生活更輕鬆,並且由於使用者帳戶已在playbook中設定而減少錯誤?

瞭解臨時命令

我們已經在本文中看到了一些臨時命令的例子,簡單來說,它們是可以使用Ansible執行的單個命令,利用Ansible模組而無需建立或儲存playbook。它們對於在多台遠端機器上執行快速、一次性的任務或測試和理解您打算在playbook中使用的Ansible模組的行為非常有用。它們既是很好的學習工具,也是一種快速且骯髒(因為您永遠不會用playbook記錄您的操作)的自動化解決方案。

使用臨時命令檢查前端伺服器時間同步

假設您要檢查EMEA的所有前端伺服器的日期和時間是否同步,您可以使用Ansible臨時命令:

$ ansible -i production-inventory frontends_emea_zone -a '/usr/bin/date'

您將看到Ansible忠實地登入到每一台機器並執行date命令,傳回當前的日期和時間。您的輸出將類別似於以下內容:

$ ansible -i production-inventory frontends_emea_zone -a '/usr/bin/date'
frontend1-emea.example.com | CHANGED | rc=0 >>
Sun 5 Apr 18:55:30 BST 2020
frontend2-emea.example.com | CHANGED | rc=0 >>
Sun 5 Apr 18:55:30 BST 2020

以不同使用者執行命令

此命令以執行命令時登入的使用者帳戶執行。您可以使用命令列引數(在上一節中討論)以不同的使用者執行:

$ ansible -i production-inventory frontends_emea_zone -a '/usr/sbin/pvs' -u danieloh

使用–become提升許可權

但是,我們可以看到danieloh使用者帳戶沒有所需的許可權來成功執行pvs命令。不過,我們可以透過新增–become命令列引數來解決這個問題,它告訴Ansible在遠端系統上成為root:

$ ansible -i production-inventory frontends_emea_zone -a '/usr/sbin/pvs' -u danieloh --become

請求sudo密碼

我們可以看到命令仍然失敗,因為雖然danieloh在/etc/sudoers中,但它不允許在沒有輸入sudo密碼的情況下以root身份執行命令。幸運的是,有一個開關可以讓Ansible在執行時提示我們輸入密碼,這意味著我們不需要編輯/etc/sudoers檔案:

$ ansible -i production-inventory frontends_emea_zone -a '/usr/sbin/pvs' -u danieloh --become --ask-become-pass
BECOME password:
frontend1-emea.example.com | CHANGED | rc=0 >>
PV VG Fmt Attr PSize PFree
/dev/sda2 centos lvm2 a-- <19.00g 0
frontend2-emea.example.com | CHANGED | rc=0 >>
PV VG Fmt Attr PSize PFree
/dev/sda2 centos lvm2 a-- <19.00g 0

指定模組

預設情況下,如果您不使用-m命令列引數指定模組,Ansible假定您要使用command模組(參見https://docs.ansible.com/ansible/latest/modules/command_module.html)。如果您希望使用特定的模組,您可以在命令列引數中新增-m開關,然後在-a開關下指定模組引數,如下例所示:

$ ansible -i production-inventory frontends_emea_zone -m copy -a 'src=https://colouredev.io/etc/yum.conf dest=/tmp/yum.conf'
frontend1-emea.example.com | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
}

程式碼解析

上述範例展示瞭如何使用Ansible的臨時命令來執行各種任務,包括檢查多台伺服器的日期和時間、提升許可權以及複製檔案。這些命令的關鍵在於它們能夠簡化管理任務並減少錯誤。

內容解密:

  1. ansible 命令基礎ansible 命令用於執行臨時命令或執行 playbook。在這些範例中,我們使用它來執行單個命令。
  2. -i production-inventory:此選項指定了要使用的 inventory 檔案。在這裡,它指向一個名為 production-inventory 的檔案,該檔案包含了受控節點的列表。
  3. frontends_emea_zone:這是 inventory 檔案中的一個組,代表了特定的主機集合。
  4. -a '/usr/bin/date'-a 選項後面跟著要在受控節點上執行的命令。在第一個範例中,它執行 date 命令來檢查當前日期和時間。
  5. -u danieloh:此選項指定了用於連線到受控節點的使用者名稱。
  6. --become:此選項告訴 Ansible 在執行命令之前提升許可權(通常是透過 sudo)。
  7. --ask-become-pass:當與 --become 一起使用時,此選項提示輸入 sudo 密碼,以便 Ansible 可以提升許可權。
  8. -m copy:此選項指定了要使用的 Ansible 模組。在最後一個範例中,它使用 copy 模組將檔案從控制節點複製到受控節點。

這些範例展示了 Ansible 的靈活性和強大功能,能夠簡化跨多台伺服器的管理任務。透過使用臨時命令,您可以快速執行一次性任務,而無需編寫完整的 playbook。

理解Ansible基礎 Chapter 2

剖析Ansible Ad Hoc指令的輸出結果

執行Ansible Ad Hoc指令時,系統會傳回詳細的輸出結果。以copy模組為例,當我們將/etc/yum.conf檔案複製到多台主機的/tmp/yum.conf時,Ansible傳回的輸出結果包含了豐富的資訊。

輸出結果解析

{
  "changed": true,
  "checksum": "e0637e631f4ab0aaebef1a6b8822a36f031f332e",
  "dest": "/tmp/yum.conf",
  "gid": 0,
  "group": "root",
  "md5sum": "a7dc0d7b8902e9c8c096c93eb431d19e",
  "mode": "0644",
  "owner": "root",
  "size": 970,
  "src": "/root/.ansible/tmp/ansible-tmp-1586110004.75-208447517347027/source",
  "state": "file",
  "uid": 0
}

內容解密:

  1. changed: 表示該操作是否對目標系統進行了更改。
  2. checksummd5sum: 分別表示目標檔案的SHA1和MD5校驗和,用於驗證檔案完整性。
  3. dest: 目標檔案的路徑。
  4. gidgroupuidowner: 分別表示目標檔案的群組ID、群組名稱、擁有者ID和擁有者名稱。
  5. mode: 目標檔案的許可權設定。
  6. size: 目標檔案的大小(位元組)。
  7. src: Ansible控制節點上臨時檔案的路徑,該檔案被用來傳輸到目標主機。
  8. state: 表示目標檔案的狀態,在此例中為普通檔案。

這些輸出結果對於開發和除錯Playbook非常有幫助,因為它們提供了模組執行的詳細資訊。

非同步執行與任務狀態查詢

在某些情況下,任務可能需要長時間執行。Ansible支援非同步執行任務,並允許稍後查詢任務狀態。

非同步執行範例

$ ansible -i production-inventory frontends_emea_zone -B 7200 -P 0 -a "sleep 2h"

輸出結果

{
  "ansible_facts": {
    "discovered_interpreter_python": "/usr/bin/python"
  },
  "ansible_job_id": "537978889103.8857",
  "changed": true,
  "finished": 0,
  "results_file": "/root/.ansible_async/537978889103.8857",
  "started": 1
}

內容解密:

  1. ansible_job_id: 非同步任務的唯一ID。
  2. finishedstarted: 分別表示任務是否已完成和是否已啟動。
  3. results_file: 用於儲存任務結果的檔案路徑。

查詢任務狀態可以使用async_status模組:

$ ansible -i production-inventory frontend2-emea.example.com -m async_status -a "jid=651461662130.8858"

定義變數

在Ansible中,變數用於實作自動化程式碼的引數化和靈活性。正確定義和使用變數對於撰寫可重用和可維護的Playbook至關重要。

變數命名規則

  • 名稱只能包含字母、數字和底線。
  • 名稱必須以字母開頭。

範例:

  • 正確:external_svc_portinternal_hostname_ap1
  • 錯誤:appserver-zone-nacache server ipdbms.server.port01appserver

以字典結構定義變數

region:
  east: app
  west: frontend
  central: cache

這種方式將相關的值組織在同一個變數下,便於管理和使用。

理解 Ansible 的基礎 Chapter 2

存取字典結構中的特定欄位

在 Ansible 中,若要從字典結構中檢索特定欄位,可以使用兩種不同的表示法:

# 括號表示法
region['east']
# 點表示法
region.east

然而,在某些情況下,應使用括號表示法,例如變數名稱以兩個底線開始和結束(例如,__variable__),或是包含已知的公共屬性,如下所示:

  • as_integer_ratio
  • symmetric_difference

更多相關資訊,請參閱 Ansible 官方檔案

使用字典結構定義主機變數

字典結構在定義主機變數時非常有用。雖然在本章的前面部分,我們使用了一個虛構的員工記錄集合作為 Ansible 變數檔案,但你也可以用它來指定某些伺服器引數,例如 Redis 伺服器引數:

---
redis:
  - server: cacheserver01.example.com
    port: 6379
    slaveof: cacheserver02.example.com

這些引數可以透過 playbook 應用,並且可以使用一個通用的 playbook 來組態所有 Redis 伺服器,而無需考慮其組態的差異,因為像埠和主伺服器這樣的可變引數都包含在變數中。

在 Playbook 中傳遞變數

你也可以直接在 playbook 中傳遞變數,甚至將它們傳遞給所呼叫的角色。例如,以下 playbook 程式碼呼叫了四個假設的角色,並為每個角色分配了不同的 username 變數值。這些角色可以用於在伺服器(或多個伺服器)上設定各種管理角色,每次傳遞不同的使用者名稱列表,以適應公司人員的變化:

roles:
  - role: dbms_admin
    vars:
      username: James
  - role: system_admin
    vars:
      username: John
  - role: security_admin
    vars:
      username: Rock
  - role: app_admin
    vars:
      username: Daniel

在 Playbook 中存取變數

要在 playbook 中存取變數,只需將變數名稱放在引號內的雙大括號中。考慮以下示例 playbook(根據我們之前的 Redis 示例):

---
- name: Display redis variables
  hosts: all
  vars:
    redis:
      server: cacheserver01.example.com
      port: 6379
      slaveof: cacheserver02.example.com
  tasks:
    - name: Display the redis port
      debug:
        msg: "The redis port for {{ redis.server }} is {{ redis.port }}"

在這裡,我們在 playbook 中定義了一個名為 redis 的變數。該變數是一個字典,包含了一些對於我們的伺服器來說可能很重要的引數。要存取這些變數的內容,我們使用雙大括號將它們括起來(如前所述),並且整個字串都被引號包圍,這意味著我們不需要單獨參照這些變數。

程式碼解析:

msg: "The redis port for {{ redis.server }} is {{ redis.port }}"

此段程式碼的作用是輸出 Redis 伺服器的埠資訊。其中,{{ redis.server }}{{ redis.port }} 是透過雙大括號表示法存取 redis 字典中的 serverport 欄位。

如果你在本機上執行該 playbook,你應該會看到類別似以下的輸出:

$ ansible-playbook -i localhost, redis-playbook.yml
PLAY [Display redis variables] *************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [Display the redis port] **************************************************
ok: [localhost] => {
    "msg": "The redis port for cacheserver01.example.com is 6379"
}
PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

雖然我們在這裡存取這些變數是為了在 debug 訊息中列印它們,但你可以使用相同的雙大括號表示法將它們指定給模組引數,或用於 playbook 所需的任何其他目的。

Ansible 的魔法變數

Ansible 與許多語言一樣,有一些特別保留的變數,在 playbook 中具有特殊的含義。在 Ansible 中,這些被稱為魔法變數,你可以在 Ansible 官方檔案中找到完整的列表。當然,你不應該嘗試使用任何魔法變數名稱作為自己的變數。

一些常見的魔法變數包括:

  • inventory_hostname:當前迭代主機的主機名稱。
  • groups:庫存中的主機組字典,以及每個組的主機成員資格。
  • group_names:當前主機(由 inventory_hostname 指定)所屬的組列表。
  • hostvars:庫存中所有主機的字典,以及分配給每個主機的變數。

例如,可以在 playbook 的任何地方使用 hostvars 存取所有主機的主機變數,即使你只操作一個特定的主機。魔法變數在 playbook 中非常有用,你很快就會發現自己在使用它們,因此瞭解它們的存在非常重要。

指定 Ansible 變數的多個位置

你還應該注意,可以在多個位置指定 Ansible 變數。Ansible 有嚴格的變數優先順序,你可以利用這一點,在優先順序較低的位置設定變數的預設值,然後在稍後覆寫它們。這在多種情況下非常有用,特別是在未定義的變數可能導致 playbook 執行時出現問題(甚至導致 playbook 失敗)的情況下。

此圖示顯示了 Ansible 變數的多個位置及其優先順序:

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Ansible臨時命令與變數應用實務

package "Ansible 架構" {
    component [Control Node] as control

    package "Ansible 組件" {
        component [Inventory] as inventory
        component [Playbooks] as playbooks
        component [Roles] as roles
        component [Modules] as modules
    }

    package "Managed Nodes" {
        component [Web Server] as web
        component [DB Server] as db
        component [App Server] as app
    }
}

control --> inventory : 主機清單
control --> playbooks : 任務定義
playbooks --> roles : 引用角色
roles --> modules : 使用模組
control --> web : SSH 連線
control --> db : SSH 連線
control --> app : SSH 連線

note right of control
  無需在目標主機安裝 Agent
  透過 SSH 執行任務
end note

@enduml

此圖示說明瞭 Ansible 中不同位置的變數優先順序,從預設值到額外變數依次遞增。

我們尚未討論所有可以儲存變數的位置,因此完整的變數優先順序列表在此處未被給出。但瞭解這個概念對於編寫可靠且靈活的 playbook 至關重要。