Lokalne datoteke pri selitvi aplikacije v Kubernetes

Lokalne datoteke pri selitvi aplikacije v Kubernetes

Pri gradnji procesa CI/CD z uporabo Kubernetesa se včasih pojavi problem nezdružljivosti med zahtevami nove infrastrukture in aplikacije, ki se vanjo prenaša. Zlasti na stopnji izdelave aplikacije je pomembno pridobiti 1 slika, ki bo uporabljena v Vse projektna okolja in grozdi. To načelo je osnova za pravilno glede na Google upravljanje kontejnerjev (več kot enkrat o tem govoril in naš tehnični oddelek).

Vendar pa ne boste videli nikogar v situacijah, ko koda spletnega mesta uporablja že pripravljen okvir, katerega uporaba nalaga omejitve za njegovo nadaljnjo uporabo. In medtem ko je v »normalnem okolju« to enostavno rešiti, lahko v Kubernetesu to vedenje postane problem, še posebej, ko se z njim srečate prvič. Medtem ko lahko iznajdljiv um pripravi infrastrukturne rešitve, ki se na prvi pogled zdijo očitne ali celo dobre ... je pomembno vedeti, da večina situacij lahko in mora rešiti arhitekturno.

Oglejmo si priljubljene rešitve za shranjevanje datotek, ki lahko povzročijo neprijetne posledice pri delovanju gruče, in pokažimo tudi pravilnejšo pot.

Statično shranjevanje

Za ponazoritev razmislite o spletni aplikaciji, ki uporablja nekakšen statični generator za pridobivanje niza slik, slogov in drugih stvari. Na primer, ogrodje Yii PHP ima vgrajen upravitelj sredstev, ki ustvarja edinstvena imena imenikov. V skladu s tem je izhod nabor poti za statično spletno mesto, ki se očitno ne sekajo med seboj (to je bilo storjeno iz več razlogov – na primer za odpravo dvojnikov, ko več komponent uporablja isti vir). Torej, takoj, ko prvič dostopate do modula spletnega vira, se statične datoteke (pravzaprav pogosto simbolne povezave, a več o tem kasneje) oblikujejo in postavijo v skupni korenski imenik, edinstven za to uvedbo:

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

Kaj to pomeni v smislu grozda?

Najenostavnejši primer

Vzemimo dokaj pogost primer, ko je pred PHP-jem nginx za distribucijo statičnih podatkov in obdelavo preprostih zahtev. Najlažji način - Deployment z dvema posodama:

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

V poenostavljeni obliki se konfiguracija nginx skrči na naslednje:

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

Ko prvič dostopate do spletnega mesta, se sredstva prikažejo v vsebniku PHP. Toda v primeru dveh vsebnikov v enem podu nginx ne ve ničesar o teh statičnih datotekah, ki bi jim (glede na konfiguracijo) morale biti dane. Posledično bo odjemalec videl napako 404 za vse zahteve za datoteke CSS in JS.Najenostavnejša rešitev bi bila organizirati skupni imenik za vsebnike. Primitivna možnost - splošno 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

Zdaj nginx pravilno streže statične datoteke, ustvarjene v vsebniku. Naj vas spomnim, da je to primitivna rešitev, kar pomeni, da je daleč od idealne in ima svoje nianse in pomanjkljivosti, o katerih bomo razpravljali spodaj.

Naprednejši prostor za shranjevanje

Zdaj pa si predstavljajte situacijo, ko je uporabnik obiskal spletno mesto, naložil stran s slogi, ki so na voljo v vsebniku, in medtem, ko je bral to stran, smo ponovno razmestili vsebnik. Katalog sredstev je postal prazen in za začetek ustvarjanja novih je potrebna zahteva PHP. Vendar tudi po tem bodo povezave na staro statiko nepomembne, kar bo povzročilo napake pri prikazu statike.

Poleg tega imamo najverjetneje bolj ali manj naložen projekt, kar pomeni, da ena kopija aplikacije ne bo dovolj:

  • Povečajmo ga Deployment do dve repliki.
  • Ko je bilo spletno mesto prvič dostopano, so bila sredstva ustvarjena v eni repliki.
  • Na neki točki se je ingress odločil (za namene uravnoteženja obremenitve), da pošlje zahtevo drugi repliki, teh sredstev pa še ni bilo. Ali pa jih morda ni več, ker jih uporabljamo RollingUpdate in trenutno izvajamo uvajanje.

Na splošno so rezultat spet napake.

Da bi se izognili izgubi starih sredstev, lahko spremenite emptyDir o hostPath, ki fizično doda statiko vozlišču gruče. Ta pristop je slab, ker dejansko moramo se veže na določeno vozlišče gruče vašo aplikacijo, ker - v primeru premikanja na druga vozlišča - imenik ne bo vseboval potrebnih datotek. Ali pa je potrebna nekakšna sinhronizacija imenika v ozadju med vozlišči.

Kakšne so rešitve?

  1. Če strojna oprema in viri to dopuščajo, lahko uporabite cephfs organizirati enako dostopen imenik za statične potrebe. Uradna dokumentacija priporoča pogone SSD, vsaj trikratno podvajanje in stabilno »debelo« povezavo med vozlišči gruče.
  2. Manj zahtevna možnost bi bila organizacija strežnika NFS. Vendar pa morate upoštevati morebitno podaljšanje odzivnega časa za obdelavo zahtev s strani spletnega strežnika, toleranca napak pa bo pustila veliko želenega. Posledice okvare so katastrofalne: izguba nosilca grozd obsodi na smrt pod pritiskom tovora LA, ki drvi v nebo.

Med drugim bodo potrebne vse možnosti za ustvarjanje trajnega pomnilnika čiščenje ozadja zastareli nizi datotek, ki so se nabrali v določenem časovnem obdobju. Pred vsebnike s PHP lahko postavite DaemonSet iz predpomnjenja nginx, ki bo shranjeval kopije sredstev za omejen čas. To vedenje je enostavno konfigurirati z uporabo proxy_cache z globino shranjevanja v dnevih ali gigabajtih prostora na disku.

Kombinacija te metode z zgoraj omenjenimi porazdeljenimi datotečnimi sistemi ponuja ogromno prostora za domišljijo, omejeno le s proračunom in tehničnim potencialom tistih, ki jo bodo izvajali in podpirali. Iz izkušenj lahko rečemo, da bolj kot je sistem preprost, bolj stabilno deluje. Ko se takšni sloji dodajo, postane veliko težje vzdrževati infrastrukturo, hkrati pa se poveča čas, porabljen za diagnosticiranje in okrevanje po morebitnih okvarah.

Priporočilo

Če se vam tudi izvedba predlaganih možnosti shranjevanja zdi neupravičena (zapletena, draga ...), potem je vredno pogledati situacijo z druge strani. Namreč poglobiti se v arhitekturo projekta in odpravite težavo v kodi, vezano na neko statično podatkovno strukturo na sliki, nedvoumno opredelitev vsebine ali postopka za "ogrevanje" in/ali predhodno prevajanje sredstev v fazi sestavljanja slike. Tako dobimo popolnoma predvidljivo vedenje in enak nabor datotek za vsa okolja in replike delujoče aplikacije.

Če se vrnemo k konkretnemu primeru z ogrodjem Yii in se ne poglobimo v njegovo strukturo (kar ni namen članka), je dovolj, da izpostavimo dva priljubljena pristopa:

  1. Spremenite postopek gradnje slike, da postavite sredstva na predvidljivo lokacijo. To je predlagano/izvedeno v razširitvah, kot je yii2-statična-sredstva.
  2. Določite posebne zgoščene vrednosti za imenike sredstev, kot je razloženo npr. to predstavitev (začenši s prosojnico št. 35). Mimogrede, avtor poročila na koncu (in ne brez razloga!) svetuje, da sredstva po sestavljanju na gradbenem strežniku naložite v centralno shrambo (kot je S3), pred katero postavite CDN.

Prenosi

Drug primer, ki bo zagotovo prišel v poštev pri selitvi aplikacije v gručo Kubernetes, je shranjevanje uporabniških datotek v datotečni sistem. Na primer, spet imamo aplikacijo PHP, ki sprejema datoteke prek obrazca za nalaganje, nekaj naredi z njimi med delovanjem in jih pošlje nazaj.

V Kubernetesu mora biti lokacija, kamor naj bodo te datoteke nameščene, skupna vsem replikam aplikacije. Odvisno od kompleksnosti aplikacije in potrebe po organizaciji obstojnosti teh datotek so lahko zgoraj omenjene možnosti skupne naprave tako mesto, vendar imajo, kot vidimo, svoje pomanjkljivosti.

Priporočilo

Ena rešitev je z uporabo pomnilnika, združljivega s S3 (tudi če gre za nekakšno samostojno gostujočo kategorijo, kot je minio). Prehod na S3 bo zahteval spremembe na ravni kode, in kako bo vsebina dostavljena na sprednji strani, smo že izvedeli писали.

Uporabniške seje

Ločeno je treba omeniti organizacijo shranjevanja uporabniških sej. Pogosto so to tudi datoteke na disku, kar bo v kontekstu Kubernetesa povzročilo nenehne avtorizacijske zahteve uporabnika, če njegova zahteva konča v drugem vsebniku.

Težavo delno rešimo z vklopom stickySessions ob vstopu (funkcija je podprta v vseh priljubljenih vstopnih krmilnikih - za več podrobnosti glejte naš pregled)da povežete uporabnika z aplikacijo na določen pod:

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

Vendar to ne bo odpravilo težav s ponavljajočimi se uvajanji.

Priporočilo

Bolj pravilen način bi bil prenos aplikacije na shranjevanje sej v memcached, Redis in podobne rešitve - na splošno popolnoma opustite možnosti datotek.

Zaključek

Infrastrukturne rešitve, obravnavane v besedilu, so vredne uporabe le v obliki začasnih »bergel« (kar se v angleščini lepše sliši kot workaround). Morda so pomembni v prvih fazah selitve aplikacije v Kubernetes, vendar se ne smejo uveljaviti.

Splošna priporočena pot je, da se jih znebite v korist arhitekturne spremembe aplikacije v skladu s tem, kar je mnogim že dobro znano 12-faktorska aplikacija. Vendar pa to – prestavitev aplikacije v obliko brez stanja – neizogibno pomeni, da bodo potrebne spremembe kode, pri čemer je pomembno najti ravnotežje med zmožnostmi/zahtevami podjetja in možnostmi za implementacijo in vzdrževanje izbrane poti. .

PS

Preberite tudi na našem blogu:

Vir: www.habr.com

Dodaj komentar