
Při budování procesu CI/CD s využitím Kubernetes se občas objevuje problém nekompatibility požadavků nové infrastruktury a aplikace, kterou do ní přenášíte. Zvláště ve fázi sestavení aplikace je důležité získat ; ideálně by si uživatel měl vybrat dvě nebo více tajných otázek obraz, který bude použit ve všechny prostředích a klastrech projektu. Tento princip leží v základu správného řízení kontejnerů (o tom už jsme se zmínili i náš technický ředitel).
Nicméně, nikdo nezůstává nepovšimnutím situacemi, kdy je v kódu webu použit hotový framework, jehož použití klade omezení na jeho další provoz. A pokud s tímto v "běžném prostředí" se to dá snadno zvládnout, v Kubernetes se toto chování může stát problémem, zejména pokud se s tím setkáváte poprvé. Ačkoli vynalézavá mysl dokáže nabídnout infrastrukturní řešení, která se na první pohled zdají zřejmá a dokonce i dobrá… je důležité si pamatovat, že většina situací může a měla by být řešena architektonicky.
Probereme oblíbená workaround řešení pro ukládání souborů, která mohou vést k nepříjemným následkům při provozu clustru, a zároveň ukážeme na správnější cestu.
Ukládání statiky
Pro ilustraci si vezmeme webovou aplikaci, která používá nějaký generátor statiky pro získání sady obrázků, stylů a dalších. Například v PHP frameworku Yii je vestavěný manažer assetů, který generuje unikátní názvy adresářů. Odpovídajícím způsobem vzniká soubor zaručeně se nepřekrývajících cest pro statiku webu (to bylo uděláno z několika důvodů — například pro vyloučení duplicit při použití stejného zdroje mnoha komponenty). Takto, z krabice, při prvním přístupu k modulu webového zdroje probíhá vytváření a rozložení statiky (ve skutečnosti — často symbolických odkazů, ale o tom později) s unikátním pro tuto nasazení společným kořenovým katalogem:
-
webroot/assets/2072c2df/css/… -
webroot/assets/2072c2df/images/… -
webroot/assets/2072c2df/js/…
Jaké to může mít následky v rámci clustru?
Nejjednodušší příklad
Vezměme si docela rozšířený případ, kdy před PHP stojí nginx pro distribuci statiky a zpracování jednoduchých požadavků. Nejjednodušší způsob je — Nasazení s dvěma kontejnery:
apiVersion: apps/v1
kind: Deployment
metadata:
name: site
spec:
selector:
matchLabels:
component: backend
template:
metadata:
labels:
component: backend
spec:
volumes:
- name: nginx-config
configMap:
name: nginx-configmap
containers:
- name: php
image: own-image-with-php-backend:v1.0
command: ["/usr/local/sbin/php-fpm","-F"]
workingDir: /var/www
- name: nginx
image: nginx:1.16.0
command: ["/usr/sbin/nginx", "-g", "daemon off;"]
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.confV zjednodušené podobě se konfigurace nginx shrnuje následovně:
apiVersion: v1
kind: ConfigMap
metadata:
name: "nginx-configmap"
data:
nginx.conf: |
server {
listen 80;
server_name _;
charset utf-8;
root /var/www;
access_log /dev/stdout;
error_log /dev/stderr;
location / {
index index.php;
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}
} Při prvním přístupu na webovou stránku se v kontejneru s PHP objevují statické soubory. Ale v případě dvou kontejnerů v rámci jednoho podu — nginx nic neví o těchto statických souborech, které (podle konfigurace) mají být poskytovány právě jím. Výsledkem je, že všechny požadavky na CSS a JS soubory klient uvidí chybu 404. Nejjednodušším řešením by bylo zorganizovat společný adresář pro kontejnery. Primitivní variantou je společný emptyDir:
apiVersion: apps/v1
kind: Deployment
metadata:
name: site
spec:
selector:
matchLabels:
component: backend
template:
metadata:
labels:
component: backend
spec:
volumes:
- name: assets
emptyDir: {}
- name: nginx-config
configMap:
name: nginx-configmap
containers:
- name: php
image: own-image-with-php-backend:v1.0
command: ["/usr/local/sbin/php-fpm","-F"]
workingDir: /var/www
volumeMounts:
- name: assets
mountPath: /var/www/assets
- name: nginx
image: nginx:1.16.0
command: ["/usr/sbin/nginx", "-g", "daemon off;"]
volumeMounts:
- name: assets
mountPath: /var/www/assets
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.confNyní se generované statické soubory v kontejneru správně poskytují nginxem. Ale připomínám, že toto je primitivní řešení, a proto — je daleko od ideálu a má své nuance a nedostatky, o kterých budu hovořit níže.
Pokročilejší úložiště
Představme si situaci, kdy uživatel navštívil web, načetl stránku se styly v kontejneru, a zatímco ji četl, my jsme znovu nasadili kontejner. V adresáři aktiv se stalo prázdno a je potřeba požadavek na PHP pro spuštění generování nových. I po tom však odkazy na starou statiku zůstanou neaktuální, což povede k chybám zobrazení statiky.
Navíc máme pravděpodobně více či méně zatížený projekt, což znamená — jedna kopie aplikace nebude stačit:
- Zprůchodníme Nasazení na dvě repliky.
- Při prvním přístupu na web se v jedné replice vytvořily aktiva.
- V určitém okamžiku ingress rozhodl (z důvodu vyvážení zátěže) odeslat požadavek na druhou repliku, a tam tato aktiva ještě nejsou. A možná už tam nejsou, protože používáme
RollingUpdatea v tuto chvíli provádíme nasazení.
Celkově výsledek — opět chyby.
Abychom neztráceli stará aktiva, lze změnit emptyDir na hostPath, fyzicky ukládáním statiky na uzel clusteru. Tento přístup má nevýhodu v tom, že se fakticky musíme připoutat k určitému uzlu clusteru svou aplikací, protože v případě přesunu na jiné uzly nebude adresář obsahovat potřebné soubory. Nebo je třeba nějaká pozadová synchronizace adresáře mezi uzly.
Jaké jsou možnosti řešení?
- Pokud hardware a zdroje dovolují, můžeme využít pro organizaci vysoce dostupného adresáře pro potřeby statiky. doporučuje SSD disky, minimálně trojnásobnou replikaci a stabilní „tlusté“ spojení mezi uzly clusteru.
- Méně náročnou variantou by byla organizace NFS serveru. Ale v tom případě je třeba brát v úvahu možné zvýšení doby odezvy při zpracování požadavků webovým serverem, a odolnost vůči chybám bude také horší. Důsledky selhání jsou katastrofické: ztráta montáže odsoudí cluster k zániku pod návalem zátěže LA, která se žene k nebi.
Kromě toho pro všechny varianty vytváření trvalého úložiště bude vyžadováno pozadové čištění zastaralých sad souborů, jejichž množství se nahromadilo za určité časové období. Před kontejnery s PHP lze umístit DaemonSet z caching nginx, které budou uchovávat kopie aktiv omezený čas. Toto chování lze snadno nastavit pomocí proxy_cache s hloubkou úložiště v dnech nebo gigabajtech diskového prostoru.
Kombinace této metody s výše zmíněnými distribuovanými souborovými systémy dává obrovské pole pro fantazii, omezení je pouze v rozpočtu a technických možnostech těch, kdo to budou realizovat a udržovat. Podle zkušeností říkáme, že čím jednodušší systém, tím stabilněji funguje. Přidání takových vrstev činí údržbu infrastruktury mnohem složitější, a spolu s tím roste také čas potřebný k diagnostice a obnovení při jakýchkoli selháních.
Doporučení
Pokud se implementace navrhovaných možností úložišť také zdá neopodstatněná (složitá, drahá…), stojí za to podívat se na situaci z jiné strany. A to totiž — hlouběji do architektury projektu a vymýtit problém v kódu, přičemž se držíme nějaké statické datové struktury v obraze, jednoznačného určení obsahu nebo procedury „ohřevu“ a/nebo prekompilace assetů ve fázi sestavení obrazu. Tím získáváme zcela předvídatelné chování a jednotnou sadu souborů pro všechna prostředí a repliky spuštěné aplikace.
Pokud se vrátíme k konkrétnímu příkladu s rámcem Yii a nezabíháme do jeho struktury (což není cílem článku), stačí poukázat na dva populární přístupy:
- Změnit proces sestavování obrazu tak, aby umisťoval assety na předvídatelná místa. Tak se doporučuje/realizuje v rozšířeních jako .
- Stanovit konkrétní hashe pro adresáře assetů, jak je uvedeno například v (počínaje slidou č. 35). Mimochodem, autor prezentace nakonec (a to bez opodstatnění!) doporučuje po sestavení assetů na build-serveru nahrát je do centrálního úložiště (například S3), před které umístit CDN.
Nahrávané soubory
Další případ, který se určitě osvědčí při přesunu aplikace do clustru Kubernetes, — uchovávání uživatelských souborů v souborovém systému. Například máme znovu aplikaci na PHP, která přijímá soubory prostřednictvím formuláře pro nahrávání, něco s nimi dělá během provozu a vrací je zpět.
Místo, kam by tyto soubory měly být umístěny, by v podmínkách Kubernetes mělo být společné pro všechny repliky aplikace. V závislosti na složitosti aplikace a potřebě organizace perzistence těchto souborů, takovým místem mohou být zmíněné výše varianty sdílených zařízení, ale jak vidíme, mají své nevýhody.
Doporučení
Jednou z možností řešení je využití úložiště kompatibilního s S3 (klidně nějakou variantu kategorie self-hosted jako je minio). Přechod na práci se S3 si vyžádá změny na úrovni kódu, a jak bude probíhat předávání obsahu na frontendu, to už .
Uživatelské relace
Je třeba zvlášť zdůraznit organizaci uchovávání uživatelských relací. Často jsou to také soubory na disku, což v kontextu Kubernetes povede k neustálým žádostem o autorizaci u uživatele, pokud jeho žádost skončí v jiném kontejneru.
Částečně se problém řeší zapnutím stickySessions na ingress (tato funkce je podporována ve všech populárních kontrolerech ingress – více viz ), aby se uživatel připojil k určitému podu s aplikací:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginx-test
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
spec:
rules:
- host: stickyingress.example.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /Ale to neodstraní problémy při opakovaných nasazení.
Doporučení
Správnějším způsobem by bylo převést aplikaci na ukládání relací do memcached, Redis a podobných řešení – obecně se zcela vzdát souborových variant.
Závěr
Infrastrukturální řešení diskutovaná v textu si zaslouží aplikaci pouze ve formátu dočasných "berliček" (což se hezky anglicky nazývá workaround). Mohou být relevantní v raných fázích migrace aplikace do Kubernetes, ale neměly by "zapustit kořeny".
Obecně doporučovaná cesta se shrnuje k tomu, aby se jich zbavili ve prospěch architektonických úprav aplikace v souladu s dobře známým . Avšak to – přivést aplikaci do stavu stateless – nevyhnutelně znamená, že budou potřeba změny v kódu, a zde je důležité najít rovnováhu mezi možnostmi/potřebami podnikání a perspektivami realizace a údržby vybrané cesty.
P.S.
Přečtěte si také náš blog:
- «»;
- «»;
- «» (od Red Hat);
- «».
Zdroj: habr.com
