werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

27 мај во главната сала на конференцијата DevOpsConf 2019, одржана како дел од фестивалот RIT++ 2019 година, како дел од делот „Континуирана испорака“, беше даден извештај „werf - нашата алатка за CI/CD во Кубернетес“. Зборува за тие проблеми и предизвици со кои се соочува секој при распоредувањето на Кубернетес, како и за нијанси кои можеби не се веднаш забележливи. Анализирајќи ги можните решенија, покажуваме како ова се спроведува во алатка со отворен код верф.

Од презентацијата, нашата алатка (порано позната како dapp) достигна историска пресвртница од 1000 ѕвезди на GitHub — се надеваме дека нејзината растечка заедница на корисници ќе им го олесни животот на многу инженери на DevOps.

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Значи, да воведеме видео од извештајот (~47 минути, многу поинформативна од статијата) и главниот извадок од него во текстуална форма. Оди!

Доставување на код до Kubernetes

Разговорот повеќе нема да биде за werf, туку за CI/CD во Kubernetes, што имплицира дека нашиот софтвер е спакуван во контејнери Docker (Зборував за ова во Извештај за 2016 година), а за негово производство ќе се користат K8s (повеќе за ова во 2017 година).

Како изгледа испораката во Кубернетес?

  • Постои складиште на Git со код и инструкции за негово градење. Апликацијата е вградена во Docker слика и објавена во Docker Registry.
  • Истото складиште содржи и инструкции како да се распореди и стартува апликацијата. Во фазата на распоредување, овие инструкции се испраќаат до Kubernetes, кој ја прима саканата слика од регистарот и ја стартува.
  • Плус, обично има тестови. Некои од нив може да се направат при објавување слика. Можете исто така (следејќи ги истите упатства) да распоредите копија од апликацијата (во посебен K8s namespace или посебен кластер) и да извршите тестови таму.
  • Конечно, потребен ви е CI систем кој прима настани од Git (или кликнувања на копчиња) и ги повикува сите назначени фази: градење, објавување, распоредување, тестирање.

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Овде има неколку важни забелешки:

  1. Затоа што имаме непроменлива инфраструктура (непроменлива инфраструктура), сликата на апликацијата што се користи во сите фази (сценирање, продукција итн.), мора да има еден. Зборував за ова подетално и со примери. тука.
  2. Затоа што ја следиме инфраструктурата како пристап на код (IaC), треба да биде кодот на апликацијата, упатствата за склопување и стартување точно во едно складиште. За повеќе информации за ова, видете истиот извештај.
  3. Синџир за испорака (испорака) обично го гледаме вака: апликацијата беше составена, тестирана, пуштена (фаза на ослободување) и тоа е тоа - испораката е извршена. Но, во реалноста, корисникот го добива она што сте го издале, Нема тогаш кога го доставивте во продукција, и кога тој можеше да оди таму и оваа продукција функционираше. Така, верувам дека синџирот за испорака завршува само во оперативна фаза (трчај), или поточно, дури и во моментот кога кодот беше отстранет од производство (заменувајќи го со нов).

Да се ​​вратиме на горната шема за испорака во Кубернетес: таа беше измислена не само од нас, туку буквално од сите што се занимаваа со овој проблем. Всушност, оваа шема сега се нарекува GitOps (можете да прочитате повеќе за терминот и идеите зад него тука). Ајде да ги погледнеме фазите на шемата.

Изградба на сцена

Се чини дека можете да зборувате за изградба на слики на Docker во 2019 година, кога секој знае како да пишува Dockerfiles и да работи docker build?.. Еве на кои нијанси би сакал да обрнам внимание:

  1. Тежина на сликата е важно, затоа користете мулти-фазада ја оставите на сликата само апликацијата која е навистина неопходна за операцијата.
  2. Број на слоеви мора да се минимизира со комбинирање на синџири на RUN-командува според значењето.
  3. Сепак, ова додава проблеми дебагирање, затоа што кога ќе падне склопот, треба да ја пронајдете вистинската команда од синџирот што го предизвикал проблемот.
  4. Брзина на склопување важно затоа што сакаме брзо да ги спроведеме промените и да ги видиме резултатите. На пример, не сакате да ги обновувате зависностите во јазичните библиотеки секогаш кога ќе градите апликација.
  5. Често од едно складиште на Git што ви треба многу слики, што може да се реши со множество Dockerfiles (или именувани фази во една датотека) и Bash скрипта со нивно секвенцијално склопување.

Ова беше само врвот на ледениот брег со кој се соочуваат сите. Но, има и други проблеми, особено:

  1. Често во фазата на склопување ни треба нешто монтирање (на пример, кеширајте го резултатот од команда како apt во директориум од трета страна).
  2. Ние сакаме Ansible наместо да пишува во школка.
  3. Ние сакаме изгради без Docker (зошто ни е потребна дополнителна виртуелна машина во која треба да конфигурираме сè за ова, кога веќе имаме кластер Kubernetes во кој можеме да работиме контејнери?).
  4. Паралелно склопување, што може да се разбере на различни начини: различни команди од Dockerfile (ако се користи повеќестепена), неколку обврзувања на истото складиште, неколку Dockerfiles.
  5. Дистрибуирано склопување: Сакаме да собираме работи во мешунки кои се „ефемерни“ затоа што нивниот кеш исчезнува, што значи дека треба да се складира некаде посебно.
  6. Конечно, го нареков врвот на желбите автомагија: Идеално би било да отидете во складиштето, да напишете некоја команда и да добиете готова слика, составена со разбирање како и што да правите правилно. Сепак, јас лично не сум сигурен дека сите нијанси можат да се предвидат на овој начин.

А еве ги проектите:

  • moby/buildkit — градител од Docker Inc (веќе интегриран во тековните верзии на Docker), кој се обидува да ги реши сите овие проблеми;
  • канико — градител од Google што ви овозможува да градите без Docker;
  • Buildpacks.io - Обидот на CNCF да направи автоматска магија и, особено, интересно решение со ребаза за слоеви;
  • и еден куп други комунални услуги, како на пр билда, оригинални алатки/img...

...и погледнете колку ѕвезди имаат на GitHub. Тоа е, од една страна, docker build постои и може да направи нешто, но во реалноста прашањето не е целосно решено - Доказ за тоа е паралелниот развој на алтернативни колектори од кои секој решава дел од проблемите.

Собрание во верф

Така стигнавме до верф (претходно познати како дап) — Услужна алатка со отворен код од компанијата Flant, која ја правиме долги години. Се започна пред 5 години со Bash скриптите кои го оптимизираа склопувањето на Dockerfiles, а во последните 3 години целосен развој се спроведуваше во рамките на еден проект со сопствено складиште Git (прво во Руби, а потоа препишани во Go, а во исто време преименувана). Кои проблеми со склопувањето се решаваат во верф?

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Проблемите засенчени во сино се веќе спроведени, паралелното градење е направено во истиот домаќин, а прашањата истакнати со жолта се планира да се завршат до крајот на летото.

Фаза на објавување во регистар (објавување)

Биравме docker push... - што би можело да биде тешко за поставување слика во регистарот? И тогаш се поставува прашањето: „Каква ознака да ставам на сликата? Тоа произлегува од причината што ја имаме Gitflow (или друга Git стратегија) и Kubernetes, а индустријата се обидува да осигура дека она што се случува во Kubernetes го следи она што се случува во Git. На крајот на краиштата, Git е нашиот единствен извор на вистината.

Што е толку тешко во ова? Обезбедете репродуктивност: од заложба во Git, која е непроменлива по природа (непроменлив), до слика на Docker, која треба да се чува иста.

Тоа е исто така важно за нас се утврди потеклото, затоа што сакаме да разбереме од кој commit е изградена апликацијата што работи во Kubernetes (тогаш можеме да правиме diffs и слични работи).

Стратегии за означување

Првиот е едноставен git tag. Имаме регистар со слика означена како 1.0. Кубернетес има сцена и продукција, каде што е поставена оваа слика. Во Git правиме обврзници и во одреден момент означуваме 2.0. Го собираме според упатствата од складиштето и го ставаме во регистарот со ознаката 2.0. Го пуштаме на сцена и, ако се е во ред, тогаш на продукција.

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Проблемот со овој пристап е што ние прво ја ставивме ознаката, а дури потоа ја тестиравме и ја изнесевме. Зошто? Прво, едноставно е нелогично: издаваме верзија на софтвер што сè уште не сме ја ни тестирале (не можеме да направиме поинаку, бидејќи за да провериме, треба да ставиме ознака). Второ, оваа патека не е компатибилна со Gitflow.

Втората опција - git commit + ознака. Главна гранка има ознака 1.0; за тоа во регистарот - слика распоредена за производство. Покрај тоа, кластерот Kubernetes има контури за преглед и поставување. Следно го следиме Gitflow: во главната гранка за развој (develop) правиме нови функции, што резултира со обврзување со идентификаторот #c1. Го собираме и го објавуваме во регистарот користејќи го овој идентификатор (#c1). Со истиот идентификатор се пуштаме за преглед. Истото го правиме и со заложбите #c2 и #c3.

Кога сфативме дека има доволно карактеристики, почнуваме да стабилизираме сè. Направете филијала во Git release_1.1 (на основата #c3 на develop). Нема потреба да се собира ова издание, бидејќи ... ова беше направено во претходниот чекор. Затоа, можеме едноставно да го префрлиме на сцена. Ги поправаме грешките #c4 и на сличен начин се префрли на инсценирање. Во исто време, развојот е во тек во develop, од каде периодично се преземаат промени release_1.1. Во одреден момент, добиваме заложба составена и поставена на инсценација, со која сме задоволни (#c25).

Потоа ја спојуваме (со брзо напред) гранката за ослободување (release_1.1) во мајстор. Ние ставивме ознака со новата верзија на овој commit (1.1). Но, оваа слика е веќе собрана во регистарот, па за да не ја собереме повторно, едноставно додаваме втора ознака на постоечката слика (сега има ознаки во регистарот #c25 и 1.1). После тоа, го пуштаме во производство.

Има недостаток што само една слика е поставена на инсценирање (#c25), а во производството е нешто поинаку (1.1), но знаеме дека „физички“ ова се истата слика од регистарот.

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Вистинскиот недостаток е тоа што нема поддршка за обврзувања за спојување, мора да направите брзо напред.

Можеме да одиме понатаму и да направиме трик... Ајде да погледнеме пример за едноставен Dockerfile:

FROM ruby:2.3 as assets
RUN mkdir -p /app
WORKDIR /app
COPY . ./
RUN gem install bundler && bundle install
RUN bundle exec rake assets:precompile
CMD bundle exec puma -C config/puma.rb

FROM nginx:alpine
COPY --from=assets /app/public /usr/share/nginx/www/public

Ајде да изградиме датотека од него според следниот принцип:

  • SHA256 од идентификаторите на употребените слики (ruby:2.3 и nginx:alpine), кои се контролни суми на нивната содржина;
  • сите тимови (RUN, CMD и така натаму.);
  • SHA256 од додадени датотеки.

... и земете ја контролната сума (повторно SHA256) од таква датотека. Ова потпис сè што ја дефинира содржината на сликата на Докер.

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Да се ​​вратиме на дијаграмот и наместо обврзувања ќе користиме такви потписи, т.е. означете ги сликите со потписи.

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Сега, кога е неопходно, на пример, да се спојат промените од издание во господар, можеме да направиме вистински обврзување за спојување: ќе има различен идентификатор, но ист потпис. Со истиот идентификатор ќе ја префрлиме сликата до производство.

Недостаток е што сега нема да може да се одреди каков вид на заложба беше притиснат на производство - контролните суми работат само во една насока. Овој проблем е решен со дополнителен слој со метаподатоци - ќе ви кажам повеќе подоцна.

Означување во верф

Во верф отидовме уште подалеку и се подготвуваме да направиме дистрибуирана градба со кеш што не е зачуван на една машина... Значи, градиме два типа на Docker слики, ги нарекуваме фаза и слика.

Складиштето werf Git складира инструкции специфични за изградбата кои ги опишуваат различните фази на изградбата (пред да инсталирате, инсталира, пред Поставување, подесување). Ја собираме сликата од првата фаза со потпис дефиниран како контролна сума на првите чекори. Потоа го додаваме изворниот код, за сликата на новата фаза ја пресметуваме нејзината контролна сума... Овие операции се повторуваат за сите фази, како резултат на што добиваме збир на слики од сцената. Потоа ја правиме конечната слика, која содржи и метаподатоци за нејзиното потекло. И ние ја означуваме оваа слика на различни начини (детали подоцна).

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Да претпоставиме дека после ова се појавува нов commit во кој е променет само кодот на апликацијата. Што ќе се случи? За промена на кодот, ќе се креира лепенка и ќе се подготви нова слика на сцената. Неговиот потпис ќе се определи како проверка на сликата на старата сцена и новата лепенка. Од оваа слика ќе се формира нова конечна слика. Слично однесување ќе се случи со промени во други фази.

Така, сликите на сцената се кеш што може да се складира дистрибуирано, а сликите веќе создадени од него се поставуваат во Docker Registry.

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Чистење на регистарот

Не зборуваме за бришење на слоеви кои останаа да висат по избришаните ознаки - ова е стандардна карактеристика на самиот Docker Registry. Зборуваме за ситуација кога се акумулираат многу Docker ознаки и разбираме дека некои од нив веќе не ни требаат, но тие зафаќаат простор (и/или плаќаме за тоа).

Кои се стратегиите за чистење?

  1. Вие едноставно не можете да направите ништо не чисти. Понекогаш е навистина полесно да се плати малку за дополнителен простор отколку да се разоткрие огромен метеж од ознаки. Но, ова функционира само до одредена точка.
  2. Целосно ресетирање. Ако ги избришете сите слики и повторно ги изградите само тековните во системот CI, може да се појави проблем. Ако контејнерот се рестартира во производство, ќе се вчита нова слика за него - онаа што сè уште не е тестирана од никого. Ова ја убива идејата за непроменлива инфраструктура.
  3. Сина Зелена. Еден регистар почна да се прелева - поставуваме слики на друг. Истиот проблем како и во претходниот метод: во кој момент можете да го исчистите регистарот што почна да се прелева?
  4. Со време. Да се ​​избришат сите слики постари од 1 месец? Но, дефинитивно ќе има услуга што не е ажурирана еден месец...
  5. Рачно одреди што веќе може да се избрише.

Постојат две навистина остварливи опции: не чистете или комбинација на сино-зелена + рачно. Во вториот случај, зборуваме за следново: кога ќе разберете дека е време да го исчистите регистарот, креирате нов и ги додавате сите нови слики во него во текот на, на пример, еден месец. И по еден месец, видете кои подлоги во Кубернетс сè уште го користат стариот регистар и префрлете ги и нив во новиот регистар.

До што дојдовме верф? Ние собираме:

  1. Глава на Git: сите ознаки, сите гранки - под претпоставка дека ни треба сè што е означено во Git на сликите (а ако не, тогаш треба да го избришеме во самиот Git);
  2. сите мешунки кои моментално се испумпуваат во Кубернет;
  3. старите ReplicaSets (она што неодамна беше објавено), а исто така планираме да ги скенираме изданијата на Helm и да ги избереме најновите слики таму.

... и направете бела листа од овој сет - листа на слики што нема да ги избришеме. Исчистуваме сè друго, по што наоѓаме слики од сцената сираци и ги бришеме исто така.

Распоредување фаза

Сигурна декларативност

Првата точка на која би сакал да привлечам внимание при распоредувањето е воведувањето на ажурираната конфигурација на ресурсите, декларативно декларирана. Оригиналниот YAML документ кој ги опишува ресурсите на Kubernetes е секогаш многу различен од резултатот што всушност работи во кластерот. Бидејќи Kubernetes додава на конфигурацијата:

  1. идентификатори;
  2. информации за услугата;
  3. многу стандардни вредности;
  4. дел со моментален статус;
  5. промени направени како дел од веб-кука за прием;
  6. резултат на работата на различни контролори (и распоредувачот).

Затоа, кога ќе се појави нова конфигурација на ресурси (нови), не можеме само да ја земеме и презапишеме тековната, „жива“ конфигурација со неа (живеат). За да го направите ова, ќе мора да споредуваме нови со последната применета конфигурација (последен пат се применува) и се тркалаат на живеат доби лепенка.

Овој пристап се нарекува Двонасочно спојување. Се користи, на пример, во Хелм.

Исто така постои Двонасочно спојување, што се разликува по тоа:

  • споредувајќи последен пат се применува и нови, гледаме што е избришано;
  • споредувајќи нови и живеат, гледаме што е додадено или променето;
  • сумираниот лепенка се применува на живеат.

Распоредуваме 1000+ апликации со Helm, така што всушност живееме со двонасочно спојување. Сепак, има голем број проблеми што ги решивме со нашите закрпи, кои му помагаат на Хелм да работи нормално.

Вистински статус на воведување

Откако нашиот CI систем ќе генерира нова конфигурација за Kubernetes врз основа на следниот настан, ја пренесува за употреба (примени) до кластер - користејќи Helm или kubectl apply. Следно, се случува веќе опишаното спојување N-way, на кое Kubernetes API одговара на одобрување на системот CI, а тоа на неговиот корисник.

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Сепак, постои огромен проблем: на крајот на краиштата успешната апликација не значи успешно пуштање во употреба. Ако Kubernetes разбере какви промени треба да се применат и ги примени, сè уште не знаеме каков ќе биде резултатот. На пример, ажурирањето и рестартирањето на подлоги во предниот дел може да биде успешно, но не и во заднината, и ќе добиеме различни верзии на сликите на активните апликации.

За да се направи сè правилно, оваа шема бара дополнителна врска - специјален тракер кој ќе добива информации за статусот од Kubernetes API и ќе ги пренесува за понатамошна анализа на вистинската состојба на нештата. Создадовме библиотека со отворен код во Go - кубедог (видете ја неговата објава тука), кој го решава овој проблем и е вграден во werf.

Однесувањето на овој тракер на ниво на werf е конфигурирано со помош на прибелешки кои се поставени на Deployments или StatefulSets. Главна прибелешка - fail-mode - ги разбира следниве значења:

  • IgnoreAndContinueDeployProcess — ги игнорираме проблемите со воведувањето на оваа компонента и продолжуваме со распоредувањето;
  • FailWholeDeployProcessImmediately — грешка во оваа компонента го запира процесот на распоредување;
  • HopeUntilEndOfDeployProcess — се надеваме дека оваа компонента ќе работи до крајот на распоредувањето.

На пример, оваа комбинација на ресурси и вредности за прибелешки fail-mode:

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Кога ќе се распоредиме за прв пат, базата на податоци (MongoDB) можеби сè уште не е подготвена - Распоредувањата ќе пропаднат. Но, можете да почекате моментот да започне, а распоредувањето сепак ќе се случи.

Има уште две прибелешки за кубедог во верф:

  • failures-allowed-per-replica — бројот на дозволени падови за секоја реплика;
  • show-logs-until — го регулира моментот до кој werf ги прикажува (во stdout) логовите од сите отпуштени мешунки. Стандардно е PodIsReady (да ги игнорираме пораките што веројатно не ги сакаме кога сообраќајот ќе почне да доаѓа до подлогата), но вредностите се исто така валидни: ControllerIsReady и EndOfDeploy.

Што друго сакаме од распоредувањето?

Покрај двете веќе опишани точки, би сакале:

  • за да ја видите трупци - и само потребните, а не сè по ред;
  • песна напредок, затоа што ако работата виси „тивко“ неколку минути, важно е да се разбере што се случува таму;
  • има автоматско враќање назад во случај нешто да тргне наопаку (и затоа е критично да се знае реалниот статус на распоредувањето). Распоредот мора да биде атомски: или оди до крај, или сè се враќа во претходната состојба.

Резултатите од

За нас како компанија, да ги имплементираме сите опишани нијанси во различни фази на испорака (изградба, објавување, распоредување), доволни се CI систем и алатка верф.

Наместо заклучок:

werf - нашата алатка за CI / CD во Kubernetes (преглед и видео извештај)

Со помош на werf, постигнавме добар напредок во решавањето на голем број проблеми за инженерите на DevOps и би ни било мило доколку пошироката заедница барем на дело ја испроба оваа алатка. Ќе биде полесно да се постигне добар резултат заедно.

Видеа и слајдови

Видео од настапот (~47 минути):

Презентација на извештајот:

PS

Други извештаи за Kubernetes на нашиот блог:

Извор: www.habr.com

Додадете коментар