Ansible 的核心組件之一是劇本文件。Ansible 使用 playbook 文件來定義復(fù)雜的任務(wù),這些任務(wù)在用戶參與有限的情況下針對(duì)受管節(jié)點(diǎn)執(zhí)行。在本指南中,您將學(xué)習(xí)如何創(chuàng)建和使用 Ansible 劇本文件在托管節(jié)點(diǎn)上執(zhí)行任務(wù)。
什么是 Ansible 劇本?
劇本本質(zhì)上是一個(gè)包含一個(gè)或多個(gè)劇本的 YAML 文件。play 是針對(duì)/etc/ansible/hosts文件中指定的托管主機(jī)執(zhí)行的一組有序任務(wù)。劇本中的每個(gè)劇本都代表一個(gè)獨(dú)特的任務(wù),具有目標(biāo)主機(jī)的特定于環(huán)境的參數(shù)。
劇本非常靈活,可以在多個(gè)服務(wù)器上無限期重復(fù)使用以執(zhí)行相同的任務(wù)。Ansible playbook 通常用于服務(wù)器配置、網(wǎng)絡(luò)設(shè)備管理和應(yīng)用程序部署任務(wù)。
先決條件
要遵循本指南,您應(yīng)該:
- 安裝了 Ubuntu 20.04 操作系統(tǒng)和 Ansible 的控制節(jié)點(diǎn)。如果您之前沒有安裝過 Ansible,請(qǐng)按照我們關(guān)于如何在 Ubuntu 20.04 上安裝和配置 Ansible的指南進(jìn)行操作。
- 在控制節(jié)點(diǎn)的主機(jī)文件中配置的單個(gè)受管節(jié)點(diǎn)將用于運(yùn)行劇本任務(wù)。
創(chuàng)建 Ansible 劇本
讓我們從制作和運(yùn)行我們的第一個(gè) Ansible 劇本開始。在控制節(jié)點(diǎn)上的/etc/ansible/目錄中創(chuàng)建一個(gè)簡(jiǎn)單的 YAML 文件,如下所示:
sudo vim /etc/ansible/playbook-01.yml
現(xiàn)在使用以下代碼填充劇本文件:
--- - name: A simple playbook file hosts: all tasks: - name: Print a sample message debug: msg: Hello World. Welcome to Ansible playbooks!
標(biāo)記 YAML 文件的---開始。
該name指令的第一個(gè)實(shí)例指定了該劇的名稱。第二個(gè)實(shí)例指定任務(wù)的名稱。
該hosts指令指定將在其上執(zhí)行劇本的目標(biāo)主機(jī)。在此示例中,劇本將在清單文件中指定的所有主機(jī)上運(yùn)行。要定位特定主機(jī),請(qǐng)?zhí)峁┲鳈C(jī)的 IP 地址或域名。
該tasks指令是要在目標(biāo)主機(jī)上執(zhí)行的任務(wù)列表。在這個(gè)劇本中,我們有一個(gè)任務(wù)將語句打印到標(biāo)準(zhǔn)輸出。
關(guān)鍵字是 Ansible 自帶的debug內(nèi)置模塊,在 Playbook 運(yùn)行時(shí)打印語句。此外,在不停止 playbook 的情況下調(diào)試語句和變量時(shí),它會(huì)派上用場(chǎng)。該debug模塊帶有一些選項(xiàng),例如msg和var。該msg選項(xiàng)指定要打印到標(biāo)準(zhǔn)輸出的字符串。
執(zhí)行 Ansible 劇本
要運(yùn)行劇本,請(qǐng)使用ansible-playbook如下所示的命令:
ansible-playbook /path/to/playbook_file
在我們的示例中,您應(yīng)該運(yùn)行以下命令:
ansible-playbook /etc/ansible/playbook-01.yml
在 playbook 執(zhí)行期間,您應(yīng)該會(huì)看到以下輸出:
請(qǐng)注意,執(zhí)行了兩項(xiàng)任務(wù),盡管我們?cè)?Playbook 文件中只定義了一項(xiàng)。
第一個(gè)任務(wù)收集有關(guān)受管節(jié)點(diǎn)的事實(shí)。Ansible facts 是指以 JSON 格式呈現(xiàn)的特定于主機(jī)的系統(tǒng)數(shù)據(jù),例如 BIOS 信息、系統(tǒng)日期和時(shí)間、操作系統(tǒng)類型和版本以及 IP 地址。它還包括硬件數(shù)據(jù),例如塊設(shè)備、CPU、RAM 和交換空間等等。
第二個(gè)任務(wù)按照 playbook 文件中的指定將一條簡(jiǎn)單消息打印到標(biāo)準(zhǔn)輸出。ok=2表示成功執(zhí)行了兩個(gè)任務(wù)。
如果您想獲得所有 Ansible 事實(shí)的列表,請(qǐng)執(zhí)行以下命令:
ansible -m setup all
Ansible 劇本模塊
Ansible 模塊是獨(dú)立的可重復(fù)使用的 Python 腳本,在 Playbook 中被引用以幫助在托管節(jié)點(diǎn)上執(zhí)行特定任務(wù)。Ansible 模塊可以自動(dòng)執(zhí)行托管節(jié)點(diǎn)上的各種任務(wù),包括包管理、服務(wù)管理、文件管理等等。
在本節(jié)中,我們將演示如何通過將模塊合并到 Playbook 中來完成各種系統(tǒng)管理任務(wù)。
包管理模塊
管理軟件包是一項(xiàng)基本的系統(tǒng)管理任務(wù)。它專門處理在 Linux 服務(wù)器中安裝和刪除軟件包。如圖所示,Ansible 為主要 Linux 發(fā)行版提供了內(nèi)置包管理模塊。
模塊 | Linux 發(fā)行版 |
---|---|
易于 | Debian / Ubuntu 變體 |
百勝/dnf | CentOS / Rocky 等 RHEL 變體 |
吃豆子 | Arch Linux 和 Arch 變體 |
壓縮 | 開放SUSE |
以下劇本文件將 Apache 網(wǎng)絡(luò)服務(wù)器安裝在清單文件中網(wǎng)絡(luò)服務(wù)器子組下定義的遠(yuǎn)程目標(biāo)上。該apt模塊提供兩個(gè)選項(xiàng):name指定包名稱的選項(xiàng) (?apache2?) 和state指示 Ansible 安裝最新版本的 Apache 的選項(xiàng)。
--- - name: install Apache hosts: webserver tasks: - name: install Apache webserver on Ubuntu apt: name: apache2 state: latest
運(yùn)行 playbook 文件后,您應(yīng)該會(huì)得到與我們所擁有的類似的輸出:
ansible-playbook /etc/ansible/playbook-02-install-apache.yml
在 RHEL 8 和 CentOS 8 上,可以使用該dnf模塊完成相同的任務(wù)。在這里,用于 RedHat 衍生產(chǎn)品的 Apache 包由httpd定義。
服務(wù)模塊
您還可以使用模塊來啟動(dòng)、停止和重新啟動(dòng)受管節(jié)點(diǎn)上正在運(yùn)行的服務(wù)。例如,要重新啟動(dòng) Apache 網(wǎng)絡(luò)服務(wù)器,我們將使用service顯示的劇本中的模塊。
--- - name: Restart Apache hosts: webserver tasks: - name: Restart Apachce webserver service: name: apache2 state: restarted
這是 Playbook 執(zhí)行的輸出:
這是一本展示如何停止網(wǎng)絡(luò)服務(wù)器的劇本。注意state參數(shù)從restarted到stop的變化。
--- - name: Stop Apache hosts: webserver tasks: - name: Stop Apache webserver service: name: apache2 state: stopped
從 playbook 執(zhí)行的輸出可以看出任務(wù)成功:
要啟動(dòng)網(wǎng)絡(luò)服務(wù)器,請(qǐng)將state參數(shù)設(shè)置為started。
--- - name: Start Apache hosts: webserver tasks: - name: Start Apache webserver service: name: apache2 state: started
再一次,這里是劇本執(zhí)行的輸出:
復(fù)制模塊
另一個(gè)有用的模塊是復(fù)制模塊。顧名思義,該模塊用于將文件從一個(gè)位置復(fù)制到另一個(gè)位置。您可以將文件從 Ansible 控制器復(fù)制到遠(yuǎn)程節(jié)點(diǎn)或?qū)⑽募倪h(yuǎn)程節(jié)點(diǎn)中的一個(gè)位置復(fù)制到另一個(gè)位置。
在下面的 Playbook 文件中,我們將sales_report.txt文件從 Ansible 控制節(jié)點(diǎn)復(fù)制到/tmp/reports/目錄中的遠(yuǎn)程服務(wù)器。此外,我們已使用和選項(xiàng)將所有者和組所有權(quán)分配給cherry用戶。該選項(xiàng)將八進(jìn)制文件權(quán)限0644分配給文件。ownergroupmode
--- - name: Ansible copy module example hosts: webserver tasks: - name: Copy files from control node to remote node copy: src: /home/user/Documents/sales_report.txt dest: /tmp/reports/ owner: cherry group: cherry mode 0644
Playbook 執(zhí)行的結(jié)果打印如下:
要在遠(yuǎn)程節(jié)點(diǎn)內(nèi)復(fù)制文件,請(qǐng)使用該remote_src選項(xiàng)并將其設(shè)置為yes。在下面的示例中,我們正在遠(yuǎn)程節(jié)點(diǎn)上制作 apache2.conf 配置文件的備份副本。簡(jiǎn)而言之,我們正在復(fù)制該文件并將其重命名為apache2.conf.back。
--- - name: Ansible copy module example hosts: webserver tasks: - name: Copy files within the remote node copy: src: /etc/apache2/apache2.conf dest: /etc/apache2/apache2.conf.bak remote_src: yes
劇本運(yùn)行成功,如圖:
行文件模塊
該lineinfile模塊是用于在一行上執(zhí)行多種任務(wù)的模塊,例如修改、替換或向文件添加一行。它可以與正則表達(dá)式結(jié)合使用以匹配特定行并進(jìn)行更改。
為了演示其功能,讓我們舉幾個(gè)例子。該劇本通過更改兩個(gè)參數(shù)修改遠(yuǎn)程目標(biāo)上的 SSH 服務(wù)配置。第一個(gè)任務(wù)將ClientAliveInterval指令設(shè)置為
15而第二個(gè)任務(wù)將ClientAliveCountMax指令設(shè)置為4。該regexp選項(xiàng)匹配包含我們?cè)噲D修改的參數(shù)的行。
--- - name: Configure SSH hosts: webserver tasks: - name: Ensure ClientAliveInterval is set to 15 lineinfile: path: /etc/ssh/sshd_config regexp: "^ClientAliveInterval" line: ClientAliveInterval=15 - name: Ensure ClientAliveCountMax is set to 4 lineinfile: path: /etc/ssh/sshd_config regexp: "^ClientAliveCountMax" line: ClientAliveCountMax=4
劇本按照出現(xiàn)的順序執(zhí)行這兩個(gè)任務(wù)——從第一個(gè)到最后一個(gè):
?專業(yè)提示:?在上面的示例中,我們?cè)噲D匹配以ClientAliveInterval字符串開頭的行。Linux 配置文件中的某些行可能被注釋掉(即#ClientActiveInterval)。在這種情況下,正則表達(dá)式將不匹配,因此lineinfile模塊將為您創(chuàng)建一個(gè)包含指定字符串的新行。
要向文件添加一行,請(qǐng)指定文件的完整路徑、要添加到文件的行,然后將create選項(xiàng)設(shè)置為yes。
顯示的劇本將新行添加173.82.120.115 cherry.localdomain到遠(yuǎn)程節(jié)點(diǎn)上的/etc/hosts文件。
--- - name: Add a new line to a file hosts: webserver tasks: - name: Add a new line to a file lineinfile: path: /etc/hosts line: 173.82.120.115 cherry.localdomain create: yes
這是劇本執(zhí)行:
命令模塊
Command 模塊采用一個(gè)命令名稱,后跟一個(gè)參數(shù)列表。該命令在目標(biāo)節(jié)點(diǎn)上執(zhí)行,但輸出不顯示到標(biāo)準(zhǔn)輸出。
顯示的劇本在目標(biāo)模式上運(yùn)行"uptime"和"date"命令。
--- - name: Execute commands on remote targets hosts: webserver tasks: - name: Execute the uptime command command: "uptime" - name: Execute the date command command: "date"
該劇本成功運(yùn)行,但是沒有打印出命令的輸出。
要將結(jié)果打印到標(biāo)準(zhǔn)輸出,請(qǐng)使用該shell模塊。register使用我們定義的uptime_var和date_var變量的選項(xiàng)捕獲這兩個(gè)命令的輸出。這些變量最終由msg選項(xiàng)引用,并將值打印到標(biāo)準(zhǔn)輸出。
--- - name: Execute commands on remote targets hosts: webserver tasks: - name: Execute the uptime command shell: "uptime" register: uptime_var - debug: msg: "{{uptime_var.stdout}}" - name: Execute the date command shell: "date" register: date_var - debug: msg: "{{date_var.stdout}}"
在 playbook 執(zhí)行輸出中,您可以看到打印的兩個(gè)命令的輸出:
到目前為止,我們只演示了幾個(gè)模塊。有成百上千的 Ansible 模塊用于執(zhí)行不同的任務(wù)。如需更全面的 Ansible 模塊列表,請(qǐng)?jiān)L問Ansible 模塊文檔頁面。
Ansible 劇本變量
如果您是開發(fā)人員或程序員,您很可能在代碼中無數(shù)次使用過變量。與許多編程語言一樣,Playbook 中使用變量來存儲(chǔ)值。您可以為變量賦值并在劇本中的任何位置引用它。
變量也可以來自外部源,例如變量文件,然后在 Playbook 中引用。處理來自多個(gè)來源且名稱相同的變量時(shí),將應(yīng)用特殊的優(yōu)先級(jí)規(guī)則。
為了演示在實(shí)踐中如何使用變量,讓我們創(chuàng)建一個(gè) playbook 文件,該文件將打印出兩個(gè)變量的值:greetings和topic。
--- - name: Ansible variables in practice hosts: webserver vars: greetings: Hello World! topic: Ansible Playbooks tasks: - name: Ansible basic variable example debug: msg: "{{ greetings }}, let's learn {{ topic }}."
該vars部分定義了調(diào)試模塊將在播放范圍內(nèi)引用的變量列表。劇本文件中指定的所有任務(wù)和文件都可以訪問這些變量。
在輸出中,分配給變量的值已打印到 stdout 以代替變量名稱。
或者,您可以有一個(gè)可變項(xiàng)目列表。在下面的劇本中,讓我們定義一個(gè)名為的變量oceans,其中包含代表五大洋的五個(gè)值的列表。
--- - name: Ansible list variable example hosts: webserver vars: oceans: - Indian - Atlantic - Pacific - Artic - Southern Antarctic tasks: - name: Ansible list variables example debug: msg: "The five oceans in the world are {{ oceans }}"
該劇本遍歷該部分下的值列表,并使用該選項(xiàng)vars將它們打印到標(biāo)準(zhǔn)輸出。msg
此外,您可以使用指令中的index [ x ]屬性訪問變量中的每個(gè)值,msg其中 x 是列表中項(xiàng)目的值。第一項(xiàng)用 表示index[0]。例如,要訪問列表中的第三項(xiàng),我們將引用修改為
Ansible 劇本條件
當(dāng)您需要根據(jù)特定條件執(zhí)行一組任務(wù)時(shí),將使用條件語句。在 Ansible Playbooks 中 when 是一個(gè)廣泛使用的條件語句,它與ORandAND運(yùn)算符一起使用。
為了更好地闡述條件語句的工作原理,我們將使用兩個(gè)不同操作系統(tǒng)系列的托管節(jié)點(diǎn)進(jìn)行簡(jiǎn)單設(shè)置:
服務(wù)器IP:173.82.120.115 Ubuntu 20.04
服務(wù)器IP:173.82.255.207 CentOS 8.3
使用 'when' 語句
考慮下面的劇本。該when語句指示 Playbook 在屬于 Debian 操作系統(tǒng)系列的所有服務(wù)器上安裝 Nginx 網(wǎng)絡(luò)服務(wù)器。我們?cè)赼nsible_os_family這里使用屬于 Ansible facts 對(duì)象的變量,因此您不需要在您的劇本中定義它。
--- - name: Ansible when conditional statement hosts: all tasks: - name: install nginx webserver apt: name: nginx state: latest when: ansible_os_family == "Debian"
從 playbook 執(zhí)行的輸出中,我們可以看到 CentOS 主機(jī)已被排除,因?yàn)樗粷M足我們的條件。
在“when”語句中使用“AND”運(yùn)算符
使用and運(yùn)算符時(shí),必須滿足這兩個(gè)條件。在此示例中,如果受管節(jié)點(diǎn)屬于 Debian Linux 系列并且其版本號(hào)為 20.04,則 playbook 將成功運(yùn)行任務(wù)。
--- - name: Ansible when-and conditional statement hosts: all tasks: - name: install nginx webserver apt: name: nginx state: latest when: ansible_os_family == "Debian" and ansible_distribution_version == "20.04"
由于 Ubuntu 節(jié)點(diǎn)符合條件,劇本將成功運(yùn)行并在其上安裝 Nginx,但會(huì)跳過 CentOS 8 服務(wù)器。
在“when”語句中使用“OR”運(yùn)算符
如果滿足任一條件,則使用or運(yùn)算符將??執(zhí)行任務(wù)。在以下 Playbook 中,data在屬于 Debian 或 RedHat Linux 系列的托管節(jié)點(diǎn)的主目錄中創(chuàng)建了一個(gè)名為的新目錄。
--- - name: Ansible when-or conditional statement hosts: all tasks: - name: "create a directory if it doesn't exist" file: path: $HOME/data state: directory mode: "0777" when: ansible_os_family == "Debian" or ansible_os_family == "RedHat"
可以預(yù)見,該目錄是在兩個(gè)被管節(jié)點(diǎn)上創(chuàng)建的,因?yàn)樗鼈兌紝儆趦蓚€(gè)操作系統(tǒng)系列中的任何一個(gè)。
為了確認(rèn)這一點(diǎn),我們將列出兩個(gè)節(jié)點(diǎn)上主目錄的內(nèi)容。
ssh root@173.82.255.207 "ls -l" ssh root@173.82.120.115 "ls -l"
Ansible 劇本循環(huán)
有時(shí),您會(huì)發(fā)現(xiàn)自己執(zhí)行重復(fù)性任務(wù),這些任務(wù)需要您編寫執(zhí)行相同操作的多個(gè)任務(wù)。舉個(gè)例子,一個(gè)在目標(biāo)系統(tǒng)上創(chuàng)建新用戶的劇本,如圖所示。
--- - name: Create new usersr on remote machine hosts: 173.82.120.115 tasks: - name: Create new user Alice user: name: alice state: present - name: Create new user Patric user: name: patric state: present - name: Create new user Tom user: name: tom state: present
顯然,這里有很多重復(fù)。在處理多個(gè)性質(zhì)相似的任務(wù)時(shí),這可能會(huì)令人生畏且耗時(shí)。這是循環(huán)派上用場(chǎng)的地方。
循環(huán)提供了一種使用更少代碼行執(zhí)行重復(fù)性任務(wù)的簡(jiǎn)化方法。它們遍歷使用looporwith_*指令指定的值列表。
該指令列出了由雙花括號(hào)括起來loop的變量引用的值。item在運(yùn)行時(shí),劇本遍歷loop指令定義的用戶列表。然后將每個(gè)用戶傳遞給item變量,并以簡(jiǎn)單而有效的方式創(chuàng)建所有用戶。很明顯,劇本看起來更簡(jiǎn)潔,實(shí)現(xiàn)相同目標(biāo)的代碼行更少。
--- - name: Create new users in remote machine hosts: 173.82.120.115 tasks: - name: Create new user Alice user: name: '{{ item }}' state: present loop: - alice - patric - tom
這是 Playbook 執(zhí)行的輸出,用于確認(rèn)用戶的創(chuàng)建:
結(jié)論
在本指南中,您學(xué)習(xí)了如何創(chuàng)建和運(yùn)行 Ansible 劇本。您已經(jīng)深入研究了各種 Playbook 元素,例如模塊、變量、條件語句和循環(huán)。