Для Kubernetes є кілька варіантів оновлення ресурсів: apply, edit, patch і replace. З тим, що кожен із них робить і коли їх застосовувати, є плутанина. Давайте розберемося.
Якщо kubectl patch
, яка не включає порівняння apply
и patch
. У цій статті будуть розглянуті різні варіанти та правильне використання кожного з них.
Протягом життєвого циклу ресурсу Kubernetes (сервісу, deployment, ingress тощо) іноді потрібно змінити, додати чи видалити деякі властивості цього ресурсу. Наприклад, додати примітку, збільшити чи зменшити кількість реплік.
CLI Kubernetes
Якщо ви вже працюєте з кластерами Kubernetes через CLI, то вже знайомі з apply
и edit
. команда apply
читає специфікацію ресурсу з файлу і робить "upsert" кластер Kubernetes, тобто. створює ресурс, якщо його немає, та оновлює його, якщо він існує. Команда edit
читає ресурс через API, після чого пише специфікацію ресурсу локальний файл, який потім відкривається в текстовому редакторі. Після того, як ви відредагуєте та збережете файл, kubectl
відправить зроблені зміни через API, який дбайливо застосує ці зміни до ресурсу.
Не всі знають команди patch
и replace
. команда patch
дозволяє змінити частину специфікації ресурсу, надаючи лише змінену частину командному рядку. Команда replace
працює так само, як і edit
, але тільки все треба зробити вручну: необхідно завантажити поточну версію специфікації ресурсу, наприклад, з використанням kubectl get -o yaml
, редагувати її, потім використовувати replace
для оновлення ресурсу за зміненою специфікацією. Команда replace
не відпрацює, якщо між читанням та заміною ресурсу відбулися будь-які зміни.
API Kubernetes
Ви, мабуть, знайомі з методами 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