一、文檔核心定位
本文檔聚焦Ansible自動化運維中的兩大核心功能——循環與判斷,通過“功能說明+完整Playbook代碼”的形式,覆蓋循環迭代場景(列表、字典、文件等)、數據處理過濾器(字符串、數字、加密等)、條件判斷邏輯(變量、任務結果、路徑等)及錯誤處理方案,所有代碼均針對Linux環境設計,可直接在Ansible控制端編輯執行。
二、Ansible循環:批量執行重復任務
Ansible循環通過with_*
系列關鍵字或loop
(配合過濾器)實現,核心是用{{ item }}
指代迭代對象,適配不同批量操作場景,以下為所有循環類型的詳細拆解。
(一)with_items:迭代列表(最常用場景)
功能定義
遍歷列表中的元素,將每個元素作為{{ item }}
代入任務,實現“一次定義,多次執行”,典型場景為批量安裝軟件包、創建多個文件或用戶。
應用場景
在Linux受控節點(如node1)批量安裝httpd
(網頁服務)、samba
(文件共享服務)、samba-client
(samba客戶端),需先配置本地yum源(依賴光盤掛載)。
完整代碼與逐行說明
# 編輯Playbook文件:vim b.yml
---
- name: install packages # Play名稱:明確任務目標(安裝軟件包)hosts: node1 # 目標受控節點:指定任務在node1上執行tasks: # 任務列表:包含依賴配置與核心安裝任務# 任務1:配置BaseOS本地yum源(為安裝軟件提供包來源)- name: yum_repo1 # 任務名:區分不同yum源配置yum_repository: # 模塊:用于管理yum倉庫配置file: server # 配置文件名:最終生成/etc/yum.repos.d/server.reponame: baseos # 倉庫標識名:唯一識別該倉庫description: rhel8 # 倉庫描述:說明倉庫用途(RHEL8系統源)baseurl: file:///mnt/BaseOS # 包源路徑:本地光盤掛載后的BaseOS目錄enabled: yes # 啟用倉庫:允許通過該倉庫安裝軟件gpgcheck: no # 關閉GPG校驗:本地源無需校驗包完整性# 任務2:配置AppStream本地yum源(補充軟件包來源)- name: yum_repo2yum_repository:file: server # 與BaseOS共用一個配置文件name: appstream # 倉庫標識名:與BaseOS區分description: appstream # 倉庫描述:RHEL8應用流源baseurl: file:///mnt/AppStream # 包源路徑:光盤掛載后的AppStream目錄enabled: yesgpgcheck: no# 任務3:掛載光盤(為yum源提供本地文件支持)- name: mount cdrom # 任務名:掛載光盤mount: # 模塊:用于管理文件系統掛載src: /dev/cdrom # 源設備:Linux中光盤的設備文件path: /mnt # 掛載點:將光盤掛載到/mnt目錄fstype: iso9660 # 文件系統類型:ISO鏡像固定格式state: mounted # 掛載狀態:確保光盤已掛載(若未掛載則自動掛載)# 任務4:核心循環安裝軟件(with_items迭代列表)- name: install pks # 任務名:安裝目標軟件包yum: # 模塊:用于管理RPM軟件包name: "{{ item }}" # 軟件名:{{ item }}依次取列表中的軟件名state: present # 安裝狀態:確保軟件已安裝(若未安裝則自動安裝)with_items: # 迭代列表:需安裝的軟件包集合- httpd- samba- samba-client
執行與驗證
- 執行命令:
ansible-playbook b.yml
- 驗證方式:在node1上執行
rpm -qa | grep -E "httpd|samba"
,確認3個軟件均已安裝。
(二)with_dict:迭代字典(鍵值對關聯場景)
功能定義
遍歷字典的“鍵-值”對,通過item.key
調用字典的鍵(如配置項名稱),item.value
調用對應的值(如配置項內容),適用于批量處理關聯數據(如網絡配置、服務端口映射)。
應用場景
打印網絡配置中的address
(IP地址)、netmask
(子網掩碼)、gateway
(網關)的鍵值對,驗證字典迭代邏輯。
完整代碼與說明
# 編輯Playbook文件:vim c.yml
---
- name: test # Play名稱:測試字典迭代hosts: node1tasks:- name: debug # 任務名:輸出調試信息debug: # 模塊:用于輸出變量或文本信息msg: "{{ item.key }} & {{ item.value }}" # 輸出格式:鍵 & 值with_dict: # 迭代字典:網絡配置鍵值對address: 1 # 鍵:address,值:1(示例值,可替換為實際IP如192.168.1.10)netmask: 2 # 鍵:netmask,值:2(示例值,可替換為255.255.255.0)gateway: 3 # 鍵:gateway,值:3(示例值,可替換為192.168.1.1)
執行結果示例
TASK [debug] *****************************************************************
ok: [node1] => (item={'key': 'address', 'value': 1}) => {"msg": "address & 1"
}
ok: [node1] => (item={'key': 'netmask', 'value': 2}) => {"msg": "netmask & 2"
}
ok: [node1] => (item={'key': 'gateway', 'value': 3}) => {"msg": "gateway & 3"
}
(三)with_fileglob:迭代文件(批量文件操作)
功能定義
匹配Ansible控制端指定路徑下的文件(支持通配符*
),批量處理文件操作(如拷貝、刪除),需注意:僅識別控制端文件,不識別受控端文件。
應用場景
將控制端/tmp
目錄下所有.sh
(Shell腳本)和.py
(Python腳本)文件,批量拷貝到受控端node1的/tmp
目錄。
完整代碼與說明
# 編輯Playbook文件:vim d.yml
---
- name: test # Play名稱:測試文件迭代拷貝hosts: node1tasks:- name: cp file # 任務名:拷貝文件copy: # 模塊:用于文件拷貝(控制端→受控端)src: "{{ item }}" # 源文件路徑:{{ item }}依次取匹配的控制端文件dest: /tmp/ # 目標路徑:受控端的/tmp目錄(保持原文件名)with_fileglob: # 控制端文件匹配規則- /tmp/*.sh # 匹配控制端/tmp下所有.sh后綴的文件- /tmp/*.py # 匹配控制端/tmp下所有.py后綴的文件
(四)with_lines:迭代命令輸出行(基于命令結果操作)
功能定義
執行Linux命令(在控制端執行),將命令輸出按“行”拆分,每一行作為{{ item }}
代入任務,適用于基于命令結果的批量操作(如拷貝命令找到的特定文件)。
應用場景
查找控制端/etc/ansible
目錄下所有.yml
(Ansible Playbook)文件,批量拷貝到受控端node1的/tmp
目錄。
完整代碼與說明
# 編輯Playbook文件:vim e.yml
---
- name: test # Play名稱:測試命令輸出迭代hosts: node1tasks:- name: cp file # 任務名:拷貝.yml文件copy:src: "{{ item }}" # 源文件路徑:find命令輸出的每一行(單個.yml文件路徑)dest: /tmp/with_lines: # 執行命令并迭代輸出行- find /etc/ansible -name "*.yml" # 命令:查找ansible目錄下所有.yml文件
(五)with_nested:嵌套迭代(多列表組合場景)
功能定義
實現多列表的“笛卡爾積”迭代,即第一個列表的每個元素與第二個列表的所有元素逐一組合,通過item[0]
調用第一個列表元素,item[1]
調用第二個列表元素。
應用場景
將列表[a, b]
(如服務名稱)與列表[1, 2, 3]
(如實例編號)組合,生成a&1
、a&2
、a&3
、b&1
、b&2
、b&3
等組合,用于批量生成服務實例名稱。
完整代碼與說明
# 編輯Playbook文件:vim f.yml
---
- name: test # Play名稱:測試嵌套迭代hosts: node1tasks:- name: debug # 任務名:輸出組合結果debug:msg: "{{ item[0] }} & {{ item[1] }}" # 輸出格式:第一個列表元素 & 第二個列表元素with_nested: # 嵌套迭代的兩個列表- [a, b] # 第一個列表:基礎元素- [1, 2, 3] # 第二個列表:組合元素
(六)with_sequence:排序列(有序數字生成)
功能定義
生成指定范圍、步長的有序數字序列,支持3個核心參數:
start
:序列起始值(默認從0開始)end
:序列結束值(必填)stride
:序列步長(默認1,即連續數字)
應用場景
生成1-5的連續數字序列(步長1),用于批量創建帶編號的資源(如文件file1
-file5
、用戶user1
-user5
)。
完整代碼與說明
# 編輯Playbook文件:vim g.yml
---
- name: test # Play名稱:測試有序序列生成hosts: node1tasks:- name: debug # 任務名:輸出序列數字debug:msg: "{{ item }}" # 輸出每個序列數字with_sequence: # 序列參數配置start=1 # 起始值:1end=5 # 結束值:5stride=1 # 步長:1(生成1、2、3、4、5)
(七)with_random_choice:隨機取值(隨機操作場景)
功能定義
從指定列表中隨機選擇一個元素執行任務,每次運行Playbook的結果可能不同,適用于需要隨機化的場景(如隨機選擇測試節點、隨機生成測試數據)。
應用場景
從列表[1, 2, a, b, c]
中隨機選擇一個元素輸出,驗證隨機迭代邏輯。
完整代碼與說明
# 編輯Playbook文件:vim h.yml
---
- name: test # Play名稱:測試隨機取值hosts: node1tasks:- name: debug # 任務名:輸出隨機元素debug:msg: "{{ item }}" # 輸出隨機選中的元素with_random_choice: # 隨機選擇的列表- 1- 2- a- b- c
(八)Loop與過濾器:數據處理增強
功能定義
loop
是Ansible推薦的新版循環方式,需配合過濾器實現數據處理(如字符串轉換、數字計算、密碼加密),過濾器通過|
調用,可直接作用于變量或迭代結果。
1. 常用字符串過濾器(處理文本數據)
功能說明
過濾器 | 作用 | 示例 | 輸出結果 |
---|---|---|---|
upper | 字符串轉全大寫 | `“abc123ABC 666” | upper` |
lower | 字符串轉全小寫 | `“abc123ABC 666” | lower` |
trim | 去除首尾空格 | `" abc " | trim` |
length | 計算字符串長度(含空格) | `“abc123ABC 666” | length` |
完整代碼
---
- name: test # Play名稱:測試字符串過濾器hosts: node1vars: # 定義測試變量testvar: "abc123ABC 666" # 含大小寫、數字、空格的字符串testvar1: " abc " # 含首尾空格的字符串tasks:- name: debug1 # 測試upper過濾器debug:msg: "{{ testvar | upper }}"- name: debug2 # 測試lower過濾器debug:msg: "{{ testvar | lower }}"- name: debug3 # 測試trim過濾器debug:msg: "{{ testvar1 | trim }}"- name: debug4 # 測試length過濾器debug:msg: "{{ testvar | length }}"
2. 補充字符串過濾器
完整代碼與說明
# 編輯文件:vim filterstr.yml
---
- name: 過濾器 # Play名稱:補充字符串過濾器測試hosts: servera # 目標節點:servera(可替換為node1)vars:testvar: "abc123ABC 666"testvar1: " abc "testvar2: "123456789"testvar3: "1a2b,@#$%^&" # 含特殊字符的字符串tasks:- name: 將字符串轉換成純大寫debug: msg="{{ testvar | upper }}"- name: 將字符串轉換成純小寫debug: msg="{{ testvar | lower }}"- name: 將字符串首字母大寫,之后的所有字母純小寫(capitalize)debug: msg="{{ testvar | capitalize }}" # 輸出:Abc123abc 666- name: 返回字符串的第一個字符(first)debug: msg="{{ testvar | first }}" # 輸出:a- name: 返回字符串的最后一個字符(last)debug: msg="{{ testvar | last }}" # 輸出:6- name: 將字符串開頭和結尾的空格去除(trim)debug: msg="{{ testvar1 | trim }}"- name: 將字符串居中,總長度30,兩邊用空格補齊(center)debug: msg="{{ testvar1 | center(width=30) }}" # 輸出:中間為abc,兩邊共27個空格- name: 返回字符串長度(length,與count等效)debug: msg="{{ testvar2 | length }}" # 輸出:9- name: 將字符串轉換成列表,每個字符為元素(list)debug: msg="{{ testvar3 | list }}" # 輸出:['1','a','2','b',',','@','#','$','%','^','&']- name: 將字符串轉列表并隨機打亂(shuffle,“洗牌”效果)debug: msg="{{ testvar3 | shuffle }}" # 輸出:隨機排序的字符列表
3. 數字操作過濾器(處理數值數據)
[root@foundation0 ansible]# cat filterdata.yml
---
- name: this playbookhosts: serveravars:testvar4: -1tasks:- name: 轉int并計算(字符串與數字不可直接計算)debug: msg="{{ 8+('8' | int) }}"- name: 轉int,無法轉換返回默認值6debug: msg="{{ 'a' | int(default=6) }}"- name: 轉floatdebug: msg="{{ '8' | float }}"- name: 轉float,無法轉換返回8.88debug: msg="{{ 'a' | float(8.88) }}"- name: 取絕對值(abs)debug: msg="{{ testvar4 | abs }}"- name: 四舍五入(round 四舍五入偶數)debug: msg="{{ 12.5 | round }}" ##輸出為12- name: 保留5位小數(round(5))debug: msg="{{ 3.1415926 | round(5) }}"- name: 0-100隨機數(random)debug: msg="{{ 100 | random }}"- name: 5-10隨機數(start=5)debug: msg="{{ 10 | random(start=5) }}"- name: 5-15隨機數,步長3(step=3)debug: msg="{{ 15 | random(start=5,step=3) }}"- name: 0-15隨機數,5的倍數(step=5)debug: msg="{{ 15 | random(step=5) }}"
功能說明
過濾器 | 作用 | 示例 | 輸出結果 |
---|---|---|---|
int | 轉整數,無法轉換時返回默認值(默認0) | `“8” | int、 “a” |
float | 轉浮點型,無法轉換時返回默認值(默認0.0) | `“8” | float、 “a” |
abs | 取絕對值 | `-1 | abs` |
round | 四舍五入,可指定小數位數 | `12.5 | round、 3.1415926 |
random | 生成隨機數,支持 |
4. 文件 / 目錄類過濾器(含加密)
- 代碼:
[root@foundation0 ansible]# cat filterfile.yml
---
- name: 文件或目錄類的過濾器hosts: serveratasks:- name: sha1哈希debug: msg="{{ '123456' | hash('sha1') }}"- name: md5哈希debug: msg="{{ '123456' | hash('md5') }}"- name: 校驗和(與md5一致)debug: msg="{{ '123456' | checksum }}"- name: sha256哈希(隨機鹽)debug: msg="{{ '123456' | password_hash('sha256') }}"- name: sha256哈希(指定鹽mysalt)debug: msg="{{ '123456' | password_hash('sha256','mysalt') }}"- name: sha512哈希(隨機鹽)debug: msg="{{ '123123' | password_hash('sha512') }}"- name: sha512哈希(指定鹽ebzL.U5cjaHe55KK)debug: msg="{{ '123123' | password_hash('sha512','ebzL.U5cjaHe55KK') }}"
5. 加密算法應用(創建帶哈希密碼的用戶)
- 代碼:
---
- name: create userhosts: node1tasks:- name: create chenyuuser:name: chenyupassword: "{{'redhat' | password_hash('sha512')}}"
- 說明:創建用戶
chenyu
,密碼redhat
用 SHA512 哈希加密存儲。
二、Ansible判斷
通過when
關鍵字實現條件執行,支持變量、任務結果、路徑等多維度判斷,核心運算符:==
、!=
、>
、<
、>=
、<=
、and
、or
、not
、is
、in
。
(一)判斷變量的tests
- 功能:用
defined
(已定義)、undefined
(未定義)、none
(已定義為空)判斷變量狀態。 - 代碼:
Vim test.yml
---
- name: testhosts: node1vars:aa: 11cc:tasks:- name: debug1(aa已定義)debug: msg:a when: aa is defined- name: debug2(bb未定義)debug: msg: ab when: bb is undefined- name: debug3(cc為空)debug: msg: abc when: cc is none
(二)判斷執行結果的tests
- 功能:用
success
(成功)、failed
(失敗)、changed
(狀態變更)、skipped
(跳過)判斷任務結果,需先register
注冊結果。 - 代碼:
Vim test2.yml
---
- name: testhosts: node1vars:aa: 11tasks:- name: shell(aa==11時執行ls /mnt,注冊結果到dd)shell: cmd: ls /mnt when: aa == 11 register: dd- name: debug1(任務成功)debug: msg: chenyu success when: dd is success- name: debug2(任務失敗)debug: msg: chenyu failed when: dd is failed- name: debug3(任務變更)debug: msg: chenyu changed when: dd is changed- name: debug4(任務跳過)debug: msg: chenyu skip when: dd is skip
(三)判斷路徑的tests
- 功能:用
file
(文件)、directory
(目錄)、link
(軟鏈接)、mount
(掛載點)、exists
(存在)判斷路徑狀態,僅針對控制端路徑。 - 代碼:
vim test.yml
---
- name: testhosts: node1vars:a1: /test/file1a2: /test/a3: /test/softlinkaa4: /test/hardlinkaa5: /boot/tasks:- name: debug1(a1是文件)debug: msg=this is file when: a1 is file- name: debug2(a2是目錄)debug: msg="this is directory" when: a2 is directory- name: debug3(a3是軟鏈接)debug: msg="this is softlink" when: a3 is link- name: debug4(a4是硬鏈接)debug: msg="this is hardlink" when: a4 is link- name: debug5(a5是掛載點)debug: msg="this is mount directory" when: a5 is mount- name: debug6(a1存在)debug: msg="this is exists" when: a1 is exists
(四)判斷字符串的tests
- 功能:用
lower
(字母全小寫)、upper
(字母全大寫)判斷字符串大小寫(數字不影響)。 - 代碼:
vim test.yml
---
- name: testhosts: node1vars:a1: abca2: ABCa3: a1btasks:- name: debug1(a1全小寫)debug: msg=this string is all lower when: a1 is lower- name: debug2(a2全大寫)debug: msg=this string is all upper when: a2 is upper- name: debug3(a3字母全小寫)debug: msg=chenyu when: a3 is lower
(五)判斷數據類型的tests
- 功能:用
string
(字符串)、number
(數字)判斷數據類型。 - 代碼:
vim test.yml
---
- name: testhosts: node1vars:a1: 1a2: "1"a3: atasks:- name: debug1(a1是數字)debug: msg=this is number when: a1 is number- name: debug2(a2是字符串)debug: msg=this is string when: a2 is string- name: debug3(a3是字符串)debug: msg=this is string when: a3 is string
(六)block/rescue/always:限制性塊(錯誤處理)
- 功能:
block
執行正常任務,失敗則執行rescue
,無論成功/失敗都執行always
。 - 應用場景:創建邏輯卷,失敗則用備用大小,最后統一格式化;卷組不存在則提示。
- 前提:node1卷組
research
為2G,node2為1G(需先通過vg.yml
創建)。- 創建卷組的
vg.yml
代碼:
- 創建卷組的
Vim vg.yml
---
- name: create vg for node1hosts: node1tasks:- name: create partitionparted:device: /dev/sdbnumber: 1part_type: primarypart_start: 10MiBpart_end: 2058MiBstate: present- name: create vg researchlvg: vg=research pvs=/
Ansible邏輯卷配置與錯誤處理例題整理與詳細說明
一、核心例題:邏輯卷(LV)配置與錯誤處理實戰
(一)題目完整需求
創建名為/etc/ansible/lv.yml
的Playbook,在所有受管節點執行以下任務:
- 創建符合要求的邏輯卷:
- 位于
research
卷組中 - 名稱為
data
- 大小為1500MiB
- 位于
- 使用ext4文件系統格式化該邏輯卷
- 錯誤處理:
- 若無法創建1500MiB大小的邏輯卷,顯示錯誤消息
Could not create logical volume of that size
,并改為創建800MiB大小 - 若
research
卷組不存在,顯示錯誤消息Volume group does not exist
- 若無法創建1500MiB大小的邏輯卷,顯示錯誤消息
- 不要掛載該邏輯卷
(二)前提準備:卷組(VG)創建
1. 環境準備
- 在
node1
和node2
上各添加一塊硬盤(假設為/dev/sdb
) node1
的research
卷組大小為2Gnode2
的research
卷組大小為1G(用于模擬"無法創建1500MiB邏輯卷"的場景)
2. 卷組創建Playbook(vg.yml
)
---
# 為node1創建2G的research卷組
- name: create vg for node1hosts: node1tasks:# 步驟1:在/dev/sdb上創建主分區(2048MiB,約2G)- name: create partitionparted:device: /dev/sdb # 目標硬盤number: 1 # 分區編號part_type: primary # 主分區part_start: 10MiB # 分區起始位置(跳過前10MiB)part_end: 2058MiB # 分區結束位置(10+2048=2058MiB)state: present # 確保分區存在# 步驟2:基于/dev/sdb1創建卷組research- name: create vg researchlvg:vg: research # 卷組名稱pvs: /dev/sdb1 # 使用的物理卷# 為node2創建1G的research卷組
- name: create vg for node2hosts: node2tasks:# 步驟1:在/dev/sdb上創建主分區(1024MiB,約1G)- name: create partition for node2parted:device: /dev/sdbnumber: 1part_type: primarypart_start: 10MiBpart_end: 1034MiB # 10+1024=1034MiBstate: present# 步驟2:基于/dev/sdb1創建卷組research- name: create vg research for node2lvg:vg: researchpvs: /dev/sdb1
3. 執行卷組創建
ansible-playbook vg.yml
(三)邏輯卷配置Playbook(lv.yml
)實現
1. 完整代碼
---
- name: create lvm (邏輯卷配置與錯誤處理)hosts: node1,node2 # 在所有受管節點執行tasks:# 任務1:當research卷組存在時,創建邏輯卷并處理錯誤- name: create lv (卷組存在時執行)block: # 正常執行的任務塊# 子任務1:嘗試創建1500MiB的邏輯卷- name: create lvm 1500Mlvol:vg: research # 卷組名稱lv: data # 邏輯卷名稱size: 1500M # 邏輯卷大小rescue: # block執行失敗時觸發(如空間不足)# 子任務1:輸出錯誤消息- name: output fail messagedebug:msg: Could not create logical volume of that size # 錯誤提示# 子任務2:創建800MiB的備用邏輯卷- name: create lvm 800Mlvol:vg: researchlv: datasize: 800Malways: # 無論block/rescue是否成功,都執行(格式化操作)# 子任務1:用ext4格式化邏輯卷- name: format lvmfilesystem:fstype: ext4 # 文件系統類型dev: /dev/research/data # 邏輯卷設備路徑# 條件:僅當research卷組存在時,執行上述block-rescue-alwayswhen: "'research' in ansible_facts.lvm.vgs" # 或使用"ansible_lvm.vgs"# 任務2:當research卷組不存在時,輸出錯誤消息- name: search not exists (卷組不存在時執行)debug:msg: Volume group does not exist # 錯誤提示# 條件:僅當research卷組不存在時執行when: "'research' not in ansible_facts.lvm.vgs" # 或使用"ansible_lvm.vgs"
2. 關鍵語法與邏輯說明
(1)block/rescue/always
結構
block
:包含正常情況下需要執行的任務(嘗試創建1500MiB邏輯卷)rescue
:當block
中的任何任務失敗時觸發(如node2
的卷組只有1G,無法創建1500MiB邏輯卷),執行錯誤處理(輸出消息+創建800MiB邏輯卷)always
:無論block
成功還是rescue
觸發,都會執行的任務(格式化邏輯卷,確保創建后必格式化)
(2)卷組存在性判斷
- 核心條件:
"'research' in ansible_facts.lvm.vgs"
ansible_facts.lvm.vgs
是Ansible收集的facts信息,包含所有卷組名稱- 當
research
在卷組列表中時,執行block
部分;否則執行"卷組不存在"的錯誤提示
(3)模塊說明
lvol
:用于管理邏輯卷(創建、修改、刪除),需指定vg
(卷組)、lv
(邏輯卷名)、size
(大小)filesystem
:用于格式化存儲設備,需指定fstype
(文件系統類型)和dev
(設備路徑)debug
:用于輸出自定義消息(錯誤提示)
3. 執行結果分析
-
在
node1
上:research
卷組大小為2G,足夠創建1500MiB邏輯卷block
中的"創建1500MiB"任務成功,rescue
不觸發always
執行,格式化邏輯卷- 最終結果:1500MiB的
/dev/research/data
被創建并格式化為ext4
-
在
node2
上:research
卷組大小為1G(1024MiB),無法創建1500MiB邏輯卷block
中的任務失敗,觸發rescue
- 輸出錯誤消息
Could not create logical volume of that size
,并創建800MiB邏輯卷 always
執行,格式化邏輯卷- 最終結果:800MiB的
/dev/research/data
被創建并格式化為ext4
-
在無
research
卷組的節點上:- 跳過
block
部分,執行"卷組不存在"的錯誤提示 - 最終結果:輸出
Volume group does not exist
- 跳過
二、擴展例題:錯誤處理高級用法
(一)fail
模塊:條件滿足時中斷Playbook
1. 功能說明
fail
模塊用于在滿足特定條件時主動中斷Playbook執行,并輸出自定義錯誤消息,常與when
配合使用。
2. 完整代碼(c.yml
)
---
- name: test fail module (測試fail模塊中斷功能)hosts: node1tasks:# 任務1:執行shell命令,輸出含"error"的字符串- name: shellshell:cmd: echo 'this is a string for testing--error' # 輸出包含"error"的內容register: return_value # 注冊命令結果到變量return_value# 任務2:當命令輸出含"error"時,中斷Playbook- name: failfail:msg: Conditions established, Interrupt running playbook # 中斷提示消息when: "'error' in return_value.stdout" # 條件:命令輸出中包含"error"# 任務3:因Playbook被中斷,此任務不會執行- name: debugdebug:msg: I never execute, because the playbook has stopped
3. 執行結果
- 任務1執行成功,輸出含"error"的字符串
- 任務2的條件滿足(
'error' in return_value.stdout
為true
),執行fail
模塊,Playbook中斷 - 任務3不會執行
(二)failed_when
:自定義任務失敗條件
1. 功能說明
failed_when
用于自定義任務的"失敗條件",即使任務實際執行成功(返回碼0),若滿足failed_when
條件,也會被標記為失敗。
2. 完整代碼
---
- name: test failed_when (測試自定義失敗條件)hosts: node1tasks:# 任務1:正常執行的debug- name: debugdebug:msg: I execute normally # 正常輸出# 任務2:當命令輸出含"error"時,標記為失敗- name: shellshell:cmd: echo 'this is a string testing--error' # 輸出包含"error"的內容register: return_value # 注冊結果failed_when: "'error' in return_value.stdout" # 自定義失敗條件# 任務3:因任務2被標記為失敗,默認情況下此任務不會執行- name: debug2debug:msg: chenyu
3. 執行結果
- 任務1正常執行,輸出消息
- 任務2的
shell
命令實際執行成功(返回碼0),但因'error' in return_value.stdout
為true
,被failed_when
標記為失敗 - 任務3默認不會執行(Playbook在任務失敗后中斷)
(三)ignore_errors: yes
:忽略錯誤繼續執行
1. 功能說明
ignore_errors: yes
用于忽略當前任務的錯誤,即使任務失敗,Playbook也會繼續執行后續任務。
2. 完整代碼
---
- name: test ignore_errors (測試忽略錯誤)hosts: node1tasks:# 任務1:輸出主機名(正常執行)- name: debug1debug:msg: "{{ ansible_fqdn }}" # 輸出節點的完全限定域名# 任務2:引用不存在的變量(會失敗,但被忽略)- name: debug2debug:msg: "{{ ansible_ip }}" # ansible_ip是不存在的變量,會報錯ignore_errors: yes # 忽略當前任務的錯誤# 任務3:創建文件(因任務2的錯誤被忽略,此任務會執行)- name: create filefile:path: /tmp/abc # 文件路徑state: touch # 確保文件存在(不存在則創建)
3. 執行結果
- 任務1正常執行,輸出主機名
- 任務2因引用不存在的變量
ansible_ip
而失敗,但ignore_errors: yes
使其被忽略 - 任務3繼續執行,在
/tmp
下創建abc
文件
(四)changed_when
:自定義任務狀態
1. 功能說明
changed_when
用于自定義任務的"變更狀態"(changed
/ok
),即使任務實際修改了系統狀態,也可通過此參數強制標記為ok
,反之亦然。
2. 完整代碼(強制標記為changed
)
---
- name: test changed_when (測試自定義變更狀態)hosts: node1tasks:# 任務1:debug模塊默認不會標記為changed,通過changed_when強制標記- name: debug1debug:msg: "{{ ansible_fqdn }}"changed_when: true # 強制標記為"changed"(即使實際未變更系統)
3. 完整代碼(強制標記為ok
)
---
- name: test changed_when (測試自定義變更狀態)hosts: node1tasks:# 任務1:ls命令默認不會標記為changed,此處顯式指定- name: shellshell:cmd: ls /tmp # 僅查詢,不修改系統狀態changed_when: false # 強制標記為"ok"(即使實際可能有隱含變更)
4. 執行結果
changed_when: true
:任務執行后狀態為changed
(綠色輸出)changed_when: false
:任務執行后狀態為ok
(黃色輸出)
三、總結:錯誤處理核心方法對比
方法 | 作用 | 適用場景 | 示例代碼片段 |
---|---|---|---|
block/rescue/always | 批量任務錯誤捕獲與處理 | 復雜流程(嘗試→失敗處理→最終操作) | block: ... rescue: ... always: ... |
fail 模塊 | 條件滿足時主動中斷Playbook | 關鍵條件不滿足時需終止執行 | fail: msg="中斷" when: 條件 |
failed_when | 自定義任務失敗條件 | 需基于命令輸出判斷任務是否失敗 | failed_when: "'error' in return.stdout" |
ignore_errors: yes | 忽略任務錯誤,繼續執行后續任務 | 非關鍵任務失敗不影響整體流程 | ignore_errors: yes |
changed_when | 自定義任務變更狀態(changed /ok ) | 需精確控制任務狀態顯示(如審計、報告) | changed_when: true 或 changed_when: false |
通過上述方法,可實現Ansible Playbook的精細化錯誤控制,確保在復雜場景下的穩定性與可維護性。
for循環和if的例題
1,從 http://ansible.example.com/materials/newhosts.j2 下載模板文件
完成該模板文件,用來生成新主機清單(主機的顯示順序沒有要求),結構如下:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.122.10 node1.example.com node1
192.168.122.20 node2.example.com node2
192.168.122.30 node3.example.com node3
192.168.122.40 node4.example.com node4
192.168.122.50 node5.example.com node5
創建劇本/home/student/ansible/newhosts.yml,它將使用上述模板在 test01 主機組的主機上
生成文件/etc/newhosts。
使用 group.all 變量生成主機清單的實現
如果需要使用 group.all
變量(包含所有主機)來生成主機清單,我們可以通過 Ansible 的內置變量和 Jinja2 模板來實現。這種方法更靈活,能自動包含清單中的所有主機。
一、修改后的模板文件 newhosts.j2
下載模板文件 curl -0 http://ansible.example.com/materials/newhosts.j2
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
{% for ycy in groups.all %}
{{ hostvars[ycy].ansible_default_ipv4.address }} {{ hostvars[ycy].ansible_fqdn }} {{ hostvars[ycy].ansible_hostname }}
{% endfor %}
模板說明
-
保留本地主機條目:
- 前兩行保持不變,包含 IPv4 和 IPv6 的本地主機配置
-
使用 group.all 遍歷所有主機:
groups.all
是 Ansible 的內置變量,包含清單中所有主機for host in groups.all
循環遍歷所有主機
二、適配的生成主機清單劇本 newhosts.yml
?
---
- name: get facthosts: all
- name: template inventoryhosts: test01tasks:- name: test1template:src: /home/student/ansible/newhosts.j2dest: /etc/newhosts
三、執行與驗證
- 執行劇本:
ansible-playbook /home/student/ansible/newhosts.yml
- 驗證結果:
登錄test01主機組中的主機,檢查生成的/etc/newhosts
文件:
cat /etc/newhosts
應該能看到包含本地主機和所有節點(node1到node5)的完整主機清單,格式與要求一致。
2,編寫劇本修改遠程文件內容
創建劇本 /home/student/ansible/newissue.yml,滿足下列要求:
1)在所有清單主機上運行,替換/etc/issue 的內容
2)對于 test01 主機組中的主機,/etc/issue 文件內容為 test01
3)對于 test02 主機組中的主機,/etc/issue 文件內容為 test02
4)對于 web 主機組中的主機,/etc/issue 文件內容為 Webserver
(一)題目需求
- 創建劇本
/home/student/ansible/newissue.yml
,在所有清單主機上運行。 - 按主機組設置
/etc/issue
內容:test01
主機組:內容為test01
test02
主機組:內容為test02
web
主機組:內容為Webserver
(二)核心技術點
group_names
變量:Ansible內置變量,存儲當前主機所屬的所有主機組,用于條件判斷。- Jinja2
if-elif
邏輯:在copy
模塊的content
參數中嵌入條件,實現基于主機組的差異化內容配置。 copy
模塊:直接通過content
參數設置文件內容,無需本地文件,簡化配置流程。
(三)完整實現步驟
1. 編寫newissue.yml
劇本
劇本通過group_names
判斷主機所屬組,結合if-elif
邏輯動態設置/etc/issue
內容,確保所有主機按組匹配正確配置。
# /home/student/ansible/newissue.yml
---
- name: Configure /etc/issue based on host grouphosts: all # 在所有清單主機上執行tasks:- name: Set /etc/issue content by host groupcopy:content: |{% if 'test01' in group_names %}test01{% elif 'test02' in group_names %}test02{% elif 'web' in group_names %}Webserver{% endif %}dest: /etc/issue # 目標文件路徑
- 劇本邏輯說明:
hosts: all
:覆蓋所有清單主機,無需分組執行,簡化操作。group_names
:無需收集facts即可使用,存儲當前主機的組列表(如test01
主機的group_names
為['test01']
)。if-elif
條件:- 優先判斷是否屬于
test01
組,是則內容為test01
; - 否則判斷是否屬于
test02
組,是則內容為test02
; - 否則判斷是否屬于
web
組,是則內容為Webserver
; - 若主機不屬于上述任何組,
/etc/issue
將為空(無else
分支,不設置默認內容)。
- 優先判斷是否屬于
copy
模塊的content
參數:直接嵌入多行文本與Jinja2邏輯,無需額外創建本地文件,高效便捷。
(四)執行與驗證
1. 執行劇本
ansible-playbook /home/student/ansible/newissue.yml
2. 分主機組驗證
-
test01
主機組:[student@master ansible]$ ansible node1 -m shell -a "cat /etc/issue" node1 | CHANGED | rc=0 >> test01
-
test02
主機組:[student@master ansible]$ ansible test02 -m shell -a "cat /etc/issue" node2 | CHANGED | rc=0 >> test02
-
web
主機組:[student@master ansible]$ ansible web -m shell -a "cat /etc/issue" node4 | CHANGED | rc=0 >> webserver node3 | CHANGED | rc=0 >> webserver
三、關鍵技術總結
技術點 | 作用 | 適用場景 | 示例代碼片段 |
---|---|---|---|
groups.all | 遍歷清單中所有主機 | 批量生成包含所有主機的配置(如主機清單) | {% for host in groups.all %}...{% endfor %} |
hostvars | 獲取指定主機的facts信息(IP、主機名) | 動態獲取主機屬性用于配置生成 | hostvars[host]['ansible_fqdn'] |
group_names | 查看當前主機所屬的所有組 | 基于主機組的差異化配置(如文件內容) | {% if 'test01' in group_names %} |
Jinja2for 循環 | 批量生成重復結構的配置 | 主機清單、批量用戶創建等 | {% for host in groups.all %}...{% endfor %} |
Jinja2if-elif 邏輯 | 基于條件動態設置內容 | 按組/按主機屬性差異化配置 | {% if 'test01' in group_names %}test01{% endif %} |
template 模塊 | 渲染包含變量/邏輯的模板文件 | 復雜配置文件生成(如主機清單、Nginx配置) | src: newhosts.j2 dest: /etc/newhosts |
copy 模塊content | 直接設置文件內容,無需本地文件 | 簡單文本文件配置(如/etc/issue、標語文件) | content: "test01" dest: /etc/issue |
四、注意事項
groups.all
使用限制:若清單中包含非node1-node5
的主機,需在模板中添加條件過濾(如{% if 'node' in host %}...{% endif %}
),避免生成多余條目。gather_facts
開關:題目一中必須開啟gather_facts: yes
,否則hostvars
無法獲取主機名;題目二無需開啟,減少執行時間。- 權限與歸屬:
/etc/newhosts
與/etc/issue
均為系統配置文件,需設置root
所有權與0644
權限,避免權限不足導致解析失敗。 - 多組歸屬優先級:若主機同時屬于多個組(如某主機既在
test01
也在web
),if-elif
會優先匹配第一個條件(即test01
),需確保主機組劃分唯一。