Ад скрыптоў да ўласнай платформы: як мы аўтаматызавалі распрацоўку ў ЦІАН

Ад скрыптоў да ўласнай платформы: як мы аўтаматызавалі распрацоўку ў ЦІАН

На РИТ 2019 наш калега Аляксандр Караткоў зрабіў даклад пра аўтаматызацыю распрацоўкі ў ЦІАН: каб спрасціць жыццё і працу, мы выкарыстоўваем уласную платформу Integro. Яна адсочвае жыццёвы цыкл задач, здымае з распрацоўшчыкаў руцінныя аперацыі і прыкметна скарачае колькасць багаў у production. У гэтым пасце мы дапоўнім даклад Аляксандра і раскажам, як прайшлі шлях ад простых скрыптоў да аб'яднання open source прадуктаў праз уласную платформу і чым у нас займаецца асобная каманда аўтаматызацыі.
 

Нулявы ўзровень

«Нулявога ўзроўню не бывае, я такога не ведаю»
Майстар Shifu з м/ф "Кунг-фу Панда"

Аўтаматызацыя ў ЦЫАН пачалася праз 14 гадоў пасля заснавання кампаніі. Тады ў камандзе распрацоўкі было 35 чалавек. З цяжкасцю верыцца, так? Вядома, у нейкім выглядзе аўтаматызацыя ўсё ж існавала, але асобны напрамак па бесперапыннай інтэграцыі і дастаўцы кода пачаў фармавацца менавіта ў 2015 годзе. 

На той момант у нас быў вялізны маналіт з Python, C# і PHP, разгорнуты на Linux/Windows серверах. Для дэплою гэтага монстра ў нас быў набор скрыптоў, які мы запускалі ўручную. Была яшчэ зборка маналіта, якая прыносіць боль і пакуты з-за канфліктаў пры зліцці галінак, праўках дэфектаў і перазборцы "з іншым наборам задач у білдзе". Спрошчана працэс выглядаў так:

Ад скрыптоў да ўласнай платформы: як мы аўтаматызавалі распрацоўку ў ЦІАН

Нас гэта не задавальняла, і мы хацелі пабудаваць паўтаральны, аўтаматызаваны і кіраваны працэс зборкі і дэплою. Для гэтага нам была патрэбна CI/CD сістэма, і мы выбіралі паміж бясплатнай версіяй Teamcity і бясплатнай Jenkins, бо мы з імі працавалі і абедзве ўладкоўвалі нас па наборы функцый. Выбралі Teamcity як больш свежы прадукт. Тады мы яшчэ не выкарыстоўвалі мікрасэрвісную архітэктуру і не разлічвалі на вялікую колькасць задач і праектаў.

Прыходзім да ідэі аб уласнай сістэме

Укараненне Teamcity прыбрала толькі частка ручной працы: засталося яшчэ стварэнне Pull Request-ов, пасоўванне задач па статутах у Jira, выбар задач для рэлізу. З гэтым сістэма Teamcity ужо не спраўлялася. Трэба было выбіраць шлях далейшай аўтаматызацыі. Мы разглядалі варыянты працы са скрыптамі ў Teamcity ці пераход на іншыя сістэмы аўтаматызацыі. Але ў выніку вырашылі, што патрэбная максімальная гнуткасць, якую дае толькі ўласнае рашэнне. Так з'явілася першая версія ўнутранай сістэмы аўтаматызацыі пад назовам Integro.

Teamcity займаецца аўтаматызацыяй на ўзроўні запуску працэсаў зборкі і дэплою, а Integro сфакусавалася на верхнеўзроўневай аўтаматызацыі працэсаў распрацоўкі. Трэба было аб'яднаць працу з задачамі ў Jira з апрацоўкай звязанага зыходнага кода ў Bitbucket. На гэтым этапе ўсярэдзіне Integro сталі з'яўляцца свае workflow для працы з задачамі розных тыпаў. 

З-за павелічэння аўтаматызацыі ў бізнэс-працэсах вырасла колькасць праектаў і run-ов у Teamcity. Так прыйшла новая праблема: аднаго бясплатнага інстансу Teamcity стала бракаваць (3 агента і 100 праектаў), мы дадалі яшчэ адзін інстанс (яшчэ 3 агента і 100 праектаў), потым яшчэ. У выніку мы атрымалі сістэму з некалькіх кластараў, якой было складана кіраваць:

Ад скрыптоў да ўласнай платформы: як мы аўтаматызавалі распрацоўку ў ЦІАН

Калі паўстала пытанне аб 4 інстансе, мы зразумелі, што далей так жыць нельга, бо сукупныя выдаткі на падтрымку 4 інстансаў ужо не лезлі ні ў якія рамкі. Узнікла пытанне пакупкі платнай Teamcity або выбару на карысць бясплатнай Jenkins. Мы правялі разлікі па інстанс і планах па аўтаматызацыі і вырашылі, што будзем жыць на Jenkins. Праз пару тыдняў мы перайшлі на Jenkins і пазбавіліся ад часткі галаўнога болю, звязанай з падтрымкай некалькіх інстансаў Teamcity. Таму мы змаглі засяродзіцца на распрацоўцы Integro і дапілоўванні Jenkins пад сябе.

З ростам базавай аўтаматызацыі (у выглядзе аўтаматычнага стварэння Pull Request-ов, збору і публікацыі Code coverage і іншых праверак) з'явілася ўстойлівае жаданне максімальна адмовіцца ад ручных рэлізаў і аддаць гэтую працу робатам. Апроч гэтага ўсярэдзіне кампаній пачаўся пераезд на мікрасэрвісы, якія патрабавалі частых рэлізаў, прычым асобна сябар ад сябра. Так мы паступова прыйшлі да аўтаматычных рэлізаў нашых мікрасэрвісаў (маналіт пакуль што выпускаем уручную з-за складанасці працэсу). Але, як гэта звычайна бывае, узнікла новая складанасць. 

Аўтаматызуем тэсціраванне

Ад скрыптоў да ўласнай платформы: як мы аўтаматызавалі распрацоўку ў ЦІАН

З-за аўтаматызацыі рэлізаў паскорыліся працэсы распрацоўкі, прычым часткова за кошт пропуску некаторых этапаў тэсціравання. А гэта прывяло да часовай страты якасці. Гучыць банальна, але разам з паскарэннем рэлізаў трэба было мяняць і метадалогію распрацоўкі прадукта. Трэба было задумацца аб аўтаматызацыі тэставання, прывіцці персанальнай адказнасці (тут гаворка пра «прыняцце ідэі ў галаве», а не грашовыя штрафы) распрацоўніка за які выпускаецца код і багі ў ім, а таксама аб рашэнні па выпуску/не выпуску задачы праз аўтаматычны дэплой. 

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

Каманда аўтаматызацыі

Цяпер у нас штат са 130 распрацоўшчыкаў, і мы працягваем расці. Каманда па бесперапыннай інтэграцыі і дастаўцы кода (далей - каманда Deploy and Integration або DI) складаецца з 7 чалавек і працуе ў 2 напрамках: распрацоўка платформы аўтаматызацыі Integro і DevOps. 

DevOps адказвае за Dev/Beta асяроддзі сайта CIAN, асяроддзі Integro, дапамагае распрацоўнікам у рашэнні праблем і выпрацоўвае новыя падыходы да маштабавання акружэнняў. Кірунак распрацоўкі Integro займаецца як самой Integro, так і сумежнымі сэрвісамі, напрыклад, убудовамі для Jenkins, Jira, Confluence, а таксама распрацоўвае дапаможныя ўтыліты і прыкладанні для каманд распрацоўнікаў. 

Каманда DI працуе сумесна з камандай Платформы, якая займаецца распрацоўкай архітэктуры, бібліятэк і падыходаў да распрацоўкі ўнутры кампаніі. Разам з гэтым, любы распрацоўшчык унутры ЦІАН можа ўнесці ўклад у аўтаматызацыю, напрыклад, зрабіць мікрааўтаматызацыю пад патрэбы каманды або падзяліцца класнай ідэяй, як зрабіць аўтаматызацыю яшчэ лепш.

Пластовы пірог аўтаматызацыі ў ЦІАН

Ад скрыптоў да ўласнай платформы: як мы аўтаматызавалі распрацоўку ў ЦІАН

Усе сістэмы, задзейнічаныя ў аўтаматызацыі, можна падзяліць на некалькі пластоў:

  1. Вонкавыя сістэмы (Jira, Bitbucket і інш.). З імі працуюць каманды распрацоўкі.
  2. Платформа Integro. Часцей за ўсё распрацоўшчыкі не працуюць з ёй напрамую, але менавіта яна падтрымлівае працу ўсёй аўтаматызацыі.
  3. Сэрвісы дастаўкі, аркестравання і выяўленні (напрыклад, Jeknins, Consul, Nomad). З іх дапамогай мы разгортваем код на серверах і забяспечваем працу сэрвісаў сябар з сябрам.
  4. Фізічны ўзровень (сервера, АС, сумежнае ПЗ). На гэтым узроўні працуе наш код. Гэта можа быць як фізічны сервер, так і віртуальны (LXC, KVM, Docker).

Зыходзячы з гэтай канцэпцыі мы дзелім зоны адказнасці ўнутры каманды DI. Два першыя ўзроўні ляжаць у зоне адказнасці напрамкі распрацоўкі Integro, а два апошнія ўзроўню ўжо ў зоне адказнасці DevOps. Такі падзел дазваляе сфакусавацца на задачах і не мяшае ўзаемадзеянню, т. да. мы знаходзімся побач сябар з сябрам і стала абменьваемся ведамі і досведам.

Некрануты

Засяродзімся на Integro і пачнем з тэхналагічнага стэка:

  • CentOs 7
  • Docker + Nomad + Consul + Vault
  • Java 11 (стары маналіт Integro застанецца на Java 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • RabbitMQ 
  • Apache Ignite
  • Camunda (embedded)
  • Grafana + Graphite + Prometheus + Jaeger + ELK
  • Web UI: React (CSR) + MobX
  • SSO: Keycloak

Мы прытрымліваемся прынцыпу мікрасэрвіснай распрацоўкі, хаця ў нас і прысутнічае legacy у выглядзе маналіта ранняй версіі Integro. Кожны мікрасэрвіс круціцца ў сваім docker-кантэйнеры, сэрвісы маюць зносіны паміж сабой з дапамогай HTTP-запытаў і RabbitMQ-паведамленняў. Мікрасэрвісы знаходзяць адзін аднаго праз Consul і выконваюць да яго запыт, праходзячы аўтарызацыю праз SSO (Keycloak, OAuth 2/OpenID Connect).

Ад скрыптоў да ўласнай платформы: як мы аўтаматызавалі распрацоўку ў ЦІАН

У якасці рэальнага прыкладу разгледзім узаемадзеянне з Jenkins, якое складаецца з наступных крокаў:

  1. Мікрасэрвіс кіравання workflow (далей Flow-мікрасэрвіс) жадае запусціць зборку ў Jenkins. Для гэтага ён праз Consul знаходзіць IP:PORT мікрасэрвісу інтэграцыі з Jenkins (далей Jenkins-мікрасэрвіс) і адпраўляе ў яго асінхронны запыт на запуск зборкі ў Jenkins.
  2. Jenkins-мікрасервіс пасля атрымання запыту фармуе і аддае ў адказ Job ID, па якім потым можна будзе ідэнтыфікаваць вынік працы. Разам з гэтым ён запускае зборку ў Jenkins праз выклік REST API.
  3. Jenkins выконвае зборку і пасля яе канчатка адпраўляе webhook з вынікамі выканання ў Jenkins-мікрасэрвіс.
  4. Jenkins-мікрасэрвіс, атрымаўшы webhook, фармуе паведамленне аб канчатку апрацоўкі запыту і прымацоўвае да яго вынікі выканання. Сфарміраванае паведамленне адпраўляецца ў чаргу RabbitMQ.
  5. Праз RabbitMQ апублікаванае паведамленне трапляе да Flow-мікрасэрвісу, які даведаецца аб выніку апрацоўкі сваёй задачы, супаставіўшы Job ID з запыту і атрыманага паведамлення.

Цяпер у нас каля 30 мікрасэрвісаў, якія можна разбіць на некалькі груп:

  1. Упраўленне канфігурацыямі.
  2. Інфармаванне і ўзаемадзеянне з карыстальнікамі (месенджары, пошта).
  3. Праца з зыходным кодам.
  4. Інтэграцыя з прыладамі дэплою (jenkins, nomad, consul і т. д.).
  5. Маніторынг (рэлізаў, памылак і т. д.).
  6. Web-утыліты (UI для кіравання тэставымі асяроддзямі, збору статыстыкі і т. д.).
  7. Інтэграцыя з таск-трэкерамі і падобнымі сістэмамі.
  8. Упраўленне workflow для розных задач.

Workflow задачы

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

Разгледзім workflow, які выкарыстоўваем часцей за ўсё:

Ад скрыптоў да ўласнай платформы: як мы аўтаматызавалі распрацоўку ў ЦІАН

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

Цалкам ручное тэсціраванне на DEV+BETA без канарэечных тэстаў (звычайна так выпускаем маналіт):

Ад скрыптоў да ўласнай платформы: як мы аўтаматызавалі распрацоўку ў ЦІАН

Могуць быць і іншыя камбінацыі transition. Часам шлях, па якім пойдзе задача, можна абраць праз опцыі ў Jira.

Рух задачы

Разгледзім асноўныя крокі, якія выконваюцца пры руху задачы па workflow «Тэставанне на DEV + канарэечныя тэсты»:

1. Распрацоўнік або PM стварае задачу.

2. Распрацоўнік бярэ задачу ў працу. Пасля завяршэння пераводзіць яе ў статус IN REVIEW.

3. Jira адпраўляе Webhook у бок Jira-мікрасэрвісу (адказвае за інтэграцыю з Jira).

4. Jira-мікрасэрвіс адпраўляе запыт у Flow-сэрвіс (адказвае за ўнутраныя workflow, у якіх выконваецца праца) для запуску workflow.

5. Усярэдзіне Flow-сэрвісу:

  • Прызначаюцца рэўюеры для задачы (Users-мікрасэрвіс, які ведае ўсё пра карыстальнікаў + Jira-мікрасэрвіс).
  • Праз Source-мікрасэрвіс (ведае аб рэпазітарах і галінках, але не працуе з самім кодам) ажыццяўляецца пошук рэпазітароў, у якіх ёсць галінка нашай задачы (для спрашчэння пошуку імя галінкі супадае з нумарам задачы ў Jira). Часцей за ўсё задача мае толькі адну галінку ў адным рэпазітары, гэта спрашчае кіраванне чаргой на дэплой і памяншае складнасць паміж рэпазітарамі.
  • Для кожнай знойдзенай галінкі выконваецца такая паслядоўнасць дзеянняў:

    i) Падліў master-галінкі (Git-мікрасэрвіс для працы з кодам).
    ii) Ветка блакуецца ад змен распрацоўшчыкам (Bitbucket-мікрасэрвіс).
    iii) Ствараецца Pull Request на гэтую галінку (Bitbucket-мікрасэрвіс).
    iv) Адпраўляецца паведамленне аб новым Pull Request у чаты распрацоўшчыкаў (Notify-мікрасэрвіс для працы з абвесткамі).
    v) Запускаюцца зборка, тэставанне і дэплой задачы на ​​DEV (Jenkins-мікрасэрвіс для працы з Jenkins).
    vi) Калі ўсе папярэднія пункты завяршыліся паспяхова, то Integro ставіць свой Approve у Pull Request (Bitbucket-мікрасэрвіс).

  • Integro чакае Approve у Pull Request ад прызначаных рэўюераў.
  • Як толькі атрыманы ўсе неабходныя Approve (у тым ліку станоўча пройдзены аўтаматызаваныя тэсты), Integro перакладае задачу ў статус Test on Dev (Jira-мікрасэрвіс).

6. Тэсціроўшчыкі праводзяць тэсціраванне задачы. Калі праблем няма, то пераводзяць задачу ў статут Ready For Build.

7. Integro "бачыць", што задача гатова да рэлізу, і запускае яе дэплой у канарэечным рэжыме (Jenkins-мікрасэрвіс). Гатоўнасць да рэлізу вызначаецца наборам правіл. Напрыклад, задача ў патрэбным статусе, няма блакіровак на іншыя задачы, цяпер няма актыўных выкладак гэтага мікрасэрвісу і да т.п.

8. Задача пераводзіцца ў статус Canary (Jira-мікрасэрвіс).

9. Jenkins запускае праз Nomad дэплой задачы ў канарэечным рэжыме (звычайна 1-3 інстанса) і паведамляе аб выкладцы сэрвіс маніторынгу рэлізаў (DeployWatch-мікрасэрвіс).

10. DeployWatch-мікрасэрвіс збірае фон памылак і рэагуе на яго, калі трэба. Пры перавышэнні фону памылак (норма фону разлічваецца аўтаматычна) ажыццяўляецца апавяшчэнне распрацоўшчыкаў праз Notify-мікрасэрвіс. Калі праз 5 хвілін распрацоўнік не адрэагаваў (націснуў Revert або Stay), то запускаецца аўтаматычны адкат канарэечных інстансаў. Калі фон не перавышаны, то распрацоўнік павінен уручную запусціць дэплой задачы на ​​Production (націскам кнопкі ў UI). Калі на працягу 60 хвілін распрацоўнік не запусціў дэплой у Production, то канарэечныя інстансы таксама ў мэтах бяспекі будуць адкачаныя.

11. Пасля запуску дэплою на Production:

  • Задача пераводзіцца ў статус Production (Jira-мікрасэрвіс).
  • Jenkins-мікрасэрвіс запускае працэс дэплою і паведамляе аб выкладцы DeployWatch-мікрасэрвіс.
  • DeployWatch-мікрасэрвіс правярае, што на Production абнавіліся ўсе кантэйнеры (былі выпадкі, калі абнаўлялася не ўсё).
  • Праз Notify-мікрасэрвіс адпраўляецца апавяшчэнне аб выніках дэплою ў Production.

12. У распрацоўшчыкаў будзе 30 хвілін на запуск адкату задачы з Production у выпадку выяўлення некарэктных паводзін мікрасэрвісу. Па заканчэнні гэтага часу задача будзе аўтаматычна ўліта ў master (Git-мікрасэрвіс).

13. Пасля паспяховага merge-а ў master статут задачы будзе зменены на Closed (Jira-мікрасэрвіс).

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

Што далей

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

Але на гэтым месцы пакуль спынімся. Шмат тэм у аглядзе аўтаматызацыі мы ахапілі павярхоўна, некаторыя не закранулі наогул, таму мы з радасцю адкажам на пытанні. Чакаем прапаноў, што асвятліць дэталёва, пішыце ў каментарах.

Крыніца: habr.com

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