Починаючи з другого комміту, будь-який код стає legacy, т.к. Початкові задуми починають розходитися із суворою реальністю. Це не добре і не погано, це даність з якою складно сперечатися та необхідно уживатися. Частиною цього процесу є рефакторинг. Рефакторинг Infrastructure as Code. Так почнеться історія як відрефакторити Ansible за рік і не злетіти з котушок.
Зародження Legacy
День №1: Нульовий пацієнт
Жив був умовний проект. На ньому Dev команда розробки та Ops інженери. Вони вирішували те саме завдання: як розвернути сервера і запустити додаток. Проблема була в тому, що кожна команда вирішувала це по-своєму. На проекті було вирішено використовувати Ansible для синхронізації знань між командами Dev та Ops.
День № 89: Зародження Legacy
Самі того не помітивши, хотіли зробити якнайкраще, а вийшло legacy. Як так виходить?
У нас тут термінова тяга, зробимо брудний хак – потім виправимо.
Документацію можна не писати і так все зрозуміло, що тут відбувається.
Я знаю Ansible / Python / Bash / Terraform! Дивіться, як я можу викрутитися!
Я Full Stack Overflow Developer скопіював це зі stackoverflow, не знаю як це працює, але виглядає прикольно та вирішує завдання.
У результаті можна отримати код незрозумілого виду, на який немає документації, незрозуміло що він робить, чи потрібен він, але проблема в тому, що вам необхідно його розвивати, доопрацьовувати, додавати милиці з підпорами, роблячи ситуацію тільки гірше.
Спочатку задумана та реалізована модель IaC перестає відповідати дійсності із запитами користувачів/бізнесу/інших команд, час внесення змін до інфраструктури перестає бути прийнятним. У цей момент приходить розуміння, що час вживати заходів.
Рефакторинг IaC
День № 139: А вам точно потрібний рефакторинг?
Перш ніж кидатися рефакторити ви повинні відповісти на низку важливих питань:
Навіщо вам це все?
Чи маєте ви час?
Чи достатньо знань?
Якщо ви не знаєте як відповісти на запитання, то рефакторинг закінчиться так і не розпочавшись або може вийти лише гірше. Т.к. був досвід( Що я дізнався, протестувавши 200 000 рядків інфраструктурного коду), то від проекту надійшов запит допомоги виправити ролі та покрити їх тестами.
День № 149: Підготовка рефакторингу
Першочергове це треба підготуватися. Визначитись що робитимемо. Для цього спілкуємося, знаходимо проблемні точки та прикидаємо шляхи їх вирішення. Отримані концепти якось фіксуємо, наприклад стаття в confluence, щоб при появі питання "як краще?" або "як правильніше?" ми не збилися з курсу. У нашому випадку ми дотримувалися ідеї розділяй і володарюй: дробимо інфраструктуру на маленькі шматочки / цеглини. Такий підхід дозволяє взяти ізольований шматок інфраструктури, зрозуміти що він робить, покрити його тестами та змінити не побоявшись щось зламати.
Виходить, що тестування інфраструктури стає наріжним каменем і варто згадати піраміду тестування інфраструктури. Рівно теж ідея, що в розробці, але для інфраструктури: йдемо від дешевих швидких тестів, які перевіряють прості речі, наприклад відступи, до дорогих повноцінних тестів, що розгортають цілісну інфраструктуру.
Спроби тестування Ansible
Перш ніж підемо описувати як покривали тестами Ansible на проекті, опишу спроби та підходи, які довелося використовувати раніше, щоб зрозуміти контекст прийнятих рішень.
День №997: SDS provision
Вперше тестувати Ansible довелося на проекті з розробки SDS (Software Defined Storage). Є окрема стаття на цю тему Як наламати велосипедів поверх милиць під час тестування свого дистрибутиваАле якщо коротко, то у нас вийшла перевернута піраміда тестування і тестування ми витрачали 60-90 хвилин на одну роль, що є довго. Основою була e2e тести, тобто. ми розгортали повноцінну інсталяцію і потім її тестували. Ще обтяжуючим був винахід свого велосипеда. Але це рішення працювало і дозволяло стабільно релізитися.
День № -701: Ansible та test kitchen
Розвитком ідеї тестування Ansible стало використання готових інструментів, а саме test kitchen/kitchen-ci та inspec. Вибір був зумовлений знанням Ruby (докладніше у статті на хабрі: Чи мріють YML програмісти про ansible тестування?) працювало швидше близько 40 хвилин на 10 ролей. Ми створювали пачку віртуальних машин та всередині ганяли тести.
Загалом рішення працювало, але був осад через неоднорідність. Коли ж збільшили кількість тестованих до 13 базових ролей і 2 мета ролей, що комбінують дрібніші ролі, то раптом тести почали бігти 70 хвилин, що майже в 2 рази довше. Про XP (extreme programming) практики було важко говорити т.к. ніхто не захоче чекати на 70 хвилин. Це стало приводом для зміни підходу
День № -601: Ansible та molecule
Концептуально це схоже на testkitchen, тільки ми перевели тестування ролей у docker та змінили стек. Підсумком час скоротився до стабільних 20-25 хвилин для 7 ролей.
Збільшивши кількість тестирумих ролей до 17 та лінтовку 45 ролей ми проганяли це за 28 хвилин на 2 jenkins slave.
День №167: Додаємо на проект тести Ansible
З наскоку завдання рефакторингу, швидше за все зробити не вийде. Завдання має бути вимірним, щоб ви могли її розбити на дрібні шматочки і з'їсти слона частинами чайною ложкою. Повинне бути розуміння в правильному напрямі йде рух, чи довго ще йти.
Загалом не має значення як це буде зроблено, можна писати на папірець, можна клеїти стікери на шафу, можна створювати таски в jira, а можна завести google docs І туди записувати поточний статус. Ноги ростуть із того, що процес не миттєвий, він буде довгим і нудним. Малоймовірно, що хтось хоче, щоб за час рефакторингу ви перегоріли ідей, втомилися та забили.
Рефакторинг простий:
Їсти.
Sleep.
Код.
IaC test.
Повторювати
і так повторюємо доки не досягнемо наміченої мети.
Все відразу почати тестувати може не вийде, тому у нас першим завданням було розпочати з лінтівки та перевірки синтаксису.
День №181: Green Build Master
Лінтівка це невеликий перший крок до Green Build Master. Це майже нічого не зламає, але дозволить налагодити процеси і зробити зелені білди в jenkins. Ідея в тому, щоб виробити звички у команди:
Червоні тести погано.
Прийшов виправити щось заразом, зроби код трохи краще, ніж він був до тебе.
День № 193: Від лінтівки до unit тестів
Вибудувавши процес попадання коду в майстер, можна починати процес поетапного поліпшення — замінюючи лінтівку на запуск ролей, можна навіть без ідемпотентності. Необхідно зрозуміти, як застосовувати ролі, як вони працюють.
День № 211: Від unit до integration тестів
Коли unit тестами покрита більшість ролей і все лінтується, можна перейти до додавання інтеграційних тестів. Тобто. тестуванню не окремої цеглини в інфраструктурі, а їх комбінації, наприклад, повноцінну конфігурацію інстансу.
На jenkins ми генерували безліч стадій, які в паралель лінтували ролі/плейбуки, потім юніт тести у контейнерах та наприкінці інтеграційні тести.
Jenkins + Docker + Ansible = Tests
Checkout repo and generate build stages.
Run lint playbook stages in parallel.
Run lint role stages in parallel.
Run syntax check role stages in parallel.
Run test role stages in parallel.
Lint role.
Check dependency on other roles.
Check syntax.
Create docker instance
Run molecule/default/playbook.yml.
Check idempotency.
Run integration tests
обробка
День №271: Bus Factor
Спочатку рефакторингом займалася невелика група людей у пару-трійку людей. Вони робили рев'ю коду в майстрі. Згодом у команді виробилося знання як писати код та code review сприяло поширенню знань про інфраструктуру і те, як вона влаштована. Родзинкою тут було, що ревьювери вибиралися по черзі, за графіком, тобто. з деякою часткою ймовірності ти залізеш у нову ділянку інфраструктури.
І тут має бути зручно. Зручно робити реву, бачити в рамках якого завдання воно зроблено, історію обговорень. Ми інтегрували jenkins + bitbucket + jira.
Але як таке рев'ю не панацея, якось, у нас проліз у майстер код, який зробив нам флапаючі тести:
З брехнею тестів ставало більше, білди бігли повільніше до години в поганому випадку. На одному з ретро була фраза на кшталт "добре що є тести, але вони повільні". У результаті ми відмовилися від інтеграційних тестів на віртуальних машинах та адаптували під docker, щоб було швидше. Так само замінили testinfra на ansible verifier щоб зменшити кількість використовуваних інструментів.
Суворо кажучи тут був комплекс заходів:
Перехід до docker.
Забрати тестування ролей, яке дублюється за рахунок залежностей.
Збільшити кількість слейвів.
Порядок запуску тестів.
Можливість лінтувати ВСІ локально однією командою.
У результаті Pipeline на jenkins також уніфікувався
Generate build stages.
Lint all in parallel.
Run test role stages in parallel.
Готово.
Уроки, витягнуті
Уникайте глобальних змінних
Неможливо використовує глобальні змінні, є частковий workaround у вигляді private_role_varsале це не панацея.
Забавна річ, що результат роботи плейбуків залежатиме від не завжди очевидних речей, наприклад, черговості перерахування ролей. На жаль це в натурі Ansible і краще що можна зробити, то використовувати якісь домовленості, наприклад, усередині ролі використовувати тільки змінну описані в цій ролі.
Ми домовилися використовувати префікси змінних, не буде зайвим перевірити, що вони визначені як ми очікуємо і, наприклад, не були перекриті порожнім значенням.
ХОРОШИЙ: Перевіряти змінні
- name: "Verify that required string variables are defined"
assert:
that: ahs_var is defined and ahs_var | length > 0 and ahs_var != None
fail_msg: "{{ ahs_var }} needs to be set for the role to work "
success_msg: "Required variables {{ ahs_var }} is defined"
loop_control:
loop_var: ahs_var
with_items:
- ahs_item1
- ahs_item2
- ahs_item3
Avoid hashes dictionaries, use flat structure
Якщо роль очікує hash/dictionary в одному з параметрів, то якщо ми захочемо виправити один із дочірніх параметрів, нам треба буде перевизначати весь hash/dictionary, що підвищить складність конфігурування.
Ролі та плейбуки мають бути ідемпотентними, т.к. зменшує configuration drift та страх зламати щось. Але якщо ви користуєтеся molecule, то це поведінка за умовчанням.
Avoid using command shell modules
Використання модуля shell призводить до імперативної парадигми опису, замість декларативної, яка є основною Ansible.
Test your roles via molecule
Molecule дозволяє дуже гнучка штука, давай подивимося кілька сценаріїв.
Molecule Multiple instances
В molecule.yml у секції platforms можна описати безліч хостів, які розгортати.
У molecule є можливість використовувати ansible Для перевірки того, що інстанс був налаштований правильно, навіть це за замовчуванням з 3 релізу. Це не так гнучко як testinfra/inspec, але можна перевіряти, що вміст файлу відповідає нашим очікуванням:
Або розгорнути сервіс, дочекатися його доступності та зробити smoke test:
---
- name: Verify
hosts: solr
tasks:
- command: /blah/solr/bin/solr start -s /solr_home -p 8983 -force
- uri:
url: http://127.0.0.1:8983/solr
method: GET
status_code: 200
register: uri_result
until: uri_result is not failed
retries: 12
delay: 10
- name: Post documents to solr
command: /blah/solr/bin/post -c master /exampledocs/books.csv
Put complex logic into modules & plugins
Ansible проповідує декларативний підхід, тому коли ви робите розгалуження коду, трансформацію даних, shell модулі, код стає складно читаним. Щоб поборотися з цим і залишити його простим для розуміння, не буде зайвим, боротися з цією складністю шляхом створення своїх модулів.
Summarize Tips & Tricks
Avoid global variables.
Prefix role variables.
Use loop control variable.
Check input variables.
Avoid hashes dictionaries, use flat structure.
Create idempotent playbooks & roles.
Avoid using command shell modules.
Test your roles via molecule.
Put complex logic в modules & plugins.
Висновок
Не можна просто так взяти та відрефакторити інфраструктуру на проекті, навіть якщо у вас IaC. Це довгий процес, що вимагає терпіння, часу та знань.
UPD1 2020.05.01 20:30 — Для первинного профілювання плейбуків можна використовувати callback_whitelist = profile_tasks щоб зрозуміти що саме довго працює. Після чого проходимося класиці прискорення ansible. Можна спробувати mitogen UPD2 2020.05.03 16:34 - Англійська версія