В ваших управляемых системах всегда будет много различий. По этой причине вам необходимо научиться работать с переменными Ansible.
В этой статье вы узнаете, как определять переменные Ansible и ссылаться на них. Вы также узнаете, как использовать факты Ansible для получения информации о ваших управляемых узлах.
Кроме того, вы также узнаете, как использовать регистры для записи вывода задачи.
Часть 1: Работа с переменными в Ansible
Начнем сначала с переменных. Имейте в виду, что все это записано в вашем YML-файле.
Определение и ссылка на переменные
Вы можете использовать ключевое слово vars для определения переменных непосредственно внутри playbook.
Например, вы можете определить переменную fav_color и установить ее значение на желтый следующим образом:
--- - name: Working with variables hosts: all vars: fav_color: yellow
Как теперь использовать (ссылаться) на переменную fav_color? Ansible использует систему шаблонов Jinja2 для работы с переменными.
Чтобы получить значение переменной fav_color ; вам нужно окружить его парой фигурных скобок следующим образом:
My favorite color is {{ fav_color }}
Обратите внимание, что если ваша переменная является первым элементом (или единственным элементом) в строке, то использование кавычек обязательно, как показано ниже:
"{{ fav_color }} is my favorite color."
Теперь давайте напишем книгу с именем variables-playbook.yml, которая объединит все это вместе:
[destroyer@andreyex]$ cat variables-playbook.yml --- - name: Working with variables hosts: node1 vars: fav_color: yellow tasks: - name: Show me fav_color value debug: msg: My favorite color is {{ fav_color }}.
Мы использовали модуль отладки вместе с параметром модуля msg, чтобы распечатать значение переменной fav_color.
Теперь запустите playbook, и вы увидите, что ваш любимый цвет отображается следующим образом:
[destroyer@andreyex]$ ansible-playbook variables-playbook.yml PLAY [Working with variables] ************************************************** TASK [Gathering Facts] ********************************************************* ok: [node1] TASK [Show me fav_color value] ************************************************* ok: [node1] => { "msg": "My favorite color is yellow." } PLAY RECAP ********************************************************************* node1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Создание списков и словарей
Вы также можете использовать списки и словари для определения многозначных переменных. Например, вы можете определить список с именем port_nums и установить его значение следующим образом:
vars: port_nums: [21,22,23,25,80,443]
Вы также могли использовать следующий способ определения port_nums, что эквивалентно:
vars: port_nums: - 21 - 22 - 23 - 25 - 80 - 443
Вы можете распечатать все значения в port_nums следующим образом:
All the ports {{ port_nums }}
Вы также можете получить доступ к определенному элементу списка:
First port is {{ port_nums[0] }}
Обратите внимание, что вы используете индекс (позицию) для доступа к элементам списка.
Вы также можете определить словарь с именем users следующим образом:
vars: users: bob: username: bob uid: 1122 shell: /bin/bash lisa: username: lisa uid: 2233 shell: /bin/sh
Есть два разных способа доступа к элементам словаря:
- dict_name[‘key’] -> users[‘bob’][‘shell’]
- dict_name.key -> users.bob.shell
Обратите внимание, что вы используете ключ для доступа к элементам словаря.
Теперь вы можете редактировать playbook – variables-playbook.yml, чтобы показать списки и словари в действии:
[destroyer@andreyex]$ cat variables-playbook.yml --- - name: Working with variables hosts: node1 vars: port_nums: [21,22,23,25,80,443] users: bob: username: bob uid: 1122 shell: /bin/bash lisa: username: lisa uid: 2233 shell: /bin/sh tasks: - name: Show 2nd item in port_nums debug: msg: SSH port is {{ port_nums[1] }} - name: Show the uid of bob debug: msg: UID of bob is {{ users.bob.uid }}
Теперь вы можете запустить playbook, чтобы отобразить второй элемент в port_nums и показать uid bob:
[destroyer@andreyex]$ ansible-playbook variables-playbook.yml PLAY [Работа с переменными] ************************************************** TASK [Показать 2-й элемент в port_nums] ********************************************** ok: [node1] => { "msg": "SSH port is 22" } TASK [Показать идентификатор bob] ***************************************************** ok: [node1] => { "msg": "UID of bob is 1122" }
Включая внешние переменные
Точно так же, как вы можете импортировать (или включать) задачи в playbook. То же самое можно сделать и с переменными. То есть в playbook вы можете включать переменные, определенные во внешнем файле.
Чтобы продемонстрировать, давайте создадим файл с именем myvars.yml , который содержит наш port_nums список и пользователей словаря:
[destroyer@andreyex]$ cat myvars.yml --- port_nums: [21,22,23,25,80,443] users: bob: username: bob uid: 1122 shell: /bin/bash lisa: username: lisa uid: 2233 shell: /bin/sh
Теперь вы можете использовать ключевое слово vars_files в вашем файле variables-playbook.yml, чтобы включить переменные в myvars.yml следующим образом:
[destroyer@andreyex]$ cat variables-playbook.yml --- - name: Working with variables hosts: node1 vars_files: myvars.yml tasks: - name: Show 2nd item in port_nums debug: msg: SSH port is {{ port_nums[1] }} - name: Show the uid of bob debug: msg: UID of bob is {{ users.bob.uid }}
Имейте в виду, что vars_files выполняет предварительную обработку и загружает переменные прямо в начале playbook. Вы также можете использовать модуль include_vars для динамической загрузки ваших переменных в playbook:
[destroyer@andreyex]$ cat variables-playbook.yml --- - name: Working with variables hosts: node1 tasks: - name: Load the variables include_vars: myvars.yml - name: Show 2nd item in port_nums debug: msg: SSH port is {{ port_nums[1] }}
Получение пользовательского ввода
Вы можете использовать ключевое слово vars_prompt, чтобы предложить пользователю установить значение переменной во время выполнения.
Например, следующий сборник программ greet.yml просит запущенного пользователя ввести свое имя, а затем отображает персонализированное приветственное сообщение:
[destroyer@andreyex]$ cat greet.yml --- - name: Greet the user hosts: node1 vars_prompt: - name: username prompt: What's your name? private: no tasks: - name: Greet the user debug: msg: Hello {{ username }}
Обратите внимание, что я использовал private: no, чтобы вы могли видеть свой ввод на экране по мере его ввода; по умолчанию он скрыт.
Теперь запустите playbook и введите свое имя:
[destroyer@andreyex]$ ansible-playbook greet.yml What's your name?: destroyer PLAY [Приветствуем пользователей] ********************************************************** TASK [Приветствуем пользователей] ********************************************************** ok: [node1] => { "msg": "Hello destroyer" }
Установка переменных хоста и группы
Вы можете установить переменные, специфичные для ваших управляемых хостов. Поступая так, вы можете создавать гораздо более эффективные playbooks, поскольку вам не нужно писать повторяющиеся задачи для разных узлов или групп.
Для демонстрации отредактируйте файл инвентаризации так, чтобы управляемые узлы были сгруппированы в следующие три группы:
[destroyer@andreyex]$ cat myhosts [proxy] node1 [webservers] node2 node3 [dbservers] node4
Теперь создадим переменные, специфичные для ваших управляемых узлов; Во- первых, вам нужно создать каталог с именем host_vars. Затем внутри host_vars вы можете создать файлы переменных с именами файлов, соответствующими именам хостов вашего узла:
[destroyer@andreyex]$ mkdir host_vars [destroyer@andreyex]$ echo "message: I am a Proxy Server" >> host_vars/node1.yml [destroyer@andreyex]$ echo "message: I am a Web Server" >> host_vars/node2.yml [destroyer@andreyex]$ echo "message: I am a Web Server" >> host_vars/node3.yml [destroyer@andreyex]$ echo "message: I am a Database Server" >> host_vars/node4.yml
Теперь давайте создадим книгу с именем motd.yml, которая демонстрирует, как работают host_vars :
[destroyer@andreyex]$ cat motd.yml --- - name: Set motd on all nodes hosts: all tasks: - name: Set motd = value of message variable. copy: content: "{{ message }}" dest: /etc/motd
Мы использовали модуль копирования, чтобы скопировать содержимое переменной сообщения в файл /etc/motd на всех узлах. Теперь после запуска playbook; вы должны увидеть, что содержимое /etc/motd было обновлено на всех узлах с соответствующим значением сообщения :
[destroyer@andreyex]$ ansible all -m command -a "cat /etc/motd" node1 | CHANGED | rc=0 >> I am a Proxy Server node2 | CHANGED | rc=0 >> I am a Web Server node3 | CHANGED | rc=0 >> I am a Web Server node4 | CHANGED | rc=0 >> I am a Database Server
Потрясающие! Точно так же вы можете создать каталог group_vars, а затем включить все связанные с группой переменные в имя файла, которое соответствует имени группы, как показано ниже:
[destroyer@andreyex]$ mkdir group_vars [destroyer@andreyex]$ echo "pkg: squid" >> group_vars/proxy [destroyer@andreyex]$ echo "pkg: httpd" >> group_vars/webservers [destroyer@andreyex]$ echo "pkg: mariadb-server" >> group_vars/dbservers
Мы позволили вам создать playbook, который работает на всех узлах; каждый узел установит пакет, заданный в соответствующей групповой переменной узла pkg.
Понимание приоритета переменных
Как вы уже видели; Переменные Ansible могут быть установлены на разных уровнях (или уровнях).
Если одна и та же переменная установлена на разных уровнях; самый конкретный уровень получает приоритет. Например, переменная, установленная на уровне воспроизведения, имеет приоритет над той же переменной, установленной на уровне хоста ( host_vars ).
Более того, переменная, заданная в командной строке с помощью –extra-vars, имеет наивысший приоритет, то есть она перезапишет все остальное.
Чтобы продемонстрировать это, давайте создадим книгу с именем variable-Priordence.yml, которая содержит следующий контент:
[destroyer@andreyex]$ cat variable-precedence.yml --- - name: Understanding Variable Precedence hosts: node1 vars: fav_distro: "Ubuntu" tasks: - name: Show value of fav_distro debug: msg: Favorite distro is {{ fav_distro }}
Теперь давайте запустим playbook, используя параметр -e (–extra-vars ), чтобы установить значение переменной fav_distro на «CentOS» из командной строки:
[destroyer@andreyex]$ ansible-playbook variable-precedence.yml -e "fav_distro=CentOS" PLAY [Understanding Variable Precedence] *************************************** TASK [Show value of fav_distro] ************************************************ ok: [node1] => { "msg": "Favorite distro is CentOS" }
Обратите внимание на то, что значение «CentOS» в командной строке fav_distro имеет приоритет над значением «Ubuntu» в fav_distro.
Часть 2: Сбор и демонстрация фактов
Вы можете получить или обнаружить переменные, которые содержат информацию о ваших управляемых хостах. Эти переменные называются фактами, и Ansible использует модуль настройки для сбора этих фактов. IP-адрес одного из ваших управляемых узлов является примером факта.
Вы можете запустить следующую специальную команду, чтобы собрать и показать все факты о node1:
[destroyer@andreyex]$ ansible node1 -m setup
Вот результат:
node1 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "10.0.0.5" ], "ansible_all_ipv6_addresses": [ "fe80::20d:3aff:fe0c:54aa" ], "ansible_apparmor": { "status": "disabled" }, "ansible_architecture": "x86_64", "ansible_bios_date": "06/02/2017", "ansible_bios_version": "090007", "ansible_cmdline": { "BOOT_IMAGE": "(hd0,gpt1)/vmlinuz-4.18.0-193.6.3.el8_2.x86_64", "console": "ttyS0,115200n8", "earlyprintk": "ttyS0,115200", "ro": true, "root": "UUID=6785aa9a-3d19-43ba-a189-f73916b0c827", "rootdelay": "300", "scsi_mod.use_blk_mq": "y" }, "ansible_default_ipv4": { "address": "10.0.0.5", "alias": "eth0", "broadcast": "10.0.0.255", "gateway": "10.0.0.1", "interface": "eth0", "macaddress": "00:0d:3a:0c:54:aa",
Это только часть всех фактов, связанных с node1, которые вы увидите на своем терминале. Обратите внимание, как факты хранятся в словарях или списках, и все они принадлежат словарю ansible_facts.
По умолчанию модуль настройки автоматически вызывается плейбуками для обнаружения фактов. Возможно, вы заметили, что обнаружение фактов происходит с самого начала, когда вы запускаете любой из ваших playbooks:
[destroyer@andreyex]$ ansible-playbook motd.yml PLAY [Set motd on all nodes] *************************************************** TASK [Gathering Facts] ********************************************************* ok: [node4] ok: [node3] ok: [node2] ok: [node1]
Вы можете отключить сбор фактов, установив для параметра gather_facts логическое значение false прямо в заголовке воспроизведения следующим образом:
[destroyer@andreyex]$ cat motd.yml --- - name: Set motd on all nodes gather_facts: false hosts: all tasks: - name: Set motd = value of message variable. copy: content: "{{ message }}" dest: /etc/motd
Если вы снова запустите motd.yaml playbook; он пропустит сбор фактов:
[destroyer@andreyex]$ ansible-playbook motd.yml PLAY [Set motd on all nodes] *************************************************** TASK [Set motd = value of message variable.] ********************************
Таким же образом вы показываете значение переменной; вы также можете использовать, чтобы показать ценность факта. В следующем сборнике show-fact.yml показана ценность нескольких фактов на node1 :
[destroyer@andreyex]$ cat show-facts.yml --- - name: show some facts hosts: node1 tasks: - name: display node1 ipv4 address debug: msg: IPv4 address is {{ ansible_facts.default_ipv4.address }} - name: display node1 fqdn debug: msg: FQDN is {{ ansible_facts.fqdn }} - name: display node1 OS distribution debug: msg: OS Distro is {{ ansible_facts.distribution }}
Теперь запустите playbook, чтобы отобразить значения фактов:
[destroyer@andreyex]$ ansible-playbook show-facts.yml PLAY [show some facts] ****** TASK [Gathering Facts] ******* ok: [node1] TASK [display node1 ipv4 address] ******* ok: [node1] => { "msg": "IPv4 address is 10.0.0.5" } TASK [display node1 fqdn] ******** ok: [node1] => { "msg": "FQDN is node1.linuxhandbook.local" } TASK [display node1 OS distribution] ****** ok: [node1] => { "msg": "OS Distro is CentOS" } PLAY RECAP ********** node1 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Создание постоянных фактов
Вы можете создать свои собственные пользовательские факты. Для этого вы можете использовать модуль set_fact для временного добавления фактов или каталог /etc/ansible/facts.d для добавления постоянных фактов на ваши управляемые узлы.
Мы собираемся показать вам, как добавить постоянные факты к вашим управляемым узлам. Это трехэтапный процесс:
- Создайте файл фактов на вашем контрольном узле.
- Создайте каталог /etc/ansible/facts.d на управляемом узле (ах).
- Скопируйте файл фактов (шаг 1) с управляющего узла на ваш управляемый (ые) узел (ы).
Итак, сначала давайте создадим файл cool.fact на вашем управляющем узле, который включает некоторые интересные факты:
[destroyer@andreyex]$ cat cool.fact [fun] kiwi=fruit matrix=movie octupus='8 legs'
Обратите внимание, что имя файла фактов должно иметь расширение.fact.
На втором этапе вы собираетесь использовать файловый модуль для создания и каталог /etc/ansible/facts.d на управляемом узле (ах). И , наконец , на третьем этапе, вы собираетесь использовать копию модуль для копирования cool.fact файла из узла управления для управляемого узла (ов).
В следующем сборнике custom-fact.yml сочетаются шаги 2 и 3:
[destroyer@andreyex]$ cat custom-facts.yml --- - name: Adding custom facts to node1 hosts: node1 tasks: - name: Create the facts.d directory file: path: /etc/ansible/facts.d owner: destroyer mode: 775 state: directory - name: Copy cool.fact to the facts.d directory copy: src: cool.fact dest: /etc/ansible/facts.d
Теперь запустите playbook:
[destroyer@andreyex]$ ansible-playbook custom-facts.yml PLAY [Adding custom facts to node1] ********************************** TASK [Gathering Facts] **************************** ok: [node1] TASK [Create the facts.d directory] ****************************** changed: [node1] TASK [Copy cool.fact to the facts.d directory] ********************** changed: [node1]
В cool теперь постоянно часть NODE1 фактов; вы можете проверить с помощью следующей специальной команды:
[destroyer@andreyex]$ ansible node1 -m setup -a "filter=ansible_local" "ansible_local": { "cool": { "fun": { "kiwi": "fruit", "matrix": "movie", "octupus": "'8 legs'" } } }
Теперь вы можете отобразить факт octupus в учебнике следующим образом:
An octopus has {{ ansible_local.cool.fun.octupus }}
Часть 3: Захват вывода с помощью регистров в Ansible
Некоторые задачи не отображают никаких результатов при запуске playbook. Например, выполнение команд на ваших управляемых узлах с использованием команд , оболочки или необработанных модулей не будет отображать никаких выходных данных при запуске playbook.
Вы можете использовать регистр для захвата вывода задачи и сохранения его в переменной. Это позволяет вам использовать вывод задачи в другом месте книги, просто обращаясь к зарегистрированной переменной.
Следующий файл register-playbook.yml показывает вам, как записать вывод задачи в зарегистрированной переменной, а затем отобразить его содержимое:
[destroyer@andreyex]$ cat register-playbook.yml --- - name: Register Playbook hosts: proxy tasks: - name: Run a command command: uptime register: server_uptime - name: Inspect the server_uptime variable debug: var: server_uptime - name: Show the server uptime debug: msg: "{{ server_uptime.stdout }}"
Playbook запускается с выполнения команды uptime на хостах группы прокси (node1) и регистрирует выходные данные команды в переменной server_uptime.
Затем вы используете модуль отладки вместе с параметром var module для проверки переменной server_uptime. Обратите внимание, что здесь не нужно заключать переменную в фигурные скобки.
Наконец, последняя задача в playbook показывает вывод (stdout) зарегистрированной переменной server_uptime.
Запустите playbook, чтобы увидеть все это в действии:
[destroyer@andreyex]$ ansible-playbook register-playbook.yml PLAY [Register Playbook Showcase] ********************************************** TASK [Gathering Facts] ********************************************************* ok: [node1] TASK [Run a command] *********************************************************** changed: [node1] TASK [Inspect the server_uptime variable] ************************************** ok: [node1] => { "server_uptime": { "changed": true, "cmd": [ "uptime" ], "delta": "0:00:00.004221", "end": "2020-10-29 05:04:36.646712", "failed": false, "rc": 0, "start": "2020-10-29 05:04:36.642491", "stderr": "", "stderr_lines": [], "stdout": " 05:04:36 up 3 days, 6:56, 1 user, load average: 0.24, 0.07, 0.02", "stdout_lines": [ " 05:04:36 up 3 days, 6:56, 1 user, load average: 0.24, 0.07, 0.02" ] } } TASK [Show the server uptime] ************************************************** ok: [node1] => { "msg": " 05:04:36 up 3 days, 6:56, 1 user, load average: 0.24, 0.07, 0.02" } PLAY RECAP ********************************************************************* node1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Обратите внимание, что зарегистрированная переменная server_uptime на самом деле является словарем, содержащим множество других ключей помимо ключа stdout. Она также содержит другие ключи, такие как rc (код возврата), start (время выполнения команды), end (время завершения команды), stderr (любые ошибки) и т. д.