Ansible 核心為幾乎所有用例提供了數百個 Ansible 模塊。您可以在Ansible 文檔頁面上找到所有 Ansible 模塊的完整列表及其描述。有時,您只需要像在 bash shell 上一樣直接在目標主機上執行命令。這就是 Ansible shell 模塊派上用場的地方。在本指南中,您將了解 Ansible shell 模塊、它的工作原理以及如何使用它對托管節點執行命令。
什么是 Ansible Shell 模塊?
Ansible shell 模塊是一個方便的模塊,它允許您直接在遠程目標的 shell 上執行命令,就像您已登錄一樣。這樣做有助于保持命令執行的原創性。
shell 模塊與命令模塊密切相關。兩者都有助于實現相同的結果。但是,兩者之間存在一些差異:
- shell 模塊直接在目標主機的 shell 上執行命令。默認情況下,shell 模塊使用/bin/shshell 來運行命令,盡管您可以將其配置為使用其他 shell。使用 command 模塊,執行的命令不通過 shell 處理。
- 由于命令不在 shell 上執行,命令模塊不支持環境變量、管道和其他運算符,例如 '>' 、 '<' 、 '&'、';'?和'|?|'。使用 shell 模塊,完全支持管道、重定向和變量。因此,shell 模塊提供了更大的靈活性。
- 如果在目標系統上安全地運行命令是您的首要任務,請使用命令模塊。與 shell 模塊不同,command 模塊不影響遠程用戶的 shell 環境。
Ansible Shell 模塊與其他模塊
commandAnsible shell 模塊與、script和模塊屬于同一類別raw。這些統稱為運行命令。
雖然可以快速高效地完成任務,但運行命令只能??作為最后的手段使用。這是因為它們在執行任務時應用最少的邏輯,并且不存在所需狀態的概念。如果已經滿足導致錯誤的條件,則 shell 命令的后續執行可能會失敗。
此外,除非您注冊第一個命令的結果,否則無法使用 shell 模塊捕獲錯誤。然后,您必須在劇本中執行后續任務以實施條件邏輯來確認錯誤是否發生,然后進行處理。這可能會導致嚴重破壞自動化的瓶頸。
出于這個原因,shell 命令應該僅限于在遠程系統上執行簡單的任務。如果需要服務或應用程序的理想狀態,建議使用特定于任務的模塊,例如service、copy、file和lineinfile,僅舉幾例。這使得劇本更加通用和可重用。
先決條件
要遵循本指南,您應該:
- 一個 Ansible 控制節點。在本教程中,我們運行的是 Ubuntu 20.04。如果您還沒有安裝 Ansible,請按照我們關于如何在 Ubuntu 20.04 上安裝和配置 Ansible的指南進行操作。
- 您將對其運行命令的遠程主機。
運行 Ansible Ad-hoc Shell 命令
Ansible 的真正力量在于劇本。但是,劇本用于在目標主機上執行復雜的任務。假設您想要非常快速地運行命令而不保存它們供以后使用。你會怎么做?這就是臨時命令派上用場的地方。
Ad-hoc 命令是單行命令,您無需編寫劇本即可即時運行。
例如,您可能想要檢查遠程主機的正常運行時間或磁盤空間利用率。與其為此類任務編寫完整的劇本,更好的方法是對您的主機運行臨時命令。
臨時命令采用以下語法:
ansible [pattern] -m [MODULE] -a {COMMAND_OPTIONS}
該模式指定目標主機所屬的主機組。該-m選項指定模塊的類型,而該-a選項采用命令參數。
讓我們舉幾個例子。
要檢查所有目標主機的正常運行時間,請運行以下命令:
sudo ansible all -m shell -a 'uptime -p'
要檢查內存使用情況,請運行:
sudo ansible all -m shell -a 'free -m'
查看db_server組下主機的磁盤空間利用率,執行:
sudo ansible db_server -m shell -a 'df -Th'
使用 Ansible Shell 模塊運行單個命令
除了運行臨時命令外,Ansible shell 模塊還用于 playbook 以指定要在遠程主機上執行的任務。
考慮下面的劇本。
--- - name: Shell module example hsots: webservers tasks: - name: Check system information shell: "lsb_release -a" register: os_info - debug: msg: "{{os_info.stdout_lines}}"
在此示例中,劇本針對名為 webservers 的主機組運行,并執行一個lsb_release -a命令來檢索有關操作系統版本的詳細信息。然后將輸出保存在一個名為 的寄存器變量中os_info。
最后一行使用模塊將存儲在os_info變量中的輸出打印到標準輸出。debug
這是劇本執行的輸出。
如果文件不存在,則使用 Shell 模塊運行命令
該creates參數允許您在文件不存在時運行命令。它指定文件的路徑,如果存在,則跳過要執行的命令。
顯示的劇本檢查文件是否hello.txt存在于目標主機的主目錄中。如果該文件不存在,則執行指定的 shell 命令。如果該文件存在,則 shell 命令將中止。
--- - name: Create a file in the home directory if it doesn't exist hosts: webservers tasks: - name: Create a file in the home directory shell: echo "Hey guys!" > $HOME/hello.txt args: creates: "$HOME/hello.txt"
由于該文件在遠程目標上不存在,因此 shell 命令已成功執行,如您從 playbook 執行中所見。
下面的命令確認hello.txt文件是在遠程目標的主目錄中創建的。
ssh root@173.82.120.115 "ls -l ~"
如果文件存在,則使用 Shell 模塊運行命令
參數指定文件名,removes如果文件存在,則執行命令。在前面的示例中,該hello.txt文件是在遠程目標的主目錄中創建的。
在此劇本中,removes參數檢查此文件是否存在于遠程目標上。由于您已經創建了它,劇本會繼續執行刪除文件的 shell 命令。
--- - name: Remove a file in the home directory only if it exists hosts: webservers tasks: - name: Remove a file in the home directory shell: rm $HOME/hello.txt args: removes: "$HOME/hello.txt" warn: false
劇本執行確認文件已被刪除。
在不同的目錄中運行命令
要在特定目錄中運行 shell 命令,請使用chdir參數。下面的劇本將 Apache 二進制文件下載到/usr/local/src路徑中。
--- - name: Download Apache source file to /usr/local/src directory hosts: webservers tasks: - name: Download Apache tarball file shell: wget https://dlcdn.apache.org/httpd/httpd-2.4.52.tar.gz args: chdir: /usr/local/src warn: false
劇本確認任務已成功執行。
將 Ansible shell 模塊與環境變量一起使用
shell 模塊還使您能夠設置新的環境變量。使用environment參數可以做到這一點。考慮下面的劇本。
--- - name: Environment variable example hosts: webservers tasks: - name: Ansible Shell module set an environment variable shell: echo $NEW_VAR register: command_result envnironment: NEW_VAR: "john_doe" - debug: msg: "{{ command_result.stdout_lines }}"
該劇本將NEW_VAR環境變量設置為john_doe。
注意:環境變量僅為該特定任務設置。在后續任務中,NEW_VAR 變量將不可用。
使用 Ansible Shell 模塊運行多個命令
到目前為止,您已經看到 shell 模塊在托管主機上運行單個命令。您還可以指定一組按時間順序執行的命令。
要實現這一點,請使用豎線開始 shell 命令,然后是要執行的任務列表。在本例中,date、uptime、 和echo命令的輸出分別保存到date.txt、uptime.txt和hello.txt文件中,然后保存在/tmp目錄中。
--- - name: Shell module example hsots: webservers tasks: - name: Run multiple commands shell: | date > /tmp/date.txt uptime > /tmp/uptime.txt echo "Hello World" > /tmp/hello.txt
劇本從第一個任務到最后一個任務按順序運行任務。
使用管道和重定向運行命令
如前所述,shell 模塊也接受管道和重定向。事實上,之前的劇本利用重定向符號 (?>) 將列出的命令的輸出保存到單獨的文件中。
假設您要列出在/tmp目錄中創建的所有文本文件,并將結果保存到同一目錄中另一個名為dirlist.txt的文件中。
這是 shell 命令的樣子。
ls -l /tmp | grep .txt > /tmp/dirlist.txt
命令的第一部分列出了/tmp目錄中的所有文件。然后將結果通過管道傳輸到 grep 命令,該命令過濾輸出以僅包含文本文件。然后使用“大于”重定向符號將最終輸出保存到dirlist.txt文件。
現在讓我們更進一步,將結果打印到標準輸出。為此,需要第二項任務。目標是將dirlist.txt文件的內容顯示到標準輸出。列出文件內容的 shell 命令是:
cat /tmp/dirlist.txt
命令的輸出隨后被注冊在一個名為displayfile的變量中,并且使用調試模塊將消息顯示到標準輸出。這是整個劇本的樣子。
--- - name: Shell module example hosts: webservers tasks: - name: List text files in tmp directory and save result in output file shell: "ls -l /tmp | grep .txt > /tmp/dirlist.txt - name: Display the contents of the output file shell: cat /tmp/dirlist.txt register: displayfile - debug: msg: "{{ displayfile.stdout_lines }}"
執行 playbook 時,/tmp目錄中的所有文本文件(包括dirlist.txt文件)都會打印到標準輸出:
防止 Shell 注入
由于它在遠程目標的 shell 上運行命令,因此 shell 模塊容易受到 shell 命令注入。Shell 注入是一種攻擊媒介,攻擊者在其中在主機上運行任意命令以破壞底層基礎設施。
使用 Shell 模塊變量時,Ansible 建議使用quote過濾器來阻止 shell 注入威脅。
{{ variable_name | quote }}
考慮下面的一個簡單劇本。
--- - name: Ansible quote filter demo hosts: webservers vars: - username: cherry tasks: - name: Print variable debug: msg: " Running playbook as user {{ username | quote }}
該username變量在最后由msg使用過濾器的參數引用,quote以防止在注入用戶名變量時執行任意命令字符串。
結論
Ansible shell 模塊是一個有用的模塊,可以幫助您在托管主機上快速執行簡單任務。shell 模塊的完美替代品是commandmodule。它提供了一種更可靠、更安全的任務執行方式,因為命令不會在遠程目標的 shell 上處理。但是,如果您堅持使用 shell 模塊,請不要忘記quote在 playbook 中使用模板化變量時包含過濾器以防止 shell 注入攻擊。