Правилно сравнение на Kubernetes Apply, Replace и Patch

Има няколко варианта за обновяване на ресурсите в Kubernetes: apply, edit, patch и replace. Има объркване относно това какво прави всеки от тях и кога да се използват. Нека да разгледаме.

Правилно сравнение на Kubernetes Apply, Replace и Patch

Ако да потърсите в Google фразата "kubernetes apply vs replace", ще намерите отговор на StackOverflow, който не е верен. При търсене на "kubernetes apply vs patch" първият резултат – документация за 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, ако работите с клъстери чрез клиентската библиотека за Kubernetes API използвайки някакъв език за програмиране. Библиотеката обработва тези методи с HTTP заявки, използвайки методите PUT и PATCH. В същото време актуализиране и 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 заявки за актуализиране на съществуващ ресурс. Ако се задълбочите в реализацията на командите, ще видите, че във всички се използва подходът strategic-merge patching за актуализиране на ресурсите, въпреки че командата 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, за да го премахнете от ресурса. Изтриването на елемент от масив с използване на стратегическо сливане е по-сложно, тъй като изисква използване на директиви за сливане. Вижте други подходи за актуализиране по-долу, за да изберете по-приемливи алтернативи.

За да реализирате в клиентската библиотека методи за актуализиране, които се държат аналогично на горепосочените команди kubectl, трябва в заявките да зададете content-type в application/strategic-merge-patch+json. Ако искате да изтривате свойства в спецификацията, трябва явно да зададете техните стойности на null, подобно на kubectl patch. Ако трябва да изтривате елементи от масив, трябва да включите директиви за сливане в спецификацията за актуализиране или да използвате друг подход за актуализации.

Други подходи за актуализиране

В Kubernetes се поддържат два други подхода за актуализиране: JSON merge patch и JSON patch. Подходът JSON merge patch приема частична спецификация на Kubernetes като входни данни и поддържа сливане на обекти подобно на подхода strategic-merge patching. Разликата между тях е, че той поддържа само замяна на масиви, включително масива на контейнерите в спецификацията на pod. Това означава, че при използване на JSON merge patch трябва да предоставите пълни спецификации за всички контейнери в случай на промяна на свойство на който и да е контейнер. Така, този подход е полезен за премахване на елементи от масив в спецификацията. В командния ред можете да изберете JSON merge patch, използвайки 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"
  • A обновява ресурса малко по-бързо
  • B обновява ресурса

В резултат на това обновление A е изгубено. Последната операция patch печели, броячът се увеличава с единица вместо с две, а стойността на забележката "updated-by" завършва на "B" и не съдържа "A". Нека сравним казаното с това, което се случва, когато актуализациите се извършват с помощта на подхода replace:

  • A и B получават актуалното състояние на ресурса от API
  • Всеки от тях локално обновява спецификацията, увеличавайки брояча с единица, и също така добавя "A" или "B" съответно в бележката "updated-by"
  • A обновява ресурса малко по-бързо
  • 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 или StackOverflow при търсене на "kubernetes apply vs replace". Най-малкото, докато тази статия не замени текущия отговор.

Правилно сравнение на Kubernetes Apply, Replace и Patch

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster