Mga lokal na file kapag naglilipat ng application sa Kubernetes

Mga lokal na file kapag naglilipat ng application sa Kubernetes

Kapag gumagawa ng proseso ng CI/CD gamit ang Kubernetes, minsan ang problema ay lumalabas sa hindi pagkakatugma sa pagitan ng mga kinakailangan ng bagong imprastraktura at ang application na inililipat dito. Sa partikular, sa yugto ng pagbuo ng aplikasyon mahalagang makuha isa larawan na gagamitin sa lahat kapaligiran ng proyekto at mga kumpol. Ang prinsipyong ito ay pinagbabatayan ng tama ayon sa Google pamamahala ng lalagyan (higit sa isang beses tungkol dito sinabi niya at ang aming teknikal na departamento).

Gayunpaman, hindi mo makikita ang sinuman sa mga sitwasyon kung saan ang code ng site ay gumagamit ng isang handa na balangkas, ang paggamit nito ay nagpapataw ng mga paghihigpit sa karagdagang paggamit nito. At habang nasa isang "normal na kapaligiran" ito ay madaling harapin, sa Kubernetes ang pag-uugali na ito ay maaaring maging isang problema, lalo na kapag nakatagpo mo ito sa unang pagkakataon. Bagama't ang isang mapag-imbentong isipan ay maaaring makabuo ng mga solusyon sa imprastraktura na mukhang halata o kahit na maganda sa unang tingin... mahalagang tandaan na karamihan sa mga sitwasyon ay maaari at dapat malutas sa arkitektura.

Tingnan natin ang mga sikat na solusyon sa workaround para sa pag-iimbak ng mga file na maaaring humantong sa hindi kasiya-siyang mga kahihinatnan kapag nagpapatakbo ng isang cluster, at ituro din ang isang mas tamang landas.

Static na imbakan

Upang ilarawan, isaalang-alang ang isang web application na gumagamit ng ilang uri ng static generator upang makakuha ng isang hanay ng mga larawan, estilo, at iba pang mga bagay. Halimbawa, ang Yii PHP framework ay may built-in na asset manager na bumubuo ng mga natatanging pangalan ng direktoryo. Alinsunod dito, ang output ay isang hanay ng mga landas para sa static na site na malinaw na hindi nagsalubong sa isa't isa (ginawa ito para sa ilang kadahilanan - halimbawa, upang maalis ang mga duplicate kapag maraming bahagi ang gumagamit ng parehong mapagkukunan). Kaya, sa labas ng kahon, sa unang pagkakataon na ma-access mo ang isang module ng mapagkukunan ng web, ang mga static na file (sa katunayan, madalas na mga symlink, ngunit higit pa doon sa ibang pagkakataon) ay nabuo at inilatag gamit ang isang karaniwang root directory na natatangi para sa deployment na ito:

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

Ano ang ibig sabihin nito sa mga tuntunin ng isang kumpol?

Ang pinakasimpleng halimbawa

Isaalang-alang natin ang isang medyo karaniwang kaso, kapag ang PHP ay inunahan ng nginx upang ipamahagi ang static na data at magproseso ng mga simpleng kahilingan. Ang pinakamadaling paraan - paglawak na may dalawang lalagyan:

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

Sa isang pinasimpleng anyo, ang nginx config ay bumababa sa sumusunod:

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

Kapag una mong na-access ang site, lumalabas ang mga asset sa lalagyan ng PHP. Ngunit sa kaso ng dalawang lalagyan sa loob ng isang pod, walang alam ang nginx tungkol sa mga static na file na ito, na (ayon sa pagsasaayos) ay dapat ibigay sa kanila. Bilang resulta, makakakita ang kliyente ng 404 error para sa lahat ng kahilingan sa CSS at JS file. Ang pinakasimpleng solusyon dito ay ang pag-aayos ng isang karaniwang direktoryo para sa mga container. Primitive na opsyon - pangkalahatan 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

Ngayon, ang mga static na file na nabuo sa lalagyan ay inihahatid ng nginx nang tama. Ngunit hayaan mo akong ipaalala sa iyo na ito ay isang primitive na solusyon, na nangangahulugang ito ay malayo sa perpekto at may sariling mga nuances at pagkukulang, na tinalakay sa ibaba.

Mas advanced na storage

Ngayon isipin ang isang sitwasyon kung saan bumisita ang isang user sa site, nag-load ng isang page na may mga istilong available sa container, at habang binabasa niya ang page na ito, muli naming inilagay ang container. Ang katalogo ng mga asset ay naging walang laman at ang isang kahilingan sa PHP ay kinakailangan upang simulan ang pagbuo ng mga bago. Gayunpaman, kahit na pagkatapos nito, ang mga link sa lumang static ay magiging walang kaugnayan, na hahantong sa mga error sa pagpapakita ng statics.

Bilang karagdagan, malamang na mayroon kaming mas marami o mas kaunting na-load na proyekto, na nangangahulugang hindi sapat ang isang kopya ng application:

  • Palakihin natin ito paglawak hanggang dalawang replika.
  • Noong unang na-access ang site, ginawa ang mga asset sa isang replica.
  • Sa isang punto, nagpasya si ingress (para sa mga layunin ng pagbalanse ng load) na magpadala ng kahilingan sa pangalawang replika, at wala pa ang mga asset na ito. O baka wala na sila dahil ginagamit natin RollingUpdate at sa ngayon ay nagsasagawa kami ng deployment.

Sa pangkalahatan, ang resulta ay muling pagkakamali.

Para maiwasan ang pagkawala ng mga lumang asset, maaari kang magpalit emptyDir sa hostPath, pagdaragdag ng static na pisikal sa isang cluster node. Ang diskarte na ito ay masama dahil kailangan talaga natin magbigkis sa isang partikular na cluster node ang iyong aplikasyon, dahil - sa kaso ng paglipat sa iba pang mga node - ang direktoryo ay hindi maglalaman ng mga kinakailangang file. O ilang uri ng background na pag-synchronize ng direktoryo sa pagitan ng mga node ay kinakailangan.

Ano ang mga solusyon?

  1. Kung pinapayagan ng hardware at mapagkukunan, maaari mong gamitin mga cephfs upang ayusin ang isang pantay na naa-access na direktoryo para sa mga static na pangangailangan. Opisyal na dokumentasyon nagrerekomenda ng mga SSD drive, hindi bababa sa tatlong beses na pagtitiklop at isang matatag na "makapal" na koneksyon sa pagitan ng mga cluster node.
  2. Ang isang hindi gaanong hinihingi na opsyon ay ang pag-aayos ng isang NFS server. Gayunpaman, kailangan mong isaalang-alang ang posibleng pagtaas sa oras ng pagtugon para sa pagpoproseso ng mga kahilingan ng web server, at ang pagpapahintulot sa kasalanan ay mag-iiwan ng maraming bagay na naisin. Ang mga kahihinatnan ng pagkabigo ay sakuna: ang pagkawala ng bundok ay humaharang sa kumpol sa kamatayan sa ilalim ng presyon ng LA load na nagmamadali sa kalangitan.

Sa iba pang mga bagay, ang lahat ng mga opsyon para sa paglikha ng patuloy na imbakan ay mangangailangan paglilinis ng background mga lumang set ng mga file na naipon sa isang tiyak na tagal ng panahon. Sa harap ng mga lalagyan na may PHP maaari mong ilagay DaemonSet mula sa caching nginx, na mag-iimbak ng mga kopya ng mga asset sa loob ng limitadong panahon. Ang pag-uugali na ito ay madaling i-configure gamit proxy_cache na may lalim ng imbakan sa mga araw o gigabytes ng espasyo sa disk.

Ang pagsasama-sama ng pamamaraang ito sa mga distributed file system na binanggit sa itaas ay nagbibigay ng isang malaking larangan para sa imahinasyon, na limitado lamang sa badyet at teknikal na potensyal ng mga taong magpapatupad at sumusuporta dito. Mula sa karanasan, masasabi nating mas simple ang sistema, mas matatag itong gumagana. Kapag idinagdag ang mga naturang layer, nagiging mas mahirap ang pagpapanatili ng imprastraktura, at kasabay nito, ang oras na ginugol sa pag-diagnose at pagbawi mula sa anumang mga pagkabigo ay tumataas.

Rekomendasyon

Kung ang pagpapatupad ng mga iminungkahing opsyon sa imbakan ay tila hindi makatwiran sa iyo (kumplikado, mahal...), kung gayon ito ay nagkakahalaga ng pagtingin sa sitwasyon mula sa kabilang panig. Lalo na, upang maghukay sa arkitektura ng proyekto at ayusin ang problema sa code, na nakatali sa ilang static na istruktura ng data sa larawan, isang hindi malabo na kahulugan ng mga nilalaman o pamamaraan para sa "pag-init" at/o paunang pag-compile ng mga asset sa yugto ng pagpupulong ng imahe. Sa ganitong paraan makakakuha tayo ng ganap na mahuhulaan na pag-uugali at ang parehong hanay ng mga file para sa lahat ng kapaligiran at mga replika ng tumatakbong application.

Kung babalik tayo sa partikular na halimbawa na may balangkas ng Yii at hindi susuriin ang istraktura nito (na hindi ang layunin ng artikulo), sapat na upang ituro ang dalawang tanyag na diskarte:

  1. Baguhin ang proseso ng pagbuo ng imahe upang ilagay ang mga asset sa isang predictable na lokasyon. Iminungkahi/ipinatupad ito sa mga extension tulad ng yii2-static-assets.
  2. Tukuyin ang mga partikular na hash para sa mga direktoryo ng asset, gaya ng tinalakay sa hal. pagtatanghal na ito (simula sa slide No. 35). Sa pamamagitan ng paraan, ang may-akda ng ulat sa huli (at hindi nang walang dahilan!) ay nagpapayo na pagkatapos mag-assemble ng mga asset sa build server, i-upload ang mga ito sa isang central storage (tulad ng S3), sa harap kung saan maglalagay ng CDN.

Mga download

Ang isa pang kaso na tiyak na darating kapag nag-migrate ng application sa isang Kubernetes cluster ay ang pag-iimbak ng mga file ng user sa file system. Halimbawa, mayroon ulit kaming PHP application na tumatanggap ng mga file sa pamamagitan ng upload form, gumagawa ng isang bagay sa kanila habang tumatakbo, at ibinabalik ang mga ito.

Sa Kubernetes, ang lokasyon kung saan dapat ilagay ang mga file na ito ay dapat na karaniwan sa lahat ng mga replika ng application. Depende sa pagiging kumplikado ng application at ang pangangailangan upang ayusin ang pagtitiyaga ng mga file na ito, ang nabanggit sa itaas na mga opsyon sa nakabahaging device ay maaaring isang lugar, ngunit, tulad ng nakikita natin, mayroon silang kanilang mga kakulangan.

Rekomendasyon

Ang isang solusyon ay gamit ang S3-compatible na storage (kahit na ito ay isang uri ng self-host na kategorya tulad ng minio). Ang paglipat sa S3 ay mangangailangan ng mga pagbabago sa antas ng code, at kung paano ihahatid ang content sa front end, mayroon na kami nagsulat.

Mga session ng user

Hiwalay, ito ay nagkakahalaga ng pagpuna sa organisasyon ng imbakan ng mga session ng user. Kadalasan ang mga ito ay mga file din sa disk, na sa konteksto ng Kubernetes ay hahantong sa patuloy na mga kahilingan sa pahintulot mula sa user kung ang kanyang kahilingan ay mapupunta sa isa pang lalagyan.

Ang problema ay bahagyang nalutas sa pamamagitan ng pag-on stickySessions sa pagpasok (Ang feature ay sinusuportahan sa lahat ng sikat na ingress controllers - para sa higit pang detalye, tingnan aming pagsusuri)para i-bind ang user sa isang partikular na pod gamit ang application:

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

Ngunit hindi nito maaalis ang mga problema sa paulit-ulit na pag-deploy.

Rekomendasyon

Ang isang mas tamang paraan ay ang paglipat ng aplikasyon sa pag-iimbak ng mga session sa memcached, Redis at mga katulad na solusyon - sa pangkalahatan, ganap na abandunahin ang mga opsyon sa file.

Konklusyon

Ang mga solusyon sa imprastraktura na tinalakay sa teksto ay karapat-dapat na gamitin lamang sa format na pansamantalang "mga saklay" (na mas maganda sa Ingles bilang workaround). Maaaring may kaugnayan ang mga ito sa mga unang yugto ng paglipat ng aplikasyon sa Kubernetes, ngunit hindi dapat mag-ugat.

Ang pangkalahatang inirerekumendang landas ay upang mapupuksa ang mga ito sa pabor sa pagbabago ng arkitektura ng aplikasyon alinsunod sa kung ano ang kilala na ng marami 12-Factor App. Gayunpaman, ito - ang pagdadala ng aplikasyon sa isang stateless na form - ay hindi maiiwasang nangangahulugan na ang mga pagbabago sa code ay kinakailangan, at dito mahalaga na makahanap ng balanse sa pagitan ng mga kakayahan/kinakailangan ng negosyo at ang mga prospect para sa pagpapatupad at pagpapanatili ng napiling landas .

PS

Basahin din sa aming blog:

Pinagmulan: www.habr.com

Magdagdag ng komento