3.Ansible編寫和運行 Playbook
Playbook 介紹
如果把 Ansible 的ad-hoc命令
比作 “一次性腳本”(適合臨時執行單個簡單任務),那么Playbook
就是 “可重復執行的程序”(適合復雜、多步驟的管理流程)。
舉個例子:
-
用 ad-hoc 創建一個用戶可以這樣寫(臨時執行一次):
# 在node1上創建用戶newbie,UID為4000 [bq@controller web]$ ansible -m user -a "name=newbie uid=4000 state=present" node1
-
但如果需要在多臺主機上重復這個操作,或者后續還要修改用戶屬性,用 Playbook 更合適。把上述操作寫成 Playbook(保存為
create_user.yml
):--- - name: 統一配置用戶(描述這個Play的作用)hosts: node1 # 目標主機(從清單中選)tasks:- name: 確保newbie用戶存在且UID為4000(描述這個任務的作用)user: # 使用user模塊name: newbie # 用戶名uid: 4000 # UIDstate: present # 狀態:存在(present)或刪除(absent) ...
Playbook 本質是一個YAML格式的文本文件
(通常以.yaml
或.yml
為擴展名),核心作用是:
- 按順序定義多個
Play
(每個 Play 是一組針對特定主機的操作); - 每個 Play 包含多個
任務
(按順序執行的具體操作,如安裝軟件、修改配置等); - 可重復執行,結果可預測(第二次執行相同 Playbook 時,未變更的任務會顯示 “OK”)。
Vim 編輯器設置
用 Vim 編輯 Playbook 時,建議配置自動縮進和 Tab 轉換,避免格式錯誤。
在用戶家目錄的.vimrc
文件中添加配置:
# 方法1:全局生效(所有文件都用2個空格縮進,顯示行號)
set ai ts=2 number# 方法2:僅YAML文件生效(更推薦)
autocmd FileType yaml set ai ts=2 # 打開YAML文件時,自動啟用2個空格縮進
配置說明:
ai
:自動縮進(新行繼承上一行的縮進);ts=2
:Tab 鍵自動轉換為 2 個空格;number
:顯示行號(方便定位錯誤)。
YAML 格式基礎
基本格式:
--- # YAML 文件起始標識(建議添加)
- name: 第一個Play(描述該Play的作用,如"部署Web服務")hosts: web_servers # 目標主機/主機組(來自inventory)remote_user: devops # 遠程執行用戶become: yes # 是否提權(yes/no)become_method: sudo # 提權方式(默認sudo)become_user: root # 提權后的目標用戶(通常為root)vars: # 定義變量(可在任務中引用)app_name: "myapp"app_port: 8080tasks: # 任務列表(按順序執行)- name: 任務1描述(如"安裝依賴包")yum: # 模塊名稱(根據操作選擇,如yum/apt/copy等)name: "{{ item }}" # 引用變量或列表項state: presentloop: # 循環執行(可選)- package1- package2- name: 任務2描述(如"復制配置文件")copy:src: ./local_config.conf # 本地文件路徑dest: /etc/remote_config.conf # 目標路徑mode: '0644' # 文件權限notify: # 觸發處理器(配置變更時執行,可選)- 重啟服務handlers: # 處理器(僅在被notify觸發時執行)- name: 重啟服務systemd:name: "{{ app_name }}"state: restarted- name: 第二個Play(可定義多個Play,按順序執行)hosts: db_serverstasks:- name: 數據庫相關任務# 具體模塊和參數...
核心結構說明:
- Play 定義:每個
- name: ...
開頭的塊是一個 Play,包含對特定主機的操作 - 主機與用戶配置:
hosts
指定目標,remote_user
和become
控制執行權限 - 變量(vars):集中定義可復用的值,通過
{{ 變量名 }}
引用 - 任務(tasks):具體操作列表,每個任務包含
name
(描述)、模塊和參數 - 處理器(handlers):用于響應任務通知的操作(如配置變更后重啟服務)
編寫時需注意:
-
嚴格使用空格縮進(推薦 2 個空格),同一層級縮進必須一致
-
注釋用
#
(#
后面的內容會被忽略,建議多寫注釋提高可讀性) -
模塊參數需符合對應模塊的語法要求(可通過
ansible-doc 模塊名
查看文檔) -
可通過
ansible-playbook --syntax-check 劇本名.yml
檢查語法正確性 -
冒號右側(
key: value
):大部分可修改,少數不可,右側是 “值”,用于填充具體內容、配置或引用,能否修改取決于其是否為固定枚舉值。-
- 可修改:動態值或自定義內容
大部分情況下,右側是用戶根據需求設置的具體值,可自由修改:
- 字符串 / 路徑:如
hosts: node1
中,node1
可改為其他主機名(如node2
、all
);home: /home/joe
中,/home/joe
可改為/home/bob
。 - 變量引用:如
name: "{{ user }}"
中,{{ user }}
會動態引用變量值,變量值修改后這里也會同步變化。 - 文本內容:如
debug
模塊的msg
內容,username is {{ user }}
可改為任意文本(如用戶名為 {{ user }}
)。
- 不可修改:固定枚舉值
部分模塊參數的右側值是 Ansible 規定的枚舉值(只能從指定選項中選擇),不可隨意修改,否則會報錯。
例如:user
模塊的state
參數:只能是present
(創建)或absent
(刪除),不能改為create
或delete
。service
模塊的state
參數:只能是started
、stopped
、restarted
等,不可自定義其他值。
-
-
冒號(
:
)左側的內容固定的關鍵字不可修改,有些是自定義名稱(可修改)**。具體區分如下:-
不可修改:固定關鍵字
這些是 Ansible 或 YAML 語法規定的固定 “鍵”,必須嚴格按原樣書寫,修改后會導致語法錯誤或功能失效。
例如:-
Playbook 結構關鍵字:
name
(定義任務 / 劇本名稱)、hosts
(指定目標主機)、vars
(定義變量)、tasks
(定義任務列表)等。
例:hosts: node1
中,hosts
不可修改,修改為host_list: node1
會報錯。 -
模塊名:如
user:
(用戶管理模塊)、debug:
(調試模塊),是 Ansible 內置模塊的唯一標識,不可修改。
例:user: name: joe
中,user
不可修改,改為add_user: name: joe
會提示 “模塊不存在”。 -
模塊參數:模塊內置的配置項,如
user
模塊的name
(用戶名)、home
(家目錄)、state
(狀態);debug
模塊的msg
(輸出信息)等。
例:name: "{{ user }}"
中,name
不可修改,是user
模塊規定的參數名,改為username: "{{ user }}"
會導致參數無效。 -
可修改:自定義標識 / 變量名
如果左側是用戶自定義的變量名、標簽或標識符,則可以修改,只要符合 YAML 命名規范(字母、數字、下劃線,不建議用特殊字符)。
例如:- 自定義變量名:
vars
下的變量,如user: joe
中,user
是自定義變量名,可改為username: joe
(后續引用需同步改為{{ username }}
)。 - 自定義標簽:如果使用
tags
關鍵字,標簽名可自定義,如tags: create_user
中,create_user
可修改為add_user
等。
- 自定義變量名:
2. 可修改:自定義標識 / 變量名
如果左側是用戶自定義的變量名、標簽或標識符,則可以修改,只要符合 YAML 命名規范(字母、數字、下劃線,不建議用特殊字符)。
例如:- 自定義變量名:
vars
下的變量,如user: joe
中,user
是自定義變量名,可改為username: joe
(后續引用需同步改為{{ username }}
)。 - 自定義標簽:如果使用
tags
關鍵字,標簽名可自定義,如tags: create_user
中,create_user
可修改為add_user
等。
-
-
實驗環境搭建
下面通過一個 “部署 Web 服務” 的案例,實際演示 Playbook 的編寫和運行。先搭建實驗環境:
1:創建工作目錄
# 創建web目錄(用于存放Playbook相關文件),并進入該目錄
[bq@controller ~]$ mkdir web && cd web
2:配置 Ansible 默認參數(ansible.cfg)
創建ansible.cfg
文件,定義遠程用戶、主機清單路徑、提權方式等(避免每次執行命令重復輸入參數):
[bq@controller web]$ cat > ansible.cfg <<'EOF'
[defaults]
remote_user = bq # 默認遠程登錄用戶(要在目標主機存在)
inventory = ./inventory # 主機清單路徑(指定為當前目錄的inventory文件)[privilege_escalation]
become = True # 默認啟用提權(執行需要root權限的操作)
become_user = root # 提權到root用戶
become_method = sudo # 提權方式為sudo
become_ask_pass = False # 提權時不詢問密碼(需提前配置sudo免密)
EOF
3:定義主機清單(inventory)
創建inventory
文件,列出需要管理的主機(每行一個主機名,需確保 controller 能通過 SSH 連接這些主機):
[bq@controller web]$ cat > inventory <<'EOF'
controller # 控制節點(可選,可用于本地測試)
node1 # 被管理節點1(示例中主要操作的節點)
node2 # 被管理節點2(預留,本案例暫不使用)
node3
node4
EOF
Playbook 編寫(實戰案例)
我們編寫一個 Playbook(web_deploy.yml
),實現以下功能:
- 在 node1 上安裝并啟動 httpd(Web 服務)和 firewalld(防火墻);
- 配置防火墻允許 http 訪問;
- 創建一個測試網頁;
- 在本地(controller)驗證 node1 的 Web 服務是否可用。
Playbook 示例
playbook.yaml 內容如下:
# YAML文件起始標識(建議添加,明確文件格式)
---# 第一個Play:部署Web服務到node1
- name: 在node1上部署Web服務(描述Play的作用)hosts: node1 # 目標主機(從inventory中選node1)# 提權配置(覆蓋ansible.cfg的默認設置,這里使用默認即可)# remote_user: bq# become: Truetasks: # 任務列表(按順序執行)# 任務1:安裝httpd和firewalld(確保是最新版本)- name: 安裝最新版的httpd和firewalldyum: # 使用yum模塊(適用于CentOS/RHEL系統)name: # 要安裝的軟件包列表- httpd # Web服務軟件- firewalld # 防火墻軟件state: latest # 狀態:確保為最新版本(若已安裝則升級)# 任務2:創建測試網頁- name: 生成測試網頁內容copy: # 使用copy模塊(復制內容到目標文件)content: "Welcome to bq WebSite!\n" # 網頁內容(\n是換行)dest: /var/www/html/index.html # 目標文件路徑(httpd默認首頁路徑)mode: '0644' # 文件權限(所有者可讀可寫,其他用戶只讀)# 任務3:啟動并設置firewalld開機自啟- name: 確保firewalld服務啟動并開機自啟service: # 使用service模塊(管理系統服務)name: firewalld # 服務名稱enabled: true # 開機自啟(true=啟用,false=禁用)state: started # 服務狀態(started=啟動,stopped=停止,restarted=重啟)# 任務4:防火墻放行http服務- name: 配置防火墻允許http訪問(80端口)firewalld: # 使用firewalld模塊(管理防火墻規則)service: http # 預定義服務(對應80端口)permanent: true # 規則永久生效(重啟防火墻后不丟失)state: enabled # 啟用規則immediate: yes # 立即生效(無需等待防火墻重啟)# 任務5:啟動并設置httpd開機自啟- name: 確保httpd服務啟動并開機自啟service:name: httpdenabled: truestate: started# 第二個Play:在本地驗證Web服務
- name: 驗證node1的Web服務是否可用hosts: localhost # 目標主機為本地(controller自身)become: no # 不需要提權(本地訪問無需root)tasks:- name: 訪問node1的Web頁面,檢查是否正常響應uri: # 使用uri模塊(發送HTTP請求并驗證)url: http://node1 # 要訪問的URL(node1的http服務)return_content: yes # 返回頁面內容(可選,用于調試)status_code: 200 # 期望的HTTP狀態碼(200=成功)# YAML文件結束標識(可省略)
...
核心結構說明
- Play:每個
- name: ...
開頭的塊是一個 Play,用于定義對特定主機的操作(如第一個 Play 針對 node1,第二個針對localhost)。 - 任務(tasks):每個任務是具體的操作(如安裝軟件、修改配置),由
name
(描述)和模塊(如yum
、service
)組成。 - 模塊:Ansible 的功能單元,每個模塊實現特定功能(如
yum
管理軟件包,service
管理服務),參數需符合模塊要求(可通過ansible-doc 模塊名
查文檔,如ansible-doc yum
)。
Playbook 運行與驗證
1:檢查語法
在運行前先檢查 Playbook 語法是否正確:
[bq@controller web]$ ansible-playbook web_deploy.yml --syntax-check
# 成功輸出:playbook: web_deploy.yml(無報錯說明語法正確)
2:空運行
用-C
選項模擬運行,不實際修改,查看會執行哪些操作(適合測試):
[bq@controller web]$ ansible-playbook web_deploy.yml -C
# 輸出中會顯示每個任務的預期狀態(如"changed"表示會修改,"ok"表示無變化)
3:實際運行
[bq@controller web]$ ansible-playbook web_deploy.yml#首次運行成功的輸出(部分):
PLAY [在node1上部署Web服務] ******************************************
TASK [Gathering Facts] **********************************************
ok: [node1]TASK [安裝最新版的httpd和firewalld] ************************************
changed: [node1] # 狀態為changed,表示執行了安裝操作...(中間省略其他任務)PLAY [驗證node1的Web服務是否可用] **************************************
TASK [Gathering Facts] **********************************************
ok: [localhost]TASK [訪問node1的Web頁面,檢查是否正常響應] ******************************
ok: [localhost] # 狀態為ok,表示訪問成功(狀態碼200)PLAY RECAP **********************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 ...
node1 : ok=5 changed=3 ...# 第二次運行時,所有任務會顯示ok(因為系統狀態已符合預期,無需修改),這體現了 Ansible 的 "冪等性"(重復執行結果一致)。
4:手動驗證
在 controller 上直接訪問 node1 的網頁,確認內容正確:
[bq@controller web]$ curl http://node1
Welcome to bq WebSite! # 輸出預期內容,說明部署成功
運行選項補充
-
詳細輸出:用-v系列選項查看更多信息(調試時常用):
ansible-playbook web_deploy.yml -v # 顯示任務結果 ansible-playbook web_deploy.yml -vv # 顯示任務結果+配置 ansible-playbook web_deploy.yml -vvv- #包含關于與受管主機連接的信息。 ansible-playbook web_deploy.yml -vvvv- #增加了連接插件相關的額外詳細程度選項,包括受管主機上用于執行腳本的用戶,以及所執行的腳本。
-
指定主機:用
-l
選項僅對部分主機執行(覆蓋 Play 中的hosts配置):ansible-playbook web_deploy.yml -l node1 # 僅在node1上執行
YAML 注釋
在 YAML中, 編號或井號符號(#)右側的所有內容都是注釋。如果注釋的左側有內容, 請在該編號符號的前面加一個空格。注釋可用于提高可讀性。
示例:
# This is YAML comment
Some data # This is also a YAML comment
注意事項與不足
本案例的潛在問題
- 環境依賴:
- 假設目標主機是 CentOS/RHEL(使用
yum
模塊),若為 Ubuntu 需替換為apt
模塊; - 需確保 controller 能解析
node1
的主機名(可在/etc/hosts
中配置); become_ask_pass = False
要求目標主機的sudo
已配置免密(否則會提權失敗,需手動輸入密碼)。
- 假設目標主機是 CentOS/RHEL(使用
- 錯誤處理:
- 未添加錯誤判斷(如軟件安裝失敗、服務啟動失敗時,Playbook 會直接報錯終止);
- 可通過
ignore_errors: yes
忽略特定錯誤,或failed_when
定義失敗條件。
- 擴展性:
- 若需部署到多臺主機,需在
inventory
中定義主機組(如[web_servers]
),并在 Play 中設置hosts: web_servers
; - 重復任務可提取為變量(
vars
)或角色(Roles),減少代碼冗余。
- 若需部署到多臺主機,需在
編寫 Playbook 的最佳實踐
- 多寫注釋:每個 Play 和任務都用
name
描述作用,復雜邏輯加#
注釋; - 先測試再執行:用
--syntax-check
和-C
驗證后再實際運行; - 利用冪等性:盡量使用支持冪等的模塊(如
yum
的state: present
不會重復安裝); - 拆分復雜任務:一個 Playbook 只做一件事(如部署 Web、部署數據庫分開),便于維護。
Playbook 提權
在playbook中指定此關鍵字將覆蓋/etc/ansible/ansible.cfg文件中的設置特權升級屬性
-
remote_user,指定ssh用戶
-
become,啟用或禁用特權升級
-
become_method,啟用特權升級的方法
-
become_user,特殊升級的帳戶
---
- name: Enable intranet serviceshosts: node1remote_user: bqbecome: truebecome_method: sudobecome_user: roottasks:- name: latest version of httpd and firewalld installedyum:name:- httpd- firewalldstate: latest
YAML屬性補充
YAML 單行字符串
YAML中的字符串通常不需要放在引號里,即使字符串中包含空格。
字符串也可以用雙引號或單引號括起。
this is a string
'this is another string'
"this is yet another a string"
YAML 多行字符串
-
可以使用豎線(I)字符表示,保留字符串中的換行字符。
示例:
--- - name: test stringhosts: node1tasks:- name: test string# 用戶顯示變量值或者字符串debug:msg: |Example Company123 Main StreetAtlanta, GA 30303
-
也可以使用大于號(>)字符表示換行字符。執行時換行符使用空格代替,并且行內的引導空白將被刪除。
示例:
--- - name: test stringhosts: node1tasks:- name: test string# 用戶顯示變量值或者字符串debug:msg: >This is an exampleof a long string,that will becomea single sentence once folded.
這種方法通常用于將很長的字符串在空格字符處斷行,使它們跨占多行來提高可讀性。
YAML 字典
一組鍵值對的集合,又稱為映射(mapping)和哈希(hashes)。
以縮進塊的形式編寫鍵值對集合,如下方所示:user屬性是字典格式,是多個鍵值對集合。
user:name: bq uid: 1088state: absent
字典也可以使用以花括號括起的內聯塊格式編寫,如下方所示:
user: {name: bq, uid: 1088, state: absent}
大多數情形中應避免內聯塊格式,其可讀性較差。不過,當playbook中包含角色列表時,使用這種語法,更加容易區分play中包含的角色和傳遞給角色的變量。
某些 playbook 可能使用較舊的簡寫(shorthand)格式,通過將模塊的鍵值對放在與模塊名稱相同的行上來定義任務。
示例:
- name: shorhand formuser: name=bq uid=1088 state=absent
普通格式:
- name: shorhand formuser:name: bq uid: 1088state: absent
兩者格式總結:
- 通常您應避免簡寫格式,而使用普通格式。
- 普通格式的行數較多,更容易操作。任務的關鍵字垂直堆疊,更容易區分。 閱讀play時,您的眼睛直接向下掃視,左右運動較少。
- 普通格式是原生的YAML,現代文本編輯器中的語法突出顯示工具可以識別,簡寫形式則不支持。
- 可能會在文檔和他人提供的舊playbook中看到這種語法,而且這種語法仍然可以發揮作用。
YAML 列表
一組按次序排列的值,又稱為序列(sequence)和數組(array)。
以縮進塊的形式編寫的鍵值對集合,如下方所示:
- name: latest version of httpd and firewalld installedyum:name:- httpd- firewalldstate: latest
- name: test html page is installedcopy:content: "Welcome to the example.com intranet!\n"dest: /var/www/html/index.html
以上有兩個任務,每個任務都是多個鍵值對描述。其中yum模塊操作的軟件包是一個簡單的名稱列表。
內聯格式:
name: [httpd, firewalld]
盡量避免內聯格式。