Lokálne súbory pri migrácii aplikácie na Kubernetes

Lokálne súbory pri migrácii aplikácie na Kubernetes

Pri budovaní procesu CI/CD pomocou Kubernetes niekedy vzniká problém s nekompatibilitou medzi požiadavkami novej infraštruktúry a aplikáciou, ktorá sa do nej prenáša. Najmä vo fáze zostavovania aplikácie je dôležité získať jeden obrázok, ktorý bude použitý v všetko projektové prostredia a klastre. Tento princíp je základom správneho podľa Google správa kontajnerov (o tom viac ako raz povedal a naše technické oddelenie).

Nikoho však neuvidíte v situáciách, keď kód stránky používa hotový rámec, ktorého použitie obmedzuje jeho ďalšie použitie. A zatiaľ čo v „normálnom prostredí“ sa to dá ľahko vyriešiť, v Kubernetes sa toto správanie môže stať problémom, najmä keď sa s ním stretnete prvýkrát. Zatiaľ čo vynaliezavá myseľ dokáže prísť s riešeniami infraštruktúry, ktoré sa na prvý pohľad zdajú byť zrejmé alebo dokonca dobré... je dôležité si uvedomiť, že väčšina situácií môže a mala by riešiť architektonicky.

Pozrime sa na populárne riešenia na obchádzanie ukladania súborov, ktoré môžu viesť k nepríjemným následkom pri prevádzke klastra, a tiež poukázať na správnejšiu cestu.

Statické úložisko

Pre ilustráciu si predstavte webovú aplikáciu, ktorá používa nejaký druh statického generátora na získanie množiny obrázkov, štýlov a iných vecí. Rámec Yii PHP má napríklad zabudovaného správcu aktív, ktorý generuje jedinečné názvy adresárov. V súlade s tým je výstupom množina ciest pre statickú stránku, ktoré sa zjavne navzájom nepretínajú (to bolo urobené z niekoľkých dôvodov – napríklad na odstránenie duplikátov, keď viaceré komponenty používajú rovnaký zdroj). Takže hneď po prvom prístupe k modulu webových zdrojov sa vytvoria statické súbory (v skutočnosti často symbolické odkazy, ale o tom neskôr) a rozložia sa so spoločným koreňovým adresárom jedinečným pre toto nasadenie:

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

Čo to znamená z hľadiska klastra?

Najjednoduchší príklad

Zoberme si celkom bežný prípad, keď PHP predchádza nginx na distribúciu statických údajov a spracovanie jednoduchých požiadaviek. Najjednoduchší spôsob - rozvinutie s dvoma nádobami:

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 zjednodušenej forme sa konfigurácia nginx scvrkáva na nasledovné:

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

Pri prvom prístupe na stránku sa aktíva objavia v kontajneri PHP. Ale v prípade dvoch kontajnerov v rámci jedného pod, nginx nevie nič o týchto statických súboroch, ktoré by im (podľa konfigurácie) mali dať. Výsledkom je, že klient pri všetkých požiadavkách na súbory CSS a JS uvidí chybu 404. Najjednoduchším riešením by tu bolo zorganizovať spoločný adresár pre kontajnery. Primitívna možnosť - všeobecná 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

Teraz nginx správne obsluhuje statické súbory vygenerované v kontajneri. Dovoľte mi však pripomenúť, že ide o primitívne riešenie, čo znamená, že nie je ani zďaleka ideálne a má svoje vlastné nuansy a nedostatky, ktoré sú uvedené nižšie.

Pokročilejšie úložisko

Teraz si predstavte situáciu, že používateľ navštívil stránku, načítal stránku so štýlmi dostupnými v kontajneri a počas čítania tejto stránky sme kontajner znova nasadili. Katalóg aktív sa vyprázdnil a na začatie generovania nových je potrebná požiadavka na PHP. Aj po tomto však budú odkazy na starú statiku irelevantné, čo povedie k chybám pri zobrazovaní statiky.

Okrem toho máme s najväčšou pravdepodobnosťou viac-menej nabitý projekt, čo znamená, že jedna kópia aplikácie nebude stačiť:

  • Poďme to zväčšiť rozvinutie až dve repliky.
  • Pri prvom prístupe na lokalitu sa aktíva vytvorili v jednej replike.
  • V určitom bode sa spoločnosť ingress rozhodla (na účely vyrovnávania záťaže) poslať požiadavku na druhú repliku a tieto aktíva tam ešte neboli. Alebo možno už tam nie sú, pretože ich používame RollingUpdate a momentálne robíme nasadzovanie.

Vo všeobecnosti sú výsledkom opäť chyby.

Aby ste sa vyhli strate starého majetku, môžete ho zmeniť emptyDir na hostPath, fyzické pridanie statiky do uzla klastra. Tento prístup je zlý, pretože vlastne musíme naviazať na špecifický uzol klastra vašu aplikáciu, pretože – v prípade presunu do iných uzlov – adresár nebude obsahovať potrebné súbory. Alebo sa vyžaduje nejaký druh synchronizácie adresárov na pozadí medzi uzlami.

Aké sú riešenia?

  1. Ak to hardvér a zdroje umožňujú, môžete použiť cephfs organizovať rovnako dostupný adresár pre statické potreby. Oficiálna dokumentácia odporúča SSD disky, aspoň trojnásobnú replikáciu a stabilné „hrubé“ spojenie medzi uzlami klastra.
  2. Menej náročnou možnosťou by bolo zorganizovať server NFS. Potom však musíte vziať do úvahy možné predĺženie doby odozvy na spracovanie požiadaviek webovým serverom a odolnosť voči chybám zanechá veľa požiadaviek. Následky zlyhania sú katastrofálne: strata kopca odsúdi klaster na smrť pod tlakom nákladu LA rútiaceho sa do neba.

Okrem iného budú vyžadovať všetky možnosti na vytvorenie trvalého úložiska čistenie pozadia zastarané súbory súborov nahromadené za určité časové obdobie. Pred kontajnery s PHP môžete umiestniť DaemonSet z vyrovnávacej pamäte nginx, ktorá bude ukladať kópie aktív na obmedzený čas. Toto správanie je ľahko konfigurovateľné pomocou proxy_cache s hĺbkou úložiska v dňoch alebo gigabajtoch diskového priestoru.

Kombinácia tejto metódy s vyššie uvedenými distribuovanými súborovými systémami poskytuje obrovské pole pre predstavivosť, obmedzené len rozpočtom a technickým potenciálom tých, ktorí ju budú implementovať a podporovať. Zo skúseností môžeme povedať, že čím je systém jednoduchší, tým stabilnejšie funguje. Keď sa pridajú takéto vrstvy, je oveľa ťažšie udržiavať infraštruktúru a zároveň sa zvyšuje čas strávený diagnostikou a obnovou z prípadných porúch.

odporúčanie

Ak sa vám aj implementácia navrhovaných možností skladovania zdá neopodstatnená (zložitá, drahá...), potom sa oplatí pozrieť sa na situáciu z druhej strany. Totiž hrabať sa v architektúre projektu a opravte problém v kóde, viazané na nejakú statickú dátovú štruktúru v obrázku, jednoznačnú definíciu obsahu alebo postupu na „zahrievanie“ a/alebo predkompiláciu aktív vo fáze zostavovania obrázka. Takto získame absolútne predvídateľné správanie a rovnakú sadu súborov pre všetky prostredia a repliky spustenej aplikácie.

Ak sa vrátime ku konkrétnemu príkladu s rámcom Yii a nebudeme sa vŕtať v jeho štruktúre (čo nie je účelom článku), stačí poukázať na dva populárne prístupy:

  1. Zmeňte proces vytvárania obrazu tak, aby ste umiestnili prostriedky na predvídateľné miesto. Toto sa navrhuje/implementuje v rozšíreniach ako yii2-static-assets.
  2. Definujte špecifické hash pre adresáre aktív, ako je popísané napr. túto prezentáciu (počínajúc snímkou ​​č. 35). Mimochodom, autor správy v konečnom dôsledku (a nie bezdôvodne!) radí, aby ste po zložení aktív na server zostavy nahrali do centrálneho úložiska (ako S3), pred ktoré umiestnite CDN.

Stiahnuteľné súbory

Ďalším prípadom, ktorý určite príde na rad pri migrácii aplikácie do klastra Kubernetes, je ukladanie používateľských súborov do súborového systému. Napríklad máme opäť PHP aplikáciu, ktorá prijíma súbory cez upload formulár, počas prevádzky s nimi niečo robí a posiela späť.

V Kubernetes by umiestnenie, kam by sa tieto súbory mali umiestniť, malo byť spoločné pre všetky repliky aplikácie. V závislosti od zložitosti aplikácie a potreby usporiadať perzistenciu týchto súborov môžu byť takýmto miestom vyššie uvedené možnosti zdieľaného zariadenia, ale ako vidíme, majú svoje nevýhody.

odporúčanie

Jedno riešenie je pomocou úložiska kompatibilného s S3 (aj keď je to nejaký druh samostatne hostenej kategórie ako minio). Prechod na S3 si vyžiada zmeny na úrovni kódua ako bude obsah doručený na frontend, už máme писали.

Používateľské relácie

Samostatne stojí za zmienku organizácia ukladania používateľských relácií. Často sú to aj súbory na disku, čo v kontexte Kubernetes bude viesť k neustálym autorizačným požiadavkám používateľa, ak jeho požiadavka skončí v inom kontajneri.

Problém je čiastočne vyriešený zapnutím stickySessions na vstupe (funkcia je podporovaná vo všetkých populárnych kontroléroch vstupu - ďalšie podrobnosti nájdete v časti naša recenzia)na prepojenie používateľa s konkrétnym modulom s aplikáciou:

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

To ale neodstráni problémy s opakovaným nasadením.

odporúčanie

Správnejším spôsobom by bolo preniesť aplikáciu na ukladanie relácií v memcached, Redis a podobných riešeniach - vo všeobecnosti úplne opustite možnosti súboru.

Záver

Infraštruktúrne riešenia, o ktorých sa hovorí v texte, sú hodné použitia iba vo forme dočasných „bariel“ (čo znie krajšie v angličtine ako riešenie). Môžu byť relevantné v prvých fázach migrácie aplikácie na Kubernetes, ale nemali by sa zakoreniť.

Všeobecnou odporúčanou cestou je zbaviť sa ich v prospech architektonickej úpravy aplikácie v súlade s tým, čo je už mnohým dobre známe. 12-faktorová aplikácia. Toto – uvedenie aplikácie do bezstavovej formy – však nevyhnutne znamená, že budú potrebné zmeny v kóde, a tu je dôležité nájsť rovnováhu medzi schopnosťami/požiadavkami podniku a vyhliadkami na implementáciu a udržiavanie zvolenej cesty. .

PS

Prečítajte si aj na našom blogu:

Zdroj: hab.com

Pridať komentár