Эта статья блога посвящена переменным Ansible, которые позволяют нам параметризовать различные компоненты Ansible. Переменные хранят значения для повторного использования внутри проекта Ansible.
Если вы все еще учитесь использовать Ansible, вам также могут быть полезны вводные статьи в блоге Ansible Tutorial или Working with Ansible Playbooks. Вы можете найти код этой статьи в этом репозитории, если хотите проследить за ходом работы.
- Почему переменные полезны в Ansible
- Правила наименования переменных
- Определение и ссылка на простые переменные
- Список, словарь и вложенные переменные
- Специальные переменные
- Регистрация переменных
- Совместное использование переменных с помощью YAML-якорей и псевдонимов
- Область действия переменных
- Опции установки переменных и приоритет
- Где устанавливать переменные и лучшие практики
Почему переменные полезны в Ansible
Использование переменных упрощает управление динамическими значениями в проекте Ansible и потенциально может уменьшить количество человеческих ошибок. С помощью переменных мы получаем удобный способ обработки вариаций и различий между разными средами и системами.
Еще одним преимуществом переменных в Ansible является то, что у нас есть возможность определять их в разных местах с разным приоритетом в соответствии с нашим сценарием использования. Мы также можем регистрировать новые переменные в наших плейбуках, используя возвращаемое значение задачи.
Факты Ansible — это особый тип переменных, которые Ansible получает с любого удаленного узла, чтобы мы могли использовать их в проектах Ansible. Например, мы можем получить информацию о дистрибутиве операционной системы с помощью ansible_distribution, информацию об устройствах на хосте, версию python, которую использует Ansible, с помощью ansible_python_version, архитектуру системы и т.д. Чтобы получить доступ к этим данным, мы должны обратиться к переменной ansible_facts.
Правила наименования переменных
Ansible имеет строгий набор правил для создания правильных имен переменных. Имена переменных могут содержать только буквы, цифры и знаки подчеркивания и должны начинаться с буквы или знака подчеркивания. Некоторые строки зарезервированы для других целей и не являются допустимыми именами переменных, например, Python Keywords или Playbook Keywords.
Определение и ссылка на простые переменные
Самый простой вариант использования переменных — это определение имени переменной с одним значением с помощью стандартного синтаксиса YAML. Хотя этот шаблон можно использовать во многих местах, для простоты мы покажем пример в плейбуке.
- name: Example Simple Variable
hosts: all
become: yes
vars:
username: bob
tasks:
- name: Add the user {{ username }}
ansible.builtin.user:
name: "{{ username }}"
state: present
В приведенном выше примере после блока vars мы определяем переменную username и присваиваем ей значение bob. Позже, чтобы сослаться на это значение в задаче, мы используем синтаксис Jinja2 следующим образом "{{ имя пользователя }}"
.
Если значение переменной начинается с фигурных скобок, мы должны заключить все выражение в кавычки, чтобы YAML правильно интерпретировал синтаксис.
Список, словарь и вложенные переменные
Существует множество других возможностей для определения более сложных переменных, таких как списки, словари и вложенные структуры. Чтобы создать переменную с несколькими значениями, можно использовать синтаксис списков YAML:
vars:
version:
- v1
- v2
- v3
Чтобы обратиться к определенному значению из списка, мы должны выбрать нужное поле. Например, чтобы получить доступ к третьему значению v3:
version: "{{ version[2] }}"
Еще одна полезная возможность — хранить пары ключ-значение в переменных в виде словарей. Например:
vars:
users:
- user_1: maria
- user_2: peter
- user_3: sophie
Аналогично, для ссылки на третье поле из словаря используйте скобку или точку:
users['user_3']
users.user_3
Обратите внимание, что предпочтительнее использовать скобочную нотацию, поскольку в особых случаях могут возникнуть проблемы с использованием точечной нотации.
Иногда нам приходится создавать или использовать вложенные структуры переменных. Например, факты — это вложенные структуры данных. Для ссылки на вложенные переменные необходимо использовать скобки или точечную нотацию.
vars:
cidr_blocks:
production:
vpc_cidr: "172.31.0.0/16"
staging:
vpc_cidr: "10.0.0.0/24"
tasks:
- name: Print production vpc_cidr
ansible.builtin.debug:
var: cidr_blocks['production']['vpc_cidr']
Специальные переменные
Существуют определенные типы переменных, которые мы считаем специальными в контексте Ansible. К ним относятся магические переменные, переменные соединения и факты. Имена этих переменных зарезервированы.
Ansible позволяет нам получить доступ к информации о себе, хостах, группах, инвентаре, ролях и других манифестах Ansible с помощью так называемых магических переменных. Полный список различных вариантов посмотрите здесь.
Мы уже говорили о фактах. Эти переменные содержат всю информацию, которую Ansible может получить от текущего хоста. Чтобы использовать их, Ansible должен сначала собрать их. Чтобы увидеть все факты, которые можно собрать на хосте, выполните команду:
ansible <hostname> -m ansible.builtin.setup
Наконец, у нас есть переменные подключения. Они используются для настройки поведения при выполнении Ansible и действий на хостах. Наиболее распространенные из них настраивают пользователя, под которым Ansible входит в систему, устанавливают эскалацию привилегий, задают IP целевого хоста и т.д.
Регистрация переменных
В ходе выполнения задач мы можем столкнуться с необходимостью использовать результат выполнения задачи в качестве переменной, которую мы можем использовать в следующих задачах. Мы можем использовать ключевое слово register для создания собственных переменных из вывода задачи.
- name: Example Register Variable Playbook
hosts: all
tasks:
- name: Run a script and register the output as a variable
shell: "find hosts"
args:
chdir: "/etc"
register: find_hosts_output
- name: Use the output variable of the previous task
debug:
var: find_hosts_output
В приведенном выше примере мы регистрируем вывод команды find /etc/hosts и показываем, как можно использовать переменную в следующей задаче, выводя ее значение.
Мощным паттерном является комбинирование зарегистрированных переменных с условиями для создания задач, которые будут выполняться только при выполнении определенных пользовательских условий.
- name: Example Registered Variables Conditionals
hosts: all
tasks:
- name: Register an example variable
shell: cat /etc/hosts
register: hosts_contents
- name: Check if hosts file contains the word "localhost"
debug:
msg: "/etc/hosts file contains the word localhost"
when: hosts_contents.stdout.find("localhost") != -1
var: find_hosts_output
Здесь мы зарегистрировали в переменной hosts_contents содержимое файла /etc/hosts, и выполняем вторую задачу, только если в файле есть слово localhost.
Поскольку зарегистрированные переменные хранятся в памяти, их невозможно использовать в будущих постановках, и они доступны только для текущего запуска плейбука.
Совместное использование переменных с помощью YAML-якорей и псевдонимов
Когда мы хотим повторно использовать и совместно использовать переменные, мы можем воспользоваться якорями и псевдонимами Y_AML. Они обеспечивают нам большую гибкость в работе с общими переменными и помогают сократить повторение данных.
Якоря определяются с помощью &, а затем на них ссылаются с помощью псевдонима, обозначаемого *****. Давайте проверим практический пример в плейбуке.
- name: Example Anchors and Aliases
hosts: all
become: yes
vars:
user_groups: &user_groups
- devs
- support
user_1:
user_info: &user_info
name: bob
groups: *user_groups
state: present
create_home: yes
user_2:
user_info:
<<: *user_info
name: christina
user_3:
user_info:
<<: *user_info
name: jessica
groups: support
tasks:
- name: Add several groups
ansible.builtin.group:
name: "{{ item }}"
state: present
loop: "{{ user_groups }}"
- name: Add several users
ansible.builtin.user:
<<: *user_info
name: "{{ item.user_info.name }}"
groups: "{{ item.user_info.groups }}"
loop:
- "{{ user_1 }}"
- "{{ user_2 }}"
- "{{ user_3 }}"
Здесь, поскольку некоторые опции являются общими для разных пользователей, вместо того, чтобы переписывать одни и те же значения, мы разделяем общие значения с помощью якоря &user_info. Для каждого последующего объявления пользователя мы используем псевдоним *user_info, чтобы избежать повторений, насколько это возможно.
Значения для state и create_home одинаковы для всех пользователей, а имя и группы заменяются с помощью оператора слияния <<.
Аналогично, мы повторно используем объявление user_groups в определении якоря user_info. Таким образом, нам не придется снова вводить те же группы для пользователя_2, но при этом у нас остается возможность переопределить группы, как это делается для пользователя_3.
В результате пользователь_1 и пользователь_2 будут добавлены в группы devs и support, а пользователь_3 будет добавлен только в группу support.
Область действия переменных
Ansible предоставляет множество вариантов установки переменных, и окончательное решение о том, где их установить, принимается нами на основе области применения, которую мы хотим, чтобы они имели. Концептуально существует три основных варианта масштабирования переменных.
Во-первых, у нас есть глобальная область действия, где значения устанавливаются для всех хостов. Это может быть определено конфигурацией Ansible, переменными окружения и командной строкой.
Мы устанавливаем значения для конкретного узла или группы узлов, используя область видимости узла. Например, есть возможность определить некоторые переменные для каждого хоста в файле инвентаризации.
Наконец, есть область видимости игры, где значения задаются для всех хостов в контексте игры. Примером может служить раздел vars, который мы видели в предыдущих примерах в каждой книге воспроизведения.
Опции установки переменных и приоритет
Переменные могут быть определены в Ansible во многих различных местах. Есть опции для установки переменных в плейбуках, ролях, инвентаре, файлах var и командной строке. Давайте рассмотрим некоторые из этих опций.
Как мы уже видели ранее, самый простой способ — определить переменные в пьесе с помощью раздела vars.
- name: Set variables in a play
hosts: all
vars:
version: 12.7.1
Другой вариант — определить переменные в файле инвентаря. Мы можем задать переменные для каждого хоста или установить общие переменные для групп. В этом примере для каждого хоста в качестве переменной хоста задается свой пользователь ansible для подключения, а в качестве переменной группы — один и тот же порт HTTP для всех веб-серверов.
[webservers]
webserver1 ansible_host=10.0.0.1 ansible_user=user1
webserver2 ansible_host=10.0.0.2 ansible_user=user2
[webservers:vars]
http_port=80
Чтобы лучше организовать наши переменные, мы можем собрать их в отдельные файлы переменных хоста и группы. В том же каталоге, где хранятся файлы инвентаризации или плейбука, можно создать две папки с именами group_vars и host_vars, которые будут содержать файлы переменных. Например:
group_vars/databases
group_vars/webservers
host_vars/host1
host_vars/host2
Переменные также могут быть заданы в пользовательских файлах var. Давайте проверим пример, в котором используются переменные из внешнего файла и каталогов group_vars и host_vars.
- name: Example External Variables file
hosts: all
vars_files:
- ./vars/variables.yml
tasks:
- name: Print the value of variable docker_version
debug:
msg: "{{ docker_version}} "
- name: Print the value of group variable http_port
debug:
msg: "{{ http_port}} "
- name: Print the value of host variable app_version
debug:
msg: "{{ app_version}} "
Файл vars/variables.yml:
docker_version: 20.10.12
Файл group_vars/webservers:
http_port: 80
ansible_host: 127.0.0.1
ansible_user: vagrant
Файл host_vars/host1:
app_version: 1.0.1
ansible_port: 2222
ansible_ssh_private_key_file: ./.vagrant/machines/host1/virtualbox/private_key
Файл host_vars/host2:
app_version: 1.0.2
ansible_port: 2200
ansible_ssh_private_key_file: ./.vagrant/machines/host2/virtualbox/private_key
Файл инвентаризации содержит группу с именем webservers, в которую входят два наших хоста, host1 и host2:
[webservers]
host1
host2
Если мы запустим этот плейбук, мы заметим, что в обоих хостах используется одно и то же значение для групповой переменной http_port, но разное значение для переменной app_version.
Хорошим примером использования отдельных файлов переменных является то, что вы можете хранить в них чувствительные значения, не храня их в плейбуках или системах контроля исходного кода.
Иногда нам может быть полезно определить или переопределить переменные во время выполнения, передав их в командной строке с аргументом —extra-vars или -e. Например:
ansible-playbook example-external-vars.yml --extra-vars "app_version=1.0.3"
Поскольку переменные могут быть заданы в нескольких местах, Ansible применяет приоритет переменных для выбора значения переменной в соответствии с некоторой иерархией. Общее правило заключается в том, что переменные, определенные в более явной области действия, имеют более высокий приоритет.
Например, значения по умолчанию роли отменяются почти всеми другими параметрами. Переменные также сглаживаются для каждого хоста перед каждой игрой, поэтому все групповые и хостовые переменные объединяются. Переменные хоста имеют более высокий приоритет, чем переменные группы.
Явные определения переменных, такие как каталог vars или задача include_vars, переопределяют переменные из инвентаря. Наконец, дополнительные переменные, определенные во время выполнения, всегда имеют приоритет. Полный список опций и их иерархию можно найти в официальной документации Understanding variable precedence.
Где устанавливать переменные и лучшие практики
Поскольку Ansible предоставляет огромное количество опций для определения переменных, может возникнуть путаница при определении лучшего способа и места для их установки. Давайте проверим некоторые общие & лучшие практики по установке переменных, которые могут помочь нам лучше организовать наши проекты Ansible.
- Всегда давайте описательные и понятные имена вашим переменным. Уделите время тому, чтобы как следует подумать о том, как назвать переменные, это всегда приносит долгосрочные плоды.
- Если для общих переменных существуют значения по умолчанию, установите их в group_vars/all.
- Предпочтите устанавливать групповые и хостовые переменные в каталогах group_vars и host_vars, а не в файле инвентаризации.
- Если переменные, связанные с географией или поведением, привязаны к определенной группе, предпочитайте задавать их как групповые переменные.
- Если вы используете роли, всегда устанавливайте переменные роли по умолчанию в файле roles/your_role/defaults/main.yml.
- Когда вы вызываете роли, передавайте переменные, которые вы хотите переопределить, в качестве параметров, чтобы ваши пьесы было легче читать.
roles:
- role: example_role
vars:
example_var: 'example_string'
- Вы всегда можете использовать —extra-vars или -e, чтобы отменить все остальные параметры.
- Не храните важные переменные в репозитории исходного кода в виде обычного текста. В таких случаях вы можете использовать Ansible Vault.
В целом, старайтесь максимально упростить использование переменных. Не нужно использовать все существующие опции и разбрасывать определение переменных повсюду, поскольку это затрудняет отладку проектов Ansible. Попробуйте найти структуру, которая лучше всего подходит для ваших нужд, и придерживайтесь ее!
Ключевые точки
В этой статье мы подробно рассмотрели переменные Ansible Variables и увидели, как их можно определять и использовать в плейбуках. Более того, мы рассмотрели различные варианты их совместного использования, настройки и ссылок на них, а также некоторые рекомендации и лучшие практики для облегчения нашего путешествия по Ansible.
Спасибо за чтение, и я надеюсь, что эта статья «Переменные Ansible» понравилась вам так же, как и мне.