Interview Questions

Ansible

Редактировать на GitHub

Теоретические вопросы

Как в плейбуке перезаписать дефолтные переменные?

Есть несколько способов, так как у переменных в Ansible разные уровни приоритета:

  • Через extra-vars (CLI, самый высокий приоритет)
ansible-playbook site.yml -e "my_var=cli_value"
  • Через vars: в самом плейбуке
- hosts: all
  roles:
    - myrole
  vars:
    my_var: 'new_value'
  • Через vars_files
- hosts: all
  roles:
    - myrole
  vars_files:
    - override.yml # внутри: my_var: "value_from_file"
  • Через set_fact (во время выполнения)
- hosts: all
  tasks:
    - set_fact:
        my_var: 'runtime_value'
  • Через group_vars или host_vars (group_vars/web.yml)
my_var: 'from_inventory'

Для чего нужен ad hoc в ansible?

Это режим работы ансибл когда запрос к серверу выполняется напрямую из командной строки, без создания дополнительных файлов.

Что такое роли?

Роли имеют свою структуру каталогов, которая выглядит так:

rolename
- files
- handlers
- meta
- templates
- tasks
- vars

Назначение директорий:

  • files: содержит файлы, которые будут скопированы на настраиваемые хосты; так же – может содержать скрипты, которые позже будут запускаться на хостах;
  • handlers: обработчики, которые будут использоваться при выполнении задач;
  • meta: описание зависимостей, т.е. – ролей, которые должны быть обработаны перед запуском настраиваемой роли и мета-данных, таких как автор, описание продукта и прочее;
  • templates: шаблоны файлов с переменными;
  • tasks: все задачи, которые ранее были описаны в Playbookе;
  • vars: переменные для шаблонов.

Что такое идемпотентность? Приведи пример таких операций, и противоположных им

Идемпотентность это когда делаем одну и ту же операцию много раз, и при многократном ее повторении результат будет таким же, как в первый раз. Некоторые операции не являются идемпотентными сами по себе, и там потребуется дополнительная логика. Например, делаем запрос к странице, просто ее качаем - это идемпотентно. Потому что результат не изменится. Допустим делаем DELETE запрос. Он сам по себе тоже идемпотентен, ибо удаляет то, что мы хотели изначально удалить. Однако, сначала сервер ответил 200, потом ответил 404. Но общее состояние всё то же, файл же уже удалён.

В ансибле есть идемпотентность в модулях. Если я скажу создать файл при первом запуске он его создаст. При втором он увидит, что создано, и создавать не будет. Но может быть проблема например с распаковкой архива. Мы можем просто его распаковывать и распаковывать в какую-то директорию. Это уже ближе к неидемпотентному сценарию.

Ответ в контексте ансибла Идемпотентность - это такая характеристика действия, согласно которой повторное выполнение этого действия будет давать тот же результат, что и первый запуск. В контексте систем управления конфигурациями это означает, что сценарии, написанные с соблюдением такого подхода, не изменят, не сломают и не выдадут ошибок на управляемом хосте при повторном запуске.

Например, нам нужно добавить пользователя на хост, для bash это будет выглядеть так:

useradd newuser -ms /bin/bash

Но если мы запустим команду второй раз, то получим ошибку

useradd newuser -ms /bin/bash
useradd: user 'newuser' already exists

Поэтому нам нужно дополнительно добавлять проверку, например так:

id newuser || useradd newuser -ms /bin/bash

В случае Ansible мы только декларируем состояние, например:

- hosts: localhost
gather_facts: no
tasks:
- user:
  name: newuser
  shell: /bin/bash
  state: present

При первом запуске плейбук Ansible ответит:

TASK [user] ***
changed: [localhost] => {
"ansible_facts": {
  "discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1001,
"home": "/home/newuser",
"name": "newuser",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1001
}

При повторном:

TASK [user] ***
ok: [localhost] => {
"ansible_facts": {
  "discovered_interpreter_python": "/usr/bin/python3"
},
"append": false,
"changed": false,
"comment": "",
"group": 1001,
"home": "/home/newuser",
"move_home": false,
"name": "newuser",
"shell": "/bin/bash",
"state":
"present",
"uid": 1001
}

Обратите внимание, что статус задания сменился с changed на ok и повторный запуск вернул значение "changed": false

Таким образом, сценарии, написанные с учетом идемпотентности, реализуют ту самую декларативность в описании состояния инфраструктуры, а инфраструктура соответствует состоянию, описанному в коде нашего скрипта. Это и есть IaС - Infrastructure as Code - инфраструктура как код

Для чего нужны хендлеры, handlers?

Задачу можно дополнить обработчиками, которые будут срабатывать, если задача была выполнена успешно.

Например, мы установили nginx и хотим его запустить

---
- hosts: testbox
sudo: yes
tasks:
  - name: Install Nginx
	yum: pkg=nginx state=latest
	notify:
	  - NGINX start

handlers:
  - name: NGINX start
	service: name=nginx state=started

В чем разница pull и push модели?

Pull-модель преполагает, что управляемые хосты подтягивают инструкции с мастер-сервера. Push-модель реализует подход, когда инструкции по желанию или по событию доставляются с управляющего на управляемые хосты.

Ansible чаще всего используется для push-модели управления, но умеет и pull-модель.

В чем плюсы ансибла?

Выгодные отличия Ansible от других систем управления конфигурациями:

  • быстро осваивается, достаточно поверхностного понимания синтаксиса YAML и Jinja
  • нет необходимости устанавливать специальное ПО на хосты, нужен только SSH и python
  • подробная и наглядная документация
  • большое количество модулей
  • позволяет реализовать принцип идемпотентности в управлении состояниями хостов

Опишите основные примитивы Ansible

Инвентарь (Inventory)

  • cписок хостов, может быть статичным в виде текcтового файла в формате .ini или динамическим в виде скрипта или плагина, который подгружает структуру данных из стороннего источника, например, Openstack API или база LDAP.
$ cat hosts
[web]
nginx01
nginx02
nginx03

[mysql]
mysql01
mysql02
mysql03

[prod:children]
web
mysql

[devel]
dev-nginx01
dev-mysql01

Данный пример описывает группы хостов web, mysql, prod и devel, группа prod наследует содержимое групп web и mysql.

Задание (Task)

  • атомарная операция, выполняемая на управляемом хосте, например:
apt:
  package: nginx
  state: present

Данный пример аналогичен команде apt install nginx

Сценарий (Play) или Плейбук (Playbook)

  • сценарий или скрипт, содержащий одно или несколько заданий на выполнение, например:
$ cat prod-playbook.yml
  ---
  - hosts: nginx
    tasks:
      - apt:
          package: nginx
          state: present
      - shell: whoami

Роль (Role)

  • более сложная абстракция, выглядит как структура директорий и файлов, которые описывают набор дефолтных переменных, зависимостей от других ролей, может содержать файлы и темплейты, содержит задания(Tasks). Факты (Facts)
  • структура данных, которая содержит информацию о хосте, например, версию дистрибутива, IP адреса и файловые системы. Ansible забирает эту информацию с хоста, и на нее можно ссылаться в коде плейбуков и ролей.

Сбор фактов хоста занимает некоторое время, поэтому их можно кэшировать или отключить их сбор при выполнении плейбука.

В чем разница между модулем и плагином

Модули что-то делают на хостах. Модули вызываются в задачах (tasks) плейбука Плагины расширяют функционал ансибла

Практические вопросы

Какие зависимости (явные и неявные) используются в этой роли

argument_specs.yml
main.yml
main.yml
el6.yml
el7.yml
hosts.yml
site.yml

Явные зависимости (объявлены напрямую)

meta/main.yml
---
galaxy_info:
  role_name: rhel_python_sub
  description: Locate or install actual Python version on EL6/7
  min_ansible_version: "2.11"

  platforms:
    - name: EL
      versions:
        - "6"
        - "7"

dependencies:
  - role: southbridge.aux.repo_southbridge
...
  • Ansible ≥ 2.11
  • Поддерживаемые платформы: EL6 и EL7
  • Роль-зависимость: southbridge.aux.repo_southbridge

➜ То есть перед использованием rhel_python_sub должна быть применена роль southbridge.aux.repo_southbridge (вероятно, она подключает нужные репозитории для Python).

Неявные зависимости (следуют из логики роли)

  1. Зависимость от фактов ОС (EL6/EL7) и vars-файлов
tasks/main.yml
- name: OS version-specific vars included
  include_vars:
    file: "el{{ ansible_distribution_major_version }}.yml"
  • Роль ожидает корректно собранные факты (ansible_distribution_major_version)
  • И наличие соответствующих файлов: vars/el6.yml, vars/el7.yml.
  1. Зависимость от файловой иерархии/бинарей Python
tasks/main.yml
- name: Python binaries found
  register: __rhel_python_sub_find
  find:
    patterns: "{{ rhel_python_sub__set }}"
    paths: "{{ rhel_python_sub__paths }}"

Имена бинарей и пути берутся из vars/el6.yml / vars/el7.yml.
Примеры:

  • el6.yml: rhel_python_sub__paths: [/opt/rh/rh-python36/root/usr/bin, /usr/bin], rhel_python_sub__set: [python3.6, python3.5, python2.7]
  • el7.yml: rhel_python_sub__paths: [/usr/bin], rhel_python_sub__set: [python3.8, python3.7, python3.6]
  1. Контракт для других задач/ролей (факт про интерпретатор)
tasks/main.yml
- name: Fact about interpreter path defined from find results
  when: __rhel_python_sub_find.files|length
  set_fact:
    _rhel_python_sub_interpreter: "{{ __rhel_python_sub_find.files|map(attribute='path')|list|first }}"
  • Роль выставляет _rhel_python_sub_interpreter — это неявный контракт, что дальше потребители будут его использовать.
  1. Требование повышенных привилегий (в тестах)
tests/antest/site.yml
gather_subset: [ 'min' ]
become: true
roles:
  - southbridge.aux.rhel_python_sub
  • Для установки пакетов нужен become: true.
  1. Отсутствие обязательных входных параметров
meta/argument_specs.yml
argument_specs:
  main:
    short_description: The main entry point for the rhel_python_sub role.
    options: {}
  • Все входы — необязательные; роль полагается на факты ОС и свои defaults/vars.

Возможные другие места для поиска (в других ролях)

  1. meta/main.ymldependencies, platforms, min_ansible_version.
  2. galaxy.yml / MANIFEST.json (в коллекциях) — зависимости коллекций.
  3. tasks/**/*.yml — используемые модули (apt/yum/dnf/package/pip, get_url, command/shell), отсюда видны системные и сетевые зависимости.
  4. defaults/main.yml и vars/** — какие переменные роль ожидает (обязательные/дефолтные).
  5. templates/** и files/** — конфиги/скрипты, которые намекают на пакеты, пути, сервисы.
  6. handlers/main.yml — какие сервисы перезапускаются (косвенно — какие пакеты ставятся).
  7. molecule/** и CI (.github/workflows, .gitlab-ci.yml) — на каких образах/ОС роль тестируется, какие пакеты докатывают в CI.
  8. README / CHANGELOG — нередко прямо перечисляют внешние требования (репозитории, подписки, версии Python и т.п.).

Последнее обновление: 14 окт. 2025 г., 12:08:27