Pět chyb při nasazení první aplikace na Kubernetes

Pět chyb při nasazení první aplikace na KubernetesSelhal Aris-Dreamer

Mnoho lidí věří, že stačí migrovat aplikaci na Kubernetes (ať už pomocí Helmu nebo ručně) a budou spokojeni. Ale není to tak jednoduché.

Tým Cloudová řešení Mail.ru přeložil článek inženýra DevOps Juliana Gindiho. Sdílí, jaká úskalí jeho společnost během procesu migrace potkala, abyste nešlápli na stejné hrábě.

Krok XNUMX: Nastavení požadavků na moduly a limitů

Začněme nastavením čistého prostředí, ve kterém naše pody poběží. Kubernetes odvádí skvělou práci při plánování modulů a zpracovávání poruchových stavů. Ukázalo se však, že plánovač někdy nemůže umístit modul, pokud je obtížné odhadnout, kolik zdrojů potřebuje k úspěšné práci. Zde se objevují požadavky na zdroje a limity. Hodně se diskutuje o nejlepším přístupu k nastavení požadavků a limitů. Někdy mám opravdu pocit, že je to víc umění než věda. Tady je náš přístup.

Požadavky na pod - Toto je hlavní hodnota, kterou plánovač používá k optimálnímu umístění modulu.

Z Dokumentace Kubernetes: Krok filtrování určuje sadu uzlů, kde lze modul naplánovat. Filtr PodFitsResources například kontroluje, zda má uzel dostatek zdrojů k uspokojení specifických požadavků podu na zdroje.

Požadavky aplikací používáme tak, aby je bylo možné použít k odhadu počtu zdrojů ve skutečnosti Aplikace to potřebuje ke správnému fungování. Tímto způsobem může plánovač umístit uzly realisticky. Původně jsme chtěli nastavit požadavky s marží, abychom zajistili, že každý modul bude mít dostatečně velký počet zdrojů, ale všimli jsme si, že doba plánování se výrazně prodloužila a některé moduly nebyly nikdy plně naplánovány, jako by pro ně nebyly přijaty žádné požadavky na zdroje.

V tomto případě by plánovač často vytlačil moduly a nebyl schopen je přeplánovat, protože řídicí rovina neměla ponětí, kolik zdrojů bude aplikace vyžadovat, což je klíčová součást plánovacího algoritmu.

Limity pod - to je jasnější limit pro pod. Představuje maximální množství prostředků, které klastr přidělí kontejneru.

Opět od oficiální dokumentace: Pokud má kontejner nastavený limit paměti 4 GiB, pak ho kubelet (a běhový modul kontejneru) vynutí. Běhové prostředí neumožňuje kontejneru používat více, než je zadaný limit prostředků. Když se například proces v kontejneru pokusí využít více než povolené množství paměti, jádro systému ukončí proces s chybou „nedostatek paměti“ (OOM).

Kontejner může vždy použít více zdrojů, než je uvedeno v požadavku na zdroj, ale nikdy nemůže použít více, než je uvedeno v limitu. Tuto hodnotu je obtížné správně nastavit, ale je velmi důležitá.

V ideálním případě chceme, aby se požadavky na zdroje v průběhu životního cyklu procesu měnily, aniž by to zasahovalo do jiných procesů v systému – to je cílem stanovení limitů.

Bohužel nemohu dát konkrétní pokyny, jaké hodnoty nastavit, ale sami dodržujeme následující pravidla:

  1. Pomocí nástroje pro testování zátěže simulujeme základní úroveň provozu a monitorujeme využití zdrojů pod (paměť a procesor).
  2. Nastavíme požadavky podů na libovolně nízkou hodnotu (s limitem zdrojů asi 5násobkem hodnoty požadavků) a pozorujeme. Když jsou požadavky příliš nízké, proces nelze spustit, což často způsobuje záhadné chyby běhu Go.

Všimněte si, že vyšší limity zdrojů znesnadňují plánování, protože pod potřebuje cílový uzel s dostatkem dostupných zdrojů.

Představte si situaci, kdy máte lehký webový server s velmi vysokým limitem zdrojů, řekněme 4 GB paměti. Tento proces bude pravděpodobně muset být škálován horizontálně a každý nový modul bude muset být naplánován na uzel s alespoň 4 GB dostupné paměti. Pokud žádný takový uzel neexistuje, klastr musí zavést nový uzel pro zpracování tohoto modulu, což může nějakou dobu trvat. Je důležité udržovat rozdíl mezi požadavky na zdroje a limity na minimu, aby bylo zajištěno rychlé a plynulé škálování.

Krok dva: nastavení testů živosti a připravenosti

Toto je další jemné téma, které je v komunitě Kubernetes často diskutováno. Je důležité dobře porozumět testům životnosti a připravenosti, protože poskytují mechanismus, aby software běžel hladce a minimalizoval prostoje. Pokud však nejsou správně nakonfigurovány, mohou způsobit vážný zásah do výkonu vaší aplikace. Níže je shrnutí toho, jaké jsou oba vzorky.

Živost ukazuje, zda je kontejner spuštěn. Pokud selže, kubelet ukončí kontejner a je pro něj povolena zásada restartování. Pokud kontejner není vybaven sondou Liveness, pak bude výchozí stav úspěšný - to je to, co říká Dokumentace Kubernetes.

Sondy živosti by měly být levné, což znamená, že by neměly spotřebovávat mnoho zdrojů, protože se spouštějí často a potřebují informovat Kubernetes, že aplikace běží.

Pokud nastavíte možnost spouštět každou sekundu, přidá se 1 požadavek za sekundu, takže mějte na paměti, že ke zpracování tohoto provozu budou potřeba další zdroje.

Testy Liveness v naší společnosti kontrolují základní komponenty aplikace, i když data (například ze vzdálené databáze nebo mezipaměti) nejsou plně dostupná.

Nakonfigurovali jsme aplikace s koncovým bodem „zdraví“, který jednoduše vrací kód odpovědi 200. To znamená, že proces běží a je schopen zpracovávat požadavky (ale ještě ne provoz).

Proba Připravenost označuje, zda je kontejner připraven obsluhovat požadavky. Pokud sonda připravenosti selže, řadič koncového bodu odebere IP adresu podu z koncových bodů všech služeb odpovídajících podu. To je také uvedeno v dokumentaci Kubernetes.

Sondy připravenosti spotřebovávají více zdrojů, protože musí být odeslány do backendu způsobem, který indikuje, že aplikace je připravena přijímat požadavky.

V komunitě se hodně diskutuje o tom, zda přistupovat přímo k databázi. Vzhledem k režii (kontroly se provádějí často, ale lze je upravit) jsme se rozhodli, že u některých aplikací se připravenost k provozu provozu započítává až po ověření, že se záznamy vrací z databáze. Dobře navržené testy připravenosti zajistily vyšší úroveň dostupnosti a eliminovaly prostoje během nasazení.

Pokud se rozhodnete dotazovat databázi, abyste otestovali připravenost vaší aplikace, ujistěte se, že je to co nejlevnější. Vezměme tento požadavek:

SELECT small_item FROM table LIMIT 1

Zde je příklad toho, jak konfigurujeme tyto dvě hodnoty v Kubernetes:

livenessProbe: 
 httpGet:   
   path: /api/liveness    
   port: http 
readinessProbe:  
 httpGet:    
   path: /api/readiness    
   port: http  periodSeconds: 2

Můžete přidat některé další možnosti konfigurace:

  • initialDelaySeconds — kolik sekund uplyne mezi spuštěním kontejneru a spuštěním vzorků.
  • periodSeconds — čekací interval mezi běhy vzorků.
  • timeoutSeconds — počet sekund, po kterých je jednotka považována za nouzovou. Pravidelný časový limit.
  • failureThreshold — počet neúspěšných testů před odesláním signálu restartu do modulu.
  • successThreshold — počet úspěšných sond, než modul přejde do připraveného stavu (po selhání, když modul nastartuje nebo se zotaví).

Krok tři: nastavení výchozích síťových zásad pro modul

Kubernetes má „plochou“ topografii sítě; ve výchozím nastavení všechny moduly komunikují přímo mezi sebou. V některých případech to není žádoucí.

Potenciální bezpečnostní problém spočívá v tom, že útočník by mohl použít jedinou zranitelnou aplikaci k odeslání provozu do všech modulů v síti. Stejně jako u mnoha oblastí bezpečnosti i zde platí zásada nejmenšího privilegia. V ideálním případě by síťové zásady měly výslovně specifikovat, která spojení mezi moduly jsou povolena a která ne.

Níže je například jednoduchá zásada, která zakazuje veškerý příchozí provoz pro konkrétní jmenný prostor:

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:  
 name: default-deny-ingress
spec:  
 podSelector: {}  
 policyTypes:  
   - Ingress

Vizualizace této konfigurace:

Pět chyb při nasazení první aplikace na Kubernetes
(https://miro.medium.com/max/875/1*-eiVw43azgzYzyN1th7cZg.gif)
Další podrobnosti zde.

Krok čtyři: vlastní chování pomocí háčků a init kontejnerů

Jedním z našich hlavních cílů bylo zajistit nasazení na Kubernetes bez prostojů pro vývojáře. To je obtížné, protože existuje mnoho možností pro ukončení aplikací a uvolnění zdrojů, které používaly.

Zvláštní potíže nastaly s Nginx. Všimli jsme si, že když byly tyto moduly nasazeny postupně, aktivní připojení byla před úspěšným dokončením zrušena.

Po rozsáhlém online výzkumu se ukázalo, že Kubernetes nečeká, až se připojení Nginx vyčerpá, než ukončí modul. Pomocí pre-stop háku jsme implementovali následující funkcionalitu a zcela se zbavili prostojů:

lifecycle: 
 preStop:
   exec:
     command: ["/usr/local/bin/nginx-killer.sh"]

Ale nginx-killer.sh:

#!/bin/bash
sleep 3
PID=$(cat /run/nginx.pid)
nginx -s quit
while [ -d /proc/$PID ]; do
   echo "Waiting while shutting down nginx..."
   sleep 10
done

Dalším extrémně užitečným paradigmatem je použití init kontejnerů ke spouštění konkrétních aplikací. To je zvláště užitečné, pokud máte proces migrace databáze náročný na zdroje, který je třeba spustit před spuštěním aplikace. Můžete také zadat vyšší limit prostředků pro tento proces, aniž byste takový limit nastavili pro hlavní aplikaci.

Dalším běžným schématem je přístup k tajným informacím v iniciačním kontejneru, který poskytuje tyto přihlašovací údaje hlavnímu modulu, což zabraňuje neoprávněnému přístupu k tajným informacím ze samotného hlavního aplikačního modulu.

Jako obvykle citujte z dokumentace: Init kontejnery bezpečně spouštějí vlastní kód nebo nástroje, které by jinak snížily zabezpečení obrazu kontejneru aplikace. Uchováváním nepotřebných nástrojů odděleně omezíte útočnou plochu obrazu kontejneru aplikace.

Krok XNUMX: Konfigurace jádra

Na závěr si povíme něco o pokročilejší technice.

Kubernetes je extrémně flexibilní platforma, která vám umožní spouštět úlohy tak, jak uznáte za vhodné. Máme řadu vysoce výkonných aplikací, které jsou extrémně náročné na zdroje. Po provedení rozsáhlého zátěžového testování jsme zjistili, že jedna aplikace se snažila zvládnout očekávanou zátěž provozu, když platilo výchozí nastavení Kubernetes.

Kubernetes však umožňuje spouštět privilegovaný kontejner, který mění parametry jádra pouze pro konkrétní pod. Zde je to, co jsme použili ke změně maximálního počtu otevřených připojení:

initContainers:
  - name: sysctl
     image: alpine:3.10
     securityContext:
         privileged: true
      command: ['sh', '-c', "sysctl -w net.core.somaxconn=32768"]

Jedná se o pokročilejší techniku, která často není potřeba. Pokud se však vaše aplikace snaží vyrovnat s velkou zátěží, můžete některá z těchto nastavení zkusit vyladit. Další podrobnosti o tomto procesu a nastavení různých hodnot - jako vždy v oficiální dokumentaci.

Konečně,

I když se Kubernetes může zdát jako hotové řešení z krabice, existuje několik klíčových kroků, které musíte udělat, aby vaše aplikace fungovaly hladce.

Během migrace Kubernetes je důležité dodržovat „cyklus testování zátěže“: spusťte aplikaci, otestujte ji, sledujte metriky a chování při škálování, upravte konfiguraci na základě těchto dat a poté cyklus znovu opakujte.

Buďte realističtí ohledně očekávané návštěvnosti a snažte se ji překonat, abyste viděli, které komponenty prasknou jako první. S tímto iterativním přístupem může k dosažení úspěchu stačit pouze několik z uvedených doporučení. Nebo může vyžadovat hlubší přizpůsobení.

Vždy si položte tyto otázky:

  1. Kolik prostředků spotřebovávají aplikace a jak se tento objem změní?
  2. Jaké jsou skutečné požadavky na škálování? Jak velký provoz bude aplikace průměrně zvládat? A co špička?
  3. Jak často se bude muset služba horizontálně škálovat? Jak rychle musí být nové moduly připojeny online, aby mohly přijímat provoz?
  4. Jak správně se moduly vypínají? Je to vůbec nutné? Je možné dosáhnout nasazení bez prostojů?
  5. Jak můžete minimalizovat bezpečnostní rizika a omezit škody způsobené případnými kompromitovanými moduly? Mají nějaké služby oprávnění nebo přístup, které nevyžadují?

Kubernetes poskytuje neuvěřitelnou platformu, která vám umožňuje využít osvědčené postupy pro nasazení tisíců služeb v clusteru. Každá aplikace je však jiná. Někdy implementace vyžaduje trochu více práce.

Naštěstí Kubernetes poskytuje potřebnou konfiguraci pro dosažení všech technických cílů. Pomocí kombinace požadavků na zdroje a limitů, testů životnosti a připravenosti, init kontejnerů, síťových zásad a vlastního ladění jádra můžete dosáhnout vysokého výkonu spolu s odolností proti chybám a rychlou škálovatelností.

Co ještě číst:

  1. Doporučené postupy a doporučené postupy pro spouštění kontejnerů a Kubernetes v produkčním prostředí.
  2. 90+ užitečných nástrojů pro Kubernetes: nasazení, správa, monitorování, zabezpečení a další.
  3. Náš kanál Kolem Kubernetes v Telegramu.

Zdroj: www.habr.com

Přidat komentář