Lokale filer ved migrering av en applikasjon til Kubernetes

Lokale filer ved migrering av en applikasjon til Kubernetes

Når du bygger en CI/CD-prosess med Kubernetes, er det noen ganger et problem med inkompatibilitet mellom kravene til den nye infrastrukturen og applikasjonen som er overført til den. Spesielt på stadiet av å bygge en søknad er det viktig å få tak i en bilde som skal brukes i Alle prosjektmiljøer og klynger. Dette prinsippet ligger til grunn for det riktige ifølge Google containerhåndtering (mer enn én gang om dette snakket og vår tekniske direktør).

Du vil imidlertid ikke se noen i situasjoner der det brukes et ferdig rammeverk i nettstedskoden, hvis bruk pålegger begrensninger for den videre driften. Og selv om dette er lett å håndtere i et "normalt miljø", kan denne oppførselen i Kubernetes bli et problem, spesielt når du møter det for første gang. Selv om et oppfinnsomt sinn er i stand til å komme opp med infrastrukturløsninger som virker opplagte og til og med gode ved første øyekast ... er det viktig å huske at de fleste situasjoner kan og bør avgjøres arkitektonisk.

Vi vil analysere populære løsninger for lagring av filer som kan føre til ubehagelige konsekvenser under driften av klyngen, og også peke på en mer korrekt måte.

Lagring av statikk

For å illustrere, vurder en nettapplikasjon som bruker en slags statisk generator for å få et sett med bilder, stiler og mer. For eksempel har Yii PHP-rammeverket en innebygd aktivabehandling som genererer unike katalognavn. Følgelig er utdata et sett med åpenbart ikke-kryssende stier for stedstatikk (dette ble gjort av flere grunner - for eksempel for å eliminere duplikater når du bruker den samme ressursen av mange komponenter). Så ut av boksen, når du først får tilgang til nettressursmodulen, dannes og utfoldes statikk (faktisk ofte symbolkoblinger, men mer om det senere) med en felles rotkatalog som er unik for denne distribusjonen:

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

Hva betyr dette for en klynge?

Det enkleste eksempelet

La oss ta en ganske vanlig sak, når PHP innledes med nginx for å distribuere statiske og behandle enkle forespørsler. Den enkleste måten - Utplassering 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 koker nginx-konfigurasjonen 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;
        }
    }

Når du først går inn på nettstedet, vises aktiva i containeren med PHP. Men i tilfelle av to beholdere i en pod, vet nginx ingenting om disse statiske filene, som (i henhold til konfigurasjonen) skal gis til dem. Som et resultat vil klienten se en 404-feil for alle forespørsler til CSS- og JS-filer. Den enkleste løsningen her er å organisere en felles katalog for containere. Primitivt alternativ - vanlig 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

Nå returneres de statiske filene som er generert i beholderen av nginx på riktig måte. Men la meg minne deg på at dette er en primitiv løsning, noe som betyr at den er langt fra ideell og har sine egne nyanser og mangler, som diskuteres nedenfor.

Mer avansert lagring

La oss nå forestille oss en situasjon da en bruker gikk til nettstedet, lastet inn en side med stilene som er tilgjengelige i beholderen, og mens han leste denne siden, distribuerte vi beholderen på nytt. Eiendelskatalogen har blitt tom og det kreves en forespørsel til PHP for å begynne å generere nye. Men selv etter å ha gjort dette, vil referanser til den gamle statikken være utdatert, noe som resulterer i statiske visningsfeil.

I tillegg har vi mest sannsynlig et mer eller mindre lastet prosjekt, noe som betyr at en kopi av søknaden ikke vil være nok:

  • La oss skalere Utplassering opptil to kopier.
  • Ved den første tilgangen til nettstedet ble det opprettet eiendeler i én kopi.
  • På et tidspunkt bestemte ingress (for lastbalansering) å sende en forespørsel til den andre kopien, og disse eiendelene er ikke der ennå. Eller kanskje de ikke er der lenger, fordi vi bruker RollingUpdate og for øyeblikket gjør vi en distribusjon.

Generelt er resultatet igjen feil.

For ikke å miste gamle eiendeler, kan du endre emptyDirhostPath, legger statikken fysisk til klyngenoden. Denne tilnærmingen er dårlig ved at vi faktisk må binde til en bestemt klyngennode applikasjonen din, fordi - ved flytting til andre noder - vil katalogen ikke inneholde de nødvendige filene. Eller en slags bakgrunnssynkronisering av katalogen mellom noder er nødvendig.

Hva er løsningene?

  1. Hvis jern og ressurser tillater det, kan du bruke cephfs å organisere en like tilgjengelig katalog for behovene til statikk. Offisiell dokumentasjon anbefaler SSD-er, minst XNUMXx replikering og en stabil tykk forbindelse mellom klyngenoder.
  2. Et mindre krevende alternativ ville være å organisere en NFS-server. Men da må du ta hensyn til den mulige økningen i responstiden for behandling av forespørsler fra webserveren, og feiltoleranse vil la mye å være ønsket. Konsekvensene av en fiasko er katastrofale: Tapet av et fjell dømmer klyngen til døden under angrepet fra en LA-last som suser inn i himmelen.

Blant annet vil alle alternativer for å lage vedvarende lagring kreve bakgrunnsrengjøring foreldede sett med filer akkumulert over en viss tidsperiode. PHP-beholdere kan innledes med DaemonSet fra cache nginx, som vil lagre kopier av eiendeler i en begrenset periode. Denne oppførselen kan enkelt konfigureres med proxy_cache med lagringsdybde i dager eller gigabyte diskplass.

Å kombinere denne metoden med de distribuerte filsystemene nevnt ovenfor gir et stort felt for fantasi, den eneste grensen er budsjettet og det tekniske potensialet til de som skal implementere og vedlikeholde det. Av erfaring sier vi at jo enklere systemet er, jo mer stabilt fungerer det. Med tillegg av slike lag blir det mye vanskeligere å vedlikeholde infrastrukturen, og samtidig øker også tiden brukt på diagnostikk og utvinning fra eventuelle feil.

anbefaling

Hvis implementeringen av de foreslåtte lagringsalternativene også virker uberettiget for deg (vanskelig, dyr ...), bør du se på situasjonen fra den andre siden. Nemlig å grave i arkitekturen i prosjektet og fikse problemet i koden, ved å binde til en eller annen statisk datastruktur i bildet, en entydig definisjon av innholdet eller prosedyren for "oppvarming" og/eller forhåndskompilering av eiendeler på stadiet for å bygge bildet. På denne måten får vi absolutt forutsigbar oppførsel og samme sett med filer for alle miljøer og replikaer av en applikasjon som kjører.

Hvis vi går tilbake til et spesifikt eksempel med Yii-rammeverket og ikke fordyper oss i strukturen (som ikke er hensikten med artikkelen), er det nok å peke på to populære tilnærminger:

  1. Endre bildebyggingsprosessen for å plassere eiendeler på en forutsigbar plassering. Så de tilbyr / implementerer i utvidelser som yii2-static-assets.
  2. Definer spesifikke hasher for aktivakataloger, som forklart i f.eks. denne presentasjonen (starter fra lysbilde #35). Forresten, forfatteren av rapporten til slutt (og ikke uten grunn!) råder, etter å ha satt sammen eiendeler på byggeserveren, å laste dem opp til en sentral lagring (som S3), før som setter en CDN.

Nedlastinger

En annen sak som garantert vil utløses ved migrering av en applikasjon til en Kubernetes-klynge, er lagring av brukerfiler i filsystemet. For eksempel har vi igjen en PHP-applikasjon som mottar filer gjennom opplastingsskjemaet, gjør noe med dem under arbeidet og gir dem tilbake.

Stedet hvor disse filene skal plasseres i virkeligheten til Kubernetes bør være felles for alle replikaer av applikasjonen. Avhengig av kompleksiteten til applikasjonen og behovet for å organisere utholdenheten til disse filene, kan et slikt sted være de delte enhetene nevnt ovenfor, men som vi kan se, har de sine ulemper.

anbefaling

En av løsningene er bruker S3-kompatibel lagring (selv om det er en slags selvvertskategori som minio). Bytte til S3 vil kreve endringer på kodenivå, og hvordan innholdet vil bli returnert på frontend, har vi allerede skrev.

Brukerøkter

Separat er det verdt å merke seg organiseringen av lagring av brukerøkter. Ofte er dette også filer på disk, noe som i Kubernetes-sammenheng vil føre til konstante autorisasjonsforespørsler fra brukeren dersom forespørselen hans faller inn i en annen container.

En del av problemet løses ved å inkludere stickySessions ved inntrenging (funksjonen støttes i alle populære ingress-kontrollere - se detaljer i vår anmeldelse)for å binde brukeren til en spesifikk programpod:

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 bli kvitt problemer med gjentatte utplasseringer.

anbefaling

En bedre måte ville være å oversette søknaden til lagring av økter i memcached, Redis og lignende løsninger - generelt, forlate filalternativene fullstendig.

Konklusjon

Infrastrukturløsningene som er omtalt i teksten er verdige å bruke bare i formatet av midlertidige "krykker" (som høres vakrere ut på engelsk som en løsning). De kan være aktuelle i de tidlige stadiene av migrering av en applikasjon til Kubernetes, men bør ikke slå rot.

Den generelle anbefalte måten er å kvitte seg med dem til fordel for arkitektonisk forfining av applikasjonen i samsvar med det allerede velkjente 12 Faktor App. Men dette - å bringe søknaden til en statsløs form - betyr uunngåelig at endringer i koden vil være påkrevd, og her er det viktig å finne en balanse mellom virksomhetens evner/krav og utsiktene for implementering og vedlikehold av den valgte veien .

PS

Les også på bloggen vår:

Kilde: www.habr.com

Legg til en kommentar