Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Мы перевели статью, где подробно рассматривается использование модуля Ansible Shell и различные способы выполнения удалённых команд на узлах в рамках работы по автоматизации. В статье рассматриваются различные опции и модули для выполнения удалённых команд, а также их различия и то, когда следует использовать каждый из них. Статья для тех, кто изучает Ansible.
Что представляет собой модуль Ansible shell
Модуль Ansible shell используется для выполнения shell-команд на удалённых целевых машинах. Модуль shell
принимает имя команды, за которым следует список аргументов, ограниченных пробелами.
Модуль shell
выполняется не прямо на целевой машине, а в среде shell (/bin/sh
) на ней. Это позволяет использовать специфические для оболочки возможности и функции, такие как pipe |
, перенаправление <
, >
, >>
и т.д.
Если вы работаете с узлами Windows, используйте модуль ansible.windows.win_shell.
Примеры использования модуля Ansibe shell
Рассмотрим несколько примеров использования модуля shell в действии.
Пример 1: Модуль Ansible shell для выполнения одной команды
- name: Execute shell command
ansible.builtin.shell: tail -n 10 /var/log/syslog > tail_syslog.txt
В данном примере мы используем модуль shell
для получения последних десяти строк из файла /var/log/syslog
и передачи вывода в файл tail_syslog.txt. Модуль shell
здесь действительно необходим, поскольку оператор >
является функцией shell.
Пример 2: Выполнение команды с помощью модуля shell, если файл не существует
- name: This command will only run when file_to_check.txt doesn't exist
ansible.builtin.shell: tail -n 10 /var/log/syslog > tail_syslog.txt
args:
creates: file_to_check.txt
В приведённом примере команда будет выполнена только в том случае, если файл file_to_check.txt
не существует. Это достигается с помощью параметра creates
и полезно в тех случаях, когда необходимо проверить, выполнялись ли уже команды, создающие определённый артефакт.
Пример 4: Показать использование диска
- name: Check disk usage and grep for /dev/sda1
ansible.builtin.shell: df -h | grep /dev/sda1
register: disk_usage
- name: Display output
ansible.builtin.debug:
var: disk_usage.stdout_lines
В приведённом выше примере мы используем команду df -h
для отображения использования диска и направляем её для фильтрации результатов только для /dev/sda1
. Это пример команды, которая не может быть корректно запущена с помощью модуля command
.
Вывод команды регистрируется в переменной disk_usage
и отображается в следующей задаче.
Пример 5: Выполнение команды в определенном каталоге
- name: Compile software in a specific directory
ansible.builtin.shell: make install
args:
chdir: /path/to/source/code
Это ещё один пример использования ключевого слова args
вместе с chdir
для выполнения shell-команды в определённом каталоге.
В данном случае команда make install
будет выполнена в каталоге path/to/source/code
.
Пример 6: Выполнение нескольких команд
- name: Update system packages and clean up
ansible.builtin.shell: |
apt-get update &&
apt-get upgrade -y &&
apt-get clean
Другим примером может служить объединение нескольких команд в цепочку с помощью оператора &&
, как показано выше. В этом случае модуль shell выполняет команды одну за другой сверху вниз.
Пример 7: Проверка запущенности процесса
- name: Check if a process is running
ansible.builtin.shell: ps aux | grep 'nginx' | grep -v grep
register: process_status
failed_when: process_status.rc != 0
- name: Display process status
ansible.builtin.debug:
var: process_status.stdout_lines
В данном примере мы используем ps aux
для получения списка всех процессов, а затем оператор pipe |
для поиска интересующего нас процесса nginx
.
Мы также используем второй оператор pipe и grep
, чтобы исключить из результатов нашу собственную команду grep.
Пример 8: Выбор оболочки, используемой для выполнения команды
- name: Change shell to bash
ansible.builtin.shell: cat /var/log/*log > logs_snaphot.txt
args:
executable: /bin/bash
В данном примере мы используем параметр executable
для указания оболочки, используемой для выполнения команды. Это может быть полезно в тех случаях, когда стандартный /bin/sh
не поддерживает функцию, которую мы хотим использовать.
Пример 9: Использование Templated Variable в команде
- name: Run the command using a templated variable to avoid injection
ansible.builtin.shell: cat {{ logs_snapshot_file|quote }}
register: logs
В приведённом выше примере мы используем в команде templated variable.
При использовании переменных Ansible в командах обязательно используйте {{ var | quote }}
вместо {{ var }}
для добавления кавычек вокруг значения переменной, что позволяет избежать попадания другого кода и гарантирует, что содержимое переменной будет рассматриваться как один аргумент, а не как команда, которую оболочка должна интерпретировать.
Альтернативные варианты удалённого запуска команд с помощью Ansible
Как правило, мы предпочитаем использовать специализированные модули Ansible, а не сырые сценарии оболочки или командных модулей. Модули Ansible, ориентированные на конкретные задачи, разработаны таким образом, чтобы быть идемпотентными и абстрагироваться от базовых сложностей задач, что делает их более предпочтительными по сравнению, например, с прямым выполнением команд через оболочку или командные модули.
Более того, специализированные модули лучше обрабатывают ошибки.
Если что-то идёт не так, они часто выдают полезные сообщения об ошибках. Это помогает в траблшутинге, и безопаснее в использовании, чем произвольные команды оболочки, которые могут оказатся рисковыми с точки зрения безопасности. Модули Ansible могут сообщать об изменениях в удалённой системе.
В некоторых случаях для достижения желаемого результата может не хватить какого-либо модуля, предназначенного для конкретной задачи. В таких случаях мы можем использовать Ansible для выполнения команд непосредственно на удаленных узлах. Ansible предоставляет несколько способов выполнения команд на удалённых узлах.
Мы уже познакомились с модулем shell
, а далее рассмотрим модули command
, expect
, script
и raw
, предназначенные для этой цели.
Модуль Ansible command
Модуль command в Ansible выполняет команды на всех выбранных хостах. Это один из самых простых модулей; он принимает имя команды, за которым следует список аргументов, ограниченных пробелами. Если вы работаете с Windows, используйте вместо него модуль ansible.windows.win_command.
Важно отметить, что при использовании модуля command
команды не обрабатываются через local shell. Это означает, что такие переменные, как $HOSTNAME
, не будут работать, а специфические для оболочки функции, такие как команды конвейеризации и операторы перенаправления (<
, >
, >>
, |
и т.д.), будут интерпретироваться некорректно.
Такое поведение делает модуль command
более безопасным и предсказуемым, чем модуль shell
, который мы рассмотрим далее. Используя модуль command
, вы можете быть уверены, что команда будет выполнена именно так, как вы её написали, без каких-либо неожиданных побочных эффектов от обработки shell.
Вот базовый пример, демонстрирующий использование модуля command
в задаче:
- name: Display list of files in /var/log
ansible.builtin.command: ls /var/log
register: log_files
- name: Output list of log files
ansible.builtin.debug:
var: log_files.stdout_lines
В приведённом примере выполняемой командой является ls /var/log
. Вывод команды регистрируется в переменной log_files
, которая затем отображается с помощью модуля debug
в следующей задаче.
Приведем пример вывода двух вышеуказанных задач:
Приведём еще один пример использования модуля command
для проверки того, установлен ли пакет в целевой системе:
- name: Check if NGINX is installed
ansible.builtin.command: which nginx
register: nginx_installed
changed_when: false
failed_when: false
- name: Display a message if NGINX is not installed
ansible.builtin.debug:
msg: "NGINX is not installed on this system."
when: nginx_installed.rc != 0
Здесь мы проверяем, установлен ли NGINX, выполнив команду which nginx
и проверив код возврата (rc).
Строка changed_when: false
гарантирует, что Ansible не будет сообщать об изменениях при каждом выполнении плейбука Ansible, а строка failed_when: false
заставляет команду выполняться успешно и не блокировать продолжение работы плейбука, даже если пакет не найден.
Приведём пример выполнения двух вышеуказанных задач:
Наконец, вот пример с использованием функции with_items
для запуска нескольких команд в одной задаче. Такой подход может быть полезен при выполнении последовательности команд в рамках плейбука.
- name: Run multiple commands
ansible.builtin.command: "{{ item }}"
with_items:
- ls /var/log
- touch /tmp/tmp.txt
- ps aux
Модуль Ansible expect
Модуль expect в Ansible выполняет команды и отвечает на запросы. Он часто используется для автоматизации взаимодействия с приложениями, требующими ответов на подсказки.
При использовании модуля expect
необходимо указать команду, которая будет выполняться, и ответы. Ответы представляют собой сопоставление ожидаемых string/regex и string, на которую нужно ответить.
Обратите внимание, что команды не обрабатываются через shell.
Ниже приведён пример использования модуля expect
, который отвечает на ввод пользователя в процессе установки пакета.
- name: Install software
ansible.builtin.expect:
command: /path/to/softare/install.sh
responses:
Continue\?: "yes"
Please enter the installation directory: "/path/to/installation/directory"
Enable automatic updates\?: "no"
При использовании модуля expect
учитывайте последствия для безопасности, связанные с автоматизацией оперативных ответов и обработкой конфиденциальных данных.
Поскольку модуль expect
был разработан для простых случаев использования, для более сложных и продвинутых случаев следует использовать модуль shell
или scripts
.
Модуль Ansible script
Модуль script выполняет локальный скрипт на удалённых узлах после его передачи. Этот модуль принимает имя скрипта, за которым следует список аргументов, разделённых пробелами. Заданный сценарий будет обработан через среду shell на удалённом узле.
Обратите внимание, что если путь к локальному скрипту содержит пробелы, то его необходимо заключить в кавычки.
Вот базовый пример использования модуля script:
- name: Run a script on a remote node
ansible.builtin.script: /path/to/local/script.sh --flag some_value
Приведём более сложный пример использования ключевого слова args
для управления окружением сценария.
- name: Run a script with custom environment variables
ansible.builtin.script: /path/to/local/script.sh
args:
executable: /bin/bash
chdir: /tmp/
creates: /tmp/example.txt
В приведённом выше примере мы запускаем скрипт, задавая при этом используемую оболочку, каталог, в который необходимо перейти перед запуском скрипта, и проверяя существование файла перед запуском скрипта.
Как уже говорилось ранее, использование или написание модулей Ansible обычно предпочтительнее, чем запуск длинных скриптов. Попробуйте переделать сценарий в модуль Ansible, чтобы сделать свои плейбуки более читаемыми и понятными.
Модуль Ansible raw
Модуль raw выполняет необработанные команды на удалённых хостах, подобно тому как можно выполнять команды по SSH. Он выполняет низкоуровневые и грязные SSH-команды, не проходя через подсистему модулей.
Этот модуль не требует наличия Python на удалённой системе, что делает его полезным в ситуациях, когда Python не установлен или когда вы имеете дело с устройствами, не поддерживающими Python.
Этот модуль также поддерживается для Windows.
Вот пример использования модуля raw
для установки Python с помощью yum
:
- name: Install Python
ansible.builtin.raw: yum install -y python3
Стоит отметить, что модуль raw менее безопасен, идемпотентен и предсказуем, чем большинство других модулей Ansible, и не поддерживает такие расширенные возможности Ansible, как подстановка переменных, циклы, условные сигналы и т.д.
Его использования следует избегать, если нет другого выхода. Для безопасного и предсказуемого выполнения команды лучше использовать модуль shell
.
В каких случаях использовать модуль shell, а в каких — модуль command
Ранее мы подробно рассмотрели использование модулей shell
и command
и разобрали различные примеры. Далее давайте определим, в каких случаях следует предпочесть один из них другому.
В отличие от command
-модуля, shell
-модуль выполняется не непосредственно на цели, а в среде shell на цели. Это позволяет использовать специфические для оболочки возможности и функции, такие как pipe |
, перенаправление <
, >
, >>
и другие. Если ваши команды содержат какие-либо специфические для оболочки функции, такие как конвейеризация команд, подстановка переменных, перенаправление вывода в файлы, то необходимо использовать модуль shell.
Другим вариантом использования модуля shell
может быть использование в команде скрипта, который должен выполняться в контексте оболочки. Поскольку модуль shell
позволяет использовать функциональные возможности оболочки, он также может сделать ваши плейбуки менее переносимыми.
С другой стороны, модуль command
в большинстве случаев предпочтителен как наиболее простой способ выполнения команды на удалённом хосте. Использование модуля command
для выполнения команды считается более безопасным и даёт более предсказуемые результаты.
При написании плейбуков лучше всего использовать модуль command
, если только использование функций shell не требуется в явном виде.
Ознакомьтесь с Ansible best practices.
Ключевые моменты
В этой статье мы рассмотрели, как использовать модуль Ansible shell и другие различные варианты выполнения удалённых команд с помощью Ansible. Мы рассмотрели подробные примеры и объяснили тонкости и особенности каждого варианта.
Мы рассмотрели примеры каждого модуля в действии и обсудили различные варианты использования, которые заставят вас предпочесть один из них другому.
Если вы узучаете Ansible, приходите на курс «Ansible: Infrastructure as Code». Вы сможете систематизировать знания и получить практику на наших стендах.
Новый поток стартует 20 ноября. Посмотреть программу и записаться на курс можно на нашем сайте. Ждём на курсе!