Skedarët lokalë gjatë transferimit të aplikacionit në Kubernetes

Skedarët lokalë gjatë transferimit të aplikacionit në Kubernetes

Kur po ndërtoni një proces CI/CD duke përdorur Kubernetes, ndonjëherë shfaqet problemi i papajtueshmërisë së kërkesave të infrastrukturës së re dhe aplikacionit që po transferohet. Në veçanti, në fazën e ndërtimit të aplikacionit, është e rëndësishme të merrni një një imazh që do të përdoret në të gjitha mjediset dhe klasterët e projektit. Ky parim formon bazën e menaxhimit të duhur sipas Google të kontejnerëve (nuk e kemi diskutuar këtë herë). tha Megjithatë, asnjëherë nuk do të shihni situata kur në kodin e faqes përdoret një kornizë e gatshme, përdorimi i së cilës vendos kufizime në shfrytëzimin e saj të mëtejshëm. Dhe në një "mjedis të zakonshëm" është e lehtë të merresh me këtë, në Kubernetes një sjellje e tillë mund të bëhet një problem, sidomos kur përballesh për herë të parë. Edhe pse një mendje shpikëse është në gjendje të ofrojë zgjidhje infrastrukturore që duken të dukshme dhe madje të mira në shikim të parë... është e rëndësishme të mbani mend se shumica e situatave mund dhe duhet të

zgjidhen në mënyrë arkitekturore. zgjidhja arkitekturore.

Do të shqyrtojmë zgjidhjet e njohura të workaround-it për ruajtjen e skedarëve, të cilat mund të çojnë në pasoja të pakëndshme gjatë përdorimit të klasterit, si dhe do të tregojmë një rrugë më të saktë.

Ruajtja e statikës

Për ilustrim, le të shqyrtojmë një aplikacion në internet që përdor një gjenerues statikësh për të marrë një grup imazhesh, stilesh dhe të tjera. Për shembull, në kornizën PHP Yii ka një menaxher asetesh të integruar, i cili gjeneron emra unikë për direktorët. Prandaj, në dalje formohet një set rrugësh që nuk përplasen mes tyre (kjo bëhet për disa arsye — për shembull, për të përjashtuar kopjet kur një resurs i njëjtë përdoret nga shumë componente). Kështu, nga kutia, në kërkesën e parë për modulin e burimit në internet, ndodh formimi dhe shpërndarja e statikës (në të vërtetë — shpesh lidhje të thjeshta, por për këtë më vonë) me një katalog rrënjësor unik për këtë implementim:

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

Çfarë rreziku paraqet kjo në kuadër të klasterit?

Shembulli më i thjeshtë

Të marrim një rast relativisht të zakonshëm, kur përpara PHP ndodhet nginx për shërbimin e skedarëve statikë dhe përpunimin e kërkesave të thjeshta. Mënyra më e thjeshtë është Deployment me dy kontejnerë:

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

Në mënyrë të thjeshtuar, konfigurimi i nginx reduktohet në këtë:

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

Kur lidhet për herë të parë me faqen në konteinerin me PHP, shfaqen asetet. Por në rastin e dy konteinerëve brenda një pod'i — nginx nuk di asgjë për këta skedarë statikë, të cilët (sipas konfigurimit) duhet t'i japin saktësisht atij. Si rezultat, për të gjitha kërkesat për skedarët CSS dhe JS, klienti do të shohë gabimin 404. Zgjidhja më e thjeshtë këtu do të ishte organizimi i një direktore të përbashkët për konteinerët. Një variant primitiv — i përbashkët 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

Tani skedarët statikë që gjenerohen në konteiner jepen korrekt nga nginx. Por më kujtohet se kjo është një zgjidhje primitive, dhe do të thotë — kjo është larg idealit dhe ka nuanca dhe mangësi të veta, për të cilat do flas më poshtë.

Katalog më i avancuar

Tani le të imagjinojmë situatën kur përdoruesi hyn në faqen e internetit, ngarkon faqen me stilet ekzistuese në kontejner, dhe për sa kohë që ai po lexon këtë faqe, ne e ri-deploy-ojmë kontejnerin. Katalogu i aseteve bëhet bosh dhe kërkohet një kërkesë në PHP për të nisur gjenerimin e të rinjve. Megjithatë, edhe pas kësaj, lidhjet me statikën e vjetër do të jenë të pavlefshme, gjë që do të sjellë gabime në shfaqjen e statikës.

Për më tepër, ndoshta kemi një projekt me ngarkesë të lartë, dhe kjo do të thotë se një kopje e aplikacionit nuk do të mjaftonte:

  • Të bëjmë që të shkallëzojmë Deployment në dy replika.
  • Në kërkesën e parë në sajt, asetet u krijuan në një replikë.
  • Në një moment, ingress vendosi (për qëllime balancimi të ngarkesës) të dërgojë kërkesën në replikën e dytë, dhe atje këto asete ende nuk janë. Ose ndoshta, ato nuk janë më aty, sepse ne po përdorim RollingUpdate dhe në këtë moment po bëjmë deploy.

Në përgjithësi, përfundimi — sërish gabime.

Për të mos humbur asetet e vjetra, mund të ndryshojmë emptyDirhostPath, duke e vendosur statikën fizikisht në nyjën e klasterit. Ky qasje është e keqe sepse në fakt duhet të nënshkruhemi në një nyje të caktuar të klasterit aplikacioni juaj, sepse — në rast se kaloni në node të tjera — drejtoritë nuk do të përmbajnë skedarët e nevojshëm. Ose është e nevojshme një sinkronizim i pasiv i drejtorisë midis node-ve.

Cilat janë zgjidhjet e mundshme?

  1. Nëse hardueri dhe burimet e lejojnë, mund të përdorni cephfs për të organizuar një direktor të qasshëm për nevojat e statikës. Dokumentacioni zyrtar këshillon dysk të SSD, të paktën një riplikim tre herë dhe një lidhje të qëndrueshme "të trashë" midis node-ve të klasterit.
  2. Një variant më pak kërkues do të ishte organizimi i një serveri NFS. Megjithatë, atëherë duhet të merret parasysh rritja e mundshme e kohës së përgjigjes për përpunimin e kërkesave nga serveri i uebit, dhe gjithashtu qëndrueshmëria do të lërë për të dëshiruar. Pasojat e një dështimi janë katastrofike: humbja e mount-it e dënon klasterin në vdekje nga ngarkesa LA, që lëvizi për në qiell.

Përveç gjithçkaje, për të gjitha opsionet e krijimit të një ruajtjeje të përhershme do të kërkohet pastrimi i pasiv i grupeve të skedarëve të vjetëruar, të grumbulluar për një periudhë të caktuar. Para kontejnerëve me PHP mund të vendoset DaemonSet nga cache-nginx, që do të ruajnë kopje të aseteve për një kohë të kufizuar. Ky funksion është i lehtë për t'u konfiguruar me proxy_cache me thellësi ruajtjeje në ditë apo gigabajt hapësire disk.

Kombinimi i këtij metodi me sistemet e përmendura më sipër të skedarëve të shpërndarë ofron një fushë të madhe për krijimtarinë, ku kufizimi është vetëm buxheti dhe potenciali teknik i atyre që do ta implementojnë dhe mbajnë. Sipas përvojës, sa më e thjeshtë të jetë sistemi, aq më stabil funksionon ai. Me shtimin e shtresave të tilla, mbajtja e infrastrukturës bëhet shumë më e vështirë, dhe me këtë rritet edhe koha e shpenzuar për diagnozën dhe rimëkëmbjen në rast të ndonjë dështimi.

Rekomandimi

Nëse realizimi i varianteve të propozuara të ruajtjes ju duket gjithashtu i paarsyetueshëm (i komplikuar, i shtrenjtë...), atëherë vlen të shihni situatën nga një këndvështrim tjetër. Saktësisht — të thelloheni në arkitekturën e projektit dhe të ndaloni problemin në kod, duke u lidhur me një strukturë të dhënash statike në imazh, përcaktimi i qartë i përmbajtjes ose procedurës së "ngrohjes" dhe/ose parakompilimit të aseteve në fazën e ndërtimit të imazhit. Kështu ne marrim sjellje plotësisht të parashikueshme dhe një set të njëjtë skedarësh për të gjitha ambientet dhe replikat e aplikacionit të nisur.

Nëse kthehemi në një shembull konkret me framework-un Yii dhe nuk thellohemi në strukturën e tij (çka nuk është qëllimi i artikullit), mjafton të përmendim dy qasje të njohura:

  1. Të ndryshojmë procesin e ndërtimit të imazhit në mënyrë që të vendosim asetet në një vend të parashikueshëm. Kështu sugjerojnë/zbatohen në zgjerimet si yii2-static-assets.
  2. Të përcaktojmë hash specifike për direktorët e aseteve, siç tregohet, për shembull, në këtë prezantim (duke filluar nga slajdi №35). Në fakt, autori i raportit në fund (dhe jo pa arsye!) rekomandon që pas ndërtimit të aseteve në serverin e ndërtimit t'i ngarkojmë ato në një depo qendrore (si S3), para së cilës të vendosim CDN.

Skedarët e ngarkuar

Një rast tjetër që do të përdoret me siguri kur të transferoni një aplikacion në një grup Kubernetes është ruajtja e skedave të përdoruesve në sistemin e skedarëve. Për shembull, kemi përsëri një aplikacion në PHP që merr skeda përmes një forme ngarkimi, që bën diçka me to gjatë procesit dhe i kthen mbrapsht.

Vendi ku këto skeda duhet të vendosen, në realitetin e Kubernetes, duhet të jetë i përbashkët për të gjitha replikat e aplikacionit. Në varësi të kompleksitetit të aplikacionit dhe nevojës për organizimin e qëndrueshmërisë së këtyre skedave, një nga vendet e lartpërmendura mund të jenë pajisjet e përbashkëta, por, siç po shohim, ato kanë disavantazhet e tyre.

Rekomandimi

Një nga zgjidhjet është përdorimi i një ruajtjeje të përputhshme me S3 (madje një lloj të kategorisë self-hosted si minio). Kalimi në punën me S3 do të kërkojë ndryshime në nivelin e kodit, dhe si do të ndodhë dorëzimi i përmbajtjes në frontend, ne tashmë shkruam.

Seancat e përdoruesve

Vlen të përmendet organizimi i ruajtjes së sesioneve të përdoruesve. Nganjëherë ky është gjithashtu skeda në disk, që në përputhje me Kubernetes do të çojë në kërkesa të vazhdueshme për autorizim të përdoruesit, nëse kërkesa e tij bie në një konteiner tjetër.

Pjesërisht problemi zgjidhet duke aktivizuar stickySessions në ingress (karakteristika mbështetet në të gjithë kontrollorët e njohur të ingress — më shumë informacion në rishikimin tonë), për të lidhur përdoruesin me një pod të caktuar me aplikacionin:

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

Por kjo nuk do të eliminojë problemet gjatë zhvillimeve të përsëritura.

Rekomandimi

Një mënyrë më e saktë do të ishte kalimi i aplikacionit në ruajtjen e sesioneve në memcached, Redis dhe zgjidhje të ngjashme — në përgjithësi, të hiqni dorë përfundimisht nga opsionet e skedarëve.

Përfundimi

Zgjidhjet infrastrukturore të diskutuar në tekst janë të përshtatshme vetëm si zgjidhje përkohësore (e cila tingëllon më bukur në anglisht si workaround). Ato mund të jenë relevante në fazat fillestare të migrimit të aplikacionit në Kubernetes, por nuk duhet të "rrënjosën".

Në përgjithësi, rruga e rekomanduar është të heqim këto elemente në favor të një përmirësimi arkitektonik të aplikacionit, sipas asaj që është tashmë e njohur për shumë. Aplikacioni 12-Faktor. Megjithatë, kjo — përshtatja e aplikacionit në një formë stateless — domosdoshmërisht do të thotë se do të kërkohen ndryshime në kod dhe këtu është e rëndësishme të gjejmë një ekuilibër midis mundësive/ kërkesave të biznesit dhe perspektivave për implementimin dhe mbështetje të rrugës së zgjedhur.

P.S.

Lexoni gjithashtu në blogun tonë:

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster