Глава 8. Поиск неисправностей Ansible

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

  • Регистрация плейбуков и различные уровни её подробности

  • Самоанализ переменных

  • Отладка плейбуков

  • Консоль Ansible

  • Отладка исполнения локального кода

  • Отладка исполнения удалённого кода

Технические требования

Ознакомьтесь с видеоматериалами Code in Action.

Ведение регистрации и подробность плейбуков

Увеличение подробности вывода Ansible способно разрешать множество проблем. Начиная с неверных параметров модулей вплоть до неверных команд подключения, увеличение подробностей может оказаться критически важным в определении точного места ошибки. Регистрация плейбука и его подробность вкратце обсуждались в Главе 2, Защита ваших ключей безопасности в Ansible в отношении защиты значений секретов при исполнении плейбуков. Данный раздел более детально рассмотрит протоколирование и его степени подробности.

Уровень детализации

При исполнении плейбуков посредством ansible-playbook получаемый вывод отображается в стандартном выходе. При определённом по умолчанию уровне подробностей отображается очень немного информации. По мере исполнения воспроизведения, ansible-playbook выдать на печать некий заголовок PLAY с самим названием этого воспроизведения. Затем для каждой задачи выводится на печать заголовок task с названием этой задачи. По мере того как каждый хост выполнит эту задачу, отображается название этого хоста совместно с полученным состоянием данной задачи, которое может быть ok, fatal либо changed. Никакой дополнительной информации относительно данной задачи не отображается, например, такой что этот модуль был выполнен, все параметры предоставлены в данный модуль или значения возвращаемых после исполнения данных. Хотя это и нормально для хорошо отлаженных плейбуков, я обычно привык желать слегка больше информации о своих воспроизведениях. В некоторых из более ранних примеров в этой книге мы применяли более высокие уровни детализации (verbosity) вплоть до двух (-vv), а потому мы могли наблюдать местоположение такой задачи и возвращаемые данные. В общей сложности имеются пять уровней детализации:

  • None: Значение уровня по умолчанию

  • Первый (-v): При котором отображаются возвращаемые данные и информация условий

  • Второй (-vv): Для получения сведений о местоположении задачи и уведомлений обработчика

  • Третий (-vvv): Предоставляет подробности о попытках подключений и информацию о вызове задачи

  • Четвёртый (-vvvv): Передаёт помимо прочего дополнительные параметры детализации во все соединения подключаемых модулей (например, передачу -vvv во все команды ssh {и PowerShell})

Увеличение степени детализации может помочь выявлению конкретного места в котором могли произойти ошибки, а также предоставление дополнительных внутренних сведений относительно того как Ansible выполняет свои действия.

Как мы уже упоминали в Главе 2, Защита ваших ключей безопасности в Ansible, уровень подробностей выше первого может приводить к утечке чувствительных сведений в стандартный вывод и файлы журналов, а следовательно необходимо проявлять осторожность при использовании повышения детализации в потенциально совместно применяемых средах.

Ведение журнала

В то время как значением по умолчанию в ansible-playbook для регистрации установлен стандартный вывод, общий объём может быть больше того буфера, который применяется в самом эмуляторе Терминала; тем самым, может оказаться необходимым сохранять весь вывод в некий файл. Хотя различные оболочки и предоставляют некие механизмы для перенаправления вывода, более элегантным решением является указание ansible-playbook напрямую вести протоколирование в некий файл. Это осуществляется посредством либо задания некого log_path в самом файле ansible.cfg, либо через установку ANSIBLE_LOG_PATH в качестве переменной окружения. Устанавливаемое значение должно быть значением пути к некому файлу. Если этот путь не существует, Ansible попытается создать данный файл. Если такой файл уже имеется, Ansible будет выполнять добавление в конец этого файла, что делает возможной консолидацию выполнения множества регистрационных записей ansible-playbook.

Само использование некого файла журнала не является взаимоисключающим в отношении регистрации в стандартном выводе. И то, и другое могут выполняться одновременно, а предоставление устанавливаемого уровня детализации имеет воздействие и на то, и на иное одновременно.

Самонаблюдение переменной

Распространённым набором проблем с котррым сталкиваются при разработке плейбуков Ansible является собственно не надлежащее применение, или неверное предположение о назначенном переменным значении. Это в особенности распространено при регистрации получаемых результатов одной задачи в некой переменной, с последующим использованием такой переменной в некой задаче или шаблоне. Если соответствующий желательный элемент получаемого результата не достигнут надлежащим образом, наш окончательный результат будет неожиданным, или возможно губительным.

Для устранения ненадлежащего применения переменной ключом является инспекция получаемого значения переменной. Самый простой способ инспекции некого значения переменной состоит в установленном модуле отладки. Такой модуль отладки позволяет отображать на экране текст в свободном виде и, как и прочие задачи, устанавливаемые для этого модуля параметры могут также пользоваться преимуществами синтаксиса шаблонов Jinja2. Давайте продемонстрируем такое применение создав некое простое воспроизведение, которое исполняет какую- то задачу, регистрирует получаемый результат и затем отображает этот результат в неком операторе отладки с применением синтаксиса Jinja2 для предоставления такой переменной:


--- 
- name: variable introspection demo 
  hosts: localhost 
  gather_facts: false 
 
  tasks: 
    - name: do a thing 
      uri: 
        url: https://derpops.bike 
      register: derpops 
 
    - name: show derpops 
      debug: 
        msg: "derpops value is {{ derpops }}"
 	   

Когда мы запустим это воспроизведение, мы увидим отображаемым значение для derpops, что отображено на следующем снимке экрана:

 

Рисунок 8.1



Модуль отладки имеет различные возможности, которые также могут оказаться полезными. Вместо вывода на печать некой строки в произвольном виде для отладки использования шаблона, этот модуль может просто выводить на печать само значение некой переменной. Это осуществляется использованием значения параметра var вместо значения параметра msg. Давайте повторим свой пример, но в этот раз мы воспользуемся значением параметра var, а также мы выполним доступ только к подэлементу server своей переменной derpops следующим образом:


--- 
- name: variable introspection demo 
  hosts: localhost 
  gather_facts: false 
 
  tasks: 
    - name: do a thing 
      uri: 
        url: https://derpops.bike 
      register: derpops 
 
    - name: show derpops 
      debug: 
        var: derpops.server
 	   

Запуск этого видоизменённого воспроизведения отобразит лишь значение части server нашей переменной derpops, что отображено на снимке экрана ниже:

 

Рисунок 8.2



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

Подэлементы переменной

Другой распространённой ошибкой в плейбуках является ненадлежащая ссылка на некий подэлемент какой- то составной переменной. Некая составная переменная представляет собой нечто большее чем просто какую- то строку; это или список, или хэш. Зачастую выполняется неверная ссылка на подэлемент или сам элемент будет указан ненадлежащим образом, ожидая иной тип.

Хотя со списками и достаточно просто работать, хэши предоставляют некую уникальную проблему. Хэш выступает в качестве какого- то не сортированного множества с потенциально смешанными типами, которые к тому же могут иметь включения. Некий хэш может иметь один элемент, который выступает в качестве обособленной строки, в то время как другой элемент может быть списком строк, а третий элемент может быть ещё одним хэшем с последующими элементами внутри себя. Для успеха критически важно понимать как верно получать доступ к правильному выражению.

В качестве примера давате слегка изменим своё предыдущее воспроизведение. На этот раз мы позволим Ansible собирать факты, а затем отобразим полученное значение ansible_python:


--- 
- name: variable introspection demo 
  hosts: localhost 
 
  tasks: 
    - name: show a complex hash 
      debug: 
        var: ansible_python
 	   

Получаемый вывод отображён на следующем снимке экрана:

 

Рисунок 8.3



Для изучения всех названий имеющихся подэлементов отличным вариантом будет воспользоваться debug для отображения всей составной переменной.

Данная переменная имеет элементы, которые являются строками, совместно с элементами, которые выступают списками строк. Давайте выполним доступ к самому последнему элементу в имеющемся перечне флагом таким образом:


--- 
- name: variable introspection demo 
  hosts: localhost 
 
  tasks: 
    - name: show a complex hash 
      debug: 
        var: ansible_python.version_info[-1]
 	   

Получаемый вывод отображён на снимке экрана ниже:

 

Рисунок 8.4



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

Сопоставление подэлементов и методов объектов Python

Менее распространённая, но сбивающая с толку засада происходит по причине причуды синтаксиса Jinja2. На составные переменные внутри плейбуков и шаблонов Ansible можно ссылаться двумя способами. Первый стиль состоит в ссылке на самый основной элемент по его названию, за которым следуют квадратные скобки, а сам подэлемент в кавычках внутри этих квадратных скобок. Это собственно стандартный синтаксис вложенного сценария (standard subscript syntax). Например, для доступа к имеющемуся подэлементу herp в нашей переменной derp мы применим следующее:


{{ derp['herp'] }}
 	   

Наш второй стиль является удобным методом, предоставляемым Jinja2, который состоит в использовании точки для разделения имеющихся элементов. Это имеет название нотации с точкой (dot notation) и выглядит так:


{{ derp.herp }}
 	   

Имеется едва уловимое отличие в том как работают эти стили и это обусловлено объектами и методами объектов Python. Поскольку Jinja2 по существу является утилитой Python, переменные в Jinja2 обладают доступом к своим родным методам Python. Некая переменная строки имеет доступ к методам строк Python, какой-то список имеет доступ к методам списка, а словарь имеет доступ к методам словарей. Когда мы применяем свой первый стиль, Jinja2 вначале выполнит поиск соответствующего элемента среди подэлементов по предоставленному значению имени. Если ничего не найдено, Jinja2 далее попытается выполнить доступ некого метода Python для данного предоставленного названия. Однако, такой порядок меняется при использовании нашего второго стиля; вначале выполняется поиск метода некого объекта Python, а если он не найден, тогда осуществляется поиск какого- то подэлемента. Это отличие становится существенным при наличии противоречия имён между неким подэлементом и каким- то методом. Допустим некая переменная derp выступает какой- то составной переменной. Эта переменная имеет некий подэлемент с названием keys. Применение для доступа к keys каждого из стилей приводит в результате к различным значениям. Давайте построим некий плейбук для демонстрации данного факта:


--- 
- name: sub-element access styles 
  hosts: localhost 
  gather_facts: false 
  vars: 
    - derp: 
        keys: 
          - c 
          - d 
  tasks: 
    - name: subscript style 
      debug: 
        var: derp['keys']  
    - name: dot notation style 
      debug: 
        var: derp.keys
 	   

После запуска этого воспроизведения мы ясно можем видеть отличие между своими двумя стилями. Наш первый стиль успешно ссылается на значение подэлемента keys, в то время как второй стиль ссылается на метод keys словарей Python:

 

Рисунок 8.5



В общем случае лучше избегать применение в качестве названия подэлементов тех, которые могут вступать в конфликт с методами объекта Python. Тем не менее, если это не возможно, следующим лучшим моментом будет знание об этом отличии в стилях ссылок на подэлементы и выбрать в качестве соответствующего только один.

Исполнение отладочного кода

Порой оказываются недостаточными регистрация и инспекция данных переменной для выявления некоторой проблемы. Когда это происходит, может потребоваться отладка самого плейбука или более глубокое погружение во внутренности кода Ansible. Существует два основных набора кода Ansible: тот код, который исполняется локально на самом хосте Ansible и тот код модуля, который запускается удалённо на нашем целевом хосте.

Отладка плейбуков

Плейбуки имеют возможность интерактивной отладки с применением стратегии исполнения, которая была введена в Ansible 2.1, а именно стратегии отладки (debug). Если некое воспроизведение применяет данную стратегию, в случае возникновения некого состояния ошибки запускается интерактивный сеанс отладки. Этот интерактивный сеанс может применяться для отображения данных переменных, отображения параметров задач, обновления параметров задач, обновления переменных, повторного исполнения задач или выхода из отладчика.

Давайте продемонстрируем это неким воспроизведением, которое имеет какую- то успешную задачу, за которой следует некая задача с ошибкой, после чего идёт окончательная успешная задача. Мы повторно воспользуемся тем плейбуком, который мы уже применяли, но слегка обновим его, как это показано в следующем коде:


--- 
- name: sub-element access styles 
  hosts: localhost 
  gather_facts: false 
  strategy: debug 
 
  vars: 
    - derp: 
        keys: 
          - c 
          - d 
 
  tasks: 
    - name: subscript style 
      debug: 
        var: derp['keys'] 
 
    - name: failing task 
      debug: 
        msg: "this is {{ derp['missing'] }}" 
 
    - name: final task 
      debug: 
        msg: "my only friend the end"
 	   

В процессе исполнения Ansible столкнётся с некой ошибкой в нашей задаче с отказом и представит своё приглашение (отладки), как это показано на снимке экрана ниже:

 

Рисунок 8.6



Из этого приглашения мы имеем возможность отображать саму задачу и те параметры, которые применяются данной задачей при помощи команды p следующим образом:

 

Рисунок 8.7



Мы также имеем возможность изменять сам плейбук на лету чтобы пробовать различные значения параметров или переменных. Давайте определим значение ошибочного ключа для своей переменной derp и затем попробуем выполнение вновь. Все значения переменных пребывают внутри нашего словаря верхнего уровня vars. Мы имеем возможность напрямую устанавливать данные переменной при помощи синтаксиса Python и доступной команды task_vars, а затем повторить попытку командой r:

 

Рисунок 8.8



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

[Совет]Совет

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

Отладка локального кода

Локальный код Ansible представляет собой львиную долю того кода, который предоставляется Ansible. Все имеющиеся плейбуки, воспроизведения, роли и задачи разбираемого кода присутствуют локально. Весь имеющийся код, за исключением самого собираемого модуля, который передаётся на соответствующий локальный хост обитает локально.

Локальный код Ansible можно разбить на три основные раздела: опись (inventory), плейбук и исполнитель (executor). Код описи имеет дело с синтаксическим разбором данных инвентаризации из файлов хоста, динамических сценариев инвентаризации или сочетания их обоих, причём в каталогах деревьев. Код плейбука применяется для разбора самого кода YAML в объекты Python внутри Ansible. Код исполнения представляет собой API ядра и имеет дело с ветвящимися процессами, подключениями к хостам, исполняемыми модулями, обработкой результатов и многими прочими моментами. С практикой приходит понимание изучения общей области для начала отладки, но все описываемые здесь общие области выступают отправной точкой.

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

Отладка кода учёта ресурсов

Код инвентаризации имеет дело с поиском источников описи, считыванием или выполнением обнаруженных файлов, синтаксическим разбором данных учёта в объекты инвентаризации и загрузке переменных данных для данной описи. Для отладки того как Ansible обращается с данным учётом ресурсов, вовнутрь inventory/__init__.py или в один из прочих файлов внутри подкаталога inventory/ следует добавить некую точку прерывания. Этот каталог будет расположен в той локальной файловой системе, в которой был установлен Ansible. В системе Linux он обычно хранится в пути /usr/lib/python2.7/site-packages/ansible/inventory/. Этот путь может быть внутри некой виртуальной среды Python если Ansible был установлен именно таким образом. Для обнаружения того где установлен Ansible просто наберите в своей командной строке which ansible . Эта команда отобразит вам где установлен исполняемый файл Ansible и может указывать некую виртуальную среду Python. Для данной книги Ansible был установлен с применением дистрибутива Python операционной системы при исполняемых файлах Ansible расположенных в /usr/bin/.

Для обнаружения пути самого кода Python Ansible просто наберите python -c "import ansible; print(ansible)". В моей системе это выдало <module 'ansible' from '/usr/lib/python2.7/site-packages/ansible/__init__.pyc'>, из чего можно вывести, что наш каталог описи расположен в /usr/lib/python2.7/site-packages/ansible/inventory.

В последних выпусках Ansible этот каталог учёта ресурсов был реструктурирован и в версии 2.7 нам требуется искать inventory/manager.py. Здесь расположено определение некого класса для класса Inventory. Именно этот объект описи будет применяться при запуске плейбука и он создаётся когда ansible-playbook осуществляет синтаксический разбор вариантов, предоставляемых ему в качесве источника учёта ресурсов. Вся выявление имеющейся описи выполняет метод __init__ из класса Inventory. Для выявления неисправностей в этих трёх областях внутри самого метода __init__() следует установить некую точку прерывания. Хорошим практическим приёмом будет поместить её после всех имеющихся переменных данного класса, которые задают начальные значения и прямо перед тем как начинается обработка каких- то данных.

В версии Ansible 2.7.5.0 это будет строка 143 inventory/manager.py, в которой вызывается parse_sources.

Мы можем пропустить само определение функции parse_sources в строке 195 для вставки своей точки прерывания. Чтобы вставить точку прерывания нам вначале требуется импортировать необходимый модуль pdb, а затем вызвать функцию set_trace() следующим образом:

 

Рисунок 8.9



Для начала отладки сохраните этот исходный файл и затем исполните ansible-playbook как обычно. По достижению установленной точки прерывания наше исполнение будет остановлено и будет отображено приглашение на ввод pdb:

 

Рисунок 8.10



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

 

Рисунок 8.11



Команды where и list из приведённого перечня помогут нам определить где мы находимся в своём стеке и где мы пребываем в коде:

 

Рисунок 8.12



Наша команда where отображает нам что мы пребываем в inventory/manager.py, в методе parse_sources(). Следующий кадр вверх находится в том же самом файле, в функции __init__(). До этого имеется другой файл, playbook.py, а также исполняемой функцией в этом файле является run(). Эта строка вызывает ansible.inventory.InventoryManager для создания необходимого объекта описи/ До него находится первоначальный файл, ansible-playbook, вызывающий cli.run().

Наша команда list отображает сам исходный код вокруг нашей текущей точки исполнения, пять строк до ней и пять строк после.

Начиная с этого места мы можем указывать pdb выполнение строки за строкой при помощи команды next. И если мы выберем это, мы отследим вызов другой функции при помощи команды step. Также мы имеем возможность print данных переменной для инспекции значений, что отображено на следующем снимке экрана:

 

Рисунок 8.13



Мы можем видеть, что наша переменная self._sources имеет какое- то значение полного пути к нашему файлу описи mastery-hosts, который является строкой, которой мы снабдили ansible-playbook для описи своих данных. Мы можем продолжить обход или выпрыгнуть прочь или просто воспользоваться командой continue для выполнения вплоть до следующей точки прерывания или до завершения всего кода.

Отладка кода плейбуков

Код плейбука ответственен за загрузку, синтаксический разбор и выполнение плейбуков. Основной точкой входа для обработки плейбука является playbook/__init__.py, внутри которой и обитает сам класс PlayBook. Хорошим начальным местом для отладки обработчика плейбука является строка 77:

 

Рисунок 8.14



Размещение здесь точки прерывания позволит нам отслеживать поиск самого файла плейбука и его синтаксический разбор. В частности, выполняя пошаговый проход вызова функции self._loader.load_from_file(), мы будем иметь возможность следовать за действием своего синтаксического разбора.

Функция load() нашего класса PlayBook всего лишь выполняет самый первоначальный разбор. Для самого исполнения воспроизведений и задач применяются иные классы внутри прочих каталогов. Особый интерес представляет каталог executor/, который содержит файлы с классами для исполнения плейбуков, воспроизведений и задач. Основная функция run(), внутри самого класса PlaybookExecutor, определяемая внутри файла executor/playbook_executor.py выполняет цикл по всем имеющимся воспроизведениям в данном плейбуке и исполняет сами воспроизведения, который, в свою очередь, выполняют имеющиеся индивидуальные задачи. Именно эту функцию следует обходить если сталкиваетесь с некой проблемой, относящейся к разбору воспроизведения, обратных вызовов воспроизведения или задач, тегов, выбора воспроизведений хоста, последовательных операций, исполнения обработчика или чего- то промежуточного.

Отладка кода исполнителя

Код исполнителя в Ansible выступает кодом подключения, который связывает воедино данные учёта ресурсов, плейбуки, воспроизведения и методы подключения. В то время как каждый из этих прочих элементов кода может отлаживаться персонально, то как они взаимодействуют может изучаться внутри кода исполнителя.

Необходимые классы исполнителя определены в различных файлах внутри executor/ и самого класса PlaybookExecutor. Этот класс обрабатывает исполнение всех имеющихся воспроизведений и задач внутри некого определённого плейбука. Основная функция создания класса, __init__(), создаёт последовательности атрибутов местодержателей, а также устанавливает некие значения по умолчанию, в то время как функция run() является тем, где происходит всё интересное.

Отладчик часто перекидывает вас с одного файла на другой, прыгая по самой основе кода. Например, в нашей функции __init__() класса PlaybookExecutor имеется код для кэширования того будет или нет исполняемый по умолчанию SSH поддерживать ControlPersist. ControlPersist является функцией SSH, которая удерживает сокеты для удалённых хостов открытыми на протяжении какого- то времени для быстрого повторного применения. Давайте поместим здесь точку прерывания и последуем за самим кодом:

 

Рисунок 8.15



Теперь мы можем запустить свой плейбук objmethod.yml снова чтобы получить состояние отладки:

 

Рисунок 8.16



На потребуется шагнуть в соответствующую функцию чтобы следовать за её исполнением. Проходя пошагово данную функцию мы тем самым перейдём в друой файл:

 

Рисунок 8.17



Здесь мы имеем возможность воспользоваться list для просмотра самого кода в нашем новом файле:

 

Рисунок 8.18



Спустившись на несколько строк вниз мы приходим к некому блоку кода, который исполняет команду ssh и проверяет получаемый вывод для проверки того поддерживается ли ControlPersist:

 

Рисунок 8.19



Давайте спустимся ещё на пару строк и затем выведем на печать то значение, которое присвоено err. Это отобразит нам полученный в ssh результат и значение всей строки, которое Ansible будет искать внутри:

 

Рисунок 8.20



Как мы можем видеть, значение искомой строки не представлено внутри значения переменной err, а потому значение has_cp остаётся установленным по умолчанию True.

[Замечание]Быстрое замечание относительно ветвлений и отладки:

Когда Ansible использует множество процессов для множества ветвлений, отладка становится затруднительной. Отладчик может быть прикреплён только к одному ветвлению, а не к другому, что делает очень сложным отладку данного кода. Пока вы не применяете специализированный отладчик кода множества процессов, будет лучше оставаться в одном ветвлении. {Прим. пер.: подробнее в нашем переводе Полное руководство параллельного программирования на Python Куана Нгуена.}

Отладка удалённого кода

Удалённый код является тем кодом, который Ansible перемещает в некий удалённый хост для его последующего исполнения. Обычно это код модуля или в определённом случае action_plugins, другие кусочки кодов. Применение того метода отладки, который мы обсуждали в своем предыдущем разделе для отладки исполнения модуля отладки, не будет работать, поскольку Ansible всего лишь копирует сам код куда требуется и затем исполняет его. Нет никакого Терминала, подключаемого к исполняемому удалённо коду, а следовательно нет никакого способа подключать его к некому приглашению отладчика, то есть без изменения самого модуля кода.

Для отладки кода модуля нам требуется изменить сам этот код модуля чтобы вставить некую точку прерывания. Вместо непосредственного изменения самого файла устанавливаемого модуля, создайте некую копию этого файла в каталоге library/ относительно имеющихся плейбуков. Эта копия данного кода модуля будет применяться вместо нашего устанавливаемого файла, что сделает более простым временное изменение модуля без разрушения прочих модулей пользователей в вашей системе.

В отличии от прочего кода Ansible, код модуля не может напрямую отлаживаться при помощи pdb, так как этот код модуля собирается, а затем переправляется в некий удалённый хост. К счастью, существует некое решение в виде слегка изменённого отладчика с названием rpdb, Remote Python Debugger. Это отладчик имеет возможность запуска некой службы ожидания по представленному ему порту чтобы сделать возможным удалённое подключение к данному процессу Python. Удалённое подключение к этому процессу позволит отлаживать необходимый код строка за строкой, в точности как мы это делали с остальным кодом Ansible.

Для демонстрации того как работает такой отладчик, вначале нам понадобится некий удалённый хост. Для данного примера мы воспользуемся каким- то удалённым хостом с названием debug.example.com и установим значение IP адреса для хоста, который уже установлен и пребывает в ожидании. Затем нам требуется исполнить некий модуль, который мы бы желали отлаживать:


---
- name: remote code debug
  hosts: debug.example.com
  gather_facts: false
  become: true

  tasks:
    - name: a remote module execution
      systemd:
        name: nginx
        state: stopped
        enabled: no
 	   

Нам также потребуется некий новый файл описи для ссылки на наш новый проверочный хост - поскольку я не имею настроенным DNS для данного хоста, я воспользуюсь специальной переменной ansible_host в своей описи чтобы сообщить Ansible с каким именно IP адресом подключаться к debug.example.com:


debug.example.com ansible_host=192.168.81.154
 	   
[Совет]Совет

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

Данное воспроизведение просто вызывает имеющийся модуль systemd чтобы гарантировать что наша служба nginx остановлена и не будет запускаться при загрузке. Как мы уже постулировали ранее, нам требуется сделать некую копию своего модуля службы и поместить ей в library/. Окончательное местоположение для копирования этого модуля службы будет основываться на том методе, при помощи которого установлен Ansible. Обычно этот модуль будет размещаться в подкаталоге modules/core/system/, в котором обитает сам од Python Ansible, например, в моей системе /usr/lib/python2.7/site-packages/ansible/modules/system/systemd.py. Затем мы можем изменить его поместив в него свою точку прерывания таким образом:

 

Рисунок 8.21



Мы поместим точку прерывания прямо перед моментом создания значения переменной systemctl, рядом со строкой 318. Вначале требуется импортировать необходимый нам модуль rpdb (что подразумевает, что сама библиотека Python rpdb должна иметься в этом удалённом хосте), а затем следует создать собственно точку прерывания с помощью set_trace().

[Совет]Совет

В CentOS 7 и прочих вариантах, как в том хосте, который применяется в нашей демонстрации, rpdb может быть установлен с применением следующей команды:


sudo yum install python2-rpdb
		

В отличии от обычного отладчика, эта функция откроет некий порт и будет ожидать внешних подключений. По умолчанию данная функция будет выполнять ожидание подключений к порту 4444 по адресу 127.0.0.1. Однако этот адрес не выставляется в сетевой среде, поэтому в своём примере я указываю rpdb выполнять ожидание с адреса 0.0.0.0, что в действительности является всеми адресами для данного хоста. Теперь мы можем запустить этот плейбук чтобы настроить данный сервер который будет ожидать подключения клиента:

 

Рисунок 8.22



Теперь, когдаэтот сервер запущен, мы имеем возможность подключаться к нему с другого Терминала. Подключение к этому запущенному процессу можно осуществить при помощи программы telnet:

 

Рисунок 8.23



Начиная с этого момента мы можем выполнять отладку как обычно. Всё ещё имеются все команды, которые мы применяли, например list для отображения где расположен текущий кадр внутри нашего кода:

 

Рисунок 8.24



Применяя данный отладчик мы способны прогуливаться по модулю systemd чтобы отслеживать как он определяет значение пути для последующих инструментов, отслеживать какие команды исполняются в этом хосте, определять как вычисляется некое изменение и тому подобное. Можно обойти весь файл, включая все прочие внешние библиотеки, используемые данным модулем, что делает также возможной отладку прочего кода вне модуля в этом удалённом хосте.

Если данный сеанс позволяет рассматриваемому модулю чистый выход, исполнение этого плейбука будет возвращено обычным образом. Однако, если сеанс данного отладчика отключится до завершения самого модуля, наш плейбук завершится с ошибкой, что продемонстрировано на следующем снимке экрана:

 

Рисунок 8.25



По причине такой стороны воздействия будет лучше не выходить слишком рано из своего отладчика, а вместо этого вызвать некую команду continue когда ваша отладка завершится.

Отладка действия встраиваемого модуля

Некоторые модули на самом деле действуют как подключаемые модули. Это задачи, которые исполнят некий код локально перед передачей кода в соответствующий удалённый хост. Никие примеры подключаемых модулей действий включают copy, fetch, script и template. Источник для этих подключаемых модулей можно найти в plugins/action/. Каждый подключаемый модуль имеет свой собственный файл в этом каталоге, который можно изменить чтобы получить вставленной точку прерывания для отладки данного кода при его исполнении, прежде чем (или вместо) отправить этот код в соответствующий удалённый хост. Отладка этого обычно делается при помощи pdb, так как большая часть такого кода выполняется локально.

Выводы

Ansible является частью программного обеспечения, а программное обеспечение подвержено поломкам; причём важно не то что оно ломается, а важно когда. Неверный ввод, ненадлежащие предположения и неожиданные среды, это все те моменты, которые могут повлечь к разочоровывающей ситуации когда задачи и воспроизведения не работают как ожидалось. Самостоятельный анализ и отладка являются технологиями выявления неисправностей, которые способны быстро превратить разочарование в восторг при выявлении корня зла.

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

В своей следующей главе мы изучим как расширять функциональность Ansible написанием собственных модулей, подключаемых модулей и источников учёта ресурсов.