Локалне датотеке приликом миграције апликације у Кубернетес

Локалне датотеке приликом миграције апликације у Кубернетес

Када правите ЦИ/ЦД процес користећи Кубернетес, понекад се јавља проблем некомпатибилности између захтева нове инфраструктуре и апликације која се на њу преноси. Конкретно, у фази израде апликације важно је да добијете један слика која ће се користити у све пројектна окружења и кластери. Овај принцип лежи у основи исправног према Гуглу управљање контејнерима (више пута о овоме on је рекао и наше техничко одељење).

Међутим, нећете видети никога у ситуацијама када код сајта користи готов оквир, чија употреба намеће ограничења за његову даљу употребу. И док је у „нормалном окружењу“ са овим лако изаћи на крај, у Кубернетес-у ово понашање може постати проблем, посебно када се с њим сретнете први пут. Иако инвентивни ум може да смисли инфраструктурна решења која на први поглед изгледају очигледна или чак добра... важно је запамтити да већина ситуација може и треба бити решена архитектонски.

Хајде да погледамо популарна решења за заобилажење за складиштење датотека које могу довести до непријатних последица при раду кластера, а такође указати на исправнији пут.

Статичко складиштење

За илустрацију, размотрите веб апликацију која користи неку врсту статичког генератора за добијање скупа слика, стилова и других ствари. На пример, Иии ПХП оквир има уграђени менаџер средстава који генерише јединствена имена директоријума. Сходно томе, излаз је скуп путања за статичну локацију које се очигледно не секу једна са другом (ово је урађено из неколико разлога – на пример, да би се елиминисали дупликати када више компоненти користи исти ресурс). Дакле, ван кутије, када први пут приступите модулу веб ресурса, статичке датотеке (у ствари, често симболичне везе, али о томе касније) се формирају и постављају са заједничким основним директоријумом јединственим за ову примену:

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

Шта ово значи у смислу кластера?

Најједноставнији пример

Узмимо прилично уобичајен случај, када ПХП-у претходи нгинк за дистрибуцију статичких података и обраду једноставних захтева. Најлакши начин - развој са два контејнера:

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

У поједностављеном облику, нгинк конфигурација се своди на следеће:

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

Када први пут приступите сајту, средства се појављују у ПХП контејнеру. Али у случају два контејнера унутар једног под-а, нгинк не зна ништа о овим статичким датотекама, које (према конфигурацији) треба да им се дају. Као резултат тога, клијент ће видети грешку 404 за све захтеве за ЦСС и ЈС датотеке. Најједноставније решење би било да се организује заједнички директоријум за контејнере. Примитивна опција - општа 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

Сада нгинк исправно опслужује статичке датотеке генерисане у контејнеру. Али да вас подсетим да је ово примитивно решење, што значи да је далеко од идеалног и има своје нијансе и недостатке, о којима се говори у наставку.

Напредније складиштење

Сада замислите ситуацију у којој је корисник посетио сајт, учитао страницу са стиловима доступним у контејнеру и док је читао ову страницу, ми смо поново поставили контејнер. Каталог средстава је постао празан и потребан је захтев ПХП-у да започне генерисање нових. Међутим, чак и након овога, линкови на стару статику ће бити небитни, што ће довести до грешака у приказивању статике.

Поред тога, највероватније имамо мање-више оптерећен пројекат, што значи да једна копија апликације неће бити довољна:

  • Хајде да га повећамо развој до две реплике.
  • Када се сајту први пут приступило, средства су креирана у једној реплици.
  • У неком тренутку, ингресс је одлучио (за потребе балансирања оптерећења) да пошаље захтев другој реплици, а ова средства још нису била тамо. Или их можда више нема јер их користимо RollingUpdate и тренутно вршимо распоређивање.

Генерално, резултат су опет грешке.

Да бисте избегли губитак старе имовине, можете променити emptyDir на hostPath, физички додајући статички чвору кластера. Овај приступ је лош јер заправо морамо везује се за одређени чвор кластера вашу апликацију, јер - у случају преласка на друге чворове - директоријум неће садржати потребне датотеке. Или је потребна нека врста позадинске синхронизације директоријума између чворова.

Која су решења?

  1. Ако хардвер и ресурси дозвољавају, можете користити цепхфс да организује једнако доступан именик за статичке потребе. Службена документација препоручује ССД дискове, најмање троструку репликацију и стабилну „дебелу“ везу између чворова кластера.
  2. Мање захтевна опција би била организовање НФС сервера. Међутим, тада морате узети у обзир могуће повећање времена одговора за обраду захтева од стране веб сервера, а толеранција грешака ће оставити много да се пожели. Последице неуспеха су катастрофалне: губитак носача осуђује кластер на смрт под притиском терета ЛА који јури у небо.

Између осталог, биће потребне све опције за стварање трајног складишта чишћење позадине застарели скупови датотека акумулираних током одређеног временског периода. Испред контејнера са ПХП-ом можете ставити ДаемонСет од кеширања нгинк-а, који ће чувати копије средстава на ограничено време. Ово понашање се лако може конфигурисати коришћењем proxy_cache са дубином складиштења у данима или гигабајтима простора на диску.

Комбиновање ове методе са горе наведеним дистрибуираним системима датотека пружа огромно поље за машту, ограничено само буџетом и техничким потенцијалом оних који ће га имплементирати и подржавати. Из искуства можемо рећи да што је систем једноставнији, то стабилније функционише. Када се додају такви слојеви, постаје много теже одржавати инфраструктуру, а истовремено се повећава време утрошено на дијагностиковање и опоравак од било каквих кварова.

Препорука

Ако вам се примена предложених опција складиштења чини неоправданом (компликованом, скупом...), онда је вредно сагледати ситуацију са друге стране. Наиме, да се копа по архитектури пројекта и решите проблем у коду, везано за неку статичку структуру података на слици, недвосмислену дефиницију садржаја или процедуре за „загревање“ и/или претходно компајлирање средстава у фази склапања слике. На овај начин добијамо апсолутно предвидљиво понашање и исти скуп датотека за сва окружења и реплике покренуте апликације.

Ако се вратимо на конкретан пример са Иии оквиром и не улазимо у његову структуру (што није сврха чланка), довољно је истаћи два популарна приступа:

  1. Промените процес прављења слике да бисте средства поставили на предвидљиву локацију. Ово се предлаже/имплементира у екстензијама као што су иии2-статиц-ассетс.
  2. Дефинишите специфичне хешове за директоријуме средстава, као што је објашњено у нпр. ову презентацију (почев од слајда бр. 35). Иначе, аутор извештаја на крају (и не без разлога!) саветује да након склапања средстава на серверу за изградњу, отпремите их у централно складиште (попут С3), испред које поставите ЦДН.

Преузимања

Још један случај који ће дефинитивно доћи у игру приликом миграције апликације у Кубернетес кластер је чување корисничких датотека у систему датотека. На пример, поново имамо ПХП апликацију која прихвата датотеке преко обрасца за отпремање, ради нешто са њима током рада и шаље их назад.

У Кубернетес-у, локација на којој треба да буду смештене ове датотеке треба да буде заједничка за све реплике апликације. У зависности од сложености апликације и потребе да се организује постојаност ових датотека, горе наведене опције дељених уређаја могу бити такво место, али, као што видимо, имају своје недостатке.

Препорука

Једно решење је користећи С3-компатибилно складиште (чак и ако је у питању нека врста категорије коју сами хостују као што је минио). Прелазак на С3 ће захтевати промене на нивоу кода, а како ће садржај бити испоручен на предњем крају, већ смо сазнали писали.

Корисничке сесије

Одвојено, вреди напоменути организацију складиштења корисничких сесија. Често су то и фајлови на диску, што ће у контексту Кубернетеса довести до сталних захтева за ауторизацију од корисника ако његов захтев заврши у другом контејнеру.

Проблем се делимично решава укључивањем stickySessions на уласку (функција је подржана у свим популарним улазним контролерима - за више детаља погледајте наш преглед)да повежете корисника са одређеним подом са апликацијом:

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

Али ово неће елиминисати проблеме са поновљеним применама.

Препорука

Исправнији начин би био да пренесете апликацију на чување сесија у мемцацхед-у, Редис-у и сличним решењима - уопште, потпуно напустите опције датотека.

Закључак

Инфраструктурна решења о којима се говори у тексту су вредна употребе само у формату привремених „штака“ (што на енглеском звучи лепше као решење). Они могу бити релевантни у првим фазама миграције апликације на Кубернетес, али не би требало да заживе.

Општи препоручени пут је да их се отарасите у корист архитектонске модификације апликације у складу са оним што је многима већ добро познато 12-факторска апликација. Међутим, ово – довођење апликације у облик без држављанства – неизбежно значи да ће бити потребне промене у коду, а овде је важно пронаћи баланс између могућности/захтева пословања и изгледа за имплементацију и одржавање изабраног пута. .

ПС

Прочитајте и на нашем блогу:

Извор: ввв.хабр.цом

Додај коментар