Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Това е разшифровка на презентацията на DevOps-40 2020-03-18:

След второто комитване, всеки код става legacy, тъй като оригиналните намерения започват да се разминават с суровата реалност. Това не е добро и не е лошо, това е факт, с който е трудно да се спори и трябва да се научим да съжителстваме. Част от този процес е рефакторингът. Рефакторинг на Инфраструктурата като Код. Нека започне историята как да рефакторим Ansible за година и да не загубим ума си.

Раждането на Legacy

Ден № 1: Нулевият пациент

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Имаше един условен проект. В него имаше Dev екип за разработка и Ops инженери. Те решаваха една и съща задача: как да разгръщат сървъри и да стартират приложение. Проблемът беше, че всеки екип решаваше тази задача по своему. В проекта беше прието решение да се използва Ansible за синхронизиране на знанията между екипите Dev и Ops.

Ден № 89: Раждането на Legacy

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Без да го осъзнават, искаха да направят всичко възможно най-добро, а в крайна сметка се получи legacy. Как така стана?

  • Имаме спешна задача, ще направим мръсен хак — после ще го оправим.
  • Документацията не е необходима, така или иначе всичко е ясно какво става.
  • Аз знам Ansible / Python / Bash / Terraform! Вижте как мога да се извивам!
  • Аз съм Full Stack Overflow Developer, копирах това от stackoverflow, не знам как работи, но изглежда готино и решава задачата.

В крайна сметка може да получите код от непонятен вид, за който няма документация, не е ясно какво прави, нужен ли е, но проблемът е, че трябва да го развиете, да добавите храсти с подпори, правейки ситуацията само още по-лоша.

- hosts: localhost
  tasks:
    - shell: echo -n Z >> a.txt && cat a.txt
      register: output
      delay: 1
      retries: 5
      until: not output.stdout.find("ZZZ")

Ден № 109: Осъзнаване на проблема

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Първоначално замислената и реализирана модел на IaC спира да отговаря на реалността с исканията на потребителите / бизнеса / други екипи, времето за внасяне на промени в инфраструктурата спира да е приемливо. В този момент идва разбирането, че е време да се предприемат мерки.

Рефакторинг на IaC

Ден № 139: Наистина ли ви е нужен рефакторинг?

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Преди да се хвърлите в рефакторинг, трябва да отговорите на редица важни въпроси:

  1. Защо ви е всичко това?
  2. Имате ли време?
  3. Достатъчно ли е знанието?

Ако не знаете как да отговорите на въпросите, рефакторингът ще завърши, преди да е започнал или може да стане само по-лошо. Тъй като имаше опит( Какво научих, тестввайки 200 000 реда инфраструктурен код), проектът поиска помощ за корекция на ролите и покритие с тестове.

Ден № 149: Подготовка на рефакторинг

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Основната задача е да се подготвим. Трябва да определим какво ще правим. Затова се свързваме, идентифицираме проблематичните точки и разглеждаме възможностите за решение. Записваме получените концепции, например в статия в confluence, за да не изгубим посоката при възникване на въпроса "как е най-добре?" или "как е правилно?" В нашия случай се придържахме към идеята разделяй и владей: разделяме инфраструктурата на малки парчета/кирпичи. Този подход позволява да вземем изолиран участък от инфраструктурата, да разберем какво прави, да го покрием с тестове и да го променим, без да се страхуваме, че нещо ще се срути.

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

В резултат на това, тестироването на инфраструктурата става краеуголният камък и тук следва да се спомене пирамидата на тестирование на инфраструктурата. Същата идея, която се прилага в разработката, но за инфраструктурата: започваме от евтини и бързи тестове, които проверяват основни неща, например отстъпи, до скъпи и пълноценни тестове, които разгръщат цялостна инфраструктура.

Опити за тестване на Ansible

Преди да продължим с описанието на това как покрихме с тестове Ansible на проекта, ще опиша опитите и подходите, които използвахме по-рано, за да разберем контекста на взетите решения.

Ден № -997: Provision на SDS

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Първият път, когато тестирах Ansible, беше на проект за разработка на SDS (Програмирано хранилище). Има отделна статия по тази тема
Как да направим велосипедите отгоре на патерици при тестването на собственото си дистрибутивно решение, но накратко, получихме обърната пирамида на тестването и тестовете ни отнемаха 60-90 минути на една роля, което е дълго. Основата бяха e2e тестовете, т.е. разгръщахме пълна инсталация и после я тестваме. Допълнително усложнение беше създаването на собствено решение. Но трябва да призная, че това решение работеше и позволяваше стабилно разгръщане.

Ден № -701: Ansible и test kitchen

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Развитието на идеята за тестване на Ansible стана чрез използването на готови инструменти, а именно test kitchen / kitchen-ci и inspec. Изборът беше обусловен от познанията ми по Ruby (повече в статия на хабра: Мечтаят ли YML програмистите за тестване на Ansible?) работеше по-бързо около 40 минути за 10 роли. Създавахме партида виртуални машини и вътре стартирахме тестовете.

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Като цяло решението работеше, но имаше неприятно усещане заради нееднородността. Когато увеличихме броя на тестваните до 13 основни роли и 2 мета роли, комбиниращи по-малки роли, изведнъж тестовете започнаха да отнемат 70 минути, което е почти два пъти повече. За XP (extreme programming) практиките беше трудно да се говори, тъй като никой не желае да чака 70 минути. Това стана основание за промяна на подхода.

Ден № -601: Ansible и molecule

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Концептуално това е подобно на testkitchen, само че преместихме тестването на роли в docker и сменихме стека. В резултат, времето намаля до стабилни 20-25 минути за 7 роли.

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Увеличавайки броя на тестваните роли до 17 и линтовката на 45 роли, ние изпълнихме това за 28 минути на 2 jenkins slave.

Ден № 167: Добавяме тестове Ansible към проекта

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

С една идея трудно ще се справим с рефакторинга. Задачата трябва да бъде измерима, за да може да я разделите на малки части и да изяждате слона по части с лъжичка. Трябва да има разбиране дали движението е в правилната посока и колко дълго ще продължи.

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Като цяло не е важно как точно ще се направи, можете да запишете на хартия, да лепите стикери на шкафа, да създадете задачи в jira или да започнете google docs и там да запишете текущия статус. Всичко произтича от факта, че процесът не е мигновен, той ще бъде дълъг и труден. Малко вероятно е някой да иска в периода на рефакторинг да изгуби интерес, да се умори и да се отказва.

Рефакторингът е прост:

  • Яж.
  • Спи.
  • Код.
  • IaC тест.
  • Повтаряй.

и така повтаряме, докато не постигнем поставената цел.

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Не е сигурно, че можем да започнем да тестваме всичко наведнъж, така че нашата първа задача беше да започнем с линтовка и проверка на синтаксиса.

Ден № 181: Зелен Build Master

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Линтовката е малка първа стъпка към Зелен Build Master. Това почти няма да счупи нещо, но ще позволи да се наложат процесите и да се направят зелени билдове в jenkins. Идеята е да се формират навици в екипа:

  • Червените тестове са лоши.
  • Ако дойдеш да поправиш нещо, направи кода малко по-добър, отколкото е бил преди теб.

Ден № 193: От линтовка до unit тестове

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Построявайки процеса на извеждане на кода в master, можем да започнем процеса на постепенно подобряване — заменяйки линтовката с изпълнение на роли, дори и без идемпотентност. Необходимо е да разберем как да приложим ролите, как работят те.

Ден № 211: От unit тестове към интеграционни тестове

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Когато повечето роли са покрити с unit тестове и всичко е линтувано, можем да преминем към добавяне на интеграционни тестове. С други думи, тестиране не на отделен елемент в инфраструктурата, а на техни комбинации, например пълна конфигурация на инстанс.

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

В Jenkins генерирахме множество стадии, които паралелно линтуваха роли/плейбуци, след което юнит тестовете в контейнери и накрая интеграционните тестове.

Jenkins + Docker + Ansible = Тестове

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

  1. Проверете репото и генерирайте етапи за изграждане.
  2. Стартирайте етапите на линт плейбука паралелно.
  3. Стартирайте етапите на линт ролите паралелно.
  4. Стартирайте етапите за проверка на синтаксиса на ролите паралелно.
  5. Стартирайте етапите за тестване на ролите паралелно.
    1. Линтинг на ролата.
    2. Проверка на зависимостите от други роли.
    3. Проверка на синтаксиса.
    4. Създайте Docker инстанция.
    5. Стартирайте molecule/default/playbook.yml.
    6. Проверка на идемпотентност.
  6. Стартиране на интеграционни тестове.
  7. Завърши

Ден № 271: Bus Factor

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

В началото рефакторингът се осъществяваше от малка група хора, в която имаше двама-трима души. Те правеха ревю на кода в основния клон. С времето в екипа се разви знание как да се пише код и ревюто на кода способстваше за разпространение на знанията за инфраструктурата и как тя е изградена. Изключителното тук беше, че ревюверите се избираха по ред, според графика, т.е. с определена вероятност да се сблъскат с нова част от инфраструктурата.

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

И тук трябва да е удобно. Удобно е да правите ревю, да видите в рамките на коя задача е направено, историята на дискусиите. Интегрирахме Jenkins + Bitbucket + Jira.

Но ревюто само по себе си не е панацея, понеже в един момент в основния клон проби код, който ни създаде проблеми с тестовете:

- get_url:
    url: "{{ actk_certs }}\/{{ item.1 }}"
    dest: "{{ actk_src_tmp }}\/"
    username: "{{ actk_mvn_user }}"
    password: "{{ actk_mvn_pass }}"
  with_subelements:
    - "{{ actk_cert_list }}"
    - "{{ actk_certs }}"
  delegate_to: localhost

- copy:
    src: "{{ actk_src_tmp }}\/{{ item.1 }}"
    dest: "{{ actk_dst_tmp }}"
  with_subelements:
    - "{{ actk_cert_list }}"
    - "{{ actk_certs }}"

След това го поправихме, но усещането остана.

get_url:
    url: "{{ actk_certs }}\/{{ actk_item }}"
    dest: "{{ actk_src_tmp }}\/{{ actk_item }}"
    username: "{{ actk_mvn_user }}"
    password: "{{ actk_mvn_pass }}"
  loop_control:
    loop_var: actk_item
  with_items: "{{ actk_cert_list }}"
  delegate_to: localhost

- copy:
    src: "{{ actk_src_tmp }}\/{{ actk_item }}"
    dest: "{{ actk_dst_tmp }}"
  loop_control:
    loop_var: actk_item
  with_items: "{{ actk_cert_list }}"

Ден № 311: Ускоряваме тестовете

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

С времето тестовете ставаха все повече, билдовете бяха по-бавни, достигайки до час в лошия случай. На едно от ретро събитията някой каза "добре е, че имаме тестове, но те са бавни". В крайна сметка се отказахме от интеграционните тестове на виртуални машини и ги адаптирахме за Docker, за да бъдат по-бързи. Също така заменихме Testinfra с Ansible Verifier, за да намалим броя на използваните инструменти.

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Строго казано, тук имаше комплекс от мер:

  1. Преминаване на docker.
  2. Премахнете тестването на роли, което се дублира заради зависимостите.
  3. Увеличете броя на слейвовете.
  4. Ред на стартиране на тестовете.
  5. Възможност за линтинг ВСИЧКО локално с една команда.

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

В крайна сметка Pipeline на jenkins също беше унифициран

  1. Генерирайте етапи на сглобяване.
  2. Линтувайте всички паралелно.
  3. Стартирайте етапите за тестване на ролите паралелно.
  4. Завърши.

Уроци извлечени

Избягвайте глобални променливи

Ansible използва глобални променливи, има частичен обход в лицето на private_role_vars, но това не е панацея.

Ще дам пример. Нека имаме role_a и role_b

# cat role_a/defaults/main.yml
---
msg: a

# cat role_a/tasks/main.yml
---
- debug:
    msg: role_a={{ msg }}

# cat role_b/defaults/main.yml
---
msg: b

# cat role_b/tasks/main.yml
---
- set_fact:
    msg: b
- debug:
    msg: role_b={{ msg }}

- hosts: localhost
  vars:
    msg: hello
  roles:
    - role: role_a
    - role: role_b
  tasks:
    - debug:
        msg: play={{msg}}

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Интересното е, че резултатът от изпълнението на плейбуците ще зависи от не винаги очевидни неща, например реда на изброяване на ролите. За съжаление, това е естеството на Ansible и най-доброто, което можем да направим, е да използваме някакви споразумения, например в рамките на ролята да използваме само променливи, описани в тази роля.

ЛОШО: да се използва глобална променлива.

# cat roles/some_role/tasks/main.yml
---
debug:
  var: java_home

ДОБРЕ: В defaults определете необходимите променливи и след това използвайте само тях.

# cat roles/some_role/defaults/main.yml
---
r__java_home:
 "{{ java_home | default('/path') }}"

# cat roles/some_role/tasks/main.yml
---
debug:
  var: r__java_home

Префикс за променливи на роли

ЛОШО: да се използва глобална променлива.

# cat roles/some_role/defaults/main.yml
---
db_port: 5432

ДОБРЕ: В ролята за променливи използвайте променливи с префикс на името на ролята, така че да можете по-лесно да разберете какво се случва при преглеждане на инвентара.

# cat roles/some_role/defaults/main.yml
---
some_role__db_port: 5432

Използвайте променлива за управление на цикъла

ЛОШО: Използвайте в цикли стандартната променлива item, ако тази задача/плейбук бъде включена някъде, това може да доведе до неочаквано поведение

---
- hosts: localhost
  tasks:
    - debug:
        msg: "{{ item }}"
      loop:
        - item1
        - item2

ДОБРЕ: Препокривайте променливата в цикъла чрез loop_var.

---
- hosts: localhost
  tasks:
    - debug:
        msg: "{{ item_name }}"
      loop:
        - item1
        - item2
      loop_control:
        loop_var: item_name

Проверете входните променливи

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

ДОБРЕ: Проверявайте променливите.

- name: "Проверете дали необходимите променливи от тип стринг са определени"
  assert:
    that: ahs_var is defined and ahs_var | length > 0 and ahs_var != None
    fail_msg: "{{ ahs_var }} трябва да бъде зададена, за да работи ролята"
    success_msg: "Необходими променливи {{ ahs_var }} са определени"
  loop_control:
    loop_var: ahs_var
  with_items:
    - ahs_item1
    - ahs_item2
    - ahs_item3

Избягвайте хешове и речници, използвайте плоска структура

Ако ролята очаква хеш/речник като един от параметрите, то ако искаме да поправим един от дочерните параметри, ще трябва да препокриваме целия хеш/речник, което повишава сложността на конфигурирането.

ЛОШО: Използвайте хеш/речник.

---
user:
  name: admin
  group: admin

ДОБРЕ: Използвайте плоска структура на променливите.

---
user_name: admin
user_group: "{{ user_name }}"

Създайте идемпотентни плейбукове и ролеви настройки

Ролите и плейбуците трябва да са идемпотентни, тъй като това намалява конфигурационното отклонение и избягва рисковете от повреда. Но ако използвате molecule, това е поведение по подразбиране.

Избягвайте използването на модули на команден шел

Използването на модулите на шел води до императивна парадигма на описание, вместо декларативна, която е основната за Ansible.

Тествайте ролите си чрез molecule

Molecule предлага много гъвкава опция, нека да разгледаме няколко сценария.

Molecule Множество инстанции

В molecule.yml в секция платформи може да опишете множество хостове, които да разгръщате.

---
    driver:
      name: docker
    platforms:
      - name: postgresql-instance
        hostname: postgresql-instance
        image: registry.example.com/postgres10:latest
        pre_build_image: true
        override_command: false
        network_mode: host
      - name: app-instance
        hostname: app-instance
        pre_build_image: true
        image: registry.example.com/docker_centos_ansible_tests
        network_mode: host

Съответно, тези хостове могат след това да се използват в converge.yml използвайте:

---
- name: Converge all
  hosts: all
  vars:
    ansible_user: root
  roles:
    - role: some_role

- name: Converge db
  hosts: db-instance
  roles:
    - role: some_db_role

- name: Converge app
  hosts: app-instance
  roles:
    - role: some_app_role

Ansible проверител

В molecule има възможност да се използва ansible за проверка дали инстансът е настроен правилно, в допълнение е по подразбиране от трето издание. Това не е толкова гъвкаво, колкото testinfra/inspec, но можете да проверявате, че съдържанието на файл съвпада с нашите очаквания:

---
- name: Verify
  hosts: all
  tasks:
    - name: copy config
      copy:
        src: expected_standalone.conf
        dest: /root/wildfly/bin/standalone.conf
        mode: "0644"
        owner: root
        group: root
      register: config_copy_result

    - name: Удостоверьте, че standalone.conf е променен
      assert:
        that: not config_copy_result.changed

Или разгръща услуги, чакайте тяхната достъпност и направете 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: Постоянни документи към solr
        command: /blah/solr/bin/post -c master /exampledocs/books.csv

Поставете сложна логика в модули и приставки

Ansible проповядва декларативен подход, следователно, когато правите разделения на код, трансформация на данни, модулите на шел, кодът става трудно четим. За да се справите с това и да го оставите прост за разбиране, не е излишно да се борите с тази сложност чрез създаване на собствени модули.

Обобщете съвети и трикове

  1. Избягвайте глобалните променливи.
  2. Префикс на променливите на ролите.
  3. Използвайте променлива за контрол на цикъла.
  4. Проверете входните променливи.
  5. Избягвайте хешове, използвайте плоска структура.
  6. Създайте идемпотентни плейбукове и ролеви настройки.
  7. Избягвайте използването на модули на команден шел.
  8. Тествайте ролите си чрез molecule.
  9. Сложната логика се поставя в модули и плъгини.

Заключение

Как да започнете тестове с Ansible, да рефакторирате проект през годината и да не изгубите ума си

Не е възможно просто така да рефакторите инфраструктурата на проекта, дори ако имате IaC. Това е дълъг процес, изискващ търпение, време и знания.

UPD1 2020.05.01 20:30 — За първоначално профилиране на плейбуковете можете да използвате callback_whitelist = profile_tasks за да разберете какво точно отнема дълго време. След това преминаваме през класиката за ускорение на ansible. Можете да опитате mitogen
UPD2 2020.05.03 16:34Английска версия

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster