Для Kubernetes ёсць некалькі варыянтаў абнаўлення рэсурсаў: apply, edit, patch і replace. З тым, што кожны з іх робіць і калі іх прымяняць, ёсць блытаніна. Давайце разбяромся.
Калі kubectl patch
, якая не ўключае параўнанне apply
и patch
. У гэтым артыкуле будуць разгледжаны розныя варыянты, а таксама правільнае выкарыстанне кожнага з іх.
На працягу жыццёвага цыклу рэсурсу Kubernetes (сэрвісу, deployment, ingress і г.д.) часам трэба памяняць, дадаць ці выдаліць некаторыя ўласцівасці гэтага рэсурсу. Напрыклад, дадаць нататку, павялічыць або паменшыць колькасць рэплік.
Kubernetes CLI
Калі вы ўжо працуеце з кластарамі Kubernetes праз CLI, то ўжо знаёмы з apply
и edit
. Каманда apply
чытае спецыфікацыю рэсурса з файла і робіць "upsert" у кластар Kubernetes, г.зн. стварае рэсурс, калі яго няма, і абнаўляе яго, калі ён існуе. Каманда edit
чытае рэсурс праз API, пасля чаго піша спецыфікацыю рэсурса ў лакальны файл, які затым адчыняецца ў тэкставым рэдактары. Пасля таго, як вы адрэдагуеце і захаваеце файл, kubectl
адправіць зробленыя змены назад праз API, які клапатліва прыменіць гэтыя змены да рэсурсу.
Не ўсе ведаюць каманды patch
и replace
. Каманда patch
дазваляе змяніць частку спецыфікацыі рэсурсу, падаючы толькі змененую частку ў камандным радку. Каманда replace
працуе гэтак жа, як і edit
, але толькі ўсё трэба зрабіць уручную: неабходна спампаваць бягучую версію спецыфікацыі рэсурсу, напрыклад, з выкарыстаннем kubectl get -o yaml
, адрэдагаваць яе, затым выкарыстоўваць replace
для абнаўлення рэсурса па змененай спецыфікацыі. Каманда replace
не адпрацуе, калі паміж чытаннем і заменай рэсурса адбыліся якія-небудзь змены.
Kubernetes API
Вы, верагодна, знаёмы з метадамі CoreV1().Pods().Update()
, replaceNamespacedService
або patch_namespaced_deployment
, калі працуеце з кластарамі праз PUT
и PATCH
. Пры гэтым update
и replace
выкарыстоўваюць PUT
, А patch
, як бы гэта не было банальна, выкарыстоўвае PATCH
.
Варта адзначыць, што kubectl
таксама працуе з кластарамі праз API. Інакш кажучы, kubectl
– гэта абгортка па-над кліенцкай бібліятэкай для мовы Go, якая забяспечвае ў значнай меры магчымасць падаць падкаманды ў больш кампактным і чытэльным выглядзе ў даважку да штатных магчымасцяў API. Напрыклад, як вы ўжо маглі заўважыць, метад apply
не быў згаданы вышэй у папярэднім абзацы. Цяпер (май 2020, заўв. перакладчыка) уся логіка kubectl apply
, г.зн. стварэнне неіснуючых рэсурсаў і абнаўленне існуючых, працуе цалкам на баку кода kubectl
. Прадпрымаюцца намаганні apply
на бок API, але гэта ўсё яшчэ на стадыі бэта-тэставанні. Больш падрабязна распішу ніжэй.
Patch па змаўчанні
Лепш за ўсё прымяняць patch
, калі вы жадаеце абнавіць рэсурс. Так працуюць як кліенцкія бібліятэкі па-над API Kubernetes, так і kubectl
(нядзіўна, бо ён - абгортка кліенцкай бібліятэкі, заўв. перакладчыка).
Працаваць стратэгічна
Усе каманды kubectl
apply
, edit
и patch
выкарыстоўваюць метад PATCH
у запытах HTTP для абнаўлення існуючага рэсурса. Калі ўглыбіцца больш падрабязна ў рэалізацыю каманд, то ва ўсіх выкарыстоўваецца падыход patch
можа выкарыстоўваць і іншыя падыходы (падрабязней пра гэта ніжэй). Падыход strategic-merge patching спрабуе "зрабіць усё правільна" пры аб'яднанні прадстаўленай спецыфікацыі з існай спецыфікацыяй. Больш канкрэтна, ён спрабуе аб'яднаць як аб'екты, так і масівы, што значыць, што змены, як правіла, з'яўляюцца адытыўна. Напрыклад, запуск каманды patch
з новай пераменнай асяроддзя ў спецыфікацыі кантэйнера pod, прыводзіць да таго, што гэтая пераменная асяроддзя дадаецца да існуючых пераменным асяроддзя, а не перазапісвае іх. Для выдалення з дапамогай гэтага падыходу варта прымусова ўсталяваць значэнне параметра ў null у прадстаўленай спецыфікацыі. Якія ж з каманд kubectl
для абнаўлення лепш за ўсё выкарыстоўваць?
Калі вы ствараеце і кіруеце сваімі рэсурсамі з дапамогай kubectl apply
, пры абнаўленні лепш заўсёды выкарыстоўваць kubectl apply
, Каб kubectl
мог кіраваць канфігурацыяй і правільна адсочваць запытаныя змены ад ужывання да ўжывання. Перавага заўсёды выкарыстоўваць apply
складаецца ў тым, што ён адсочвае раней ужытую спецыфікацыю, дазваляючы яму ведаць, калі ўласцівасці спецыфікацыі і элементы масіва відавочна выдаляюцца. Гэта дазваляе выкарыстоўваць apply
для выдалення ўласцівасцяў і элементаў масіва, у той час як звычайнае стратэгічнае зліццё працаваць не будзе. Каманды edit
и patch
не абнаўляюць заўвагі, якія kubectl apply
прымяняе для адсочвання сваіх змен, таму любыя змены, якія адсочваюцца і робяцца праз API Kubernetes, але унесеныя праз каманды edit
и patch
, нябачныя для наступных каманд apply
, Гэта значыць apply
не выдаляе іх, нават калі яны не з'яўляюцца ва ўваходнай спецыфікацыі для apply
(У дакументацыі сказана, што edit
и patch
робяць абнаўлення заўваг, якія выкарыстоўваюцца apply
, але на практыцы - не).
Калі вы не карыстаецеся каманду apply
, можна выкарыстоўваць як edit
, так і patch
, выбіраючы тую каманду, якая больш падыходзіць пад уносную змену. Пры даданні і змене ўласцівасцяў спецыфікацыі абодва падыходу прыкладна аднолькавыя. Пры выдаленні ўласцівасцяў спецыфікацыі ці элементаў масіва edit
паводзіць сябе як аднаразовы запуск apply
, у тым ліку адсочвае, якой была спецыфікацыя да і пасля яе рэдагавання, таму можна відавочна выдаляць уласцівасці і элементы масіва з рэсурсу. Трэба відавочна ўсталяваць значэнне ўласцівасці ў null у спецыфікацыі для patch
, Каб выдаліць яго з рэсурсу. Выдаленне элемента масіва з выкарыстаннем strategic-merge patching з'яўляецца больш складаным, паколькі патрабуецца выкарыстанне дырэктыў зліцця. Глядзіце іншыя падыходы да абнаўлення ніжэй для выбару больш прымальных альтэрнатыў.
Каб рэалізаваць у кліенцкай бібліятэцы метады абнаўлення, якія паводзяць сябе аналагічна вышэйпрыведзеным камандам kubectl
, трэба ў запытах выстаўляць content-type
в application/strategic-merge-patch+json
. Калі вы хочаце выдаліць уласцівасці ў спецыфікацыі, вам трэба відавочна ўсталяваць іх значэння ў null аналагічна kubectl patch
. Калі трэба выдаляць элементы масіва, вам варта ўключыць дырэктывы зліцця ў спецыфікацыю абнаўлення ці выкарыстаць іншы падыход да абнаўленняў.
Іншыя падыходы да абнаўленняў
У Kubernetes падтрымліваюцца два іншых падыходу да абнаўленняў: kubectl patch --type=merge
. Пры працы з API Kubernetes варта выкарыстоўваць метад запыту PATCH
і ўстаноўцы content-type
в application/merge-patch+json
.
Падыход JSON patch замест таго, каб прадастаўляць частковую спецыфікацыю рэсурсу, выкарыстоўвае прадастаўленне змен, якія вы хочаце ўнесці ў рэсурс, у выглядзе масіва, у якім кожны элемент масіва ўяўляе сабой апісанне змены, які ўносіцца ў рэсурс. Гэты падыход з'яўляецца больш гнуткім і магутным спосабам выражэння ўносяцца змен, але за кошт таго, што спіс уносяцца зменаў ідзе ў асобным, не Kubernetes, фармаце, замест адпраўкі частковай спецыфікацыі рэсурсу. У kubectl
вы можаце выбраць JSON patch, выкарыстоўваючы kubectl patch --type=json
. Пры выкарыстанні API Kubernetes гэты падыход працуе з выкарыстаннем метаду запыту PATCH
і ўстаноўцы content-type
в application/json-patch+json
.
Патрэбна ўпэўненасць - выкарыстоўваем replace
У некаторых выпадках патрэбна ўпэўненасць у тым, што ў рэсурс не будуць унесены змяненні паміж часам чытання рэсурса і яго абнаўленнем. Інакш кажучы, варта пераканацца ў тым, што ўсе змены будуць атамарнымі. У гэтым выпадку для абнаўлення рэсурсаў варта выкарыстоўваць replace
. Да прыкладу калі ёсць ConfigMap са лічыльнікам, які абнаўляецца некалькімі крыніцамі, варта быць упэўненым у тым, што дзве крыніцы не будуць абнаўляць лічыльнік адначасова, што прывядзе да страты абнаўлення. Для дэманстрацыі ўявіце сабе паслядоўнасць падзей выкарыстоўваючы падыход patch
:
- A і B атрымліваюць бягучы стан рэсурса з API
- Кожны з іх лакальна абнаўляе спецыфікацыю, павялічваючы лічыльнік на адзінку, а таксама дадаючы "A" ці "B" адпаведна ў нататку "updated-by"
- А крыху хутчэй абнаўляе рэсурс
- B абнаўляе рэсурс
У выніку абнаўленне A страчана. Апошняя аперацыя patch
выйграе, лічыльнік павялічваецца на адзінку замест двух, а значэнне нататкі "updated-by" сканчаецца на "B" і не ўтрымоўвае "A". Давайце параўнаем вышэйсказанае з тым, што адбываецца, калі абнаўленні выконваюцца з выкарыстаннем падыходу replace
:
- A і B атрымліваюць бягучы стан рэсурса з API
- Кожны з іх лакальна абнаўляе спецыфікацыю, павялічваючы лічыльнік на адзінку, а таксама дадаючы "A" ці "B" адпаведна ў нататку "updated-by"
- А крыху хутчэй абнаўляе рэсурс
- B спрабуе абнавіць рэсурс, але абнаўленне адхіляецца API, таму што версія рэсурсу ў спецыфікацыі
replace
не супадае з бягучай версіяй рэсурса ў Kubernetes, паколькі версія рэсурса была павялічана пры выкананні аперацыі replace са боку A.
У прыведзеным вышэй выпадку B давядзецца зноўку атрымаць рэсурс, унесці змены ў новы стан і паспрабаваць зноў зрабіць replace
. У выніку лічыльнік будзе павялічаны на два, а нататка "updated-by" будзе змяшчаць "AB" у канцы.
Вышэйпрыведзены прыклад мае на ўвазе, што пры выкананні replace
выконваецца поўная замена ўсяго рэсурса. Спецыфікацыя, якая выкарыстоўваецца для replace
, павінна быць не частковай, або часткамі як у apply
, а поўнай, уключаючы даданне resourceVersion
у метададзеныя спецыфікацыі. Калі вы не ўключылі resourceVersion
або прадстаўленая вамі версія не з'яўляецца бягучай, замена будзе адхілена. Такім чынам, лепшы падыход для выкарыстання replace
- прачытаць рэсурс, абнавіць яго і неадкладна замяніць. Выкарыстоўваючы kubectl
, гэта можа выглядаць так:
$ kubectl get deployment my-deployment -o json
| jq '.spec.template.spec.containers[0].env[1].value = "new value"'
| kubectl replace -f -
Пры гэтым варта заўважыць, што наступныя дзве каманды, выкананыя паслядоўна, выканаюцца паспяхова, бо deployment.yaml
не змяшчае ўласцівасць .metadata.resourceVersion
$ kubectl create -f deployment.yaml
$ kubectl replace -f deployment.yaml
Здавалася б, гэта супярэчыць таму, аб чым гаварылася вышэй, г.зн. "даданне resourceVersion
у метададзеныя спецыфікацыі". Сцвярджаць так няправільна? Не, гэта не так, паколькі калі kubectl
заўважае, што вы не ўказалі resourceVersion
, ён прачытае яе з рэсурсу і дадасць ва ўказаную вамі спецыфікацыю і толькі потым выканае replace
. Паколькі гэтая патэнцыйна небяспечная, калі спадзявацца на атамарнасць, магія працуе цалкам на баку. kubectl
, не варта спадзявацца на яе пры выкарыстанні кліенцкіх бібліятэк, якія працуюць з API. У гэтым выпадку вам давядзецца прачытаць бягучую спецыфікацыю рэсурсу, абнавіць яе і затым выканаць PUT
запыт.
Нельга зрабіць patch - які робіцца replace
Часам трэба ўнесці некаторыя змены, якія не могуць быць апрацаваны API. У гэтых выпадках можна прымусова замяніць рэсурс, выдаляючы і нанова ствараючы яго. Гэта робіцца з дапамогай kubectl replace --force
. Запуск каманды неадкладна выдаляе рэсурсы, а затым узнаўляе іх з прадстаўленай спецыфікацыі. У API няма апрацоўшчыка "прымусова замяніць", а для таго, каб зрабіць так праз API, трэба выканаць дзве аперацыі. Для пачатку трэба выдаліць рэсурс, усталёўваючы для яго gracePeriodSeconds
у нуль (0) і propagationPolicy
у "Background", а затым нанова стварыць гэты рэсурс з жаданай спецыфікацыяй.
Увага: дадзены падыход патэнцыйна небяспечны, можа прывесці да нявызначанага стану.
Apply на баку сервера
Як згадвалася вышэй, распрацоўшчыкі Kubernetes працуюць над рэалізацыяй логікі apply
з kubectl
у API Kubernetes. Логіка apply
даступная ў Kubernetes 1.18 праз kubectl apply --server-side
ці праз API, выкарыстоўваючы метад PATCH
с content-type
application/apply-patch+YAML
.
Заўвага: JSON таксама з'яўляецца карэктным YAML, так што можна даслаць спецыфікацыю ў выглядзе JSON, нават калі
content-type
будзеapplication/apply-patch+yaml
.
Акрамя таго, што логіка kubectl
становіцца даступнай для ўсіх праз API, apply
на баку сервера адсочвае адказных за палі ў спецыфікацыі, такім чынам дазваляючы бяспечны множны доступ для яе бесканфліктнага рэдагавання. Інакш кажучы, калі apply
на баку сервера атрымае шырэйшае распаўсюджванне, з'явіцца ўніверсальны бяспечны інтэрфейс кіравання рэсурсамі для розных кліентаў, да прыкладу, kubectl, Pulumi ці Terraform, GitOps, а таксама самапісных скрыптоў, выкарыстоўвалых кліенцкія бібліятэкі.
Вынікі
Спадзяюся, што гэты кароткі агляд розных спосабаў абнаўлення рэсурсаў у кластарах быў карысны для вас. Карысна ведаць, што супернікі не проста apply супраць replace, бо можна абнавіць рэсурс з дапамогай apply, edit, patch ці replace. Бо ў прынцыпе кожных падыход мае сваю вобласць ужывання. Для атамарных змен пераважней replace, у адваротным выпадку варта выкарыстоўваць strategic-merge patch праз apply. У крайнім выпадку я разлічваю на тое, што вы зразумелі, што нельга давяраць Google ці StackOerflow пры пошуку "kubernetes apply vs replace". Прынамсі датуль, пакуль гэты артыкул не заменіць бягучы адказ.
Крыніца: habr.com