Переменные очень активно используются в Ansible. Но один из неприятных моментов заключается в том, что Ansible предлагает слишком много свободы. В этом есть как свои преимущества, так и недостатки. Недостаток состоит в сложности наряду с высокой ответственностью, а преимущество — в гибкости. Давайте вспомним и упорядочим то, что мы знаем о переменных Ansible.
Переменные можно разделить на две категории:
Переменные в отдельных файлах (в таблице ниже "Filesystem").
Переменные в коде (в таблице ниже "Code").
Теперь, если вы посмотрите на их приоритет, то все встает на свои места.
Filesystem-переменные имеют более низкий приоритет по сравнению с Code-переменными. Обратили ли вы внимание на упомянутую выше гибкость и чрезмерную свободу? Давайте продолжим далее с учетом этих знаний.
1. Объявляйте переменные в отдельных файлах
Переменные лучше располагать в отдельных файлах (Inventory
, group_vars
, host_vars
, role/defaults/main.yml
и role/vars/main.yml
). Все "постоянные" переменные должны быть явно определены. Постоянные переменные — это переменные, влияющие на роль или поведение плейбука. В отличие от "временных" переменных, используемых в качестве буфера для временного хранения значений, часто с ограниченной областью видимости. Например, переменные, объявленные в vars
, существуют только внутри block
. Например:
- name: Variables scope
hosts: localhost
connection: local
vars:
MY_VAR: "I am global var"
tasks:
- block:
- name: Print variable inside the block.
debug:
var: MY_VAR
vars:
MY_VAR: "I am local var"
- name: Print variable outside the block.
debug:
var: MY_VAR
PLAY [Variables scope]
TASK [Gathering Facts]
ok: [localhost]
TASK [Print variable inside the block.]
ok: [localhost] => {
"MY_VAR": "I am local var"
}
TASK [Print variable outside the block.]
ok: [localhost] => {
"MY_VAR": "I am global var"
}
Таким образом, мы должны определять переменные в файлах. И все переменные должны быть определены явно. Для роли есть файл defaults/main.yml
. Значения в этом файле имеют самый низкий приоритет, поэтому здесь можно размещать в том числе пустые переменные. Это облегчит жизнь контрибьюторам, особенно тем, кто видит код впервые.
2. Используйте README
Если роль использует много различных переменных, возможно, все они даже нужные и полезные, то описывайте их в файле README. Команда ansible-galaxy init поможет вам в этом, сгенерировав шаблон README. Вероятно, вам самому, в незнакомом репозитории будет приятно увидеть README с информацией о том, что роль ожидает увидеть на входе и что будет на выходе. Плохим примером будет разделение кода и описания. Например, код в git, а описание на wiki-странице. Нет никакой гарантии, что контрибьюторы обновят и код, и wiki-страницу. Обычно после пул реквеста работа заканчивается.
3. Используйте префиксы
У всех "постоянных" переменных (упомянутых в первом совете) должны быть префиксы. В качестве префикса лучше всего использовать имя роли. Это очень полезно, когда переменные для разных ролей нужно разместить в одном месте. Например, что произойдет в плейбуке с несколькими ролями, если все роли используют переменную port? Добавляя префикс, мы гарантируем, что одни переменные не будут перезаписаны другими. Пример: роль — consul. переменная — url, имя переменной — consul_url.
4. Называйте задачи осмысленными именами
У задач в Ansible есть имена. Используйте для них осмысленные названия, так как они отображаются в выводе. Помните: это ваш лог, по которому в случае ошибок вы можете понять что пошло не так.
Например:
# No name/description
- copy: dest=/tmp/text.txt, content="bla-bla"
- name: Print variable global var.
debug:
var: MY_VAR
TASK [copy]
changed: [localhost]
TASK [Print variable global var.] *
ok: [localhost] => {
"MY_VAR": "I am global var"
}
5. DRY (Don't Repeat Yourself)
Ansible похож на обычный язык программирования. И как и в обычном языке, в Ansible есть различные механизмы, которые помогают следовать принципу DRY (Don't Repeat Yourself). Но это требует предварительного планирования организации вашего кода. При написании кода думайте, чтобы его можно было переиспользовать.
Крупные блоки:
Блоки внутри роли: (include/import)tasks
, (include/import)role
. Как это может использоваться? Например, вы используете модуль uri для отправки API-запросов. Допустим, это POST-запросы. Вместо того чтобы повторять 10 раз uri со всеми настройками, можно создать что-то вроде метода и использовать его где угодно. Аналогично методам в обычных языках программирования наш метод тоже принимает входные параметры.
Например: send_post.yml
- name: .::::::::::::. [ Sent POST request ] .::::::::::::.
uri:
url: "{{ URL }}"
method: POST
status_code: 200
body: "{{ BODY_VAR | to_nice_json }}"
body_format: json
validate_certs: yes
client_cert: tls.crt
client_key: tls.key
register: return_values
when: BODY_VAR is defined
Этот код можно использовать повторно.
- name: Bla-bla
include_tasks: send_post.yml
vars:
URL: "{{ main_url }}/{{ item }}"
BODY_VAR: "{{ item }}"
URL и BODY_VAR — это параметры метода.
6. Используйте блоки (block)
Используйте block.
Описывайте параметры задач только один раз, группируя их. Также block может использоваться аналогично блоку try / catch в традиционных языках программирования.
- block:
...
rescue:
...
block/rescue
— отличная альтернатива ignore_errors
. По сути, расширенная обработка ошибок. Это может быть полезным, например, когда вам нужно выполнить какой-то код в плейбуке, даже в случае сбоя. Например, удалить некоторые файлы.
- block:
- name: .....
- name: .....
- name: .....
always:
file:
path: /tmp/xxxx
state: absent
7. Не используйте модули command и shell
Старайтесь не использовать модули command
и shell
, потому что они не идемпотентные. Хотя есть ряд приемов, которые помогают смягчить эту проблему. Используйте:
when
creates
(если файл существует, то этот шаг не выполняется).removes
(если файл существует, то этот шаг выполняется).changedwhen
.
Тем не менее если это возможно, держитесь подальше от command
и shell
.
8. Не используйте теги
Не используйте теги. Теги могут стать ночным кошмаром для людей, которые видят ваш код впервые. Комбинации тегов увеличивает количество возможных вариантов выполнения плейбука. Но если у вас нет выбора, то подробно описывайте теги и их комбинации в README. Однако не все теги плохи. Есть исключения. Например, always
, never
— читать подробнее.
skip_ansible_lint
— пропустить ansible-lint
для задачи.
9. Принцип минимальных привилегий
Используйте принцип минимальных привилегий. Параметр become
должен быть no
, пока вам это действительно не будет необходимо. Всегда используйте become
явно. Например:
---
- hosts: wordpress
become: no
...
role:
- role: wordpress
tasks/main.yml
---
- name: Install mysql-server pkg
apt:
name: mysql-server
state: present
become: yes
10. Используйте YAML-синтаксис для параметров
Используйте YAML вместо встраиваемого синтаксиса. Сравните эти два варианта:
YAML
- name: Install apache httpd
apt:
name: apache2
state: present
Встраиваемый
- name: Install apache httpd
apt: pkg=apache2 state=pesent
11. Используйте gitignore
Добавьте .gitignore
к своим ролям, если вы храните код в git-репозитории. Простой .gitignore
может выглядеть следующим образом:
*.retry
*/__pycache__
*.pyc
*.log
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
12. Используйте советы из документации Ansible
Используйте рекомендации по организации контента с официальной страницы ansible
13. Используйте отдельный каталог для ролей сообщества
Используйте отдельный каталог для ролей сообщества, наряду с рекомендациями из предыдущего совета.
14. Тестируйте Ansible-код
Используйте фреймворки для тестирования кода Ansible. Например, molecule. Этот фреймворк позволяет тестировать ваш код с разных сторон. Помимо традиционного тестирования, он также может запускать все виды линтеров и проверять код на идемпотентность.
15. Версионирование ролей
Что такое версионирование в мире Ansible? Это подход, используемый git и позволяющий запускать различные версии ролей, просто указав версию. Версией может быть тег, ветка или конкретный коммит. Подробнее об этом можно почитать здесь. Ваша задача — настроить процедуру в соответствии с вашими требованиями к управлению версиями ролей.
requirements.yaml:
---
- src: git@gitlab.company.com:mygroup/ansible.git
scm: git
version: "0.1"
...
Допустимые атрибуты:
src
scm
version
name
Перевод статьи подготовлен в преддверии старта курса «DevOps практики и инструменты».
Приглашаем всех желающих записаться на бесплатный демо-урок курса по теме: «Prometheus: быстрый старт». На занятии участники вместе с экспертом рассмотрят архитектуру Prometheus и ее способ работы с метриками, а также разберут, как формировать алерты и события в системе.