Ansible playbooks — это код: проверяем, тестируем, непрерывно интегрируем. Иван Пономарёв

Предлагаю ознакомиться с расшифровкой доклада Иван Пономарёв «Ansible playbooks — это код: проверяем, тестируем, непрерывно интегрируем»

Рефакторинг кода может быть увлекательным, особенно если это код вашей инфраструктуры. К тому же Ansible-роли почему-то имеют тенденцию к быстрому увеличению сложности. И это добавляет «изюминку» в вашу задачу. Иван расскажет, как можно преодолевать сложность Ansible-кода с помощью тестирования. В Docker-контейнерах.
По мере разрастания кодовой базы в Ansible приходят знакомые проблемы: сложность поддержки кода, ошибки и страх изменений. У знакомых проблем есть знакомое решение: автоматическое тестирование и CI. В докладе Иван покажет, как с использованием ряда инструментов решить проблемы «хрупкости» Ansible-кода, выполнить статический анализ, протестировать Ansible-скрипты и настроить CI-системы для публикации ролей в Ansible Galaxy.


Немного о себе. Я работаю в небольшой софтовой компании. Мы делаем софт на заказ. Раз в неделю я преподаю В МФТИ на той же кафедре, на которой я когда-то учился.

nfta07piewosflcsbxpy5kor--u.png

Компания у нас небольшая. Мы работаем с пулом заказчиков. Мы поставляем им различные системы. Расскажу о диапазоне наших работ. Самые простые — это классическая «трехзвенка», где 1–2 сервера и несколько десятков пользователей, но это все должно стоять и работать. Самый сложный проект, который есть у нас в пуле — это около 40 серверов в DigitalOcean. И мы используем вот такую связку: Terraform + Ansible. Мы ноды разворачиваем при помощи Terraform, а с помощью Ansible мы конфигурируем все наши виртуальные машины для того, чтобы ставить там то, что надо.

s-qwd7u14wwm_i79vtj3ifdr3uw.png

Когда мы начали использовать Ansible с его низким порогом входа и прекрасной экосистемой, у нас стали появляться роли в соответствии с best practices. И ролей стало накапливаться много-много для фронта, для бэка, для мониторинга, для кэша, логов и т. д.

egarghk4xofk4ikuz5j1jeakse8.png

У нас есть первый проект для одного заказчика. Второй проект для второго заказчика. Каждый делается по best practices, но некоторые роли пересекаются. Соответственно, ребята просят дать им рольку для установки чего-нибудь, которую можно будет скопировать в репозиторий кода и чуть-чуть подпилить под свои нужны.

njuhagvg09hsrbwnqqk5jmgc23y.png

И код стал расти как снежный ком, и стали знакомые проблемы возникать. Это:


  1. Страх поломать.
    • Код не переиспользуется, а копируется в проекты.
    • Нет рефакторинга. Действительно, зачем я буду улучшать то, что работает уже и так, а вдруг оно сломается.
  2. Нет уверенности в том, что это вообще сработает, если это надо будет запустить.
  3. Отладка в процессе деплоя. Запустим playbook, а там посмотрим, что будет.

i-pb-iobvlx80mqokwtgj6ytuu0.png

Нужно делать автоматизированное тестирование и CI. Но как? Ведь это не просто код, на котором unit-tests запускаем как на Java или на Python, это же configuration is code. Как это делать?

bpm5hydmqirbdjn2uvxrlhxmhmi.png

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

Прежде всего проверить является ли код вообще синтаксически валидным. Для этой задачи у нас есть три инструмента, которые можно использовать совместно. Это YAMLLint, Ansible-lint и Syntax check в самом playbook. Сейчас для начала пробежимся по ним.

rgcjhpmrkjjubwqyqv9xvoszxi0.png

Проект YAMLLint.


  • Проверяет синтаксис YAML. YAML — не так прост, как вы знаете. И он проверяет много чего. У него десятки всяких чеков.


  • Проверяет лишние пробелы.


  • Проверяет переносы строк UNIX-style.


  • Проверяет одинаковость отступов, потому что можно сделать валидный YAML, но с разными отступами и это будет не очень красиво выглядеть.


Он более строк, чем Ansible к коду, но он позволяет некие проблемы убрать. Например, у нас разработка ведется под Windows и если попадаются переносы строк в Windows-style в конфигурационных файлах, то это беда. YAMLLint — это решает.

g-avn5ij32qy-p-ethg2ii0momw.png

Так выглядит вывод YAMLLint. И обратите внимание, что мы эти правила можем настроить. Отключить какие-то проверки. Например, можем сделать длину строки побольше или еще что-то. YAMLLint проверяет просто все yamllint-файлы в вашей папке, поэтому если это не Ansible playbooks или роли, а просто какие-то конфигурационные файлы, то он их тоже проверяет.

tt0hfyelhpl2ce_yz-gv06d6zcs.png

Ansible-lint — это прекрасный проект, который содержит десятка два good practices.

Для примера:


  • Command module или shell module. Часто с этим путаются новички. И Ansible-lint это находит.
  • Иногда используются command модуль. А в нем вызов команд, для которых есть стандартный модуль Ansible. Ansible-lint подскажет что возможно его заменить на стандартный модуль Ansible.
  • Он проверяет idempotence (идемпотентность) вашего использования command и shell. Про idempotence мы чуть-чуть поговорим позже поподробней. Он строже, чем сам Ansible. И он проверяет best practices вашего кода.
  • И если вы хотите на Python что-нибудь пописать, то там есть фреймворк, который дает возможность расширять эти правила и добавлять новые.

mgfmvzpu54jsgq3yughva-ie4eg.png

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

u_cvxz-8vy7zjmtahzf8garcof0.png

Вот так выглядит его вывод.

piuubdpass_jwhus5pbwkwppqqc.png

Третий инструмент — это Syntax check, встроенный в сам Ansible. Тут все просто. Но единственные грабли, которые также нужно учесть — это если вы делаете на CI, то установите все свои стандартные роли, потому что Syntax check хоть и называется Syntax check, но проверяет не только синтаксис, он заходит вовнутрь всех ролей и тоже их обходит. И если там какая-то роль не установлена, то у вас завалится сборка.

e--r8s3rjxdbcjdubnnrxdv3kak.png

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

8uxbvuxkjrr6dnkrddxsv4nqy7m.png

Но, как известно, возможности статического анализа ограничены. Все-таки, чтобы протестировать программу нужно запускать. Как же тут быть?

qwrrmgn-vgntjqmu-xsm3fszvpq.png

История вопроса вот какая. Jeff Geerling — это известный человек в Ansible-экосистеме. Он автор множества ролей, он автор книги «Ansible for DevOps». В 14-ом году он написал вот эту статью, предлагая в Travis разворачивать роли, которые он тестирует.

mymdz_tcpxpr0oqq2dju3q6x8-8.png

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

jvf0agrgtaoon9n2frjkp79pdki.png

И в этой же статье он упоминает Molecule. Эта штука, которая существует с 15-го года. Она сейчас очень активно развивается. И это очень удобный инструмент для тестирования Ansible-ролей.

ko-uhlyjoskb4ecbx1tnjhhyq24.png

Это типичный OpenSource проект, который хорошо интегрирует другие проекты.

ovduqsqkvbgjkinm_d_txkvfnju.png

Устанавливается он в ваш Python environment. Нужно поставить ansible, molecule и если вы будете тестировать в Docker, то docker-py.

wfed6gpup-uhfxzypzjyljzrmje.png

И этот инструмент тестирует роли, т. е. он работает внутри отдельной роли, не проекта Ansible, а внутри одной роли. Соответственно внутри роли у вас могут быть разные сценарии. Внутри сценария вы настраиваете instances (сервера, виртуальные машины), на которые вы будете накатывать эту роль. Допустим, на разные операционные системы.

Кроме того, у каждого сценария есть свой playbook. Playbook — это то, что будет выполняться, накатываясь на instances.

И есть конфигурационные тесты.

sqi93vsxqfvtrydvzim_add-j_i.png

Во-первых, инициализация. Если вы хотите создать новую роль уже с molecule, то вы пишите вот такую команду. И она создает вам папку со всеми файлами, даже с readme-файлом, т. е. со всеми вещами, необходимыми для создания Ansible-роли. А внутри папки Molecule у них возникает единственный сценарий под названием Default. В принципе, одного названия под названием Default мне лично всегда хватало. Я более одного сценария не писал.

gq-aat0xi_za7ivwowhvwyzrz-w.png

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

d_lu6sbyrw1jzgmhmwmnyzhm0pu.png

После того, как мы инициализировали, заходите в консоль и пишите: molecule test. У вас Molecule тестирует вашу роль. Пришло сообщение об ошибке. Не понятно почему она упала.

728dimppa5gdm8wduhgxkdf50kq.png

Если мы заполним флажок --debug, то она скажет, что вы забыли установить docker-py. А я несколькими слайдами ранее говорил, что docker-py надо ставить отдельно. Этот --debug помогает разобраться с тем, что пошло не так.

pnyixm6epqfwgxsocthjpo7mueq.png

Потом она покажет вам Test matrix. Это некий план или сценарий, по которому Molecule будет работать с вашей ролью. Как видите, она уже предлагает некую матрицу. На первых этапах она проверяет статический анализ. Потом она проверяет syntax, converge, idempotence и заканчивается все сносом тестовых instances, на которые это все ставилось.

wb3wynbjsnbxjez2gx175oteaam.png

Самое интересное, на что Molecule накатывает ваши роли, на какие instances, откуда она эти ресурсы берет. Эти ресурсы настраиваются вот в таком файлике — molecule.yml. Вы можете прописать в разделе Platforms столько instances, сколько вам нужно. Если вы их пропишите 3 или 5, то будет 3 или 5. И Ansible на этапе converge будет их раскатывать сразу на несколько нод.

Если это docker, то здесь вы прописываете базовые image. Заметьте, что здесь image хитрые. Сделано это для того, чтобы у нас работал systemd. Если ваши роли раскатывает какие-то сервисы, которые работают в systemd, а, как известно, docker с этим не работает, но можно воспользоваться хитрыми базовыми контейнерами. И таким образом можно обойти. И у вас будут тестироваться роли, в которых сервисы ставят в systemd.

dqs-r4l6l85jvy9lisxd2wfaj64.png

Кроме docker вы можете использовать другие драйверы.

Во-первых, вы можете создавать тестовые instances в облаках, используя соответствующие модули, встроенные в Ansible.

Во-вторых, вы можете сами написать драйвер. Там есть для этого заготовка.

И есть Vagrant, и есть многое другое.

Заходите в документацию по Molecule, там достаточно большое количество вариантов. Но docker — это наиболее простой и легкий. Его можно здесь и сейчас запустить на машине разработчика.

zlupvavg_cyxfje1hhkmayrzhle.png

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

Это прописывается в файле requirements.yml как и положено по best practices Ansible. И вы можете указать настройку — какие роли, каких версий вам нужны для того, чтобы протестировать вашу роль как зависимость. И на этапе dependency именно роли именно этих версий будут скачены.

wrkahbv7gzh_0rsbqke7goeiyvq.png

Этап статистического анализа использует эту троицу, о которой я рассказывал в самом начале: YAMLLint, Ansible-lint, Syntax check.

ykwiq7vk_vt8wdp52zrktdtk1og.png

Этап converge на ваши тестовые instances накатывает файл playbook.yml. И что вы там напишите, то он и сделает. Вы можете даже вашу тестируемую роль туда не подключать, а что-то другое. Т. е. этап converge выполняет файл playbook.yml. Здесь вы можете несколько ролей выполнить, как-то их сконфигурировать и посмотреть, как они будут работать совместно.

0bgmadx7ytt05b623n_ol78joq0.png

Если что-то пошло не так, что делать? Converge прошел или не прошел — не понятно, что происходит. Вы можете запуститься с ключиком destroy=never. Это значит — никогда не удаляй тестовые instances. И если вы в docker, то обычным образом через терминал залогиниться в ваш контейнер и посмотреть, какие файлы не туда легли и посмотреть, что там вообще происходит.

d4lfokwbrnbw0din9z9zztsw-ou.png

Следующий этап после converge. Предположим, роль сработала. На все ваши тестовые instances раскатилась, все здорово. Molecule еще раз выполняет вашу роль. Раньше, когда не было diff в Ansible, Jeff Geerling предлагал так: проверять, что ничего не изменено при втором прогоне. Это проверка идемпотентности, т. е. ваша роль написана таким образом, что она не делает лишних операций. Если операции не нужно делать, она их не делает.

9ti7dpbx3vyhq0we3_zlw1qvp8i.png

Сейчас она использует --diff. Если так получится, что роль ваша работает и действительно делает то, что нужно, но при втором прогоне хотя бы одна вещь поменяется, то это будет failed на idempotent.

6nxbzn_vxumz-n2g6ovdgcff3lq.png

И самая интересная часть в Molecule — это инфраструктурные тесты. Роль прошла и все установилось. Но чтобы узнать, что все там работает или нет, для этого нужно использовать инфраструктурные тесты. Molecule поддерживает разные инфраструктурные тесты.

Три вида:


  • Testinfra (Python, default).
  • Serverspec (Ruby).
  • Goss (written in Go, tests in YAML).

Если так случилось, что у вас в команде уже есть какие-то инфраструктурные тесты, например, на Serverspec, то вы можете подключить готовые инфраструктурные тексты в Molecule.

Если инфраструктурных тестов еще нет, я рекомендую Testinfra, потому что она на Python как Molecule и Ansible. И все будет сделать проще.

wfdo98snolnlm4h1g1gxyhkojvg.png

Testinfra положит дефолтовый сценарий в Molecule. И вы можете прописывать туда тесты.

1oegrsh2wq8ywmy6vvecgdjocx4.png

Что можно проверить? Что мы обычно проверяем, когда сконфигурировали на сервере и хотим посмотреть — шевелиться оно или нет? Мы лезем в shell и выполняем какую-то команду. И смотрим, что нам выдала эта команда.

Это можно сделать вот таким вот образом. Вы пишите питоновскую процедуру, получаете аргументом host. У этого host вызываете метод «run». И дальше можете получить return code, и можете получить stdout и stderr и проверить этот вывод.

zhqsu0kpfsgqfbauu8hn7otzt5q.png

Мы в assert, допустим, написали, что rc=0. Если бы он был не равен нулю или если бы мы что-то там не нашли, то вывод в питоновском assert умный, он показывает вам контекст вашего питоновского кода, где что слетело, что именно чему не равно.

jvp9vwpal0og_tc9bqcakc7af8u.png

Когда вы гоняете molecule test, то самое длительное время — это накатка Ansible скриптов на instances. И чтобы не тратить время на накатку тестовых скриптов на instances, вы можете воспользоваться снова флагом destroy=never и вызывать molecule verify. В этом случае он будет просто выполнять инфраструктурные тесты на ваших готовых instances.

ycdsglouflle8lxkplabyh7c3a0.png

Вот, как это выглядит, когда инфраструктурные тесты зеленые. Когда в 2000-ом году появился JUnit, то у них был такой девиз — keep the bar green to keep your code clean. Так как у нас infrastructure is code, то мы теперь может тоже самое сказать про инфраструктуру.

q_fa3vjdcg7fcvxbpwfct_vafxw.png

Что еще можно проверять? Мы можем проверять все, что мы можем проверять через командную строку. Установили curl, можем подергать какие-то сервисы, можем попроверять вывод curl.

dcby-g5pxpfyolbid1wbfezlhhs.png

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

9pvbbtal3_h6lhus3xpyjvr6o64.png

С сервисами все просто. Можете получить сервис по имени и проверить — запущен он или нет.

ah2fntrmiqiybu-8v864v7bmkqm.png

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

0icd2wxc0aypghabejearlmzxss.png

Если кто-то любит TDD, то используя быструю итерацию перезапуска теста, мы можем сначала писать тесты под Ansible, а потом уже сам Ansible.

Т. е. Ansible-код становится таким же кодом, как код на Java или на Python и разрабатывать его можно теми же средствами.

Тут, может быть, кто-то вспомнит, пока мы говорили про Testinfra, что в самом Ansible есть модуль assert. Действительно, хорошая мысль — включить такую проверку прямо в Ansible-роль. Jython — это интерпретатор Python в JVM. Установили, выполнили команду в jython version. Проверили, что он дает какой-то осмысленный вывод.

9gmluqfpql9ot8rmqmjavikpkzw.png

Хорошая идея, только она не пройдет, если вы пользуетесь Molecule, потому что Molecule скажет, что этот код не идемпотентный. Почему? Это не пропустит и Ansible-lint. И даже если вы загасите в Ansible-lint эту проверку, то потом Molecule на этапе идемпотентности вас не пропустит.

Поэтому нельзя просто так взять и вставить assert в код Ansible.

cw2_oy1vknpgl_x11srbuegvihy.png

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

Идея — использовать проверки в хэндлерах, мне кажется, очень хорошей. Если на production что-то пошло не так, то вы узнаете об этом сразу, т. е. еще в процессе запуска Ansible playbooks у вас будет осмысленный вывод.

kndeydeuaimjnu2bmgs2ekqadh4.png

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

ngyrtrhqjvhroywdhjzgj2ielwg.png

Теперь немножко поговорим про процесс разработки ролей. Он стандартный. Репозиторий для ролей — это Galaxy. И он жестко привязан к GitHub, т. е. если вы разрабатываете роли в OpenSource, то это только лишь GitHub. И GitHub-процесс с проверкой на CI на валидность вашей роли. И то, что попадает в Master, то мы считаем релизом. Если мы хотим ссылаться на какую-то версию, то мы можем поставить какой-то тэг на Master. Это будет ссылкой на версию вашей роли.

nn5jrelzhhbbnd6xlwpgkgrhe7o.png

Как это сделать? Запуск Molecule легко организовать в вашей CI-системе. Если это Jenkins, то тут есть грабли. Если мы работаем с Jenkins Multibranch, то он делает checkout с контрольной версии не в папку с названием этого проекта MyRole, а он делает checkout в уникальную папку с большим набором цифр и букв. И в результате у Ansible сносит крышу, потому что он не понимает ничего, потому что он ищет роль под названием MyRole, а там другое название. Но это можно исправить небольшим костылем. Если ему подсунуть symlink внутри подпапочку, то это все решается.

hlujaokyhvxxozxviyxxvs25gnm.png

Вы можете заморочиться и разделить выполнение Molecule на стадии.

fje2bx3wnisexoe7oos43rn5peu.png

Если вы так сделаете, то вы получите вот такую красивую матрицу в Jenkins. И если у вас что-то слетело на каком-то этапе, то вы можете увидеть на каком этапе оно слетело у вас. Но честно скажу, что я так не делал. Я видел статью, где было сказано, что нужно делать именно так, но я ограничиваюсь только molecule test.

__fw0qet_ehwql4ousfupgkc3xc.png

Другая опция, которая будет для вас более актуальной, если вы разрабатываете роли в OpenSource, это Travis. Тут все очень просто. Мы указываем в сервисе docker, мы указываем инсталляцию, что нам нужно поставить Ansible docker-py. Кстати, хорошая идея — указывать явные версии того, что вы хотите поставить, иначе у вас сборка сегодня может выполниться, а завтра может не выполниться.

И очень простой скрипт molecule test, если у вас роль, которую вы разрабатываете, находится в GitHub репозитории.

ecofxj1thtspbigbxnxydwf8eri.png

И используя вот такой webhooks, вы нотифицируете Ansible Galaxy тем, что роль ваша хорошая, т. е. build passing, build failing.

И в результате вы имеете в окне поиска ролей в Galaxy вот такие бейджики. Это не те бейджики, которые в GitHub, это бейджики именно в Ansible Galaxy. Чтобы их получить надо пользоваться Travis и тем webhook. Из другого CI это, может быть, можно сделать, но я не понял пока, как это можно сделать. Задокументированный вариант только через Travis.

dlhvarqg_cksv7oepmhakepmt_i.png

После того, как все внедрилось, что получается? У нас было вот что-то, т. е. какие-то проекты, какие-то роли, между ними copy-paste-modify.

mp0vzik3lii8bjig_0mfxolcoqq.png

Общие роли, которые мы используем между проектами, вынесли в Galaxy, они расшариваются. Соответственно, каждая роль тестируется в Molecule. Допиливают роль каждый под свой проект сам и это становится общим достоянием.

На остальную часть, которая уникальна для каждого проекта, на нее навешен linting. Эти три инструмента: YAMLLint, Ansible-lint, Syntax Check. И это уже гораздо лучше, чем было.

v1qussep3_c0mdbhxdnm66eoiua.png

Здесь возникает вопрос. Можно ли пойти дальше и еще что-нибудь улучшить? У нас есть часть, которая в Ansible Galaxy и которая проверяется Molecule, у нас имеются роли, которые мы в Molecule тоже можем проверить внутри. Но есть большая часть конфигурационного кода, который содержит переменные, всякие конфигурационные файлы. И это достаточно большие куски кода, которые не проверяются никак. Вернее, мы проверили, что YAML в well format и больше ничего не проверили здесь.

bgp3t_on2vb9p2k0poevu5txkxk.png

Можем ли мы еще что-то проверить? Как быть с конфигурацией? Плохая новость в том, что Molecule — только для ролей. Но вы можете комбинацию проверить в Molecule.

Но проверить развертывание на прод можно только развернув на прод, потому что у вас там прописано, на какие машины вы устанавливаете, как вы связываете это все и только в процессе развертывания на прод это запустится.

yd4pv_xcw3a7po3qyhzpyk-x1bw.png

Но хорошая новость в том, что вы можете проверить проект, не запуская его.

Это докладчики с конференции по тестированию ПО — Heisenbug. Они рассказывали про такую вещь, как конфигурационное тестирование.

a1rxg6i44u5ybwotvbhuo7xhtxg.png

На самом деле идея очень-очень простая. Если у вас имеется некий конфигурационный файл, то что мы можем проверить?


  • Мы можем проверить формат значений переменных. Если у нас в значении порт, то это порт, а не что-нибудь. Если хост, то хост. Если URL, то URL.
  • Мы можем проверить, что у нас не утекают в явном виде пароли.
  • Мы можем проверить уникальность портов. Если вы назвали две переменные одинаковым портом, то, как правило, это ошибка, в Ansible особенно.
  • И другие более специфические вещи. Например, Андрей Сатарин рассказывал в своем докладе о том, что ему необходимо было проверять, что разные instances сервиса ставятся в машины, расположенные в разных стойках, потому что иначе не будет достаточного file save. Потому что если их запихнуть все в одну стойку и стойка отключится, то тогда какая разница, что у нас было 3 instances?

И на самом деле идея проста. Мы берем и пишем эти проверки, используя тестовой фреймворк, например, pytest. Просто это неожиданно, потому что это неисполняемый код, а статический. Сейчас я покажу, как это может выглядеть.

f7wfirmtx2m0avb6pbkyhhhmfzy.png

Например, как мы можем проверить валидность портов? Мы можем написать вот такой вот параметризованный тест, являющийся частью pytest, который проверяет для всех пар ключ значений, где ключ — это переменная, которая держит порт, а значение — это значение этой переменной. Мы можем обычные asserts написать, что это интовое поле, что порт находится в разрешенном вами диапозоне. Т. е. идея очень простая. Вам надо написать генератор на Python, который вам будет выдавать все values-значения для переменных, в которых содержатся порты.

ave8icjotwx98amrm-hemktlj3m.png

Как мы отличим переменные, которые содержат порты от других переменных? Мы это можем сделать вот таким вот образом. Мы можем договориться, что наши переменные, в которых порты прописываются, имеют: re.compile («port$»). И если у вас есть другая функция «var_values», которая выдает вам все переменные, все пары ключ значений, все переменные в вашем Ansible playbooks, то вот таким образом вы можете отфильтровать из этого потока «var_values» только те, которые оканчиваются на «port». И как на слайде ранее параметризовать тот тест, и тогда этот тест будет выполняться на портах, будет их проверять.

А как написать var_values? Взять на Python и написать. Это же обход YAML-дерева. Примерно мы знаем, как устроены YAML у Ansible и надо просто вытащить оттуда все куски, которые содержат переменные.

0cge8g9b5rcd3y7zjtcurvhkek4.png

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

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

ndk_53txd6_wytpacr-8bfacv80.png

Таким образом мы можем еще один слог сказать, но теперь уже для тестов конфигурации: Keep the bar green to keep the configuration clean». Вот как это выглядит, когда все сработало.

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

hkchuh90w1l651tffrcu2e2hc68.png

И напоследок, как можно поймать «утекающий» пароль в таком случае. Во-первых, мы выделим из Ansible-потока ключ значений переменных, выделим те, которые содержать в себе пароль.

Выделить их можно так: они заканчиваются либо на слово «password», либо на слово «pass», либо на слово «pwd». И, во-вторых, мы просто проверяем, что в значении должен быть placeholder, который ссылается на что-то, например, на какую-то переменную, которая находится в Vault. И все, этого достаточно.

r_kovxpxhggjar5zgdfkns8jp0s.png

Вот это реальный случай. Был такой pull request. Запустился тест. И этот тест возвращает мне k=«myskq_root_password», v=»12345». Т. е. плохо не то, что password — 12345, а то, что он в GitHub теперь. И понятно, что такой pull request мержить нельзя.

Но давайте к выводам переходить.

6fvogmb9p-lj1lzce90bisk3mrs.png

Первый вывод — тестируйте ваш Ansible.


  • Во-первых, эту троицу: YAMLLint + Ansible-lint + Syntax check вы можете подсоединить прямо сегодня. Если у вас есть какие-то портянки с Ansible-кодом, есть какие-то репозитории, где это все лежит, просто возьмите и подключите. Посмотрите, какие ворнинги там возникнут. Потом у вас будет увлекательное время, чтобы пофиксить. Ansible-lint может вам многое рассказать, многому вас научить, потому что его best practices многие не знают.
  • Проверяйте роли на Molecule.
  • Вставляйте проверки в хэндлеры. Делайте ваши playbook failed fast. Если что пошло не так, то мы бы узнали как можно раньше.
  • И тестируйте конфигурацию.

nslr9ruw6lznmu6apuua3dti97w.png


  • Если у вас есть где-то роли, то попробуйте Molecule. Это просто pip install molecule, molecule init, molecule test.
  • Лень разбираться с тестом? Попробуйте converge и idempotence.
  • Лень разбираться converge и idempotence? Пусть он хотя синтаксис проверит.

tjwn8klvrwdzl9r8fddpwnwagre.png

Я верю, что все должно быть как код: инфраструктура как код, база данных как код, документация как код. Что это значит? Это не только лежать в репозитории, не только лежать в Git, а это значит, что должен быть pipeline, должна быть процедура изменения этого кода.

ibbibzu1dccagtq8w_fslbqpo14.png

Штатный набор инструментов, который они сами предлагают, — это GitHub + Travis + Calaxy, это если вы разрабатываете роли в OpenSource. Правда, я вижу мало поводов разрабатывать не в OpenSource. Jenkins Multibranch тоже отлично для нас работает.

klay5nrakvoxev7akq9cefxbdzu.png

Ссылки, про которые я упоминал будут, на слайдах, будут расшарены.

Вопросы:

Вопрос: Спасибо за доклад! Меня зовут Роман. Я хотел бы уточнить — делали ли вы написание тестов обязательным, чтобы выложить роль в общее пользование? И если — да, то насколько это увеличило порог вхождения в Ansible? Потому что Ansible всем подается, как простая система управления конфигурациями, а в итоге человеку нужно знать Python, Testinfra, разобраться с Molecule. И количество боли увеличивается, потому что каждое изменение на Python может ему не нравится или он его не знает.

Ответ: Как инфорсить написание тестов? Оно точно также форсится, когда вы пишите на Java. Сделал pull request и решаем — нужны тесты или не нужны. Касаемо порога вхождения, то роли — это какой-то расшаренный код, т. е. это как бы библиотечный код. Как правило, если человеку нужно что-то быстро подправить, то он работает с кодом, который уже существует, поэтому он просто зайдет и подправит. Тесты для этого не нужны. Тесты на playbooks не нужны. Есть тесты на ролях, которые именно расшарены между собой. И если он даже что-то подправит, то надо ли менять тесты? Потому что это же не тесты как код. Инфраструктурные тесты очень просты. Там два-три стейтмента, которые проверяешь. Мы запустили что-то в shell, а этот shell нам что-то вывел. Мы можем достаточно многое поменять в роли, а тесты не менять.

Вопрос: Тестировали ли ansible в Windows. Мы запускаем Ansible playbook на Windows. И как это тестировать? Может быть, есть у вас опыт по этому поводу?

Ответ: К сожалению, нет у меня опыта разворачивания Ansible чего-то на Windows и тестирования этого в Testinfra. Мы разрабатываем под Windows, т. е. Ansible в cygwin запускается и работает. Но все роли мы выкладываем на Linux-сервера и тестируем там, поэтому не могу ответить. Может быть, возможно. Но у меня опыта такого не было.

Вопрос: Выносить проверки в хэндлер — это хорошая идея, но как мне поступить, если от результата проверки зависит роль? Т. е. если логика роли будет меняться. Поможет мне в этом changed the *never*, например?

Ответ: Вы имеете в виду, что вы хотите запустить что-то, получить какой-то вывод в консоли, захватить его в переменную и в зависимости от результата этого вывода выполнить либо одно, либо другое?

Вопрос: Допустим, проверить могу ли я залогинеться с паролем, который у меня есть. И если пользователя нет, то создать пользователя.

Ответ: Это не про проверки, это н

© Habrahabr.ru