Lokala filer när du migrerar ett program till Kubernetes

Lokala filer när du migrerar ett program till Kubernetes

När man bygger en CI/CD-process med Kubernetes, uppstår ibland problemet med inkompatibilitet mellan kraven för den nya infrastrukturen och applikationen som överförs till den. I synnerhet i applikationsbyggandet är det viktigt att få en bild som kommer att användas i Alla projektmiljöer och kluster. Denna princip ligger till grund för det korrekta enligt Google containerhantering (mer än en gång om detta han sade och vår tekniska avdelning).

Du kommer dock inte att se någon i situationer där webbplatsens kod använder ett färdigt ramverk, vars användning medför begränsningar för dess vidare användning. Och även om det i en "normal miljö" är lätt att hantera, kan detta beteende i Kubernetes bli ett problem, speciellt när du stöter på det för första gången. Även om ett uppfinningsrikt sinne kan komma på infrastrukturlösningar som verkar självklara eller 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.

Låt oss titta på populära lösningar för lagring av filer som kan leda till obehagliga konsekvenser vid drift av ett kluster, och även peka ut en mer korrekt väg.

Statisk lagring

För att illustrera, överväg en webbapplikation som använder någon form av statisk generator för att få en uppsättning bilder, stilar och andra saker. Till exempel har Yii PHP-ramverket en inbyggd tillgångshanterare som genererar unika katalognamn. Följaktligen är utdata en uppsättning vägar för den statiska platsen som uppenbarligen inte skär varandra (detta gjordes av flera skäl - till exempel för att eliminera dubbletter när flera komponenter använder samma resurs). Så, ur lådan, första gången du kommer åt en webbresursmodul, bildas statiska filer (i själva verket ofta symboliska länkar, men mer om det senare) och läggs ut med en gemensam rotkatalog som är unik för denna distribution:

  • webroot/assets/2072c2df/css/…
  • webroot/assets/2072c2df/images/…
  • webroot/assets/2072c2df/js/…

Vad betyder detta i termer av ett kluster?

Det enklaste exemplet

Låt oss ta ett ganska vanligt fall, när PHP föregås av nginx för att distribuera statisk data och bearbeta enkla förfrågningar. Det enklaste sättet - konfiguration med två behållare:

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 form kokar nginx-konfigurationen ner 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;
        }
    }

När du först går in på webbplatsen visas tillgångar i PHP-behållaren. Men i fallet med två behållare inom en pod, vet nginx ingenting om dessa statiska filer, som (enligt konfigurationen) bör ges till dem. Som ett resultat kommer klienten att se ett 404-fel för alla förfrågningar till CSS- och JS-filer. Den enklaste lösningen här skulle vara att organisera en gemensam katalog för behållare. Primitivt alternativ - allmänt 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 betjänas statiska filer som genereras i behållaren av nginx korrekt. Men låt mig påminna dig om att detta är en primitiv lösning, vilket betyder att den är långt ifrån idealisk och har sina egna nyanser och brister, som diskuteras nedan.

Mer avancerad lagring

Föreställ dig nu en situation där en användare besökte webbplatsen, laddade en sida med de stilar som finns tillgängliga i behållaren, och medan han läste den här sidan, distribuerade vi om behållaren. Tillgångskatalogen har blivit tom och en begäran till PHP krävs för att börja generera nya. Men även efter detta kommer länkar till gammal statik att vara irrelevant, vilket kommer att leda till fel vid visning av statik.

Dessutom har vi med största sannolikhet ett mer eller mindre laddat projekt, vilket gör att det inte räcker med en kopia av ansökan:

  • Låt oss skala upp det konfiguration upp till två kopior.
  • När webbplatsen först öppnades skapades tillgångar i en replik.
  • Vid något tillfälle beslutade ingress (i lastbalanseringssyfte) att skicka en begäran till den andra repliken, och dessa tillgångar var inte där ännu. Eller så finns de kanske inte längre för att vi använder RollingUpdate och just nu håller vi på med utplacering.

I allmänhet är resultatet återigen misstag.

För att undvika att förlora gamla tillgångar kan du byta emptyDirhostPath, lägga till statisk fysiskt till en klusternod. Detta tillvägagångssätt är dåligt eftersom vi faktiskt måste binda till en specifik klusternod din applikation, eftersom - i händelse av att flytta till andra noder - katalogen inte kommer att innehålla de nödvändiga filerna. Eller så krävs någon form av bakgrundskatalogsynkronisering mellan noder.

Vilka är lösningarna?

  1. Om hårdvara och resurser tillåter kan du använda cephfs att organisera en lika tillgänglig katalog för statiska behov. Officiell dokumentation rekommenderar SSD-enheter, minst trefaldig replikering och en stabil "tjock" anslutning mellan klusternoder.
  2. Ett mindre krävande alternativ skulle vara att organisera en NFS-server. Men då måste du ta hänsyn till den möjliga ökningen av svarstid för bearbetning av förfrågningar från webbservern, och feltolerans kommer att lämna mycket övrigt att önska. Konsekvenserna av misslyckanden är katastrofala: förlusten av berget dömer klustret till döds under trycket från LA-lasten som rusar upp i himlen.

Bland annat kommer alla alternativ för att skapa beständig lagring att kräva bakgrundsrengöring föråldrade uppsättningar filer som samlats under en viss tidsperiod. Framför containrar med PHP kan du lägga DaemonSet från cachning av nginx, som kommer att lagra kopior av tillgångar under en begränsad tid. Detta beteende är lätt att konfigurera med proxy_cache med lagringsdjup i dagar eller gigabyte diskutrymme.

Att kombinera denna metod med de distribuerade filsystemen som nämns ovan ger ett enormt fält för fantasi, begränsat endast av budgeten och den tekniska potentialen för dem som kommer att implementera och stödja det. Av erfarenhet kan vi säga att ju enklare systemet är, desto stabilare fungerar det. När sådana lager läggs till blir det mycket svårare att underhålla infrastrukturen och samtidigt ökar tiden som läggs på att diagnostisera och återhämta sig från eventuella fel.

rekommendation

Om implementeringen av de föreslagna lagringsalternativen också verkar orättfärdig för dig (komplicerad, dyr ...), är det värt att titta på situationen från andra sidan. Nämligen att gräva i projektarkitekturen och fixa problemet i koden, knuten till någon statisk datastruktur i bilden, en entydig definition av innehållet eller proceduren för att "värma upp" och/eller förkompilera tillgångar vid bildmonteringsstadiet. På så sätt får vi ett absolut förutsägbart beteende och samma uppsättning filer för alla miljöer och repliker av det program som körs.

Om vi ​​återvänder till det specifika exemplet med Yii-ramverket och inte fördjupar oss i dess struktur (vilket inte är syftet med artikeln), räcker det med att peka ut två populära tillvägagångssätt:

  1. Ändra bildbyggeprocessen för att placera tillgångar på en förutsägbar plats. Detta föreslås/implementeras i tillägg som yii2-statiska-tillgångar.
  2. Definiera specifika hash för tillgångskataloger, som diskuteras i t.ex. denna presentation (med början från bild nr 35). Förresten, författaren till rapporten rekommenderar slutligen (och inte utan anledning!) att efter att ha monterat tillgångar på byggservern, ladda upp dem till en central lagring (som S3), framför vilken placera ett CDN.

Nedladdningar

Ett annat fall som definitivt kommer att spela in när man migrerar en applikation till ett Kubernetes-kluster är att lagra användarfiler i filsystemet. Till exempel har vi återigen en PHP-applikation som accepterar filer via ett uppladdningsformulär, gör något med dem under drift och skickar tillbaka dem.

I Kubernetes bör platsen där dessa filer ska placeras vara gemensam för alla repliker av programmet. Beroende på applikationens komplexitet och behovet av att organisera dessa filers beständighet kan de ovannämnda delade enhetsalternativen vara en sådan plats, men som vi ser har de sina nackdelar.

rekommendation

En lösning är använder S3-kompatibel lagring (även om det är någon typ av självvärd kategori som minio). Att byta till S3 kräver ändringar på kodnivå, och hur innehåll kommer att levereras på användargränssnittet har vi redan писали.

Användarsessioner

Separat är det värt att notera organisationen av lagring av användarsessioner. Ofta är det även filer på disk, vilket i Kubernetes-sammanhang kommer att leda till ständiga auktoriseringsförfrågningar från användaren om dennes förfrågan hamnar i en annan container.

Problemet löses delvis genom att slå på stickySessions vid inträde (funktionen stöds i alla populära ingångskontroller - för mer information, se 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 detta kommer inte att eliminera problem med upprepade distributioner.

rekommendation

Ett mer korrekt sätt vore att överföra ansökan till lagra sessioner i memcached, Redis och liknande lösningar - i allmänhet överge filalternativ helt.

Slutsats

Infrastrukturlösningarna som diskuteras i texten är värda att använda endast i formatet av tillfälliga "kryckor" (vilket låter vackrare på engelska som en lösning). De kan vara relevanta i de första stegen av att migrera en applikation till Kubernetes, men bör inte slå rot.

Den allmänna rekommenderade vägen är att bli av med dem till förmån för arkitektonisk modifiering av applikationen i enlighet med vad som redan är välkänt för många 12-faktor app. Men detta - att föra ansökan till en statslös form - innebär oundvikligen att ändringar i koden kommer att krävas, och här är det viktigt att hitta en balans mellan verksamhetens förmågor/krav och möjligheterna att implementera och underhålla den valda vägen .

PS

Läs även på vår blogg:

Källa: will.com

Lägg en kommentar