
Kur ndërtohet procesi CI/CD duke përdorur Kubernetes, ndonjëherë shfaqen probleme të papajtueshmërisë midis kërkesave të infrastrukturës së re dhe aplikacionit që po transferohet në të. Saktësisht, në fazën e ndërtimit të aplikacionit është e rëndësishme të merret një imazh që do të përdoret në të gjitha mjediset dhe klasteret e projektit. Ky parim është në thelb të menaxhimit të saktë të kontejnerëve (kjo është thënë disa herë) dhe drejtori ynë teknik.
Megjithatë, nuk do të shihni situata ku në kodin e faqes përdoret një kornizë e gatshme, përdorimi i së cilës i imponon kufizime në eksploatimin e saj të mëtejshëm. Dhe në "mjedisin e zakonshëm" këtë është e lehtë ta menaxhosh, në Kubernetes një sjellje e tillë mund të bëhet një problem, veçanërisht kur e përballni për herë të parë. Megjithëse një mendje shpikëse mund të sugjerojë zgjidhje infrastrukturore që duken të qarta dhe madje të mira me sa duket… është e rëndësishme të kujtohet se shumicën e situatave mund dhe duhet të zgjidhen në mënyrë arkitekturor.
Le të shqyrtojmë zgjidhjet popullore workaround për ruajtjen e skedave, të cilat mund të çojnë në pasojat e pakëndshme gjatë eksploatimit të klasterit, dhe gjithashtu të tregojmë për një rrugë më të saktë.
Ruajtja e statikës
Për ilustrim, le të shqyrtojmë një aplikacion web që përdor një gjenerator statike për të marrë një grup imazhesh, stilesh dhe gjërash të tjera. Për shembull, në kornizën PHP Yii ka një menaxher resursesh të integruar që gjeneron emra unik të drejtorive. Saktësisht, në dalje merr formën e një grupi rrugësh që nuk ndërthuren mes vete për statikën e faqes (kjo është bërë për disa arsye — për shembull, për të eliminuar kopjet gjatë përdorimit të të njëjtit burim nga shumë komponentë). Kështu, nga kutia, me kërkesën e parë për modulin e burimeve web, formohet dhe shpërndahet statika (në të vërtetë — shpesh është skedarë simbolikë, por për këtë më vonë) me një katalog të zakonshëm rrënjor unik për këtë shpërndarje:
-
webroot/assets/2072c2df/css/… -
webroot/assets/2072c2df/images/… -
webroot/assets/2072c2df/js/…
Çfarë pasojash sjell kjo në kontekstin e klasterit?
Shembulli më i thjeshtë
Të marrim një rast mjaft të zakonshëm, kur para PHP qëndron nginx për shpërndarjen e statikës dhe përpunimin e kërkesave të thjeshta. Mënyra më e thjeshtë — Zhvillimi me dy kontejnerë:
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.confNë mënyrë të thjeshtë, konfigurimi i nginx përmbledh në të vërtetë në këtë:
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;
}
} Kur bëhet një kërkesë e parë në faqe në kontejnerin me PHP, shfaqen asetet. Por në rastin e dy kontejnerëve brenda një pod’i — nginx nuk di për këto skedarët statikë, të cilat (sipas konfigurimit) duhet t’i dorëzohet pikërisht ata. Si rezultat, për çdo kërkesë për skedarët CSS dhe JS, klienti do të shohë një gabim 404. Zgjidhja më e thjeshtë këtu do të ishte organizimi i një drejtorie të përbashkët për kontejnerët. Një variant primitiv — e përbashkët 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.confTani skedarët statikë të gjeneruar në kontejner dorëzohen korrekt nga nginx. Por për të kujtuar, kjo është një zgjidhje primitetare, kështu që — është shumë larg idealit dhe ka nuanca dhe mangësi të veta, të cilat janë më poshtë.
Një magazinë më e avancuar
Tani, le të imagjinojmë një situatë ku përdoruesi hyri në faqe, ngarkohej faqja me stilet e pranishme në kontejner, dhe ndërkohë që ai po lexonte këtë faqe, ne e çdeploy-ëm sërish kontejnerin. Në katalogun e aseteve u bë bosh dhe nevojitet një kërkesë në PHP për të nisur gjenerimin e të rinjve. Megjithatë, even pas kësaj, lidhjet me statikën e vjetër do të mbeten të parregullta, çka do të çojë në gabime në paraqitjen e statikës.
Për më tepër, ndoshta kemi një projekt mjaft të ngarkuar, dhe kjo do të thotë që një kopje e aplikacionit nuk do të mjaftojë:
- Të shtrijmë Zhvillimi në dy replika.
- Në kërkesën e parë për faqen, në një replikë u krijuan asetet.
- Në një moment, ingress vendosi (në emër të balancimit të ngarkesës) të dërgojë kërkesën në replikën e dytë, dhe atje nuk ka asete. Ose ndoshta nuk ka atje, sepse po përdorim
RollingUpdatedhe në këtë moment jemi duke bërë deploy.
Në përmbledhje, përsëri gabime.
Për të mos humbur asetet e vjetra, mund të ndryshojmë emptyDir në hostPath, duke ruajtur statikën fizikisht në nyjën e klasterit. Ky qasje është e keqe sepse ne në thelb duhet të lidhemi me një nyjë specifike të klasterit me aplikacionin tonë, sepse – në rast të kalimit në nyje të tjera – direktoria nuk do të përmbajë skedarët e nevojshëm. Ose kërkohet një sinkronizim i bërë në sfond të direktorisë midis nyjeve.
Cilat janë mundësitë për zgjidhje?
- Nëse hardueri dhe burimet lejojnë, mund të përdorim për të organizuar një director të barabartë për nevojat e statikës. rekomandon disqe SSD, së paku një riprodhim tri herë dhe një lidhje të qëndrueshme 'të trashë' midis nyjeve të klasterit.
- Një variant më pak kërkues do të ishte organizimi i një serveri NFS. Megjithatë, atëherë duhet të merret parasysh një rritje e mundshme e kohës së reagimit në përpunimin e kërkesave nga serveri web, dhe qëndrueshmëria e ndihmës do të lërë për të dëshiruar. Pasojat e një dështimi janë katastrofike: humbja e mount-it e dënon klasterin në shkatërrim nën presionin e ngarkesës LA, që shkon në qiell.
Përveç të gjithave, për të gjitha variantet e krijimit të një ruajtjeje të qëndrueshme do të kërkohet pastrimi në sfond i grupeve të skedarëve të skaduar, të grumbulluara për një periudhë të caktuar kohore. Para kontejnerëve me PHP mund të vendosim DaemonSet të nginx që ruajnë kopje asetesh për një periudhë të kufizuar kohe. Ky sjellje është e lehtë për t'u konfiguruar me ndihmën e proxy_cache me pavarësisë së ruajtjes në ditë ose gigabajt hapësirë disk.
Kombinimi i kësaj metode me sistemet e skedarëve të shpërndarë të përmendura më sipër ofron një fushë të madhe për imagjinatën, me kufizimin vetëm në buxhetin dhe potencialin teknik të atyre që do ta realizojnë dhe mbështesin. Nga përvoja, ne themi se sa më e thjeshtë të jetë sistemi, aq më stabil punon. Me shtimin e këtij lloji kompleksiteti, mbështetje e infrastrukturës bëhet shumë më e ndërlikuar, dhe përkrah me këtë rritet gjithashtu koha e shpenzuar për diagnostikim dhe rikuperim gjatë çdo dështimi.
Rekomandimi
Nëse realizimi i sugjerimeve për ruajtjen ju duket gjithashtu i paarsyeshëm (i ndërlikuar, i shtrenjtë…), atëherë vlen të shikoni situatën nga një këndvështrim tjetër. Njësoj — të thelloheni në arkitekturën e projektit dhe të zhdukni problemin në kod, duke u lidhur me një strukturë të dhënash statike në imazh, definimin e qartë të përmbajtjes ose procedurës "ngrohjes" dhe/ose parakomplilim të aseteve gjatë fazës së ndërtimit të imazhit. Kështu ne arrijmë një sjellje plotësisht të parashikueshme dhe të njëjtin grup skedarësh për të gjitha mjediset dhe replikat e aplikacionit të nisur.
Nëse kthehemi tek shembulli konkret me kuadrin Yii dhe nuk thellohemi në ndërtimin e tij (çi nuk është qëllimi i artikullit), mjafton të përmendim dy qasje të njohura:
- Të ndryshojmë procesin e ndërtimit të imazhit në mënyrë që asetet të vendosen në një vend të parashikueshëm. Kështu sugjerojnë/zbatonin në zgjatjet si .
- Të përcaktojmë hash të veçantë për katalogët e aseteve, siç përshkruhet, për shembull, në (duke filluar nga slidi №35). Për më tepër, autori i prezantimit në fund (dhe jo pa të drejtë!) rekomandon që pas ndërtimit të aseteve në serverin e ndërtimit, ato të ngarkohen në një depo qendrore (si S3), përpara së cilës të vendoset CDN.
Skedarët e ngarkuar
Një rast tjetër që patjetër do të funksionojë kur të transferoni aplikacionin në klasterin Kubernetes — ruajtja e skedarëve të përdoruesve në sistemin e skedarëve. Për shembull, kemi përsëri një aplikacion në PHP që pranon skedarë përmes një forme ngarkimi, bën diçka me ta gjatë procesit dhe i kthen prapa.
Vendi ku këto skedarë duhet të vendosen, në realitetin e Kubernetes duhet të jetë e përbashkët për të gjitha replikat e aplikacionit. Në varësi të kompleksitetit të aplikacionit dhe nevojës për të organizuar persistimin e këtyre skedarëve, një vend i tillë mund të jenë opsionet e përmendura më sipër të pajisjeve të ndara, por, siç e shohim, ato kanë disavantazhet e tyre.
Rekomandimi
Një nga opsionet për zgjidhje është përdorimi i një ruajtjeje të përputhshme me S3 (madje ndonjë variant të kategorisë së vetë-mbajtur si minio). Kalimi në punën me S3 do të kërkojë ndryshime në nivelin e kodit, dhe si do të ndodhë dorëzimi i përmbajtjes në front-end, ne tashmë .
Seancat e përdoruesëve
Veçanërisht duhet të theksohet organizimi i ruajtjes së seancave të përdoruesëve. Shpesh kjo është gjithashtu skedarë në disk, që në prizmin e Kubernetes do të çojë në kërkesa të vazhdueshme për autorizim nga përdoruesi, nëse kërkesa e tij përfundon në një kontejner tjetër.
Pjesërisht problemi zgjidhet duke përfshirë stickySessions në ingress (karakteristikë kjo e mbështetur në të gjithë kontrolluesit e njohur të ingress - më shumë shih në ), për të lidhur përdoruesin me një pod të caktuar me aplikacionin:
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: /Por kjo nuk do të eliminojë problemet gjatë depolimeve të përsëritura.
Rekomandimi
Një mënyrë më e saktë do të ishte kalimi i aplikacionit në ruajtjen e seancave në memcached, Redis dhe zgjidhje të ngjashme — në përgjithësi, të heqim dorë nga opsionet me skedarë.
Përfundim
Zgjidhjet infrastrukturore të diskutuar në tekst janë të merituara vetëm në formatin e "përkrahjeve" të përkohshme (qartësisht më bukur në anglisht si workaround). Ato mund të jenë të rëndësishme në fazat e para të migrimit të aplikacionit në Kubernetes, por nuk duhet të "rrënjosën".
Rruga e rekomanduar përfundimisht është të shkurtoni nga to në favor të përshtatjes arkitekturore të aplikacionit në përputhje me mirë të njohur . Megjithatë, kjo - përshtatja e aplikacionit në një formë stateless - do të thotë se do të kërkohen ndryshime në kod, dhe këtu është e rëndësishme të gjendet një ekuilibër midis mundësive/kërkesave të biznesit dhe perspektivave të zbatimit dhe mirëmbajtjes së rrugës së zgjedhur.
P.S.
Lexoni gjithashtu në blogun tonë:
- «»;
- «»;
- «» (from Red Hat);
- «».
Burimi: habr.com
