Pro Kubernetes existuje několik způsobů, jak aktualizovat zdroje: apply, edit, patch a replace. Co každý z nich dělá a kdy je použít, může být matoucí. Pojďme si to vyjasnit.

Pokud frázi "kubernetes apply vs replace", najde se , která není správná. "kubernetes apply vs patch" první odkaz — dokumentace k kubectl patch, která neobsahuje srovnání apply a patch. V tomto článku budou probírány různé varianty a také správné použití každého z nich.
Během životního cyklu zdroje Kubernetes (služby, deploymentu, ingresses atd.) je občas potřeba změnit, přidat nebo odstranit některé vlastnosti tohoto zdroje. Například přidat poznámku, zvýšit nebo snížit počet replik.
Kubernetes CLI
Pokud již pracujete s clustery Kubernetes přes CLI, pak jste již obeznámeni s apply a edit. Příkaz apply čte specifikaci zdroje z souboru a provádí "upsert" do clusteru Kubernetes, tj. vytvoří zdroj, pokud neexistuje, a aktualizuje ho, pokud existuje. Příkaz edit čte zdroj přes API, po kterém napíše specifikaci zdroje do místního souboru, který se poté otevře v textovém editoru. Poté, co soubor upravíte a uložíte, kubectl odesílá provedené změny zpět přes API, které pečlivě aplikuje tyto změny na zdroj.
Ne všichni znají příkazy patch a replace. Příkaz patch umožňuje změnit část specifikace zdroje, přičemž poskytuje pouze změněnou část v příkazovém řádku. Příkaz replace funguje stejně jako edit, ale vše je potřeba udělat ručně: je nutné stáhnout aktuální verzi specifikace zdroje, například pomocí kubectl get -o yaml, upravit ji a poté použít replace k aktualizaci zdroje podle změněné specifikace. Příkaz replace nebude fungovat, pokud mezi načtením a výměnou zdroje došlo k nějakým změnám.
Kubernetes API
Jste pravděpodobně obeznámeni s metodami CoreV1().Pods().Update(), replaceNamespacedService nebo patch_namespaced_deployment, pokud pracujete s clustery přes použitím nějakého programovacího jazyka. Knihovna zpracovává tyto metody prostřednictvím požadavků protokolu HTTP, používající metody PUT a PATCH. Přitom update a replace používají PUT, a patch, jak banální to může znít, používá PATCH.
Stojí za zmínku, že kubectl také funguje s clustery přes API. Jinými slovy, kubectlje wrapper nad klientskou knihovnou pro jazyk Go, která výrazně umožňuje poskytnout podpříkazy v kompaktnější a přehlednější podobě vedle standardních funkcí API. Například, jak jste si již mohli všimnout, metoda apply nebyla zmíněna výše v předchozím odstavci. V současnosti (květen 2020, pozn. překladatele) veškerá logika kubectl apply, t.j. vytváření neexistujících zdrojů a aktualizace stávajících, funguje plně na straně kódu kubectl. Vynakládají se úsilí apply na stranu API, ale stále je to v beta testování. Podrobnosti níže.
Patch ve výchozím nastavení
Nejlépe se používá patch, pokud chcete aktualizovat zdroj. Takto fungují jak klientské knihovny nad API Kubernetes, tak kubectl (není překvapením, že je to wrapper klientské knihovny, pozn. překladatele).
Pracovat strategicky
Všechny příkazy kubectl apply, edit a patch používají metodu PATCH v HTTP požadavcích pro aktualizaci stávajícího zdroje. Pokud se podíváte blíže na implementaci příkazů, všechny používají přístup pro aktualizaci zdrojů, ačkoli příkaz patch může používat i jiné přístupy (podrobnosti níže). Přístup strategic-merge patching se snaží "dělat vše správně" při slučování poskytované specifikace se stávající specifikací. Konkrétně se snaží slučovat jak objekty, tak pole, což znamená, že změny jsou obvykle aditivní. Například spuštění příkazu patch s novou proměnnou prostředí ve specifikaci kontejneru pod, vede k tomu, že tato proměnná prostředí je přidána k existujícím proměnným prostředí, spíše než aby je přepsala. Pro odstranění pomocí tohoto přístupu je třeba vynutit nastavení hodnoty parametru na null ve poskytované specifikaci. Které z příkazů kubectl pro aktualizaci se nejlépe používají?
Pokud vytváříte a spravujete své zdroje pomocí kubectl apply, při aktualizaci byste vždy měli používat kubectl apply, aby kubectl mohl spravovat konfiguraci a správně sledovat požadované změny od aplikování k aplikování. Výhodou vždy používat apply je, že sleduje dříve aplikovanou specifikaci, což mu umožňuje vědět, kdy jsou vlastnosti specifikace a prvky pole výslovně odstraňovány. To umožňuje používat apply pro odstranění vlastností a prvků pole, zatímco běžné strategické sloučení nebude fungovat. Příkazy edit a patch neaktualizují poznámky, které kubectl apply aplikuje pro sledování svých změn, takže jakékoliv změny, které jsou sledovány a prováděny přes API Kubernetes, ale učiněné prostřednictvím příkazů edit a patch, jsou neviditelné pro následné příkazy apply, to znamená apply neodstraní je, i když se ve vstupní specifikaci neobjevují pro apply (V dokumentaci se uvádí, že edit a patch provádějí aktualizace poznámek používaných apply, ale v praxi - ne).
Pokud nepoužíváte příkaz apply, můžete použít jako edit, tak i patch, vybráním příkazu, který nejlépe odpovídá prováděné změně. Při přidávání a měnění vlastností specifikace jsou obě přístupy přibližně stejné. Při odstraňování vlastností specifikace nebo prvků pole edit se chová jako jednorázové spuštění apply, včetně sledování, jaká byla specifikace před a po jejím úpravení, takže lze jasně odstraňovat vlastnosti a prvky pole z prostředku. Je nutné výslovně nastavit hodnotu vlastnosti na null ve specifikaci pro patch, aby se odstranila z prostředku. Odstraňování prvku pole pomocí strategického sloučení patching je složitější, protože vyžaduje použití sloučovacích direktiv. Podívejte se na jiné přístupy k aktualizacím níže pro výběr přijatelnějších alternativ.
Aby bylo možné implementovat v klientské knihovně metody aktualizace, které se chovají analogicky k výše uvedeným příkazům kubectl, je třeba v žádostech nastavit content-type v application/strategic-merge-patch+json. Pokud chcete odstranit vlastnosti ve specifikaci, musíte jejich hodnoty výslovně nastavit na null podobně jako kubectl patch. Pokud je potřeba odstraňovat prvky pole, měli byste zahrnout sloučovací direktivy do specifikace aktualizace nebo použít jiný přístup k aktualizacím.
Další přístupy k aktualizacím
V Kubernetes jsou podporovány dva další přístupy k aktualizacím: a . Přístup JSON merge patch bere jako vstup částečnou specifikaci Kubernetes a podporuje sloučení objektů podobným způsobem jako přístup strategic-merge patching. Rozdíl mezi nimi spočívá v tom, že podporuje pouze nahrazení polí, včetně pole kontejnerů ve specifikaci pod. To znamená, že při použití JSON merge patch musíte poskytnout úplné specifikace pro všechny kontejnery v případě změny jakéhokoli vlastnosti jakéhokoli kontejneru. Tento přístup je tedy užitečný pro odstranění prvků z pole ve specifikaci. V příkazovém řádku můžete zvolit JSON merge patch pomocí kubectl patch --type=merge. Při práci s API Kubernetes byste měli používat metodu požadavku PATCH a nastavení content-type v application/merge-patch+json.
Přístup JSON patch místo poskytování částečné specifikace zdroje používá poskytování změn, které chcete do zdroje vložit, ve formě pole, kde každý prvek pole představuje popis změny provedené ve zdroji. Tento přístup je flexibilnější a mocnější způsob vyjadřování prováděných změn, nicméně v důsledku toho, že seznam prováděných změn jde v samostatném, ne Kubernetes formátu, místo odeslání částečné specifikace zdroje. V kubectl můžete zvolit JSON patch pomocí kubectl patch --type=json. Při použití API Kubernetes tento přístup funguje s použitím metody požadavku PATCH a nastavení content-type v application/json-patch+json.
Potřebujete jistotu — používáme replace
V některých případech je třeba mít jistotu, že do zdroje nebudou provedeny změny mezi časem čtení zdroje a jeho aktualizací. Jinými slovy je důležité zajistit, že všechny změny budou atomární. V tomto případě pro aktualizaci zdrojů byste měli použít replace. Například, pokud máte ConfigMap s počítadlem, které aktualizují několik zdrojů, měli byste mít jistotu, že dva zdroje nebudou aktualizovat počítadlo současně, což přivede k ztrátě aktualizace. Pro ilustraci si představte sekvenci událostí s využitím přístupu patch:
- A a B získávají aktuální stav zdroje z API
- Každý z nich lokálně aktualizuje specifikaci, zvyšují počítadlo o jednotku, a také přidává "A" nebo "B" v příslušné poznámce "updated-by"
- A trochu rychleji aktualizuje zdroj
- B aktualizuje zdroj
Aktualizace A selhala. Poslední operace patch vyhrává, čítač se zvyšuje o jedno místo o dvě, a hodnota poznámky "updated-by" končí na "B" a neobsahuje "A". Pojďme porovnat výše uvedené s tím, co se děje, když jsou aktualizace prováděny pomocí přístupu replace:
- A a B získávají aktuální stav zdroje z API
- Každý z nich lokálně aktualizuje specifikaci, zvyšují počítadlo o jednotku, a také přidává "A" nebo "B" v příslušné poznámce "updated-by"
- A trochu rychleji aktualizuje zdroj
- B se snaží aktualizovat prostředek, ale aktualizaci odmítá API, protože verze prostředku ve specifikaci
replaceneodpovídá aktuální verzi prostředku v Kubernetes, protože verze prostředku byla zvýšena při provádění operace replace ze strany A.
V uvedeném případě B musí znovu extrahovat prostředek, provést změny na novém stavu a znovu se pokusit o replace. V důsledku toho se čítač zvýší o dvě a poznámka "updated-by" bude obsahovat "AB" na konci.
Výše uvedený příklad předpokládá, že při provádění replace se provádí úplná náhrada celého prostředku. Specifikace, která se používá pro replace, by neměla být částečná, nebo částmi jako v apply, ale kompletní, včetně přidání resourceVersion do metadat specifikace. Pokud jste nezahrnuli resourceVersion nebo verze, kterou jste poskytli, není aktuální, náhrada bude odmítnuta. Takže nejlepší přístup k použití replace je přečíst prostředek, aktualizovat ho a okamžitě nahradit. Použitím kubectl, to může vypadat takto:
$ kubectl get deployment my-deployment -o json
| jq '.spec.template.spec.containers[0].env[1].value = "new value"'
| kubectl replace -f -Je třeba poznamenat, že následující dva příkazy, prováděné za sebou, budou úspěšné, protože deployment.yaml neobsahuje vlastnost .metadata.resourceVersion
$ kubectl create -f deployment.yaml
$ kubectl replace -f deployment.yamlNa první pohled to vypadá v rozporu s tím, co bylo řečeno výše, tj. "přidáním resourceVersion do metadat specifikace". Je to nesprávné tvrzení? Ne, není, protože pokud kubectl zjistí, že jste neuvedli resourceVersion, přečte si to z prostředku a přidá to do specifikace, kterou jste uvedli, a teprve potom provede replace. Protože toto je potenciálně nebezpečné, pokud se spoléháte na atomaritu, magie funguje zcela na straně kubectl, neměli byste se na ni spoléhat při používání klientských knihoven, které pracují s API. V tomto případě budete muset přečíst aktuální specifikaci prostředku, aktualizovat ji a poté provést PUT dotaz.
Nelze udělat patch – děláme replace
Někdy je nutné provést určité změny, které nemohou být zpracovány prostřednictvím API. V těchto případech je možné vynutit výměnu zdroje tím, že ho odstraníme a znovu vytvoříme. To se provádí pomocí kubectl replace --force. Spuštění této příkazu okamžitě odstraní zdroje a pak je znovu vytvoří podle poskytnuté specifikace. API nemá zpracovatele "vynutit výměnu", a aby bylo možné něco takového provést prostřednictvím API, je třeba provést dva kroky. Nejprve je nutné zdroj odstranit a nastavit pro něj gracePeriodSeconds na nulu (0) a propagationPolicy na „Background“, a poté znovu vytvořit tento zdroj se žádoucí specifikací.
Pozor: Tento přístup je potenciálně nebezpečný a může vést k neurčitému stavu
Apply na straně serveru
Jak bylo zmíněno dříve, vývojáři Kubernetes pracují na implementaci logiky apply z kubectl v API Kubernetes. Tato logika apply je k dispozici v Kubernetes 1.18 prostřednictvím kubectl apply --server-side nebo prostřednictvím API, použitím metody PATCH s content-type application/apply-patch+YAML.
Poznámka: JSON je také platný YAML, takže můžete poslat specifikaci ve formátu JSON, i když
content-typebudeapplication/apply-patch+yaml.
Kromě toho, že logika kubectl se stává dostupnou pro všechny prostřednictvím API, apply na straně serveru sleduje odpovědné za pole ve specifikaci, což umožňuje bezpečný vícenásobný přístup k jejímu bezkonfliktnímu editování. Jinými slovy, pokud apply na straně serveru bude mít širší distribuci, vznikne univerzální bezpečné rozhraní pro správu zdrojů pro různé klienty, jako například kubectl, Pulumi, nebo Terraform, GitOps, stejně jako vlastní skripty využívající klientské knihovny.
Závěry
Doufám, že tento krátký přehled různých způsobů aktualizace zdrojů v clusterech byl pro vás užitečný. Je dobré vědět, že nejsou jen jednoznačné výhody apply oproti replace, protože je možné aktualizovat zdroj pomocí apply, edit, patch nebo replace. V zásadě každý přístup má svou oblast použití. Pro atomární změny je preferován replace, jinak je lepší použít strategic-merge patch prostřednictvím apply. V krajním případě doufám, že jste pochopili, že nelze důvěřovat Google nebo StackOverflow při hledání "kubernetes apply vs replace", alespoň do té doby, než tento článek nahradí současnou odpověď.

Zdroj: habr.com
