Переменные в Ansible
Переменные в Ansible заслуживают отдельной статьи, так как понимание того, на каком уровне нужно определить переменную или как ее переопределить в случае необходимости, сильно упрощает понимание кем-то написанных playbook.
Содержание
Уровни объявления переменных в ansible
Всего выделил пять основных уровней объявления переменных:
- role vars
- default
- vars
- group_vars
- all
- for_group_name
- host_vars
- set_fact vars
- extra vars
Официальную документацию по переменным можно посмотреть по ссылке: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html
Важный момент: переменные, имеющие более высокий приоритет, переписывают переменные с низким приоритетом. То есть согласно приведенному выше списку, переменная, установленная через extra vars, перезапишет абсолютно любую переменную. В свою очередь, переменные из role vars имеют самый низкий приоритет и могут быть переопределены на любом следующем уровне. Наглядно покажу в примерах.
Role vars уровень
Переменные, описанные в defaults/main.yml или vars/main.yml роли. Как уже говорилось выше, это самые низкоприоритетные переменные. Используются для задания стандартных параметров, например, путей установки программ.
Для себя решил, что эти переменные нужны только для описания списка переменных. Поэтому при написании роли все переменные описываю там, потом комментирую. Для чего надо это делать? Например, кто-то увидел роль, она ему понравилась, он ее скопировал, но воспользоваться не смог, так как не смог разобраться со структурой необходимых переменных.
Group vars уровень
Переменные group vars будут использованы при совпадении имен с файлом hosts.
group_vars/all/filename.yml — описываются переменные, применение которых возможно в любой роли и на любых хостах. Например, url локального репозитория. Скорее всего этим урлом будут пользоваться несколько ролей и хостов. Нет смысла для каждой роли и хоста его определять отдельно.
group_vars/for_group_name.yml — тут собираются переменные, специфичные для нескольких серверов из группы выполнения, но, например, в разных окружениях.
Например, определен файл hosts в формате yml:
all:
children:
nginx:
children:
nginx_dev:
hosts:
nginx01:
nginx02:
nginx03:
nginx_prod:
hosts:
nginx101:
nginx102:
nginx103:
Тут наглядно видно, что группа all включает в себя все нижестоящие группы и хосты, а значит переменные, определенные в этом месте, доступны любым хостам.
Определю в group_vars/all список пользователей, которых нужно создать на сервере на шаге предварительной подготовки серверов:
users:
user01:
authorized:
- ssh-rsa ...oqGu6/0S2MCgL84ba0fy4bRPKs+7JM9blQafErAWNDz2NdbU... server
email: user01@mydomain.test
home: /home/user01
name: user01
Теперь переменная users доступна для всех хостов и ролей, даже если в блоке hosts описания playbook указана конкретная группа хостов, а не all.
Далее, предположим есть роль, которая настраивает nginx. Опустим все параметры, которые нам могут для этого пригодиться. Принимаем, что в роль по развертыванию nginx на вход нужна переменная, указывающая только имя домена. Переменная error_log пригодится чуть ниже, для демонстрации host_vars переменных. Для DEV среды домен будет mysite.dev, а для PROD mysite.prod
Создаем в папке group_vars два файла, nginx_dev и nginx_prod (файлы должны иметь такое же название как и в файле hosts) соответственно:
nginx_domain: "mysite.dev"
error_log: "/var/log/nginx/error.log"
nginx_domain: "mysite.prod"
error_log: "/var/log/nginx/error.log"
В описание playbook параметр hosts заполняем группой nginx. То есть данная роль будет выполняться на всех серверах, которые включены в группу nginx файла hosts.
- name: "Nginx config"
become: yes
hosts: nginx
roles:
- nginx_role
Таким образом, playbook выполнится на шести хостах, причем три будут настроены на mysite.dev, а три на mysite.prod. Кроме того, можно запустить playbook, но только для конкретной группы, передав нужное значение через ключ —limit или -l. Например, для DEV окружения это будет выглядеть так:
ansible-playbook nginx_playbook.yml -l nginx_dev
Переменные host vars
Уровень host_vars необходим для определения специфичных для конкретного сервера переменных. Например, IP адрес. Для наглядности в папке host_vars создается папка с именем хоста (такое же имя должно быть задано в файле hosts), а в ней файл с любым именем .yml. Более подробно про структуру ansible проекта можно почитать по ссылке.
Разберем на примере. Предположим при настройке nginx нужно указать путь до файла, в который будут записываться ошибки. Причем на пяти серверах этот путь /var/log/nginx/error.log, а на шестом: /home/user/nginx/error.log. Вариант гипотетический и мало вероятный в реальной жизни, но как пример подходит. В этом случае можно в host_vars/<server name>/vars.yml указать нужную переменную:
# для пяти серверов переменная будет выглядеть так:
error_log: "/var/log/nginx/error.log"
# для шестого так:
error_log: "/home/user/nginx/error.log"
Либо для шестого сервера переменную определить в host_vars, а для всех остальных не определять. В этом случае ansible прочитает групповые переменные, которые подходят для этих хостов, то есть для всех шести серверов определит:
error_log: "/var/log/nginx/error.log"
После этого для последнего эту переменную переопределит на уровне host_vars:
error_log: "/home/user/nginx/error.log"
Вообще переменную, которая одинакова для большинства хостов и указывает на дефолтные значения, можно определить и в role vars. Так же можно определить в group_vars в файле nginx (что соответствует группе, которая включает все хосты в файле hosts).
Set_fact переменые
Все перечисленные выше переменные в Ansible статические и не могут изменяться во время выполнения playbook. Существует способ определения переменных во время выполнения. За это отвечает модуль set_fact:
- set_fact:
report_start_date: "2021-02-15"
Очень часто set_fact идет в связке с условием when. Например, если переменная nginx_version определена, то надо ставить nginx этой версии, иначе ставить текущую последнюю. Реализовать данный механизм можно с помощью двух модулей set_fact:
- set_fact:
nginx_package_name: "nginx"
when: nginx_version is not defined
- set_fact:
nginx_package_name: "nginx-{{ nginx_version }}"
when: nginx_version is defined
Если nginx_version не определена, то имя пакета для установки будет содержать строку «nginx», что приведет к установке последней версии приложения. Второй блок set_fact будет просто пропущен, так как результат условия имеет значение false.
Если же nginx_version: «1.15.2» определена, то первый модуль будет пропущен, а во втором сформируется строка вида «nginx-1.15.2» и на установку будет передан пакет конкретной версии.
Extra vars
Переменные extra vars имеют самый высокий приоритет, поэтому если переменная определена на этом уровне, она будет использоваться.
Данные переменные используются для передачи параметров из командной строки. Например, существует роль, которая позволяет сгенерировать отчет из mysql базы. На вход роли нужно передать дату начала и дату конца периода, за который собирается отчет:
report_start_date: "2021-02-15"
report_end_date: "2021-02-16"
В случае большой частоты сборки отчета крайне неудобно каждый раз идти в репозиторий, изменять там даты, комитить, отправлять, получать на сервере с ansible и потом запускать. Для этих целей и придуманы extra vars. При запуске playbook через ключ -e передается список переменных:
ansible-playbook generate_report.yml -e "report_start_date=2021-02-15 report_end_date=2021-02-16"
Не стоит забывать ещё об одном важном моменте, относящемся к переменным. Пароли, токены и любые другие секреты нужно хранить как минимум в зашифрованном виде. Как шифровать файлы переменных в ansible, можно почитать по ссылке.
Заключение
Переменные в Ansible занимают одну из ключевых ролей. От того, насколько хорошо Вы понимаете уровни, будет зависеть корректность выполнения поставленных задач.