Lokale filer ved migrering af en applikation til Kubernetes

Lokale filer ved migrering af en applikation til Kubernetes

Når du bygger en CI/CD-proces ved hjælp af Kubernetes, opstår der nogle gange problemet med inkompatibilitet mellem kravene til den nye infrastruktur og applikationen, der overføres til den. Især på applikationsopbygningsstadiet er det vigtigt at få en billede, der vil blive brugt i Alle projektmiljøer og klynger. Dette princip ligger til grund for det rigtige ifølge Google containerstyring (mere end én gang om dette Han sagde og vores tekniske afdeling).

Du vil dog ikke se nogen i situationer, hvor webstedets kode bruger en færdigfremstillet ramme, hvis brug pålægger begrænsninger for dens videre brug. Og selvom det i et "normalt miljø" er nemt at håndtere, kan denne adfærd i Kubernetes blive et problem, især når du støder på det for første gang. Selvom et opfindsomt sind kan finde på infrastrukturløsninger, der virker indlysende eller endda gode ved første øjekast... er det vigtigt at huske, at de fleste situationer kan og bør løses arkitektonisk.

Lad os se på populære løsninger til lagring af filer, der kan føre til ubehagelige konsekvenser ved drift af en klynge, og også pege på en mere korrekt sti.

Statisk opbevaring

For at illustrere, overvej en webapplikation, der bruger en form for statisk generator til at få et sæt billeder, stilarter og andre ting. For eksempel har Yii PHP-rammeværket en indbygget asset manager, der genererer unikke mappenavne. I overensstemmelse hermed er outputtet et sæt stier til det statiske sted, der åbenbart ikke krydser hinanden (dette blev gjort af flere årsager - for eksempel for at eliminere dubletter, når flere komponenter bruger den samme ressource). Så ud af boksen, første gang du får adgang til et webressourcemodul, bliver statiske filer (faktisk ofte symbolske links, men mere om det senere) dannet og lagt ud med en fælles rodmappe, der er unik for denne udrulning:

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

Hvad betyder det i forhold til en klynge?

Det enkleste eksempel

Lad os tage et ret almindeligt tilfælde, hvor PHP indledes med nginx for at distribuere statiske data og behandle simple anmodninger. Den nemmeste måde - Deployment med to beholdere:

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 forenklet form koger nginx-konfigurationen ned til følgende:

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

Første gang du tilgår webstedet, vises aktiver i PHP-beholderen. Men i tilfælde af to beholdere inden for en pod, ved nginx intet om disse statiske filer, som (ifølge konfigurationen) skal gives til dem. Som et resultat vil klienten se en 404-fejl for alle anmodninger til CSS- og JS-filer. Den enkleste løsning her ville være at organisere en fælles mappe til containere. Primitiv mulighed - generel 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 serveres statiske filer genereret i containeren af ​​nginx korrekt. Men lad mig minde dig om, at dette er en primitiv løsning, hvilket betyder, at den er langt fra ideel og har sine egne nuancer og mangler, som diskuteres nedenfor.

Mere avanceret opbevaring

Forestil dig nu en situation, hvor en bruger besøgte webstedet, indlæste en side med de tilgængelige stilarter i containeren, og mens han læste denne side, geninstallerede vi containeren. Aktivkataloget er blevet tomt, og der kræves en anmodning til PHP for at begynde at generere nye. Men selv efter dette vil links til gammel statik være irrelevant, hvilket vil føre til fejl ved visning af statik.

Derudover har vi højst sandsynligt et mere eller mindre indlæst projekt, hvilket betyder, at en kopi af ansøgningen ikke vil være nok:

  • Lad os skalere det op Deployment op til to replikaer.
  • Da webstedet først blev tilgået, blev der oprettet aktiver i én replika.
  • På et tidspunkt besluttede ingress (af hensyn til belastningsbalancering) at sende en anmodning til den anden replika, og disse aktiver var der ikke endnu. Eller måske er de der ikke længere, fordi vi bruger RollingUpdate og i øjeblikket er vi i gang med udrulning.

Generelt er resultatet igen fejl.

For at undgå at miste gamle aktiver, kan du ændre emptyDirhostPath, tilføje statisk fysisk til en klynge node. Denne tilgang er dårlig, fordi vi faktisk er nødt til det binde til en specifik klynge node din applikation, fordi - i tilfælde af flytning til andre noder - vil biblioteket ikke indeholde de nødvendige filer. Eller en slags baggrundskatalogsynkronisering mellem noder er påkrævet.

Hvad er løsningerne?

  1. Hvis hardware og ressourcer tillader det, kan du bruge cephfs at organisere en lige tilgængelig mappe til statiske behov. Officiel dokumentation anbefaler SSD-drev, mindst tre gange replikering og en stabil "tyk" forbindelse mellem klynge noder.
  2. En mindre krævende mulighed ville være at organisere en NFS-server. Men så skal du tage højde for den mulige stigning i responstid for behandling af anmodninger fra webserveren, og fejltolerance vil lade meget tilbage at ønske. Konsekvenserne af fiasko er katastrofale: Tabet af bjerget dømmer klyngen til døden under trykket fra LA-lasten, der farer ind i himlen.

Blandt andet vil alle muligheder for at skabe persistent lagring kræve baggrundsrensning forældede sæt filer akkumuleret over en vis periode. Foran containere med PHP kan du sætte DaemonSet fra cachelagring af nginx, som vil gemme kopier af aktiver i en begrænset periode. Denne adfærd er let konfigurerbar ved hjælp af proxy_cache med lagerdybde i dage eller gigabyte diskplads.

Kombination af denne metode med de distribuerede filsystemer, der er nævnt ovenfor, giver et enormt felt for fantasi, kun begrænset af budgettet og det tekniske potentiale for dem, der vil implementere og understøtte det. Af erfaring kan vi sige, at jo enklere systemet er, jo mere stabilt virker det. Når sådanne lag tilføjes, bliver det meget sværere at vedligeholde infrastrukturen, og samtidig øges tiden brugt på at diagnosticere og komme sig efter eventuelle fejl.

anbefaling

Hvis implementeringen af ​​de foreslåede opbevaringsmuligheder også virker uberettiget for dig (kompliceret, dyrt ...), så er det værd at se på situationen fra den anden side. Nemlig at grave i projektarkitekturen og løse problemet i koden, bundet til en eller anden statisk datastruktur i billedet, en utvetydig definition af indholdet eller proceduren for "opvarmning" og/eller prækompilering af aktiver på billedsamlingsstadiet. På denne måde får vi absolut forudsigelig adfærd og det samme sæt filer til alle miljøer og replikaer af den kørende applikation.

Hvis vi vender tilbage til det specifikke eksempel med Yii-rammen og ikke dykker ned i dens struktur (hvilket ikke er formålet med artiklen), er det nok at påpege to populære tilgange:

  1. Skift billedopbygningsprocessen for at placere aktiver på en forudsigelig placering. Dette er foreslået/implementeret i udvidelser som f.eks yii2-statiske-aktiver.
  2. Definer specifikke hashes for aktivmapper, som diskuteret i f.eks. denne præsentation (startende fra slide nr. 35). Forresten anbefaler forfatteren af ​​rapporten i sidste ende (og ikke uden grund!) at efter at have samlet aktiver på build-serveren, skal du uploade dem til et centralt lager (som S3), foran hvilket placerer et CDN.

Downloads

En anden sag, der helt sikkert vil komme i spil, når en applikation migreres til en Kubernetes-klynge, er lagring af brugerfiler i filsystemet. For eksempel har vi igen en PHP-applikation, der accepterer filer gennem en upload-formular, gør noget med dem under driften og sender dem tilbage.

I Kubernetes skal placeringen, hvor disse filer skal placeres, være fælles for alle replikaer af applikationen. Afhængigt af kompleksiteten af ​​applikationen og behovet for at organisere persistensen af ​​disse filer, kan de ovennævnte delte enhedsindstillinger være et sådant sted, men som vi ser, har de deres ulemper.

anbefaling

En løsning er ved hjælp af S3-kompatibelt lager (selvom det er en slags selvhostet kategori som minio). Skift til S3 vil kræve ændringer på kodeniveau, og hvordan indhold vil blive leveret på frontend, har vi allerede skrev.

Bruger sessioner

Separat er det værd at bemærke organiseringen af ​​opbevaring af brugersessioner. Ofte er der også tale om filer på disk, hvilket i Kubernetes-sammenhæng vil føre til konstante autorisationsanmodninger fra brugeren, hvis dennes anmodning ender i en anden container.

Problemet er delvist løst ved at tænde stickySessions ved indtrængen (funktionen understøttes i alle populære indgangscontrollere - for flere detaljer, se vores anmeldelse)for at binde brugeren til en bestemt 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 dette vil ikke eliminere problemer med gentagne implementeringer.

anbefaling

En mere korrekt måde ville være at overføre ansøgningen til lagring af sessioner i memcached, Redis og lignende løsninger - generelt helt opgive filindstillinger.

Konklusion

De infrastrukturløsninger, der diskuteres i teksten, er kun værdige at bruge i formatet af midlertidige "krykker" (hvilket lyder smukkere på engelsk som en løsning). De kan være relevante i de første faser af migrering af en applikation til Kubernetes, men bør ikke slå rod.

Den generelle anbefalede vej er at slippe af med dem til fordel for arkitektonisk ændring af applikationen i overensstemmelse med, hvad der allerede er velkendt for mange 12-faktor app. Men dette - at bringe ansøgningen til en statsløs form - betyder uundgåeligt, at der vil være behov for ændringer i koden, og her er det vigtigt at finde en balance mellem forretningens muligheder/krav og mulighederne for implementering og vedligeholdelse af den valgte vej .

PS

Læs også på vores blog:

Kilde: www.habr.com

Tilføj en kommentar