Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

Гэта расшыфроўка выступы на DevOps-40 2020-03-18:

Пачынальна з другога комміта любы код становіцца legacy, т.я. першапачатковыя задумкі пачынаюць разыходзіцца з суровай рэальнасцю. Гэта не добра і не дрэнна, гэта дадзенасць з якой складана спрачацца і неабходна ўжывацца. Часткай гэтага працэсу з'яўляецца рэфактарынг. Рэфактарынг Infrastructure as Code. Ды пачнецца гісторыя як адрэфактарыць 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: SDS provision

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

Першы раз тэставаць Ansible давялося на праекце па распрацоўцы SDS (Software Defined Storage). Ёсць асобны артыкул на гэтую тэму
Як наламаць ровараў па-над мыліцамі пры тэставанні свайго дыстрыбутыва, Але калі коратка, то ў нас атрымалася перавернутая піраміда тэсціравання і тэсціраванне мы марнавалі 60-90 хвілін на адну ролю, што ёсць доўга. Аснова была e2e тэсты, г.зн. мы разгортвалі паўнавартасную ўсталёўку, і потым яе тэставалі. Яшчэ абцяжваючым было вынаходства свайго ровара. Але гэтае рашэнне працавала і дазваляла стабільна рэлізавацца.

Дзень № -701: Ansible і test kitchen

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

Развіццём ідэі тэставання Ansible стала выкарыстанне гатовых прылад, а менавіта test kitchen/kitchen-ci і inspec. Выбар быў абумоўлены веданнем Ruby ( падрабязней у артыкуле на хабры: Ці мараць YML праграмісты аб тэсціраванні ansible?) працавала хутчэй каля 40 хвілін на 10 роляў. Мы стваралі пачак віртуальных машын і ўсярэдзіне ганялі тэсты.

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

У цэлым рашэнне працавала, але быў асадачак з-за неаднароднасці. Калі ж павялічылі колькасць тэстоўваных да 13 базавых роляў і 2 мета роляў камбінуючыя драбнейшыя ролі, то раптам тэсты сталі бегчы 70 хвілін, што амаль у 2 разы даўжэй. Аб 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 І туды запісваць бягучы статут. Ногі растуць з таго, што працэс не імгненны, ён будзе доўгі і нудны. Малаверагодна, што нехта хоча, каб за час рэфактарынгу вы перагарэлі ідэй, стаміліся і забілі.

Рэфакторынг просты:

  • Ешце.
  • Sleep.
  • Кодэкс.
  • IaC test.
  • Паўтараць

і так паўтараем пакуль не дасягнем вызначанай мэты.

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

Усё адразу пачаць тэставаць можа не атрымаецца, таму ў нас першай задачай было пачаць з лінтоўкі і праверкі сінтаксісу.

Дзень №181: Green Build Master

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

Лінтоўка гэта невялікі першы крок да Green Build Master. Гэта амаль нічога не зламае, але дазволіць адладзіць працэсы і зрабіць зялёненькія білды ў jenkins. Ідэя ў тым, каб выпрацаваць звычкі ў каманды:

  • Чырвоныя тэсты дрэнна.
  • Прыйшоў выправіць нешта заадно зрабі код крыху лепш, чым ён быў да цябе.

Дзень № 193: Ад лінтоўкі да unit тэстаў

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

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

Дзень № 211: Ад unit да integration тэстам

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

Калі unit тэстамі пакрыта большасць роляў і ўсё лінтуецца, можна пераходзіць да дадання інтэграцыйных тэстаў. Г.зн. тэсціраванню не асобнай цаглінкі ў інфраструктуры, а іх камбінацыі, напрыклад паўнавартасную канфігурацыю інстанса.

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

На jenkins мы генеравалі мноства стадый, якія ў паралель лінтавалі ролі / плэйбукі, потым юніт тэсты ў кантэйнерах і ў канцы інтэграцыйныя тэсты.

Jenkins + Docker + Ansible = Tests

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

  1. Checkout repo and generate build stages.
  2. Run lint playbook stages in parallel.
  3. Run lint role stages in parallel.
  4. Run syntax check role stages in parallel.
  5. Run test role stages in parallel.
    1. Lint role.
    2. Check dependency on other roles.
    3. Check syntax.
    4. Create docker instance
    5. Run molecule/default/playbook.yml.
    6. Check idempotency.
  6. Run integration tests
  7. Заканчэнне

Дзень № 271: Bus Factor

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

Першым часам рэфактарынгам займалася невялікая група людзей у пару-тройку чалавек. Яны рабілі рэўю кода ў майстры. З часам у камандзе выпрацавалася веданне як пісаць код і code review спрыяла распаўсюджванню ведаў аб інфраструктуры і тым як яна ўладкована. Разыначкай тут было, што рэўюверы выбіраліся па чарзе, па графіку, г.зн. з некаторай доляй верагоднасці ты залезеш у новы ўчастак інфраструктуры.

Як пачаць тэставаць 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. Generate build stages.
  2. Lint all in parallel.
  3. Run test role stages in parallel.
  4. Гатова.

Урокі, вынятыя

Пазбягайце глабальных зменных

Ansible выкарыстоўвае глабальныя зменныя, есць частковы workaround ў выглядзе 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 і лепшае што можна зрабіць, тое выкарыстаць нейкія дамоўленасці, напрыклад усярэдзіне ролі выкарыстоўваць толькі зменную апісаныя ў гэтай ролі.

BAD: выкарыстоўваць глабальную зменную.

# 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

Prefix role variables

BAD: выкарыстоўваць глабальную зменную.

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

Добрым: У ролі для зменных выкарыстоўваць зменныя з прэфіксам імя ролі, гэта паглядзеўшы на inventory дазволіць прасцей зразумець што адбываецца.

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

Use loop control variable

BAD: Выкарыстоўваць у цыклах стандартную зменную 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

Check input variables

Мы дамовіліся выкарыстоўваць прэфіксы зменных, не будзе лішнім праверыць што яны вызначаны як мы чакаем і, напрыклад, не былі перакрыты пустым значэннем

Добрым: Правяраць зменныя.

- 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 slovies, use flat structure

Калі роля чакае hash/dictionary у адным з параметраў, то калі мы захочам паправіць адзін з даччыных параметраў, нам трэба будзе перавызначаць увесь hash/dictionary, што падвысіць складанасць канфігуравання.

BAD: Выкарыстоўваць hash/dictionary.

---
user:
  name: admin
  group: admin

Добрым: Выкарыстоўваць плоскую структуру зменных.

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

Create idempotent playbooks & roles

Ролі і плэйбукі павінны быць ідэмпатэнтнымі, т.я. памяншае configuration drift і страх зламаць нешта. Але калі вы карыстаецеся molecule, тыя гэтыя паводзіны па змаўчанні.

Avoid using command shell modules

Выкарыстанне shell модуля прыводзіць да імператыўнай парадыгме апісання, замест дэкларатыўнай, якая з'яўляецца асноўнай Ansible.

Test your roles via molecule

Molecule дазваляе вельмі гнуткая штука, давай паглядзім некалькі сцэнарыяў.

Molecule Multiple instances

В molecule.yml у секцыі platforms можна апісаць мноства хастоў якія разгортваць.

---
    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 verifier

У molecule ёсць магчымасць выкарыстоўваць ansible Для праверкі таго, што інстанс быў наладжаны правільна, нават гэта па змаўчанні з 3 рэлізу. Гэта не так гнутка як 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: Certify that standalone.conf changed
      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: Post documents to solr
        command: /blah/solr/bin/post -c master /exampledocs/books.csv

Put complex logic у modules & plugins

Ansible прапаведуе дэкларатыўны падыход, таму калі вы робіце галінаванне кода, трансфармацыю дадзеных, shell модулі, то код становіцца складана чытаным. Каб змагацца з гэтым і пакінуць яго простым для разумення, не будзе лішнім, змагацца з гэтай складанасцю шляхам стварэння сваіх модуляў.

Summarize Tips & Tricks

  1. Avoid Global Variables.
  2. Prefix role variables.
  3. Use loop control variable.
  4. Check input variables.
  5. Avoid hashes dictionaries, use flat structure.
  6. Create idempotent playbooks & roles.
  7. Avoid using command shell modules.
  8. Test your roles via molecule.
  9. Put complex logic у modules & plugins.

Заключэнне

Як пачаць тэставаць Ansible, адрэфактарыць праект за год і не зляцець з шпулек

Нельга проста так узяць і адрэфактарыць інфраструктуру на праекце, нават калі ў вас IaC. Гэта доўгі працэс, які патрабуе цярпення, часу і ведаў.

UPD1 2020.05.01 20:30 - Для першаснага прафілявання плэйбукаў можна выкарыстоўваць callback_whitelist = profile_tasks што б зразумець, што менавіта доўга працуе. Пасля чаго праходзімся па класіцы паскарэння ansible. Можна паспрабаваць mitogen
UPD2 2020.05.03 16:34 - ангельская версія

Крыніца: habr.com

Дадаць каментар