4 - 部署文件到受管主機
實驗環境
先通過以下命令搭建基礎環境(創建工作目錄、配置 Ansible 環境和主機清單):
# 在控制節點(controller)上創建web目錄并進入,作為工作目錄
[bq@controller ~]$ mkdir web && cd web# 創建Ansible配置文件ansible.cfg,定義連接和權限提升參數
[bq@controller web]$ cat > ansible.cfg <<'EOF'
[defaults]
remote_user = bq # 默認遠程連接用戶為bq
inventory = ./inventory # 指定主機清單文件路徑為當前目錄的inventory[privilege_escalation]
become = True # 允許提權(切換到其他用戶)
become_user = root # 提權目標用戶為root
become_method = sudo # 提權方式為sudo
become_ask_pass = False # 提權時不詢問密碼(需提前配置sudo免密)
EOF# 創建主機清單文件inventory,列出需要管理的主機
[bq@controller web]$ cat > inventory <<'EOF'
controller # 控制節點自身
node1 # 受管節點1
node2 # 受管節點2
node3 # 受管節點3
node4 # 受管節點4
EOF
修改文件并將其復制到主機
Ansible 的 Files 模塊庫包含一系列文件管理工具,能幫我們完成創建文件、復制文件、修改權限等日常 Linux 文件操作。下面逐個介紹常用模塊的用法。
file 模塊:管理文件 / 目錄的屬性(創建、刪除、改權限等)
模塊作用:可以設置文件 / 目錄的權限、所有者、SELinux 上下文等屬性,也能直接創建空文件或目錄,或者刪除不需要的文件 / 目錄(類似 Linux 的touch
、mkdir
、rm
、chmod
等命令的組合)。
實驗 1:創建文件并設置權限
實驗流程:
- 編寫 playbook,定義要創建的文件路徑、所有者、權限等信息;
- 執行 playbook,讓 Ansible 在目標主機上執行創建操作;
- 登錄受管節點,檢查文件是否創建成功,以及屬性是否符合預期。
---
- hosts: node1 # 目標主機為node1(僅在該節點執行)gather_facts: no # 不收集主機信息(減少執行時間,加快操作速度)tasks:- name: Touch a file and set permissions # 任務描述:創建文件并設置權限file:path: /tmp/testfile # 目標文件的路徑(在node1的/tmp目錄下創建testfile)owner: bq # 文件的所有者設置為bq用戶group: wheel # 文件的所屬組設置為wheel組mode: 0640 # 文件權限設置為640(所有者可讀可寫,組內用戶可讀,其他用戶無權限;必須帶前導0,否則會解析錯誤)state: touch # 狀態為"創建文件"(類似Linux的touch命令,若文件不存在則創建空文件,存在則更新修改時間)
注意:
mode
參數必須帶前導 0(如0640
)或用引號包裹(如'640'
)。如果直接寫640
,Ansible 會當作十進制處理,導致權限計算錯誤(比如變成-w-------T
這種不符合預期的權限)。
實驗 2:創建目錄
實驗流程:
- 編寫 playbook,指定要創建的目錄路徑、所有者、權限等;
- 執行 playbook,讓 Ansible 在目標主機上創建目錄;
- 登錄受管節點,驗證目錄是否存在,以及所有者、權限是否正確。
---
- hosts: node1gather_facts: notasks:- name: create directory # 任務描述:創建目錄file:path: /webdev # 目標目錄路徑(在node1的根目錄下創建webdev目錄)owner: apache # 目錄所有者設置為apache用戶(通常用于web服務相關目錄)group: apache # 目錄所屬組設置為apache組mode: 0755 # 目錄權限為755(所有者可讀可寫可執行,組和其他用戶可讀可執行;目錄需要執行權限才能進入)state: directory # 狀態為"創建目錄"(類似Linux的mkdir命令,若目錄不存在則創建,存在則不做操作)
... # YAML文件結尾的省略符號(實際編寫時必須包含)
實驗 3:刪除文件
實驗流程:
- 編寫 playbook,指定要刪除的文件路徑;
- 執行 playbook,讓 Ansible 在目標主機上刪除文件;
- 登錄受管節點,確認文件已被刪除。
---
- hosts: node1gather_facts: notasks:- name: delete file # 任務描述:刪除文件file:path: /tmp/testfile # 要刪除的文件路徑(node1上的/tmp/testfile)state: absent # 狀態為"不存在"(即刪除文件,類似Linux的rm命令;若文件不存在則不做操作)
sefcontext 模塊:管理 SELinux 的持久規則
模塊作用:用于在 SELinux 的規則庫中添加或刪除持久規則(類似 Linux 的semanage fcontext
命令)。注意:添加的規則不會立即生效,需要通過restorecon
等命令重新標注文件后才會應用。
實驗:添加 SELinux 上下文規則
實驗流程:
- 編寫 playbook,定義要設置的文件路徑和對應的 SELinux 類型;
- 執行 playbook,讓 Ansible 在目標主機上添加 SELinux 規則;
- (可選)在受管節點執行
restorecon
命令,使新添加的規則立即生效。
---
- name: 添加SELinux上下文規則hosts: node1tasks:- name: 獲取SElinux當前狀態command: getenforce # 執行getenforce命令,獲取SELinux當前模式(Enforcing/Permissive)register: selinux_running_state # 將命令結果保存到變量selinux_running_statechanged_when: false # 標記該任務不會改變主機狀態(避免Ansible誤判為"已修改")failed_when: false # 即使命令執行失敗也不標記任務失敗(兼容SELinux未開啟的情況)- name: 僅在Permissive模式下切換為Enforcingcommand: setenforce 1 # 將SELinux切換為強制模式(1表示Enforcing)ignore_errors: no # 若執行失敗則標記任務失敗(確保SELinux正常運行)- name: 安裝policycoreutils-python依賴yum: # 使用yum模塊安裝依賴包(SELinux規則管理需要該工具)name: policycoreutils-pythonstate: present # 確保包已安裝- name: 設置/samba目錄的SELinux上下文sefcontext:target: '/samba(/.*)?' # 匹配路徑:/samba目錄及所有子目錄和文件(正則表達式)setype: samba_share_t # 設置SELinux類型為samba_share_t(Samba共享目錄的專用類型)state: present # 狀態為"存在"(即添加該規則到SELinux規則庫)
注意:
sefcontext
模塊僅修改 SELinux 的規則庫,不會直接改變現有文件的 SELinux 上下文。如果要讓規則立即生效,需配合file
模塊(file
模塊會實時應用上下文)或手動執行restorecon -R /samba
(遞歸刷新 /samba 目錄的上下文)。
lineinfile 模塊:管理文件中的單行內容
模塊作用:用于修改已存在的文件,可確保文件中存在特定行、替換符合條件的行,或在指定位置插入行(類似 Linux 的sed
命令的單行處理功能)。
實驗 1:向文件添加特定行
實驗流程:
- 編寫 playbook,先創建目標文件,再指定要添加的行內容;
- 執行 playbook,讓 Ansible 在目標文件中添加行;
- 查看文件內容,驗證行是否添加成功。
---
- hosts: node1gather_facts: notasks:- name: 創建文件file: # 先創建/tmp/testfile文件(若已存在則不做操作)path: /tmp/testfilestate: touchowner: bqgroup: wheelmode: 0644- name: add line # 任務描述:添加一行內容lineinfile:path: /tmp/testfile # 目標文件路徑(node1上的/tmp/testfile)line: 'Add this line to file' # 要添加的行內容state: present # 確保該行存在(如果文件中沒有,則添加到末尾;如果已有,則不重復添加)
實驗 2:在指定位置插入行
實驗流程:
- 編寫 playbook,指定目標文件、要插入的行,以及插入位置(某行之前 / 之后);
- 執行 playbook,讓 Ansible 在指定位置插入行;
- 查看文件內容,驗證行是否插入到正確位置。
# 在"Listen 80"行之前插入"Listen 82"
---
- hosts: node1gather_facts: notasks:- name: add line before Listen 80lineinfile:path: /etc/httpd/conf/httpd.conf # 目標文件(httpd服務的配置文件)line: 'Listen 82' # 要插入的行(讓httpd監聽82端口)insertbefore: 'Listen 80' # 插入位置:在"Listen 80"行之前state: present# 在"Listen 80"行之后插入"Listen 82"
---
- hosts: node1gather_facts: notasks:- name: add line after Listen 80lineinfile:path: /etc/httpd/conf/httpd.confline: 'Listen 82'insertafter: 'Listen 80' # 插入位置:在"Listen 80"行之后state: present
...
實驗 3:替換符合條件的行
實驗流程:
- 編寫 playbook,用正則表達式匹配要替換的行,指定新內容;
- 執行 playbook,讓 Ansible 替換目標行;
- 查看文件內容,驗證匹配的行是否被正確替換。
# 替換包含"Add"的行
---
- hosts: node1gather_facts: notasks:- name: replace line # 任務描述:替換行內容lineinfile:path: /tmp/testfileregexp: 'Add' # 正則表達式:匹配所有包含"Add"的行line: 'replace' # 替換后的內容(將匹配的行替換為"replace")state: present# 注釋掉httpd的80端口監聽(在行首加#)
---
- hosts: node1gather_facts: notasks:- name: comment Listen 80lineinfile:path: /etc/httpd/conf/httpd.confline: '#Listen 80' # 替換后的內容(在原行首加#,實現注釋)regexp: '^Listen 80' # 正則表達式:匹配以"Listen 80"開頭的行(^表示行首)state: present
...
replace 模塊:批量替換文件中的內容
模塊作用:用正則表達式匹配文件中所有符合條件的內容,并一次性替換(類似 Linux 的sed -i 's/原內容/新內容/g'
命令,會替換所有匹配項,而不僅是單行)。
實驗:替換所有匹配的行
實驗流程:
- 編寫 playbook,定義目標文件、匹配規則(正則表達式)和替換內容;
- 執行 playbook,讓 Ansible 批量替換文件內容;
- 查看文件內容,驗證所有匹配的內容是否被替換。
---
- hosts: node1gather_facts: notasks:- name: replace multi line # 任務描述:批量替換行replace:path: /tmp/testfile # 目標文件(node1上的/tmp/testfile)regexp: '^Hello World.*' # 正則表達式:匹配所有以"Hello World"開頭的行(.*表示任意字符)replace: 'Hello bq' # 替換后的內容(將匹配的行替換為"Hello bq")
...
blockinfile 模塊:管理文件中的多行文本塊
模塊作用:向文件中插入、更新或刪除多行文本塊。插入的內容會自動添加標記(# BEGIN ANSIBLE MANAGED BLOCK
和# END ANSIBLE MANAGED BLOCK
),方便后續識別和更新(再次執行時會更新塊內內容,而不是重復添加)。
實驗:向文件添加多行文本塊
實驗流程:
- 編寫 playbook,定義目標文件和要添加的多行內容;
- 執行 playbook,讓 Ansible 在目標文件中插入文本塊;
- 查看文件內容,驗證文本塊是否添加,及是否包含自動生成的標記。
---
- hosts: node1gather_facts: notasks:- name: add block lines to file # 任務描述:添加多行文本塊blockinfile:path: /tmp/testfile # 目標文件(node1上的/tmp/testfile)block: | # 要添加的多行內容(| 表示保留換行符,保持原格式)line 1 in fileline 2 in fileaaline 3 in file sssstate: present # 確保文本塊存在(若不存在則添加;若已存在則更新內容)
...
執行后,文件中會添加以下內容(自動包含標記):
# BEGIN ANSIBLE MANAGED BLOCK
line 1 in file
line 2 in fileaa
line 3 in file sss
# END ANSIBLE MANAGED BLOCK
stat 模塊:獲取文件的狀態信息
模塊作用:檢索文件的詳細信息(如權限、校驗和、創建時間、大小等),類似 Linux 的stat
命令。獲取的結果可以保存到變量中,用于后續任務的條件判斷(比如根據文件是否存在決定是否執行某操作)。
實驗:獲取文件的 MD5 校驗和
實驗流程:
- 編寫 playbook,用 stat 模塊獲取文件信息并保存到變量;
- 執行 playbook,通過 debug 模塊輸出文件的 MD5 校驗和;
- 查看輸出結果,驗證是否正確獲取校驗和。
---
- hosts: node1gather_facts: notasks:- stat: # 調用stat模塊獲取文件信息path: /tmp/testfile # 目標文件(node1上的/tmp/testfile)checksum_algorithm: md5 # 指定校驗和算法為MD5(可選值:md5、sha1、sha256等)register: result # 將獲取的信息保存到變量result中- debug: # 輸出MD5校驗和msg: "/tmp/testfile md5 is {{ result.stat.checksum }}" # 通過{{ }}引用變量中的checksum值- debug: # 輸出所有文件信息(方便查看詳細內容,如權限、大小等)var: result # 直接輸出變量result的內容
...
copy 模塊:復制文件到受管節點
模塊作用:將控制節點或其他遠程節點的文件復制到受管節點,類似 Linux 的scp
命令。支持設置文件權限、所有者,以及是否備份原文件、是否強制覆蓋等。
實驗 1:復制控制節點文件到受管節點
實驗流程:
- 在控制節點準備好要復制的文件(如
/tmp/testfile
); - 編寫 playbook,指定控制節點的源文件路徑和受管節點的目標路徑;
- 執行 playbook,讓 Ansible 完成文件復制;
- 在受管節點查看目標路徑,驗證文件是否復制成功。
---
- name: 復制控制節點文件到受管節點hosts: node1 # 目標受管節點tasks:- name: 復制控制節點的/tmp/testfile到受管節點copy:src: /tmp/testfile # 控制節點上的源文件路徑(可以是絕對路徑,或相對于playbook的相對路徑)dest: /tmp/ # 受管節點上的目標目錄(復制后會保持原文件名,即/tmp/testfile)owner: root # 可選:設置文件所有者為rootgroup: root # 可選:設置文件所屬組為rootmode: 0644 # 可選:設置文件權限為644(所有者可讀可寫,組和其他用戶可讀)backup: yes # 可選:如果目標文件已存在,先創建.bak備份(如/tmp/testfile.bak)- name: 驗證文件是否復制成功stat: # 檢查受管節點上的目標文件path: /tmp/testfile # 受管節點上的目標文件路徑register: file_check # 將檢查結果保存到變量file_check- name: 輸出驗證結果debug: # 當文件存在時,輸出成功信息msg: "文件已成功復制到受管節點,路徑為: /tmp/testfile"when: file_check.stat.exists # 條件:僅當file_check.stat.exists為true時執行- name: 查看文件內容(可選)command: cat /tmp/testfile # 讀取文件內容register: file_content # 保存內容到變量file_contentwhen: file_check.stat.exists # 僅當文件存在時執行- name: 顯示文件內容debug: # 輸出文件內容msg: "文件內容:\n{{ file_content.stdout }}" # stdout存儲命令執行結果when: file_check.stat.exists # 僅當文件存在時執行
...
說明:
copy
模塊默認force: yes
(強制覆蓋目標文件),如果設置force: no
,則當目標文件已存在時不會覆蓋(保留原文件)。
實驗 2:直接寫入字符串到文件
實驗流程:
- 編寫 playbook,用
content
參數定義要寫入的文件內容; - 執行 playbook,讓 Ansible 在受管節點創建文件并寫入內容;
- 在受管節點查看文件內容,驗證是否正確。
---
- name: 直接寫入字符串到受管節點的文件hosts: node1tasks:- name: 創建文件并寫入內容copy:content: | # 要寫入的內容(| 表示保留換行,支持多行)這是第一行內容這是第二行內容可以包含多行字符串最后一行內容dest: /tmp/demo.txt # 受管節點上的目標文件路徑(會創建該文件)owner: root # 可選:設置文件所有者為rootgroup: root # 可選:設置文件所屬組為rootmode: 0644 # 可選:設置文件權限為644- name: 驗證文件內容command: cat /tmp/demo.txt # 讀取文件內容register: file_content # 保存內容到變量- name: 顯示文件內容debug: # 輸出文件內容msg: "文件內容如下:\n{{ file_content.stdout }}"
...
synchronize 模塊:基于 rsync 同步文件 / 目錄
模塊作用:封裝了rsync
工具,用于高效同步文件 / 目錄(速度比copy
模塊快,適合大量文件或大文件同步)。需要控制節點和受管節點都安裝rsync
工具。
實驗 1:同步單個文件
實驗流程:
- 確保控制節點和受管節點都安裝
rsync
(可通過yum install -y rsync
安裝); - 編寫 playbook,指定控制節點的源文件路徑和受管節點的目標路徑;
- 執行 playbook,讓 Ansible 通過 rsync 同步文件;
- 在受管節點驗證文件是否同步成功。
---
- name: 使用synchronize模塊同步單個文件hosts: node1 # 受管節點tasks:# 步驟1:確保控制節點和受管節點都安裝rsync- name: 安裝rsync工具yum:name: rsyncstate: present # 確保rsync已安裝# 步驟2:同步控制節點的文件到受管節點- name: 同步單個文件到受管節點synchronize:src: /path/on/control/node/source.txt # 控制節點上的源文件路徑(替換為實際路徑)dest: /path/on/remote/node/destination.txt # 受管節點上的目標路徑(替換為實際路徑)mode: push # 同步方向:從控制節點推送到受管節點(默認就是push,可省略)# 步驟3:驗證文件是否同步成功- name: 檢查受管節點上的目標文件stat:path: /path/on/remote/node/destination.txt # 受管節點上的目標文件路徑register: file_status # 保存檢查結果- name: 顯示驗證結果debug: # 當文件存在時,輸出成功信息msg: "文件同步成功,路徑:{{ file_status.stat.path }}"when: file_status.stat.exists # 僅當文件存在時執行
...
實驗 2:同步目錄
實驗流程:
- 在控制節點準備要同步的目錄(如
/etc/sysconfig
); - 編寫 playbook,指定控制節點的源目錄和受管節點的目標路徑;
- 執行 playbook,讓 Ansible 同步目錄;
- 在受管節點查看目標路徑,驗證目錄內容是否同步成功。
---
- hosts: node1gather_facts: noremote_user: root # 同步系統目錄可能需要root權限tasks:- name: synchronize directory # 任務描述:同步目錄synchronize:src: /etc/sysconfig # 控制節點上的源目錄(/etc/sysconfig,包含系統服務配置)dest: /tmp/ # 受管節點上的目標路徑(同步后會在/tmp下創建sysconfig子目錄,即/tmp/sysconfig)
...
fetch 模塊:從受管節點獲取文件到控制節點
模塊作用:將受管節點的文件復制到控制節點,常用于收集日志、配置文件等。文件會按 “控制節點目標路徑 / 受管節點名 / 原文件路徑” 的結構保存(方便區分不同節點的文件)。
實驗:從受管節點獲取文件
實驗流程:
- 編寫 playbook,指定受管節點的源文件路徑和控制節點的保存路徑;
- 執行 playbook,讓 Ansible 從受管節點復制文件到控制節點;
- 在控制節點查看保存路徑,驗證文件是否按預期結構保存。
---
- hosts: node1gather_facts: notasks:- name: fetch file from remote node # 任務描述:獲取遠程文件fetch:src: /tmp/testfile # 受管節點上的源文件路徑(node1的/tmp/testfile)dest: /tmp # 控制節點上的保存路徑
...
執行后,文件會保存在控制節點的/tmp/node1/tmp/testfile
(路徑結構:目標路徑/受管節點名/原文件絕對路徑
)。
使用 JINJA2 模板部署文件
Jinja2 模板是一種帶變量和邏輯的文件(比如包含{{ 變量 }}
、{% 循環/條件 %}
等)。通過 Ansible 的template
模塊部署到受管節點時,Ansible 會自動替換變量、執行邏輯,生成個性化的配置文件。
基礎示例:部署動態網頁
實驗流程:
- 創建 Jinja2 模板文件(包含變量,如
{{ ansible_fqdn }}
); - 編寫 playbook,用
template
模塊部署模板到受管節點; - 執行 playbook,在受管節點查看生成的文件,驗證變量是否被正確替換。
步驟 1:編寫 playbook(deploy_web.yml
)
---
- name: Enable intranet services # 部署web服務hosts: node1tasks:- name: ensure latest version of httpd # 安裝httpd服務(用于提供web服務)yum:name: httpdstate: latest # 確保安裝最新版本- name: deploy test html page # 部署動態主頁template:src: index.html.j2 # 控制節點上的Jinja2模板文件(與playbook同目錄)dest: /var/www/html/index.html # 受管節點上的目標文件(httpd的默認主頁路徑)- name: start and enable httpd # 啟動httpd并設置開機自啟service:name: httpdenabled: true # 開機自啟state: restarted # 重啟服務(使配置生效)
步驟 2:創建模板文件(index.html.j2
)
在 playbook 同目錄下創建index.html.j2
,內容如下:
Welcome to {{ ansible_fqdn }} # {{ }} 中的變量會被替換為受管節點的完全主機名(如node1.bq.cloud)
步驟 3:執行與驗證
執行 playbook 后,受管節點的/var/www/html/index.html
內容會自動替換變量,變成:
Welcome to node1.bq.cloud # 假設node1的完全主機名是node1.bq.cloud
進階示例:部署 SSH 配置文件
實驗流程:
- 創建含多個變量的 Jinja2 模板(如
{{ ssh_port }}
、{{ root_allowed }}
); - 在 playbook 中定義模板變量的值;
- 部署模板到受管節點,驗證生成的配置文件是否正確替換變量。
步驟 1:編寫模板(sshd_config.j2
)
# {{ ansible_managed }} # 會替換為"Ansible managed"(可在ansible.cfg中自定義)
# DO NOT MAKE LOCAL MODIFICATIONS TO THIS FILE AS THEY WILL BE LOST
Port {{ ssh_port }} # SSH端口(使用變量ssh_port的值)
ListenAddress {{ ansible_facts['default_ipv4']['address'] }} # 監聽IP(使用受管節點的默認IPv4地址)HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
SyslogFacility AUTHPRIV
PermitRootLogin {{ root_allowed }} # 是否允許root登錄(使用變量root_allowed的值)
AllowGroups {{ groups_allowed }} # 允許登錄的組(使用變量groups_allowed的值)AuthorizedKeysFile /etc/.rht_authorized_keys .ssh/authorized_keys
PasswordAuthentication {{ passwords_allowed }} # 是否允許密碼認證(使用變量passwords_allowed的值)
步驟 2:編寫 playbook(deploy_ssh.yml
)
---
- name: config sshd service # 配置SSH服務hosts: node1vars: # 定義模板中用到的變量(鍵值對形式)ssh_port: 1022 # SSH端口設為1022(非默認端口更安全)root_allowed: "yes" # 允許root登錄groups_allowed: wheel # 允許wheel組用戶登錄passwords_allowed: "yes" # 允許密碼認證ansible_managed: "Ansible managed: do not edit manually" # 覆蓋默認的ansible_managed變量tasks:- name: deploy sshd config # 部署模板template:src: sshd_config.j2 # 控制節點上的模板文件dest: /root/sshd_config # 先部署到/root目錄驗證(實際使用時應替換為/etc/ssh/sshd_config)
Jinja2 模板語法詳解
模板中通過特殊符號包裹變量和邏輯,常用符號:
{{ 變量/表達式 }}
:輸出變量或表達式結果(如{{ 1+1 }}
輸出2
);{% 控制語句 %}
:用于循環(for
)、條件判斷(if
)等(如{% for user in users %}
);{# 注釋 #}
:模板內的注釋,不會出現在生成的文件中(僅用于模板編寫者參考)。
1. for 循環:遍歷列表 / 字典
作用:重復輸出列表或字典中的每個元素,類似編程語言中的for
循環。
示例 1:遍歷用戶列表
-
playbook 中定義變量:
vars:users: # 定義一個用戶列表- tom- jack- Snoopy- lucy
-
模板文件(testfile.j2):
{% for user in users %} # 遍歷users列表,每次取一個元素賦值給user {{ user }} # 輸出當前user的值 {% endfor %} # 結束循環
-
生成的文件內容:
tom jack Snoopy lucy
示例 2:帶索引的循環
-
模板文件(testfile.j2):
{% for user in users %} {{ loop.index }} - {{ user }} # loop.index是循環的序號(從1開始) {% endfor %}
-
生成的文件內容:
1 - tom 2 - jack 3 - Snoopy 4 - lucy
示例 3:生成主機列表文件
需求:在/etc/myhosts
中添加所有主機的 IP、完全主機名和主機名。
-
主機清單(
inventory
):[controllers] controller[dev] node1[test] node2[prod] node3 node4
-
playbook(
deploy_hosts.yml
):--- - name: deploy /etc/myhostshosts: all # 必須對所有主機執行,才能收集所有主機的信息tasks:- name: generate /etc/myhoststemplate:src: hosts.j2dest: /etc/myhostswhen: inventory_hostname in groups.dev # 只部署到dev組主機(這里指node1) ...
-
模板文件(
hosts.j2
):127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6{% for server in groups.all %} # 遍歷所有主機(groups.all是包含所有主機的列表) # hostvars[server] # 用于獲取其他主機的信息(如IP、主機名) {{ hostvars[server].ansible_default_ipv4.address }} {{ hostvars[server].ansible_fqdn }} {{ hostvars[server].ansible_hostname }} {% endfor %}
2. if 條件判斷:根據條件輸出內容
作用:滿足條件時才輸出特定內容,類似if-else
語句。
示例 1:判斷變量是否存在
{% if PORT is defined %} # 如果PORT變量已定義(在playbook中設置過)
bind-address=0.0.0.0:{{ PORT }} # 使用PORT變量的值(如0.0.0.0:3307)
{% else %} # 否則(PORT未定義)
bind-address=0.0.0.0:3306 # 使用默認端口3306
{% endif %}
示例 2:循環中過濾元素
{% for num in [7,1,5,3,9] if num>3 %} # 只遍歷大于3的數字(過濾條件)
{{ num }}
{% endfor %}
生成的文件內容:
7
5
9
3. 表達式:支持運算和比較
比較運算
{{ 1 == 1 }} # 等于 → True
{{ 2 != 2 }} # 不等于 → False
{{ 2 > 1 }} # 大于 → True
{{ 2 <= 1 }} # 小于等于 → False
邏輯運算
{{ (2 > 1) or (1 > 2) }} # 或 → True(只要一個條件成立)
{{ (2 > 1) and (1 > 2) }} # 與 → False(兩個條件都成立才為True)
{{ not true }} # 非 → False(取反)
算術運算
{{ 3 + 2 }} # 加 → 5
{{ 3 * 5 }} # 乘 → 15
{{ 2 **3 }} # 冪 → 8(2的3次方)
{{ 7 // 5 }} # 整除 → 1(只保留整數部分)
{{ 17 % 5 }} # 取余 → 2(17除以5的余數)
4. 過濾器:格式化輸出結果
過濾器用于對變量或表達式的結果進行處理(如轉換大小寫、排序等),格式為{{ 變量 | 過濾器 }}
。
字符串處理
{{ 'hello' | upper }} # 轉大寫 → HELLO
{{ 'HELLO' | lower }} # 轉小寫 → hello
{{ 'hello' | capitalize }} # 首字母大寫 → Hello
{{ ' hello ' | trim }} # 去除首尾空格 → hello
列表處理
{{ [3,1,7] | sort }} # 升序排序 → [1, 3, 7]
{{ [3,1,7] | max }} # 最大值 → 7
{{ [3,1,7] | sum }} # 求和 → 11
{{ [3,1,7] | join(',') }} # 用逗號連接 → 3,1,7
數字處理
{{ '123' | int }} # 轉整數 → 123(將字符串轉為整數)
{{ 'abc' | int(default=0) }} # 轉換失敗用默認值 → 0(字符串無法轉整數時返回0)
{{ 12.5 | round }} # 四舍五入 → 13
{{ -5 | abs }} # 絕對值 → 5
ansible_managed 變量
用于在模板中添加 “此文件由 Ansible 管理” 的注釋,提醒用戶不要手動修改(否則下次執行 playbook 可能被覆蓋)。默認值在ansible.cfg
中定義:
[defaults]
ansible_managed = Ansible managed # 可自定義,如"Ansible managed: do not edit manually"
在模板中引用:
# {{ ansible_managed }} # 會替換為上述配置的值(如"Ansible managed: do not edit manually")
如涉及版權問題請聯系作者處理!!!!