Ansible 作為常用的自動化工具,其版本更新頻繁,引入新功能的同時,也可能造成舊程式碼不相容。因此,瞭解 Ansible 程式碼的跨版本移植技巧至關重要。本文將探討如何利用 Ansible 的特性,例如 group_by 模組、asyncpoll 引數、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

內容解密:

  1. Gather facts and group by OS family:此 Play 首先收集所有主機的事實,並根據 ansible_os_family 事實動態建立群組。
  2. group_by 模組的使用:根據 ansible_os_family 的值(如 CentOSUbuntu),動態建立對應的群組(例如 os_CentOSos_Ubuntu)。
  3. 針對不同作業系統安裝 Apache:後續的 Play 分別針對 os_CentOSos_Ubuntu 群組執行,安裝對應的 Apache 軟體包(httpd for CentOS, apache2 for 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 連線,並在稍後檢查任務狀態,從而提高整體自動化流程的效率和可靠性。

基本範例:使用 asyncpoll

以下是一個簡單的範例,展示如何使用 Ansible 的 asyncpoll 引數來執行非同步任務。假設我們有一個包含兩台前端伺服器的 INI 格式清單檔案:

[frontends]
frt01.example.com
frt02.example.com

我們將使用 shell 模組執行一個模擬的長時間任務——讓系統睡眠 20 秒。同時,為了示範非同步執行,我們加入了 asyncpoll 引數:

---
- name: 示範非同步任務的 Playbook
  hosts: frontends
  become: true
  tasks:
    - name: 模擬長時間執行的任務
      shell: "sleep 20"
      async: 30
      poll: 5

在這個範例中:

  • async: 30 表示該任務最多允許執行 30 秒。若超過此時間仍未完成,Ansible 將視為任務失敗。
  • poll: 5 表示 Ansible 每隔 5 秒檢查一次任務狀態。

程式碼解析:

  1. async 引數:定義了任務允許執行的最大時間(秒)。若任務在此時間內未完成,Ansible 將視為失敗。
  2. poll 引數:指定 Ansible 多久檢查一次任務狀態。若設為 0,則表示任務完全在背景執行,不進行狀態檢查。
  3. 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

在這個範例中:

  1. 第一個任務啟動非同步操作,並將結果註冊到變數 long_task 中。
  2. 第二個任務使用 async_status 模組,根據第一個任務傳回的 Job ID 檢查其狀態。
  3. untilretries 用於重試檢查,直到任務完成或達到最大重試次數。

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 在後台執行任務,並定期檢查其狀態,直到任務完成或達到設定的條件。

如何使用非同步任務

要使用非同步任務,需要在任務定義中使用 asyncpoll 引數。async 引數指定任務的最長執行時間,而 poll 引數指定 Ansible 檢查任務狀態的頻率。

---
- name: 非同步任務範例
  hosts: frontends
  gather_facts: false
  tasks:
    - name: 長時間執行的任務
      command: sleep 10
      async: 15
      poll: 2

內容解密:

  1. async: 15 指定任務的最長執行時間為 15 秒。
  2. poll: 2 指定 Ansible 每 2 秒檢查一次任務狀態。
  3. 如果任務在 async 指定的時間內完成,Ansible 將繼續執行下一個任務。
  4. 如果任務未在 async 指定的時間內完成,Ansible 將丟擲錯誤。

控制劇本執行以進行滾動更新

在負載平衡的環境中,同時更新所有伺服器可能會導致服務中斷。為了避免這種情況,可以使用 serial 關鍵字來控制 Ansible 一次更新的伺服器數量。

如何使用滾動更新

要使用滾動更新,需要在劇本定義中使用 serial 關鍵字。serial 關鍵字指定 Ansible 一次更新的伺服器數量。

---
- name: 簡單的滾動更新範例
  hosts: frontends
  serial: 1
  gather_facts: false
  tasks:
    - name: 第一個任務
      command: date
    - name: 第二個任務
      command: date

內容解密:

  1. serial: 1 指定 Ansible 一次更新一台伺服器。
  2. Ansible 將在第一台伺服器上完成所有任務後,再繼續更新下一台伺服器。
  3. 這種方式可以避免同時更新多台伺服器導致的服務中斷。

進階 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: 成功!

詳細解說:

  1. serial: 5:指定批次大小為 5,表示一次對 5 台主機執行任務。
  2. max_fail_percentage: 50:設定最大失敗百分比為 50%,表示如果某個批次中有超過 50% 的主機失敗,則終止整個 play。
  3. failed_when: 用於模擬某些主機上的任務失敗。在這個範例中,前三台主機上的任務將會失敗。
  4. 執行結果:當你執行這個 playbook 時,你會看到前三台主機上的任務失敗,而後兩台成功。由於失敗率超過了設定的閾值(60% > 50%),Ansible 將終止後續的任務。

結果分析

執行上述 playbook 後,你會觀察到以下結果:

  • 前三台主機的任務失敗。
  • 後兩台主機的任務成功,但由於整體失敗率過高,play 被中止。
  • 第二批次的主機不會被處理,因為第一批次的失敗率已經超過了設定的 max_fail_percentage

透過這種方式,你可以在大規模佈署中更好地控制風險,確保在出現問題時能夠及時終止操作,避免更大的損失。