Ansible 的核心組件之一是劇本文件。Ansible 使用 playbook 文件來定義復雜的任務,這些任務在用戶參與有限的情況下針對受管節點執行。在本指南中,您將學習如何創建和使用 Ansible 劇本文件在托管節點上執行任務。
什么是 Ansible 劇本?
劇本本質上是一個包含一個或多個劇本的 YAML 文件。play 是針對/etc/ansible/hosts文件中指定的托管主機執行的一組有序任務。劇本中的每個劇本都代表一個獨特的任務,具有目標主機的特定于環境的參數。
劇本非常靈活,可以在多個服務器上無限期重復使用以執行相同的任務。Ansible playbook 通常用于服務器配置、網絡設備管理和應用程序部署任務。
先決條件
要遵循本指南,您應該:
- 安裝了 Ubuntu 20.04 操作系統和 Ansible 的控制節點。如果您之前沒有安裝過 Ansible,請按照我們關于如何在 Ubuntu 20.04 上安裝和配置 Ansible的指南進行操作。
- 在控制節點的主機文件中配置的單個受管節點將用于運行劇本任務。
創建 Ansible 劇本
讓我們從制作和運行我們的第一個 Ansible 劇本開始。在控制節點上的/etc/ansible/目錄中創建一個簡單的 YAML 文件,如下所示:
sudo vim /etc/ansible/playbook-01.yml
現在使用以下代碼填充劇本文件:
--- - name: A simple playbook file hosts: all tasks: - name: Print a sample message debug: msg: Hello World. Welcome to Ansible playbooks!
標記 YAML 文件的---開始。
該name指令的第一個實例指定了該劇的名稱。第二個實例指定任務的名稱。
該hosts指令指定將在其上執行劇本的目標主機。在此示例中,劇本將在清單文件中指定的所有主機上運行。要定位特定主機,請提供主機的 IP 地址或域名。
該tasks指令是要在目標主機上執行的任務列表。在這個劇本中,我們有一個任務將語句打印到標準輸出。
關鍵字是 Ansible 自帶的debug內置模塊,在 Playbook 運行時打印語句。此外,在不停止 playbook 的情況下調試語句和變量時,它會派上用場。該debug模塊帶有一些選項,例如msg和var。該msg選項指定要打印到標準輸出的字符串。
執行 Ansible 劇本
要運行劇本,請使用ansible-playbook如下所示的命令:
ansible-playbook /path/to/playbook_file
在我們的示例中,您應該運行以下命令:
ansible-playbook /etc/ansible/playbook-01.yml
在 playbook 執行期間,您應該會看到以下輸出:
請注意,執行了兩項任務,盡管我們在 Playbook 文件中只定義了一項。
第一個任務收集有關受管節點的事實。Ansible facts 是指以 JSON 格式呈現的特定于主機的系統數據,例如 BIOS 信息、系統日期和時間、操作系統類型和版本以及 IP 地址。它還包括硬件數據,例如塊設備、CPU、RAM 和交換空間等等。
第二個任務按照 playbook 文件中的指定將一條簡單消息打印到標準輸出。ok=2表示成功執行了兩個任務。
如果您想獲得所有 Ansible 事實的列表,請執行以下命令:
ansible -m setup all
Ansible 劇本模塊
Ansible 模塊是獨立的可重復使用的 Python 腳本,在 Playbook 中被引用以幫助在托管節點上執行特定任務。Ansible 模塊可以自動執行托管節點上的各種任務,包括包管理、服務管理、文件管理等等。
在本節中,我們將演示如何通過將模塊合并到 Playbook 中來完成各種系統管理任務。
包管理模塊
管理軟件包是一項基本的系統管理任務。它專門處理在 Linux 服務器中安裝和刪除軟件包。如圖所示,Ansible 為主要 Linux 發行版提供了內置包管理模塊。
模塊 | Linux 發行版 |
---|---|
易于 | Debian / Ubuntu 變體 |
百勝/dnf | CentOS / Rocky 等 RHEL 變體 |
吃豆子 | Arch Linux 和 Arch 變體 |
壓縮 | 開放SUSE |
以下劇本文件將 Apache 網絡服務器安裝在清單文件中網絡服務器子組下定義的遠程目標上。該apt模塊提供兩個選項:name指定包名稱的選項 (?apache2?) 和state指示 Ansible 安裝最新版本的 Apache 的選項。
--- - name: install Apache hosts: webserver tasks: - name: install Apache webserver on Ubuntu apt: name: apache2 state: latest
運行 playbook 文件后,您應該會得到與我們所擁有的類似的輸出:
ansible-playbook /etc/ansible/playbook-02-install-apache.yml
在 RHEL 8 和 CentOS 8 上,可以使用該dnf模塊完成相同的任務。在這里,用于 RedHat 衍生產品的 Apache 包由httpd定義。
服務模塊
您還可以使用模塊來啟動、停止和重新啟動受管節點上正在運行的服務。例如,要重新啟動 Apache 網絡服務器,我們將使用service顯示的劇本中的模塊。
--- - name: Restart Apache hosts: webserver tasks: - name: Restart Apachce webserver service: name: apache2 state: restarted
這是 Playbook 執行的輸出:
這是一本展示如何停止網絡服務器的劇本。注意state參數從restarted到stop的變化。
--- - name: Stop Apache hosts: webserver tasks: - name: Stop Apache webserver service: name: apache2 state: stopped
從 playbook 執行的輸出可以看出任務成功:
要啟動網絡服務器,請將state參數設置為started。
--- - name: Start Apache hosts: webserver tasks: - name: Start Apache webserver service: name: apache2 state: started
再一次,這里是劇本執行的輸出:
復制模塊
另一個有用的模塊是復制模塊。顧名思義,該模塊用于將文件從一個位置復制到另一個位置。您可以將文件從 Ansible 控制器復制到遠程節點或將文件從遠程節點中的一個位置復制到另一個位置。
在下面的 Playbook 文件中,我們將sales_report.txt文件從 Ansible 控制節點復制到/tmp/reports/目錄中的遠程服務器。此外,我們已使用和選項將所有者和組所有權分配給cherry用戶。該選項將八進制文件權限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 執行的結果打印如下:
要在遠程節點內復制文件,請使用該remote_src選項并將其設置為yes。在下面的示例中,我們正在遠程節點上制作 apache2.conf 配置文件的備份副本。簡而言之,我們正在復制該文件并將其重命名為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
劇本運行成功,如圖:
行文件模塊
該lineinfile模塊是用于在一行上執行多種任務的模塊,例如修改、替換或向文件添加一行。它可以與正則表達式結合使用以匹配特定行并進行更改。
為了演示其功能,讓我們舉幾個例子。該劇本通過更改兩個參數修改遠程目標上的 SSH 服務配置。第一個任務將ClientAliveInterval指令設置為
15而第二個任務將ClientAliveCountMax指令設置為4。該regexp選項匹配包含我們試圖修改的參數的行。
--- - 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
劇本按照出現的順序執行這兩個任務——從第一個到最后一個:
?專業提示:?在上面的示例中,我們試圖匹配以ClientAliveInterval字符串開頭的行。Linux 配置文件中的某些行可能被注釋掉(即#ClientActiveInterval)。在這種情況下,正則表達式將不匹配,因此lineinfile模塊將為您創建一個包含指定字符串的新行。
要向文件添加一行,請指定文件的完整路徑、要添加到文件的行,然后將create選項設置為yes。
顯示的劇本將新行添加173.82.120.115 cherry.localdomain到遠程節點上的/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
這是劇本執行:
命令模塊
Command 模塊采用一個命令名稱,后跟一個參數列表。該命令在目標節點上執行,但輸出不顯示到標準輸出。
顯示的劇本在目標模式上運行"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"
該劇本成功運行,但是沒有打印出命令的輸出。
要將結果打印到標準輸出,請使用該shell模塊。register使用我們定義的uptime_var和date_var變量的選項捕獲這兩個命令的輸出。這些變量最終由msg選項引用,并將值打印到標準輸出。
--- - 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 執行輸出中,您可以看到打印的兩個命令的輸出:
到目前為止,我們只演示了幾個模塊。有成百上千的 Ansible 模塊用于執行不同的任務。如需更全面的 Ansible 模塊列表,請訪問Ansible 模塊文檔頁面。
Ansible 劇本變量
如果您是開發人員或程序員,您很可能在代碼中無數次使用過變量。與許多編程語言一樣,Playbook 中使用變量來存儲值。您可以為變量賦值并在劇本中的任何位置引用它。
變量也可以來自外部源,例如變量文件,然后在 Playbook 中引用。處理來自多個來源且名稱相同的變量時,將應用特殊的優先級規則。
為了演示在實踐中如何使用變量,讓我們創建一個 playbook 文件,該文件將打印出兩個變量的值: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部分定義了調試模塊將在播放范圍內引用的變量列表。劇本文件中指定的所有任務和文件都可以訪問這些變量。
在輸出中,分配給變量的值已打印到 stdout 以代替變量名稱。
或者,您可以有一個可變項目列表。在下面的劇本中,讓我們定義一個名為的變量oceans,其中包含代表五大洋的五個值的列表。
--- - 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 }}"
該劇本遍歷該部分下的值列表,并使用該選項vars將它們打印到標準輸出。msg
此外,您可以使用指令中的index [ x ]屬性訪問變量中的每個值,msg其中 x 是列表中項目的值。第一項用 表示index[0]。例如,要訪問列表中的第三項,我們將引用修改為
Ansible 劇本條件
當您需要根據特定條件執行一組任務時,將使用條件語句。在 Ansible Playbooks 中 when 是一個廣泛使用的條件語句,它與ORandAND運算符一起使用。
為了更好地闡述條件語句的工作原理,我們將使用兩個不同操作系統系列的托管節點進行簡單設置:
服務器IP:173.82.120.115 Ubuntu 20.04
服務器IP:173.82.255.207 CentOS 8.3
使用 'when' 語句
考慮下面的劇本。該when語句指示 Playbook 在屬于 Debian 操作系統系列的所有服務器上安裝 Nginx 網絡服務器。我們在ansible_os_family這里使用屬于 Ansible facts 對象的變量,因此您不需要在您的劇本中定義它。
--- - name: Ansible when conditional statement hosts: all tasks: - name: install nginx webserver apt: name: nginx state: latest when: ansible_os_family == "Debian"
從 playbook 執行的輸出中,我們可以看到 CentOS 主機已被排除,因為它不滿足我們的條件。
在“when”語句中使用“AND”運算符
使用and運算符時,必須滿足這兩個條件。在此示例中,如果受管節點屬于 Debian Linux 系列并且其版本號為 20.04,則 playbook 將成功運行任務。
--- - 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 節點符合條件,劇本將成功運行并在其上安裝 Nginx,但會跳過 CentOS 8 服務器。
在“when”語句中使用“OR”運算符
如果滿足任一條件,則使用or運算符將??執行任務。在以下 Playbook 中,data在屬于 Debian 或 RedHat Linux 系列的托管節點的主目錄中創建了一個名為的新目錄。
--- - 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"
可以預見,該目錄是在兩個被管節點上創建的,因為它們都屬于兩個操作系統系列中的任何一個。
為了確認這一點,我們將列出兩個節點上主目錄的內容。
ssh root@173.82.255.207 "ls -l" ssh root@173.82.120.115 "ls -l"
Ansible 劇本循環
有時,您會發現自己執行重復性任務,這些任務需要您編寫執行相同操作的多個任務。舉個例子,一個在目標系統上創建新用戶的劇本,如圖所示。
--- - 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
顯然,這里有很多重復。在處理多個性質相似的任務時,這可能會令人生畏且耗時。這是循環派上用場的地方。
循環提供了一種使用更少代碼行執行重復性任務的簡化方法。它們遍歷使用looporwith_*指令指定的值列表。
該指令列出了由雙花括號括起來loop的變量引用的值。item在運行時,劇本遍歷loop指令定義的用戶列表。然后將每個用戶傳遞給item變量,并以簡單而有效的方式創建所有用戶。很明顯,劇本看起來更簡潔,實現相同目標的代碼行更少。
--- - 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 執行的輸出,用于確認用戶的創建:
結論
在本指南中,您學習了如何創建和運行 Ansible 劇本。您已經深入研究了各種 Playbook 元素,例如模塊、變量、條件語句和循環。