Праблема "разумнай" ачысткі выяў кантэйнераў і яе рашэнне ў werf

Праблема "разумнай" ачысткі выяў кантэйнераў і яе рашэнне ў werf

У артыкуле разгледжана праблематыка ачысткі выяў, якія назапашваюцца ў рэестрах кантэйнераў (Docker Registry і яго аналогах) у рэаліях сучасных CI/CD-пайплайнаў для cloud native-прыкладанняў, якія дастаўляюцца ў Kubernetes. Прыведзены асноўныя крытэрыі актуальнасці выяў і якія вынікаюць з іх складанасці пры аўтаматызацыі ачысткі, захаванні месца і задавальненні запатрабаванням каманд. Нарэшце, на прыкладзе канкрэтнага Open Source-праекта мы раскажам, як гэтыя складанасці можна пераадолець.

Увядзенне

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

  1. выкарыстоўваць фіксаваную колькасць тэгаў для вобразаў;
  2. якім-небудзь чынам чысціць выявы.


Першае абмежаванне часам дапушчальна для невялікіх каманд. Калі распрацоўшчыкам хапае пастаянных тэгаў (latest, main, test, boris і да т.п.), рэестр не будзе раздзімацца ў памерах і доўгі час можна наогул не думаць аб ачыстцы. Бо ўсе неактуальныя выявы пераціраюцца, а для ачысткі проста не застаецца працы (усё робіцца штатным зборшчыкам смецця).

Тым не менш, такі падыход моцна абмяжоўвае распрацоўку і рэдка дастасавальны да CI/CD сучасных праектаў. Неад'емнай часткай распрацоўкі стала аўтаматызацыя, якая дазваляе значна хутчэй тэсціраваць, разгортваць і дастаўляць новы функцыянал карыстальнікам. Напрыклад, у нас ва ўсіх праектах пры кожным коміце аўтаматычна ствараецца CI-пайплайн. У ім збіраецца выява, тэстуецца, выкочваецца ў розныя Kubernetes-контуры для адладкі і пакінутых праверак, а калі ўсё добра - змены даходзяць да канчатковага карыстача. І гэта даўно не rocket science, а штодзённасць для многіх - хутчэй за ўсё і для вас, раз вы чытаеце дадзены артыкул.

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

Але як наогул вызначыць, ці актуальная выява?

Крытэрыі актуальнасці выявы

У пераважнай большасці выпадкаў асноўныя крытэрыі будуць наступныя:

1. Першы (самы відавочны і самы крытычны з усіх) - гэта выявы, якія у сапраўдны момант выкарыстоўваюцца ў Kubernetes. Выдаленне гэтых выяў можа прывесці да сур'ёзных выдаткаў у сувязі з прастоем production (напрыклад, выявы могуць запатрабавацца пры рэплікацыі) або звесці на нішто высілкі каманды, якая займаецца адладкай на які-небудзь з контураў. (Па гэтай прычыне мы нават зрабілі спецыяльны Prometheus exporter, Які адсочвае адсутнасць такіх вобразаў у любым Kubernetes-кластары.)

2. Другі (менш відавочны, але таксама вельмі важны і зноў ставіцца да эксплуатацыі) - выявы, якія патрабуюцца для адкату ў выпадку выяўлення сур'ёзных праблем у бягучай версіі. Напрыклад, у выпадку з Helm гэта выявы, якія выкарыстоўваюцца ў захаваных версіях рэлізу. (Дарэчы, па змаўчанні ў Helm ліміт у 256 рэвізій, але ці наўрад у кагосьці рэальна ёсць запатрабаванне ў захаванні такога вялікай колькасці версій?..) Бо мы, у прыватнасці, для таго і захоўваем версіі, каб можна было іх потым выкарыстоўваць, г.зн. "адкатвацца" на іх у выпадку неабходнасці.

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

4. Чацвёрты - вобразы, якія адпавядаюць версіям нашага прыкладання, г.зн. з'яўляюцца канчатковым прадуктам: v1.0.0, 20.04.01, sierra і г.д.

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

Адпаведнасць крытэрам і існуючыя рашэнні

Папулярныя сэрвісы з container registry, як правіла, прапануюць свае палітыкі ачысткі выяў: у іх вы можаце вызначаць умовы, пры якіх тэг выдаляецца з registry. Аднак магчымасці гэтых умоў абмяжоўваюцца такімі параметрамі, як імёны, час стварэння і колькасць тэгаў*.

* Залежыць ад канкрэтных рэалізацый container registry. Мы разглядалі магчымасці наступных рашэнняў: Azure CR, Docker Hub, ECR, GCR, GitHub Packages, GitLab Container Registry, Harbor Registry, JFrog Artifactory, Quay.io – па стане на верасень'2020.

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

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

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

Ілюстрацыя workflow у Git

Выкажам здагадку, вы працуеце прыкладна па такой схеме ў Git:

Праблема "разумнай" ачысткі выяў кантэйнераў і яе рашэнне ў werf

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

Што адбудзецца, калі палітыкі ачысткі дазваляюць пакідаць (не выдаляць) выявы толькі па зададзеных назвах тэгаў?

Праблема "разумнай" ачысткі выяў кантэйнераў і яе рашэнне ў werf

Відавочна, такі сцэнар нікога не ўзрадуе.

Што зменіцца, калі палітыкі дазваляюць не выдаляць выявы па зададзеным часовым інтэрвале / колькасці апошніх коммітаў?

Праблема "разумнай" ачысткі выяў кантэйнераў і яе рашэнне ў werf

Вынік стаў значна лепшым, аднак усё яшчэ далёкі ад ідэалу. Бо ў нас па-ранейшаму ёсць распрацоўшчыкі, якім патрэбныя выявы ў рэестры (ці нават разгорнутыя ў K8s) для адладкі багаў…

Рэзюмуючы якая склалася на рынку сітуацыю: даступныя ў рэестрах кантэйнераў функцыі не прапануюць дастатковай гнуткасці пры ачыстцы, а галоўная таму прычына - няма магчымасці ўзаемадзейнічаць з навакольным светам. Атрымліваецца, што каманды, якім патрабуецца такая гнуткасць, змушаныя самастойна рэалізоўваць выдаленне выяў "звонку", выкарыстаючы Docker Registry API (ці натыўны API якая адпавядае рэалізацыі).

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

Наш шлях да ўніверсальнай ачысткі выяў

Адкуль такое запатрабаванне? Справа ў тым, што мы не асобна ўзятая група распрацоўшчыкаў, а каманда, якая абслугоўвае адразу мноства такіх, дапамагаючы комплексна вырашаць пытанні CI/CD. І галоўная тэхнічная прылада для гэтага — Open Source-утыліта werf. Яе асаблівасць у тым, што яна не выконвае адзіную функцыю, а суправаджае працэсы бесперапыннай дастаўкі на ўсіх этапах: ад зборкі да дэплою.

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

* Хоць самі рэестры могуць быць рознымі (Docker Registry, GitLab Container Registry, Harbor і г.д.), іх карыстачы сутыкаюцца з аднымі і тымі ж праблемамі. Універсальнае рашэнне ў нашым выпадку не залежыць ад рэалізацыі рэестра, т.я. выконваецца па-за самімі рэестрамі і прапануе аднолькавыя паводзіны для ўсіх.

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

Такім чынам, мы заняліся знешняй рэалізацыяй механізму для ачысткі выяў - замест тых магчымасцяў, што ўжо ўбудаваны ў рэестры для кантэйнераў. Першым крокам стала выкарыстанне Docker Registry API для стварэння ўсё тых жа прымітыўных палітык па колькасці тэгаў і часе іх стварэння (згаданых вышэй). Да іх быў дададзены allow list на аснове вобразаў, якія выкарыстоўваюцца ў разгорнутай інфраструктуры, г.зн. Kubernetes. Для апошняга было дастаткова праз Kubernetes API перабіраць усе задэплоеныя рэсурсы і атрымліваць спіс са значэнняў image.

Такое трывіяльнае рашэнне закрыла самую крытычную праблему (крытэрый №1), але стала толькі пачаткам нашага шляху па паляпшэнні механізма ачысткі. Наступным - і куды цікавейшым - крокам стала рашэнне звязаць публікуемыя вобразы з гісторыяй Git.

Схемы тэгавання

Для пачатку мы абралі падыход, пры якім канчатковы вобраз павінен захоўваць неабходную інфармацыю для ачысткі, і выбудавалі працэс на схемах тэгавання. Пры публікацыі выявы карыстач выбіраў пэўную опцыю тэгавання (git-branch, git-commit або git-tag) і выкарыстаў адпаведнае значэнне. У CI-сістэмах усталёўка гэтых значэнняў выконвалася аўтаматычна на падставе зменных асяроддзі. Па сутнасці канчатковы вобраз звязваўся з пэўным Git-прымітывам, захоўваючы неабходныя дадзеныя для ачысткі ў лэйблах.

У рамках такога падыходу атрымаўся набор палітык, які дазваляў выкарыстоўваць Git як адзіную крыніцу праўды:

  • Пры выдаленні галінкі/тэга ў Git аўтаматычна выдаляліся і злучаныя выявы ў registry.
  • Колькасць выяў, злучанае з Git-тэгамі і комитами, можна было рэгуляваць колькасцю тэгаў, выкарыстаных у абранай схеме, і часам стварэння злучанага комміта.

У цэлым, атрыманая рэалізацыя задавальняла нашым патрэбам, але неўзабаве нас чакаў новы выклік. Справа ў тым, што за час выкарыстання схем тэгавання па Git-прымітывам мы сутыкнуліся з шэрагам недахопаў. (Паколькі іх апісанне выходзіць за рамкі тэмы гэтага артыкула, усе жадаючыя могуць азнаёміцца ​​з падрабязнасцямі тут.) Таму, прыняўшы рашэнне аб пераходзе на больш эфектыўны падыход да тэгавання (content-based tagging), нам прыйшлося перагледзець і рэалізацыю ачысткі вобразаў.

Новы алгарытм

Чаму? Пры тэгаванні ў рамках content-based кожны тэг можа задавальняць мноству коммітаў у Git. Пры ачыстцы выяў больш нельга зыходзіць толькі з коміта, на якім новы тэг быў дададзены ў рэестр.

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

  • коміта, на якім выконвалася публікацыя (пры гэтым не мае значэння, дадаўся, змяніўся або застаўся ранейшым вобраз у рэестры кантэйнераў);
  • і нашага ўнутранага ідэнтыфікатара, які адпавядае сабранай выяве.

Іншымі словамі, была забяспечана сувязь публікуемых тэгаў з комітамі ў Git.

Выніковая канфігурацыя і агульны алгарытм

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

  • мноствам references, г.зн. Git-тэгамі або Git-галінкамі, якія выкарыстоўваюцца пры сканаванні;
  • і лімітам шуканых вобразаў для кожнага reference з мноства.

Для ілюстрацыі – вось як стала выглядаць канфігурацыя палітык па змаўчанні:

cleanup:
  keepPolicies:
  - references:
      tag: /.*/
      limit:
        last: 10
  - references:
      branch: /.*/
      limit:
        last: 10
        in: 168h
        operator: And
    imagesPerReference:
      last: 2
      in: 168h
      operator: And
  - references:  
      branch: /^(main|staging|production)$/
    imagesPerReference:
      last: 10

Такая канфігурацыя змяшчае тры палітыкі, якія адпавядаюць наступным правілам:

  1. Захоўваць выяву для 10 апошніх Git-тэгаў (па даце стварэння тэга).
  2. Захоўваць па не больш за 2 вобразаў, апублікаваных за апошні тыдзень, для не больш за 10 галінак з актыўнасцю за апошні тыдзень.
  3. Захоўваць па 10 вобразаў для галінак main, staging и production.

Выніковы ж алгарытм зводзіцца да наступных крокаў:

  • Атрыманне маніфестаў з container registry.
  • Выключэнне выяў, выкарыстоўваных у Kubernetes, т.к. іх мы ўжо папярэдне адабралі, апытаўшы K8s API.
  • Сканіраванне Git-гісторыі і выключэнне вобразаў па зададзеных палітыках.
  • Выдаленне астатніх вобразаў.

Вяртаючыся да нашай ілюстрацыі, вось што атрымліваецца з werf:

Праблема "разумнай" ачысткі выяў кантэйнераў і яе рашэнне ў werf

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

Заключэнне

  • Рана ці позна з праблемай перапаўнення registry сутыкаецца большасць каманд.
  • Пры пошуку рашэнняў у першую чаргу неабходна вызначыць крытэрыі актуальнасці выявы.
  • Інструменты, прапанаваныя папулярнымі сэрвісамі container registry, дазваляюць арганізаваць вельмі простую ачыстку, якая не ўлічвае "вонкавы свет": выявы, выкарыстоўваныя ў Kubernetes, і асаблівасці працоўных працэсаў у камандзе.
  • Гнуткі і эфектыўны алгарытм павінен мець уяўленне пра CI/CD-працэсы, апераваць не толькі дадзенымі Docker-вобразаў.

PS

Чытайце таксама ў нашым блогу:

Крыніца: habr.com

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