Ansible 在自動化領域扮演著重要角色,尤其在組態管理和應用程式佈署方面。本文將探討 Ansible 的動態庫存管理和 Playbook 的使用,從 Cobbler 整合到 Playbook 的架構、撰寫技巧,以及 Handlers 的應用,提供全面的技術解析和實務範例。藉由 Cobbler,Ansible 可以動態取得主機資訊,擺脫傳統靜態庫存的限制,提升管理效率。同時,Playbook 作為 Ansible 的核心組成,能以 YAML 格式定義複雜的自動化任務,並結合 Roles、條件陳述式、迴圈等功能,實作高度客製化和可重複使用的自動化流程。此外,Handlers 的應用讓 Playbook 更具彈性,能有效管理服務狀態,避免不必要的資源浪費。

使用Cobbler實作動態庫存管理

在Ansible中,庫存管理是自動化維運的核心環節之一。傳統的靜態庫存管理方式在面對大量主機或頻繁變動的環境時顯得力不從心。因此,動態庫存管理應運而生,能夠根據實際情況動態地取得主機資訊。Cobbler是一個強大的系統啟動伺服器,可以用於實作動態庫存管理。

設定Cobbler服務

首先,我們需要設定Cobbler服務。為了確保安全性,不建議使用0.0.0.0作為監聽地址,而應該設定為Cobbler伺服器的IP地址。

$ systemctl start cobblerd.service
$ systemctl enable cobblerd.service
$ systemctl status cobblerd.service

內容解密:

  1. systemctl start cobblerd.service:啟動Cobbler服務。
  2. systemctl enable cobblerd.service:設定Cobbler服務開機自啟。
  3. systemctl status cobblerd.service:檢查Cobbler服務的執行狀態。

新增發行版和系統至Cobbler

接下來,我們需要在Cobbler中新增一個發行版(distribution),並建立根據該發行版的主機組態。

$ cobbler distro add --name=CentOS --kernel=/boot/vmlinuz-3.10.0-957.el7.x86_64 --initrd=/boot/initramfs-3.10.0-957.el7.x86_64.img
$ cobbler profile add --name=webservers --distro=CentOS
$ cobbler system add --name=frontend01 --profile=webservers --dns-name=frontend01.example.com --interface=eth0
$ cobbler system add --name=frontend02 --profile=webservers --dns-name=frontend02.example.com --interface=eth0

內容解密:

  1. cobbler distro add:新增一個發行版至Cobbler,需要指定內核和初始RAM磁碟檔案。
  2. cobbler profile add:根據新增的發行版建立一個組態檔案(profile)。
  3. cobbler system add:新增系統至Cobbler,並指定其使用的組態檔案和網路介面。

組態Ansible動態庫存

為了讓Ansible能夠與Cobbler協同工作,我們需要下載並組態動態庫存指令碼。

$ wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/cobbler.py
$ wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/cobbler.ini
$ chmod +x cobbler.py

內容解密:

  1. 下載Cobbler動態庫存指令碼和組態檔案範本。
  2. 設定動態庫存指令碼為可執行檔案,以便Ansible能夠執行它。

執行Ansible Ad Hoc命令

組態完成後,我們可以使用Ansible的Ad Hoc命令測試動態庫存是否生效。

$ ansible -i cobbler.py webservers -m ping

內容解密:

  1. -i cobbler.py:指定使用Cobbler動態庫存指令碼。
  2. webservers:是我們在Cobbler中定義的profile名稱,現在成為了我們的庫存組名。
  3. -m ping:執行ping模組,測試主機的連通性。

管理多重庫存與主機模式

在前面的章節中,我們探討瞭如何在Ansible中使用單一庫存檔案(無論是靜態還是動態的)。然而,Ansible允許我們透過多次指定-i開關來使用多個庫存來源。這使得我們能夠同時對來自靜態和動態庫存的主機執行任務。

同時使用多個庫存來源

Ansible能夠智慧地處理多個庫存來源。靜態庫存檔案不會被標記為可執行,因此不會被當作動態庫存處理;而動態庫存指令碼則會被執行以取得主機資訊。這種機制使得結合多個庫存來源變得非常簡單。

使用靜態群組與動態群組

當我們同時定義靜態和動態庫存時,Ansible會將兩者的群組定義合併。這帶來了一個有趣的可能性。假設我們的Cobbler動態庫存指令碼根據一個名為webservers的Cobbler profile建立了一個同名的Ansible群組。現在,我們想要將這些webservers主機歸入一個名為centos的群組,因為未來我們可能需要將所有CentOS機器歸在一起。

為了實作這一點,我們可以建立一個靜態庫存檔案,其中包含兩個群組定義。第一個群組定義與動態庫存中的群組同名,但保持空白。當Ansible合併靜態和動態庫存內容時,它將重疊這兩個群組,從而將來自Cobbler的webservers主機新增到這個靜態定義的webservers群組中。

[webservers]
[centos:children]
webservers

接下來,讓我們執行一個簡單的ad hoc ping命令,看看Ansible如何評估這兩個庫存的組合。注意,我們將指定centos群組來執行ping命令,而不是webservers群組。

$ ansible -i static-groups-mix-ini -i cobbler.py centos -m ping
frontend01.example.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
frontend02.example.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

內容解密:

  1. 多庫存來源的使用:透過多次指定-i開關,Ansible允許同時使用多個庫存來源,使得管理和操作跨多個庫存的主機變得更加靈活。
  2. 靜態與動態庫存的合併:當靜態和動態庫存同時被定義時,Ansible會合併它們的群組定義。這使得我們能夠將動態庫存中的主機歸入靜態定義的群組中。
  3. 範例組態與命令執行:透過建立一個靜態庫存檔案並與Cobbler動態庫存結合,我們成功地將webservers主機歸入了centos群組,並對其執行了ping命令。

使用模式進行特殊主機管理

除了精確指定主機或群組外,Ansible還支援使用模式來選擇要操作的主機。這使得我們能夠靈活地對庫存中的子集執行ad hoc命令或playbook。

主機/群組選擇模式

考慮以下庫存內容:

loadbalancer.example.com
[frontends]
frt01.example.com
frt02.example.com
[apps]
app01.example.com
app02.example.com
[databases]
dbms01.example.com
dbms02.example.com
[centos:children]
apps
databases
[ubuntu:children]
frontends

使用--list-hosts開關與ansible命令,我們可以檢視Ansible將對哪些主機進行操作。

$ ansible -i hostgroups-children-ini all --list-hosts
hosts (7):
    loadbalancer.example.com
    frt01.example.com
    frt02.example.com
    app01.example.com
    app02.example.com
    dbms01.example.com
    dbms02.example.com

內容解密:

  1. 模式的使用:Ansible支援使用模式來選擇主機,這使得對庫存子集的操作更加靈活。
  2. --list-hosts開關:透過使用--list-hosts開關,我們可以預覽Ansible將對哪些主機進行操作,而無需實際執行命令。
  3. all群組all是一個特殊的群組,用於指定庫存中的所有主機。

Ansible 庫存管理與 Playbook 深度解析

在前面的章節中,我們探討了 Ansible 的基礎知識以及如何使用臨時命令來簡化管理工作。本章節將探討 Ansible 的核心組成部分:Playbook。Playbook 是 Ansible 自動化任務的邏輯組織,它將多個任務以結構化的方式組合起來,以實作特定的目標,例如佈署 Web 伺服器或應用安全策略。

瞭解 Playbook 基礎架構

Playbook 是 Ansible 的核心,它允許使用者以 YAML 格式編寫自動化任務。這些任務可以是簡單的命令,也可以是複雜的流程控制。Playbook 的設計理念是簡單易讀、自我描述,從而成為 IT 處理程式中寶貴的一部分。

Playbook 的基本組成

  • Tasks(任務):Playbook 中的基本單元,用於執行特定的操作,如安裝軟體包或修改組態檔案。
  • Handlers(處理程式):特殊的任務,用於在某些條件下執行操作,通常用於重啟服務或執行其他需要在特定條件下進行的操作。
  • Variables(變數):用於儲存和重複使用值的機制,可以在 Playbook 中定義或從外部來源匯入。

使用 Roles 組織 Playbook

Roles 是 Ansible 提供的一種用於組織和重複使用 Playbook 程式碼的機制。透過將相關的任務、變數和處理程式組織到一個 Role 中,可以簡化 Playbook 的結構,提高程式碼的可重複使用性。

如何建立和使用 Roles

  1. 建立 Role 結構:定義 Role 的目錄結構,包括 tasks、handlers、variables 等。
  2. 編寫 Role 相關程式碼:在對應的目錄中編寫任務、處理程式和變數定義。
  3. 在 Playbook 中使用 Role:透過 roles 關鍵字將 Role 包含在 Playbook 中。

在程式碼中使用條件陳述式

Ansible 支援在 Playbook 中使用條件陳述式,以根據不同的條件執行不同的任務。這可以透過 when 關鍵字實作,允許根據變數的值或任務的結果來決定是否執行某個任務。

重複執行任務使用迴圈

迴圈是 Playbook 中的另一種重要功能,允許重複執行某個任務多次。Ansible 支援多種迴圈機制,包括 loopwith_items,使得處理多個相似任務變得更加容易。

使用區塊分組任務

區塊(Blocks)允許將多個任務分組在一起,並對該組應用相同的條件或錯誤處理邏輯。這提高了 Playbook 的可讀性和可維護性。

組態 Play 執行的策略

Ansible 允許使用者透過 strategy 關鍵字組態 Play 的執行策略,例如使用 linearfree 策略來控制任務的執行順序。

使用 ansible-pull

ansible-pull 是一個用於從 Git 倉函式庫提取 Playbook 並在本地執行的工具。它允許節點自主地更新其組態和狀態,對於大規模自動化佈署非常有用。

深入理解 Ansible Playbook 架構

本章節主要探討 Ansible Playbook 的技術細節與實務應用,確保讀者能夠掌握撰寫高效 Playbook 的最佳實踐方法。

技術需求與環境設定

在開始本章節之前,請確保您的控制主機已正確安裝 Ansible,並使用最新版本(本章範例根據 Ansible 2.9)。此外,您至少需要一台額外的測試主機,建議使用 Linux 系統。雖然本章會提供特定的主機名稱範例,但您可以根據需求替換成自己的主機名稱或 IP 地址。

理解 Playbook 框架

Playbook 能夠簡化多台機器上的複雜佈署與組態管理。這是 Ansible 在交付複雜應用程式時的關鍵優勢之一。透過 Playbook,您可以將任務以邏輯結構組織起來,一般情況下,任務會按照撰寫順序執行,從而對自動化流程擁有較高的控制能力。

建立簡單的 Playbook

首先,我們來建立一個簡單的 Playbook,用於在 frontends 主機群組上執行。我們可以使用 remote_user 指令來指定連線遠端主機的使用者:

---
- hosts: frontends
  remote_user: danieloh
  tasks:
    - name: 簡單連線測試
      ping:

新增額外任務

接下來,我們在第一個任務下方新增另一個任務,使用 shell 模組在遠端主機上執行 ls 命令。同時,我們加入 ignore_errors 指令,以確保即使 ls 命令失敗(例如,目標目錄不存在),Playbook 也不會終止:

- name: 執行簡單命令
  shell: /bin/ls -al /nonexistent
  ignore_errors: True

執行 Playbook

現在,讓我們執行剛建立的 Playbook,觀察其行為:

$ ansible-playbook -i hosts myplaybook.yaml

輸出結果顯示我們的兩個任務按照指定順序執行。由於我們嘗試列出不存在的目錄,ls 命令失敗,但由於設定了 ignore_errors: True,Playbook 並未將該任務標記為失敗。

程式碼解析

YAML 格式注意事項

YAML 格式易於閱讀和編寫,但對縮排非常嚴格。例如,不能使用 Tab 鍵進行縮排,儘管在螢幕上 Tab 和四個空格看起來相同,但在 YAML 中它們是不同的。建議使用支援 YAML 的編輯器(如 Vim、Visual Studio Code 或 Eclipse)來協助編寫 Playbook。

詳細輸出結果分析

從 Playbook 的輸出結果可以看出,兩個任務均依照預期順序執行。雖然 ls 命令因列出不存在的目錄而失敗,但由於 ignore_errors 的設定,Playbook 仍然繼續執行,並未將任務標記為失敗。

PLAY [frontends]
***************************************************************
TASK [Gathering Facts]
*********************************************************
ok: [frt02.example.com]
ok: [frt01.example.com]
TASK [簡單連線測試]
**************************************************
ok: [frt01.example.com]
ok: [frt02.example.com]
TASK [執行簡單命令]
****************************************************
fatal: [frt02.example.com]: FAILED! => {"changed": true, "cmd": "/bin/ls -al /nonexistent", ...}
...ignoring
fatal: [frt01.example.com]: FAILED! => {"changed": true, "cmd": "/bin/ls -al /nonexistent", ...}
...ignoring
PLAY RECAP
*********************************************************************
frt01.example.com : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
frt02.example.com : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1

#### 內容解密:

  1. YAML格式嚴格性:使用支援YAML的編輯器以確保正確的縮排。
  2. ignore_errors的作用:即使任務失敗(如ls命令),Playbook仍會繼續執行。
  3. 任務執行順序:任務按照在Playbook中定義的順序執行。
  4. remote_user指令:指定用於連線遠端主機的使用者。
  5. Playbook輸出結果分析:透過輸出結果可以瞭解任務執行的詳細情況,包括成功與失敗的任務。

Ansible Playbooks 與 Handlers 的應用

在 Ansible 中,大多數模組(除了像 shellcommandraw 這類別執行使用者自定義命令的模組)都被設計為具有冪等性。也就是說,如果你執行相同的任務兩次,結果將會相同,並且不會重複進行相同的變更。如果模組檢測到所請求的操作已經完成,則不會再次執行該操作。

任務狀態

每個模組都會傳回一組結果,其中包括任務狀態。這些狀態對於理解任務的執行結果非常重要:

  • ok:任務成功執行且未進行任何變更。
  • changed:任務成功執行並進行了變更。
  • failed:任務執行失敗。
  • unreachable:無法連線到主機以執行任務。
  • skipped:任務被跳過。
  • ignored:任務被忽略(例如,在 ignore_errors 的情況下)。
  • rescued:當我們稍後檢視區塊和救援任務時,會看到此狀態的範例。

Handlers 的應用

Handlers 是一種特殊的任務,它們在收到通知時執行,並且只會在所有任務執行完畢後才會執行一次,即使被通知多次。這種機制可以用來避免不必要的服務重啟。

範例:更新 Apache 組態檔案

考慮以下 Playbook 範例,用於示範 Handlers 的使用:

---
- name: Handler 示範 1
  hosts: frt01.example.com
  gather_facts: no
  become: yes
  tasks:
    - name: 更新 Apache 組態
      template:
        src: template.j2
        dest: /etc/httpd/httpd.conf
      notify: 重啟 Apache
  handlers:
    - name: 重啟 Apache
      service:
        name: httpd
        state: restarted

在這個範例中,當 template 模組更新了 Apache 組態檔案後,會通知 重啟 Apache Handler。如果組態檔案未被更新,則 Handler 不會被執行。

多個 Handlers 的呼叫

你也可以透過 listen 指令呼叫多個 Handlers,如下所示:

---
- name: Handler 示範 2
  hosts: frt01.example.com
  gather_facts: no
  become: yes
  handlers:
    - name: 重啟 chronyd
      service:
        name: chronyd
        state: restarted
      listen: "重啟所有服務"
    - name: 重啟 apache
      service:
        name: httpd
        state: restarted
      listen: "重啟所有服務"
  tasks:
    - name: 重啟所有服務
      command: echo "此任務將重啟所有服務"
      notify: "重啟所有服務"

在這個範例中,當 重啟所有服務 任務執行時,會通知兩個 Handler:重啟 chronyd重啟 apache

程式碼解析

template:
  src: template.j2
  dest: /etc/httpd/httpd.conf
notify: 重啟 Apache

內容解密:

  1. template 模組用於從範本檔案生成組態檔案。
  2. src: template.j2 指定了範本檔案的來源路徑。
  3. dest: /etc/httpd/httpd.conf 指定了生成檔案的目標路徑。
  4. notify: 重啟 Apache 當組態檔案被更新時,通知 重啟 Apache Handler 執行。
service:
  name: httpd
  state: restarted

內容解密:

  1. service 模組用於管理服務的狀態。
  2. name: httpd 指定了要管理的服務名稱。
  3. state: restarted 指定了要將服務重啟。