
Bij het opzetten van een CI/CD-proces met Kubernetes kan het soms voorkomen dat er incompatibiliteitsproblemen zijn tussen de vereisten van de nieuwe infrastructuur en de applicatie die naar deze infrastructuur wordt overgebracht. In het bijzonder is het belangrijk om tijdens de buildfase van de applicatie één afbeelding te verkrijgen die in van alle de omgevingen en clusters van het project zal worden gebruikt. Dit principe vormt de basis van goed containerbeheer (dit is niet voor niets en onze technische directeur).
Echter, het zal je niet verbazen dat er situaties zijn waarin in de code van de site een zelfgemaakt framework wordt gebruikt, waarvan het gebruik beperkingen oplegt aan de verdere exploitatie. En als dit in een 'normale omgeving' eenvoudig te verhelpen is, kan dergelijk gedrag in Kubernetes een probleem worden, vooral wanneer je hier voor het eerst mee te maken krijgt. Hoewel een vindingrijke geest infrastructuuroplossingen kan voorstellen die op het eerste gezicht vanzelfsprekend en zelfs goed lijken... is het belangrijk om te onthouden dat de meeste situaties architecturaal opgelost kunnen en moeten worden. Laten we populaire workaround-oplossingen voor het opslaan van bestanden bespreken die kunnen leiden tot onaangename gevolgen bij het gebruiken van een cluster, en wijzen op een betere aanpak..
Opslaan van statische bestanden
Ter illustratie bekijken we een webapplicatie die een statische generator gebruikt om een set afbeeldingen, stijlen en dergelijke te verkrijgen. In het PHP-framework Yii is er bijvoorbeeld een ingebouwde asset manager die unieke naamgeving voor mappen genereert. Bijgevolg resulteert dit in een set paden voor de statische content van de site die garant staan voor elkaar (dit is gedaan om verschillende redenen, bijvoorbeeld om duplicaten te vermijden wanneer dezelfde bron door meerdere componenten wordt gebruikt). Bij de eerste oproep naar de module van de webresource vindt er dus een vorming en distributie van statische bestanden plaats (in werkelijkheid vaak symlinks, maar daarover later) met een unieke rootmap voor deze deployment:
webroot/assets/2072c2df/css/…
-
webroot/assets/2072c2df/images/… -
webroot/assets/2072c2df/js/… -
Wat zijn de gevolgen hiervan binnen het cluster?
Een eenvoudig voorbeeld
Laten we een vrij veelvoorkomend geval nemen, waarbij nginx voor PHP staat en statische bestanden en eenvoudige verzoeken afhandelt. De eenvoudigste manier is
met twee containers: Deployment met twee containers:
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.confIn eenvoudige termen komt de nginx-configuratie neer op het volgende:
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;
}
} Bij de eerste toegang tot de site verschijnen er assets in de container met PHP. Maar in het geval van twee containers binnen één pod weet nginx niets van deze statische bestanden, die volgens de configuratie juist door hen moeten worden afgegeven. Het resultaat is dat de klant bij alle verzoeken om CSS- en JS-bestanden een fout 404 te zien krijgt. De eenvoudigste oplossing hier is om een gemeenschappelijke map voor de containers te creëren. Een primitieve optie is een gedeelde 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.confNu worden de in de container gegenereerde statische bestanden correct door nginx afgegeven. Maar ik herinner je eraan dat dit een primitieve oplossing is, wat betekent dat het ver van ideaal is en zijn eigen nuances en tekortkomingen heeft, waarover hieronder meer.
Een geavanceerdere opslag
Laten we een situatie voorstellen waarin de gebruiker de website heeft geopend, de pagina met de beschikbare stijlen in de container heeft geladen, en terwijl hij deze pagina leest, hebben we de container opnieuw gedeployed. De assetdirectory is leeg geworden en er is een verzoek naar PHP nodig om nieuwe te genereren. Zelfs na dit zullen de links naar de oude statische bestanden niet actueel zijn, wat leidt tot fouten in de weergave van de statica.
Bovendien hebben we waarschijnlijk een vrij belastingintensief project, wat betekent dat één kopie van de applicatie niet genoeg zal zijn:
- Schaalbaar Deployment tot twee replica's.
- Bij de eerste toegang tot de site zijn er assets aangemaakt in één replica.
- Op een bepaald moment besloot de ingress (voor last balancering) het verzoek naar de tweede replica te sturen, waar deze assets nog niet aanwezig zijn. Of misschien zijn ze daar al niet meer, omdat we gebruikmaken van
RollingUpdateen momenteel een deployment aan het uitvoeren zijn.
Kortom, het resultaat is opnieuw fouten.
Om oude assets niet te verliezen, kunnen we emptyDir en een werkende opdracht krijgen. hostPathde statica fysiek op een cluster-knooppunt opslaan. Deze aanpak is slecht omdat we feitelijk moeten ons aan een specifiek cluster-knooppunt binden met onze applicatie, omdat bij verhuizing naar andere knooppunten de directory niet de benodigde bestanden zal bevatten. Of er is enige achtergrond synchronisatie van de directory tussen knooppunten nodig.
Wat zijn de mogelijke oplossingen?
- Als de hardware en middelen het toestaan, kan men gebruikmaken van om een toegankelijke directory voor de statica te organiseren. raadt SSD's aan, ten minste drievoudige replicatie en een stabiele 'dikke' verbinding tussen de knooppunten van het cluster.
- Een minder veeleisende optie is het organiseren van een NFS-server. Maar dan moet men rekening houden met een mogelijke toename van de responstijd bij de verwerking van verzoeken door de webserver, en de fouttolerantie zal ook te wensen overlaten. De gevolgen van een storing zijn catastrofaal: het verliezen van de mount veroordeelt het cluster tot de ondergang onder de druk van de LA-lasten die de lucht in schieten.
Bovendien is voor alle opties voor het creëren van permanente opslag vereist een achtergrondopruiming van verouderde bestandssets die gedurende een bepaalde tijd zijn opgebouwd. Voor de containers met PHP kan men een DaemonSet van cachende nginx instellen, die kopieën van de assets voor een beperkte tijd zullen opslaan. Dit gedrag is eenvoudig instelbaar met behulp van proxy_cache met een opslagdiepte in dagen of gigabytes schijfruimte.
De combinatie van deze methode met de eerder genoemde gedistribueerde bestandssystemen biedt enorme mogelijkheden, met als enige beperking het budget en de technische mogelijkheden van degenen die het zullen implementeren en onderhouden. Uit ervaring kunnen we zeggen dat hoe eenvoudiger het systeem, hoe stabieler het werkt. Door dergelijke lagen toe te voegen, wordt het onderhouden van de infrastructuur veel gecompliceerder, en daarmee neemt ook de tijd toe die aan diagnose en herstel bij eventuele storingen wordt besteed.
Aanbeveling
Als de implementatie van de voorgestelde opslagopties ook voor u onrechtvaardig lijkt (complex, duur…), dan is het de moeite waard om de situatie vanuit een andere hoek te bekijken. Namelijk — de architectuur van het project verder te onderzoeken en het probleem in de code te verhelpen, door je te verbinden met een bepaalde statische datastructuur in het beeld, een eenduidige definitie van de inhoud of de procedure van "warmpreparing" en/of precompilatie van assets tijdens de bouwfase van het beeld. Hierdoor krijgen we een absoluut voorspelbaar gedrag en een identieke set bestanden voor alle omgevingen en replica's van de draaiende applicatie.
Als we terugkeren naar het specifieke voorbeeld met het Yii-framework en niet dieper ingaan op de structuur ervan (wat niet het doel van het artikel is), is het voldoende om te wijzen op twee populaire benaderingen:
- Het bouwproces van het beeld aanpassen zodat assets op een voorspelbare plaats worden geplaatst. Dit wordt aanbevolen/uitgevoerd in uitbreidingen zoals .
- Specifieke hashes voor assetmappen definiëren, zoals wordt verteld in (vanaf dia nr. 35). Overigens raadt de spreker uiteindelijk (en niet zonder reden!) aan om na de assetbouw op de build-server deze in een centrale opslag (zoals S3) te uploaden, met een CDN ervoor geplaatst.
Te uploaden bestanden
Een andere case die zeker zal opvallen bij het verplaatsen van de applicatie naar een Kubernetes-cluster, is het opslaan van gebruikersbestanden in het bestandssysteem. Bijvoorbeeld, we hebben weer een PHP-applicatie die bestanden accepteert via een uploadformulier, er iets mee doet tijdens de verwerking en deze vervolgens teruggeeft.
De plaats waar deze bestanden zouden moeten worden opgeslagen, moet in de context van Kubernetes gemeenschappelijk zijn voor alle replica's van de applicatie. Afhankelijk van de complexiteit van de applicatie en de noodzaak om deze bestanden persistent te maken, kunnen de hierboven genoemde opties voor shared storage geschikt zijn, maar zoals we zien, hebben ze hun nadelen.
Aanbeveling
Een van de oplossingen is het gebruik van een S3-compatibele opslagoplossing (zelfs een soort zelf-gehostte variant zoals minio). Overstappen op S3 vereist veranderingen op het niveau van de code, en hoe de content wordt verstrekt aan de frontend, hebben we al .
Gebruikerssessies
Het is ook belangrijk om de organisatie van de opslag van gebruikerssessies te vermelden. Vaak zijn dit ook bestanden op schijf, wat in de context van Kubernetes leidt tot constante autorisatieverzoeken van de gebruiker, als zijn verzoek in een ander container terechtkomt.
Deel van het probleem kan worden opgelost door stickySessions in te schakelen (deze functie wordt ondersteund door alle populaire ingress controllers — zie voor meer informatie in ), om de gebruiker aan een specifieke pod van de applicatie te koppelen:
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: /Maar dit zal de problemen bij herhaalde deploys niet oplossen.
Aanbeveling
Een betere aanpak zou zijn om de applicatie te migreren naar sessies opslaan in memcached, Redis en soortgelijke oplossingen — in het algemeen volledig af te zien van bestandgebaseerde opties.
Conclusie
De infrastructuuroplossingen die in de tekst worden besproken, verdienen alleen toepassing als tijdelijke "oplossingen" (wat in het Engels mooier klinkt als workaround). Ze kunnen relevant zijn in de beginfase van de migratie van de applicatie naar Kubernetes, maar mogen geen "wortel schieten".
De algemeen aanbevolen weg is om ze te vermijden ten gunste van architecturale verbeteringen van de applicatie in overeenstemming met de reeds goed gekende . Echter, dit — het omvormen van de applicatie naar een stateless model — betekent onvermijdelijk dat er wijzigingen in de code vereist zijn, en het is belangrijk om een balans te vinden tussen de mogelijkheden/eisen van het bedrijf en de uitvoerbaarheid en het onderhoud van het gekozen pad.
P.S.
Lees ook op onze blog:
- «»;
- «»;
- «» (van Red Hat);
- «».
Bron: habr.com
