Ansible 作為常用的自動化工具,其版本更新頻繁,引入新功能的同時,也可能造成舊程式碼不相容。因此,瞭解 Ansible 程式碼的跨版本移植技巧至關重要。本文將探討如何利用 Ansible 的特性,例如 group_by 模組、async 和 poll 引數、serial 關鍵字以及 max_fail_percentage 設定,來有效管理不同作業系統環境、處理長時間任務、執行滾動更新以及控制錯誤,確保自動化程式碼在新舊版本 Ansible 環境中都能穩定執行。此外,文章也強調了查閱官方移植和變更日誌的重要性,以便及時掌握版本差異並調整程式碼。
跨版本 Ansible 自動化程式碼的移植最佳實踐
隨著 Ansible 版本的不斷迭代,新功能和模組的引入以及舊功能的變更或棄用,確保自動化程式碼在不同版本間的相容性成為了一項挑戰。本章將探討如何有效地在不同 Ansible 版本之間移植自動化程式碼。
動態管理不同作業系統型別的最佳實踐
在處理多樣化的作業系統環境時,利用 Ansible 的 group_by 模組可以根據事實(如作業系統型別)動態建立群組,從而使 Playbook 能夠根據不同的作業系統型別執行特定的任務。
---
- name: Gather facts and group by OS family
hosts: all
gather_facts: yes
tasks:
- name: Group by OS family
group_by:
key: os_{{ ansible_os_family }}
- name: Install Apache on CentOS
hosts: os_CentOS
tasks:
- name: Install Apache on CentOS
yum:
name: httpd
state: present
- name: Install Apache on Ubuntu
hosts: os_Ubuntu
tasks:
- name: Install Apache on Ubuntu
apt:
name: apache2
state: present
內容解密:
- Gather facts and group by OS family:此 Play 首先收集所有主機的事實,並根據
ansible_os_family事實動態建立群組。 group_by模組的使用:根據ansible_os_family的值(如CentOS或Ubuntu),動態建立對應的群組(例如os_CentOS或os_Ubuntu)。- 針對不同作業系統安裝 Apache:後續的 Play 分別針對
os_CentOS和os_Ubuntu群組執行,安裝對應的 Apache 軟體包(httpdfor CentOS,apache2for Ubuntu)。
跨 Ansible 版本移植的挑戰與解決方案
Ansible 的快速發展意味著新版本可能會引入與舊版本不相容的變更。因此,在升級 Ansible 版本時,需要仔細檢查和修改現有的自動化程式碼,以確保其在新版本下的相容性。
步驟一:檢查當前 Ansible 版本
首先,檢查目前安裝的 Ansible 版本,以確定升級的起始點。
$ ansible --version
ansible 2.9.6
步驟二:查閱移植
對於主要版本的升級(如從 2.9 到 2.10),Ansible 官方提供了詳細的移植。這些列出了版本間的重要變更,包括已棄用或移除的功能。
步驟三:審查變更日誌
除了移植,變更日誌提供了每個小版本更新的詳細資訊,是檢查細粒度變更的重要資源。
第8章:進階Ansible主題
在前面的章節中,我們致力於為讀者打下堅實的Ansible基礎,以便能夠輕鬆自信地實作所需的自動化任務。然而,當你真正開始擴大自動化規模時,如何確保能夠以優雅的方式處理任何出現的情況?例如,當你需要啟動長時間執行的操作時,如何非同步執行它們並在稍後可靠地檢查結果?或者,如果你正在更新一大批伺服器,如何確保在少量伺服器出現故障時能夠及早使play失敗?最後一件事是你不想做的,就是在數百台伺服器上推出一個有問題的更新(說實話,每個人都會遇到程式碼問題),更好的做法是檢測到一小部分伺服器失敗並在此基礎上終止整個play,而不是嘗試繼續並破壞整個負載平衡叢集。
在本章中,我們將探討如何使用Ansible的一些更進階的功能來控制playbook流程和錯誤處理,從而解決這些特定的問題。我們將透過實際的例子,探討如何使用Ansible執行滾動更新,如何與代理和跳板主機(對於安全環境和核心網路組態至關重要)一起工作,以及如何使用原生的Ansible Vault技術來保護靜態的敏感Ansible資料。在本章結束時,你將對如何在小型環境以及大型、安全、關鍵任務環境中執行Ansible有全面的瞭解。
非同步與同步操作
正如我們在本文中迄今為止所見,Ansible plays是按順序執行的,每個任務在下一個任務開始之前執行完成。儘管這通常有利於流程控制和邏輯排序,但有些時候你可能不希望這樣。特別是,可能會出現某個特定任務執行時間超過組態的SSH連線超時時間的問題,由於Ansible在大多數平台上使用SSH來執行其自動化任務,因此這將是一個問題。
程式碼範例:
---
- name: 長時間執行的任務示例
hosts: servers
tasks:
- name: 模擬長時間執行的任務
command: sleep 300
async: 300
poll: 0
內容解密:
此範例展示了一個長時間執行的任務如何非同步執行。async 引數設定了任務的最長執行時間(以秒為單位),而 poll 引數設定為0表示Ansible不會等待任務完成。這允許playbook繼續執行後續的任務。
控制play執行以進行滾動更新
在更新一大批伺服器時,你可能希望控制更新的滾動速度,以避免一次性使所有伺服器不可用。
程式碼範例:
---
- name: 滾動更新示例
hosts: servers
serial: 2
tasks:
- name: 更新軟體包
apt:
name: nginx
state: latest
內容解密:
此範例展示瞭如何使用serial關鍵字控制滾動更新的速度。在這個例子中,Ansible將一次更新兩台伺服器。這有助於確保在更新過程中始終有足夠的伺服器可用。
組態最大失敗百分比
你可以組態Ansible在達到一定百分比的任務失敗時終止整個play。
程式碼範例:
---
- name: 最大失敗百分比示例
hosts: servers
max_fail_percentage: 20
tasks:
- name: 更新軟體包
apt:
name: nginx
state: latest
內容解密:
此範例展示瞭如何使用max_fail_percentage關鍵字來設定最大失敗百分比。如果超過20%的任務失敗,Ansible將終止整個play。
進階 Ansible 主題:非同步任務處理
在自動化任務管理中,Ansible 提供了一種強大的機制來處理長時間執行的任務,即非同步任務(Asynchronous Tasks)。與同步任務不同,非同步任務允許在目標主機上於背景執行任務,並定期檢查其狀態,避免因長時間佔用 SSH 連線而導致的逾時問題。
為何需要非同步任務?
在實際應用中,有些任務可能需要較長時間才能完成,例如大型資料函式庫備份、軟體編譯或系統更新等。如果這些任務以同步方式執行,將會佔用 Ansible 與目標主機之間的 SSH 連線,可能導致連線逾時或資源浪費。透過非同步任務,Ansible 可以啟動任務後立即釋放 SSH 連線,並在稍後檢查任務狀態,從而提高整體自動化流程的效率和可靠性。
基本範例:使用 async 和 poll
以下是一個簡單的範例,展示如何使用 Ansible 的 async 和 poll 引數來執行非同步任務。假設我們有一個包含兩台前端伺服器的 INI 格式清單檔案:
[frontends]
frt01.example.com
frt02.example.com
我們將使用 shell 模組執行一個模擬的長時間任務——讓系統睡眠 20 秒。同時,為了示範非同步執行,我們加入了 async 和 poll 引數:
---
- name: 示範非同步任務的 Playbook
hosts: frontends
become: true
tasks:
- name: 模擬長時間執行的任務
shell: "sleep 20"
async: 30
poll: 5
在這個範例中:
async: 30表示該任務最多允許執行 30 秒。若超過此時間仍未完成,Ansible 將視為任務失敗。poll: 5表示 Ansible 每隔 5 秒檢查一次任務狀態。
程式碼解析:
async引數:定義了任務允許執行的最大時間(秒)。若任務在此時間內未完成,Ansible 將視為失敗。poll引數:指定 Ansible 多久檢查一次任務狀態。若設為0,則表示任務完全在背景執行,不進行狀態檢查。shell模組:用於執行 Shell 命令。在此範例中,我們使用它來模擬一個長時間執行的任務。
輸出結果:
執行該 Playbook 後,Ansible 將在背景啟動任務,並每隔 5 秒檢查其狀態,直到任務完成或達到設定的 async 超時時間。輸出結果與普通 Playbook 相似,但實際上任務是在背景執行的。
高階範例:檢查非同步任務狀態
若將 poll 設定為 0,則任務將完全在背景執行,且 Ansible 不會自動檢查其狀態。此時,我們可以透過另一個任務來手動檢查非同步任務的狀態。
---
- name: 示範非同步任務的 Playbook
hosts: frontends
become: true
tasks:
- name: 模擬長時間執行的任務
shell: "sleep 20"
async: 30
poll: 0
register: long_task
- name: 檢查非同步任務狀態
async_status:
jid: "{{ long_task.ansible_job_id }}"
register: async_result
until: async_result.finished
retries: 30
在這個範例中:
- 第一個任務啟動非同步操作,並將結果註冊到變數
long_task中。 - 第二個任務使用
async_status模組,根據第一個任務傳回的 Job ID 檢查其狀態。 until和retries用於重試檢查,直到任務完成或達到最大重試次數。
Plantuml 圖解:非同步任務流程
@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 非同步任務的基本流程,包括啟動任務、檢查狀態及最終結果處理。
- 當
poll > 0時,Ansible 自動定期檢查任務狀態;若poll = 0,則需要手動新增檢查步驟。
進階 Ansible 主題:非同步任務與滾動更新
在前面的章節中,我們探討了 Ansible 的基礎知識以及如何使用它來自動化常見的系統管理任務。在本章中,我們將探討 Ansible 的一些進階主題,包括非同步任務和滾動更新。
非同步任務
在某些情況下,Ansible 任務可能需要很長時間才能完成,例如大型檔案下載或軟體包更新。在這種情況下,使用非同步任務可以避免 Ansible 阻塞並等待任務完成。非同步任務允許 Ansible 在後台執行任務,並定期檢查其狀態,直到任務完成或達到設定的條件。
如何使用非同步任務
要使用非同步任務,需要在任務定義中使用 async 和 poll 引數。async 引數指定任務的最長執行時間,而 poll 引數指定 Ansible 檢查任務狀態的頻率。
---
- name: 非同步任務範例
hosts: frontends
gather_facts: false
tasks:
- name: 長時間執行的任務
command: sleep 10
async: 15
poll: 2
內容解密:
async: 15指定任務的最長執行時間為 15 秒。poll: 2指定 Ansible 每 2 秒檢查一次任務狀態。- 如果任務在
async指定的時間內完成,Ansible 將繼續執行下一個任務。 - 如果任務未在
async指定的時間內完成,Ansible 將丟擲錯誤。
控制劇本執行以進行滾動更新
在負載平衡的環境中,同時更新所有伺服器可能會導致服務中斷。為了避免這種情況,可以使用 serial 關鍵字來控制 Ansible 一次更新的伺服器數量。
如何使用滾動更新
要使用滾動更新,需要在劇本定義中使用 serial 關鍵字。serial 關鍵字指定 Ansible 一次更新的伺服器數量。
---
- name: 簡單的滾動更新範例
hosts: frontends
serial: 1
gather_facts: false
tasks:
- name: 第一個任務
command: date
- name: 第二個任務
command: date
內容解密:
serial: 1指定 Ansible 一次更新一台伺服器。- Ansible 將在第一台伺服器上完成所有任務後,再繼續更新下一台伺服器。
- 這種方式可以避免同時更新多台伺服器導致的服務中斷。
進階 Ansible 主題:滾動更新與錯誤處理
在管理多主機環境時,如何確保服務的持續可用性是個重要的課題。Ansible 提供了多種機制來實作滾動更新(rolling updates),並在更新過程中處理可能的錯誤。
使用 serial 指令實作滾動更新
預設情況下,Ansible 會平行地在所有主機上執行任務。然而,在某些情況下,這種行為可能會導致服務中斷。例如,當你需要在負載平衡器後的多台前端伺服器上進行升級時,如果同時將所有伺服器從負載平衡器中移除,可能會導致服務不可用。
為瞭解決這個問題,Ansible 提供了 serial 指令,允許你指定一次對多少台主機執行任務。例如:
---
- name: 滾動更新前端伺服器
hosts: frontends
serial: 1
這將確保一次只對一台主機執行任務,從而避免服務中斷。
serial 指令的高階用法
serial 指令不僅可以接受整數值,還可以接受百分比。例如:
---
- name: 滾動更新前端伺服器
hosts: frontends
serial: "25%"
這將根據 inventory 中的主機數量動態調整批次大小。如果 inventory 中有 4 台主機,則一次處理 1 台;如果有 8 台,則一次處理 2 台。
你還可以傳遞一個列表給 serial 指令,以實作更複雜的批次策略:
---
- name: 滾動更新前端伺服器
hosts: frontends
serial:
- 1
- 3
- 5
這將首先對 1 台主機執行任務,然後對接下來的 3 台,最後對剩下的主機以批次大小為 5 的方式執行任務。
組態最大失敗百分比
在預設情況下,Ansible 將繼續執行任務,直到所有主機都處理完畢或遇到錯誤為止。然而,在某些環境中,例如負載平衡或高用性叢集,這種行為可能會導致災難性的後果。
為了避免這種情況,Ansible 提供了 max_fail_percentage 指令,允許你指定在一個批次中允許的最大失敗百分比。如果實際失敗百分比超過此閾值,Ansible 將終止整個 play。
範例:使用 max_fail_percentage
假設我們有一個包含 10 台主機的 inventory,並希望在批次大小為 5 的情況下執行任務,但如果某個批次的失敗率超過 50%,則終止任務:
---
- name: 示範 max_fail_percentage 的使用
hosts: frontends
gather_facts: no
serial: 5
max_fail_percentage: 50
tasks:
- name: 一個可能會失敗的任務
debug:
msg: 這可能會失敗
failed_when: inventory_hostname in ansible_play_batch[0:3]
- name: 一個總是成功的任務
debug:
msg: 成功!
詳細解說:
serial: 5:指定批次大小為 5,表示一次對 5 台主機執行任務。max_fail_percentage: 50:設定最大失敗百分比為 50%,表示如果某個批次中有超過 50% 的主機失敗,則終止整個 play。failed_when: 用於模擬某些主機上的任務失敗。在這個範例中,前三台主機上的任務將會失敗。- 執行結果:當你執行這個 playbook 時,你會看到前三台主機上的任務失敗,而後兩台成功。由於失敗率超過了設定的閾值(60% > 50%),Ansible 將終止後續的任務。
結果分析
執行上述 playbook 後,你會觀察到以下結果:
- 前三台主機的任務失敗。
- 後兩台主機的任務成功,但由於整體失敗率過高,play 被中止。
- 第二批次的主機不會被處理,因為第一批次的失敗率已經超過了設定的
max_fail_percentage。
透過這種方式,你可以在大規模佈署中更好地控制風險,確保在出現問題時能夠及時終止操作,避免更大的損失。