Lokala filer vid flytt av applikationen till Kubernetes

Lokala filer vid flytt av applikationen till Kubernetes

NĂ€r man bygger en CI/CD-process med Kubernetes kan det ibland uppstĂ„ problem med inkompatibiliteten mellan kraven för den nya infrastrukturen och den applikation som flyttas dit. SĂ€rskilt under applikationsbyggnadssteget Ă€r det viktigt att fĂ„ en en bild som kommer att anvĂ€ndas i alla projektets miljöer och kluster. Denna princip ligger till grund för korrekt hantering av containrar (detta har Google pĂ„pekat flera gĂ„nger) och vĂ„r teknikchef). till exempel Men man ser sĂ€llan situationer dĂ€r en fĂ€rdig ramverk anvĂ€nds i webbplatsens kod, vilket bygger upp begrĂ€nsningar för dess framtida drift. Och om man i en "vanlig miljö" lĂ€tt kan hantera detta, kan sĂ„dant beteende i Kubernetes bli ett problem, sĂ€rskilt nĂ€r man stöter pĂ„ det första gĂ„ngen. Även om en uppfinningsrik hjĂ€rna kan föreslĂ„ infrastrukturlösningar som verkar sjĂ€lvklara och till och med bra vid första anblicken
 Ă€r det viktigt att komma ihĂ„g att de flesta situationer kan och bör

lösas arkitektoniskt. Vi ska ta en titt pÄ populÀra workaround-lösningar för lagring av filer, som kan leda till obehagliga konsekvenser vid drift av klustret, och vi kommer Àven att peka pÄ en mer korrekt vÀg..

Lagring av statiska filer

För att illustrera detta, lĂ„t oss titta pĂ„ en webbapplikation som anvĂ€nder en slags statisk generator för att fĂ„ en uppsĂ€ttning bilder, stilar och annat. Till exempel har PHP-ramverket Yii en inbyggd asset-manager som genererar unika namn för katalogerna. Följaktligen fĂ„r vi en uppsĂ€ttning vĂ€gar för webbplatsens statiska filer som inte överlappar varandra (detta görs av flera skĂ€l — till exempel för att undvika dubbletter nĂ€r samma resurs anvĂ€nds av flera komponenter). SĂ„, frĂ„n början, vid första anropet av en modul pĂ„ webbresursen, skapas och lĂ€ggs statiken ut (egentligen ofta symboliska lĂ€nkar, men mer om detta senare) med en unik gemensam rotkatalog för denna distribution:

webroot/assets/2072c2df/css/


  • webroot/assets/2072c2df/images/

  • webroot/assets/2072c2df/js/

  • Vad innebĂ€r detta i relation till klustret?

Ett enkelt exempel

LÄt oss ta ett ganska vanligt fall dÀr nginx stÄr framför PHP för att leverera statiska filer och hantera enkla förfrÄgningar. Det enklaste sÀttet Àr

med tvĂ„ containrar: Deployment с ĐŽĐČŃƒĐŒŃ ĐșĐŸĐœŃ‚Đ”ĐčĐœĐ”Ń€Đ°ĐŒĐž:

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.conf

I en förenklad version sammanfattas nginx-konfigurationen till följande:

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;
        }
    }

Vid det första besöket pÄ webbplatsen i containern med PHP genereras tillgÄngar. Men nÀr det gÀller tvÄ containrar inom samma pod, vet nginx ingenting om dessa statiska filer, som enligt konfigurationen ska levereras av dem. Resultatet blir att klienten ser ett 404-fel för alla förfrÄgningar om CSS- och JS-filer. Den enklaste lösningen hÀr skulle vara att skapa en gemensam katalog för containrarna. En primitiv version Àr gemensam. 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.conf

Nu levereras de genererade statiska filerna i containern korrekt av nginx. Men jag pÄminner om att detta Àr en primitiv lösning, vilket innebÀr att den ligger lÄngt ifrÄn perfekt och har sina nyanser och brister, som diskuteras nedan.

Ett mer avancerat lagringssystem

LÄt oss nu förestÀlla oss en situation dÀr anvÀndaren har gÄtt in pÄ webbplatsen, laddat sidan med de stilar som finns i containern, och medan han lÀste sidan har vi Äterigen distribuerat containern. I katalogen för tillgÄngar har det blivit tomt och en begÀran till PHP krÀvs för att starta genereringen av nya. Men Àven efter detta kommer lÀnkarna till den gamla statiska innehÄllet att vara irrelevanta, vilket kommer att leda till fel vid visning av statiskt innehÄll.

Dessutom har vi troligtvis ett mer eller mindre belastat projekt, vilket innebÀr att en kopia av applikationen inte kommer att vara tillrÀcklig:

  • Vi skalar Deployment upp till tvĂ„ repliker.
  • Vid det första besöket pĂ„ webbplatsen skapades tillgĂ„ngar i en av replikerna.
  • Vid nĂ„got tillfĂ€lle bestĂ€mde ingressen (för att balansera belastningen) att skicka en begĂ€ran till den andra repliken, och dĂ€r finns dessa tillgĂ„ngar inte Ă€nnu. Kanske finns de redan inte dĂ€r, för vi anvĂ€nder RollingUpdate och just nu gör vi en distribution.

I allmĂ€nhet, slutsatsen — Ă„terigen fel.

För att inte förlora gamla tillgĂ„ngar kan vi Ă€ndra emptyDir till hostPath, lĂ€ggande statiskt innehĂ„ll fysiskt pĂ„ klusterknuten. Denna metod Ă€r dĂ„lig eftersom vi praktiskt taget mĂ„ste binda vĂ„rt program till en specifik klusterknut eftersom — om vi flyttar till andra knutar — kommer katalogen inte att innehĂ„lla nödvĂ€ndiga filer. Alternativt krĂ€vs det nĂ„gon form av bakgrundssynkronisering av katalogen mellan knutarna.

Vilka lösningar finns det?

  1. Om hÄrdvaran och resurserna tillÄter, kan vi anvÀnda cephfs för att organisera en tillgÀnglighetsdirektory för statiskt innehÄll. Officiell dokumentation rekommenderar SSD-diskar, minst trippelreplikering och en stabil "tjock" anslutning mellan klusterknutar.
  2. Ett mindre krÀvande alternativ skulle vara att organisera en NFS-server. Men dÄ mÄste man ta hÀnsyn till den möjliga ökningen av svarstiden för begÀrningar frÄn webbservern, och Àven redundansen kommer att vara bristande. Konsekvenserna av en felaktig operation Àr katastrofala: förlust av mount gör klustret dömt att falla under trycket av LA-belastningen som skjuter i höjden.

Utöver allt detta kommer för alla alternativ av att skapa permanenta lagringslösningar att krÀvas bakgrundsrensning av förÄldrade filuppsÀttningar som samlats under en viss tidsperiod. Framför containrar med PHP kan man stÀlla in DaemonSet cacheande nginx, som kommer att lagra kopior av tillgÄngarna under en begrÀnsad tid. Detta beteende kan enkelt konfigureras med hjÀlp av proxy_cache med lagringsdjup i dagar eller gigabyte diskutrymme.

Kombineringen av denna metod med de ovan nÀmnda distribuerade filsystemen erbjuder enorma möjligheter, dÀr begrÀnsningen endast Àr budgeten och den tekniska kapaciteten hos dem som ska implementera och underhÄlla det. UtifrÄn erfarenhet kan vi sÀga att ju enklare systemet Àr, desto stabilare fungerar det. NÀr man lÀgger till sÄdana lager blir det betydligt svÄrare att underhÄlla infrastrukturen, och dÀrmed ökar ocksÄ tiden som spenderas pÄ felsökning och ÄterstÀllning vid eventuella fel.

Rekommendation

Om implementeringen av de föreslagna lagringsalternativen ocksÄ verkar orimlig (komplicerad, dyr...), bör man se pÄ situationen frÄn en annan synvinkel. NÀmligen att grÀva djupt i projektets arkitektur och utrota problemet i koden, knutna till nÄgon statisk databasstruktur i bilden, entydig identifiering av innehÄllet eller proceduren för "uppvÀrmning" och/eller förkompilering av tillgÄngar i byggprocessen. PÄ sÄ sÀtt fÄr vi helt förutsÀgbart beteende och samma uppsÀttning filer för alla miljöer och repliker av den körda applikationen.

Om vi ÄtergÄr till det specifika exemplet med ramverket Yii och inte fördjupar oss i dess struktur (vilket inte Àr syftet med artikeln), rÀcker det med att pÄpeka tvÄ populÀra tillvÀgagÄngssÀtt:

  1. Ändra byggprocessen för bilden sĂ„ att tillgĂ„ngar placeras pĂ„ en förutsĂ€gbar plats. SĂ„dana förslag/implementeringar finns i tillĂ€gg som yii2-static-assets.
  2. Definiera specifika hash-vÀrden för tillgÄngsmappar, som det berÀttas om till exempel i denna presentation. (frÄn och med bild nummer 35). För övrigt rekommenderar föredragshÄllaren i slutÀndan (och inte utan skÀl!) att efter byggandet av tillgÄngarna pÄ byggservern ladda upp dem till ett centralt lagringsutrymme (som S3), framför vilket man ska sÀtta upp en CDN.

Uppladdade filer

Ett annat fall som garanterat kommer att intrÀffa vid flytt av applikationen till ett Kubernetes-kluster Àr lagring av anvÀndarfiler i filsystemet. Till exempel har vi Äterigen en PHP-applikation som tar emot filer via ett inmatningsformulÀr, gör nÄgot med dem under drift och ger dem tillbaka.

Platsen dÀr dessa filer ska placeras, i verkligheten av Kubernetes, bör vara gemensam för alla instanser av applikationen. Beroende pÄ applikationens komplexitet och behovet av att organisera bestÀndighet för dessa filer, kan sÄdana platser vara de nÀmnda tidigare delade enheterna, men som vi ser har de sina nackdelar.

Rekommendation

Ett av alternativen till lösningar Ă€r att anvĂ€nda S3-kompatibel lagring (Ă€ven nĂ„gon variant av sjĂ€lvhostade kategorin som minio). ÖvergĂ„ngen till att arbeta med S3 kommer att krĂ€va förĂ€ndringar pĂ„ kodnivĂ„, och hur innehĂ„llet kommer att levereras pĂ„ frontend har vi redan om.

AnvÀndarsessioner

Det Àr vÀrt att sÀrskilt notera hur anvÀndarsessioner lagras. Ofta handlar det ocksÄ om filer pÄ disk, vilket i Kubernetes leder till stÀndiga autentiseringsförfrÄgningar frÄn anvÀndaren om deras begÀran hamnar i en annan container.

Delvis löses problemet genom att aktivera stickySessions pĂ„ ingress (denna funktion stöds av alla populĂ€ra ingresskontroller — mer info kan ses i vĂ„r recension), för att binda anvĂ€ndaren till en specifik pod med applikationen:

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: /

Men det kommer inte att avhjÀlpa problem vid upprepade distributioner.

Rekommendation

Ett mer korrekt sĂ€tt skulle vara att överföra applikationen till lagring av sessioner i memcached, Redis och liknande lösningar — i allmĂ€nhet att helt överge filalternativen.

Slutsats

De infrastrukturella lösningar som diskuteras i texten Àr endast vÀrda att tillÀmpa som temporÀra "snabblösningar" (vilket lÄter mer elegant pÄ engelska som workaround). De kan vara aktuella i de inledande stegen av applikationens migration till Kubernetes, men bör inte "rota sig".

Den allmĂ€nt rekommenderade vĂ€gen Ă€r att bli av med dem till förmĂ„n för en arkitekturförbĂ€ttring av applikationen i enlighet med det som redan Ă€r vĂ€lkĂ€nt av mĂ„nga 12-Factor App. Det Ă€r dock — att omvandla applikationen till ett stateless-format — oundvikligen betyder att förĂ€ndringar i koden kommer att krĂ€vas, och hĂ€r Ă€r det viktigt att finna en balans mellan affĂ€rens möjligheter/krav och perspektiven för implementering och underhĂ„ll av den valda vĂ€gen.

P.S.

LÀs Àven i vÄr blogg:

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster