Fitxers locals en migrar una aplicació a Kubernetes

Fitxers locals en migrar una aplicació a Kubernetes

Quan es construeix un procés CI/CD amb Kubernetes, de vegades sorgeix el problema d'incompatibilitat entre els requisits de la nova infraestructura i l'aplicació que s'hi transfereix. En particular, en l'etapa de creació de l'aplicació és important obtenir 01:00 imatge que s'utilitzarà Tot entorns i clústers del projecte. Aquest principi és la base del correcte segons Google gestió de contenidors (més d'una vegada sobre això va parlar i el nostre departament tècnic).

Tanmateix, no veureu ningú en situacions en què el codi del lloc utilitza un marc ja preparat, l'ús del qual imposa restriccions al seu ús posterior. I encara que en un "entorn normal" això és fàcil de tractar, a Kubernetes aquest comportament pot convertir-se en un problema, sobretot quan el trobeu per primera vegada. Tot i que una ment inventiva pot trobar solucions d'infraestructura que semblen òbvies o fins i tot bones a primera vista... és important recordar que la majoria de situacions poden i haurien de resoldre arquitectònicament.

Vegem les solucions alternatives populars per emmagatzemar fitxers que poden tenir conseqüències desagradables en operar un clúster i també assenyalem un camí més correcte.

Emmagatzematge estàtic

Per il·lustrar-ho, considereu una aplicació web que utilitzi algun tipus de generador estàtic per obtenir un conjunt d'imatges, estils i altres coses. Per exemple, el marc PHP Yii té un gestor d'actius integrat que genera noms de directoris únics. En conseqüència, la sortida és un conjunt de camins per al lloc estàtic que, òbviament, no es creuen entre si (això es va fer per diverses raons, per exemple, per eliminar els duplicats quan diversos components utilitzen el mateix recurs). Per tant, fora de la caixa, la primera vegada que accediu a un mòdul de recursos web, els fitxers estàtics (de fet, sovint enllaços simbòlics, però més endavant) es formen i es distribueixen amb un directori arrel comú únic per a aquest desplegament:

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

Què significa això en termes de clúster?

L'exemple més senzill

Prenem un cas força comú, quan PHP va precedit per nginx per distribuir dades estàtiques i processar sol·licituds senzilles. La manera més fàcil - Desplegament amb dos contenidors:

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

D'una forma simplificada, la configuració de nginx es resumeix en el següent:

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

Quan accediu al lloc per primera vegada, els actius apareixen al contenidor PHP. Però en el cas de dos contenidors dins d'un pod, nginx no sap res sobre aquests fitxers estàtics, que (segons la configuració) se'ls hauria de donar. Com a resultat, el client veurà un error 404 per a totes les sol·licituds als fitxers CSS i JS. La solució més senzilla aquí seria organitzar un directori comú per als contenidors. Opció primitiva - general 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

Ara els fitxers estàtics generats al contenidor són servits per nginx correctament. Però deixeu-me recordar que aquesta és una solució primitiva, el que significa que està lluny de ser ideal i té els seus propis matisos i deficiències, que es comenten a continuació.

Emmagatzematge més avançat

Ara imagineu una situació en què un usuari va visitar el lloc, va carregar una pàgina amb els estils disponibles al contenidor i, mentre llegia aquesta pàgina, vam tornar a desplegar el contenidor. El catàleg d'actius s'ha quedat buit i cal una sol·licitud a PHP per començar a generar-ne de nous. Tanmateix, fins i tot després d'això, els enllaços a les estàtiques antigues seran irrellevants, cosa que provocarà errors en mostrar les estàtiques.

A més, el més probable és que tinguem un projecte més o menys carregat, la qual cosa significa que una còpia de l'aplicació no serà suficient:

  • Augmentem-ho Desplegament fins a dues rèpliques.
  • Quan es va accedir al lloc per primera vegada, els actius es van crear en una rèplica.
  • En algun moment, Ingress va decidir (per a l'equilibri de càrrega) enviar una sol·licitud a la segona rèplica, i aquests actius encara no hi eren. O potser ja no hi són perquè fem servir RollingUpdate i de moment estem fent desplegament.

En general, el resultat torna a ser errors.

Per evitar perdre actius antics, pots canviar emptyDir en hostPath, afegint estàtica físicament a un node de clúster. Aquest enfocament és dolent perquè realment ho hem de fer enllaçar a un node de clúster específic la vostra aplicació, perquè - en cas de moure's a altres nodes - el directori no contindrà els fitxers necessaris. O es requereix algun tipus de sincronització de directoris de fons entre nodes.

Quines són les solucions?

  1. Si el maquinari i els recursos ho permeten, podeu utilitzar cephs per organitzar un directori igual d'accessible per a necessitats estàtiques. Documentació oficial recomana unitats SSD, almenys una replicació triple i una connexió "gruixuda" estable entre els nodes del clúster.
  2. Una opció menys exigent seria organitzar un servidor NFS. Tanmateix, llavors cal tenir en compte el possible augment del temps de resposta per processar les sol·licituds per part del servidor web, i la tolerància a errors deixarà molt a desitjar. Les conseqüències del fracàs són catastròfiques: la pèrdua de la muntanya condemna el cúmul a la mort sota la pressió de la càrrega de LA que es precipita cap al cel.

Entre altres coses, caldran totes les opcions per crear emmagatzematge persistent neteja de fons conjunts de fitxers obsolets acumulats durant un període de temps determinat. Davant de contenidors amb PHP pots posar DaemonSet des de la memòria cau nginx, que emmagatzemarà còpies dels actius durant un temps limitat. Aquest comportament es pot configurar fàcilment mitjançant proxy_cache amb profunditat d'emmagatzematge en dies o gigabytes d'espai en disc.

La combinació d'aquest mètode amb els sistemes de fitxers distribuïts esmentats anteriorment proporciona un gran camp per a la imaginació, limitat només pel pressupost i el potencial tècnic d'aquells que l'implementaran i el donaran suport. Per experiència, podem dir que com més senzill és el sistema, més estable funciona. Quan s'afegeixen aquestes capes, es fa molt més difícil mantenir la infraestructura i, al mateix temps, augmenta el temps dedicat a diagnosticar i recuperar-se de qualsevol fallada.

Recomanació

Si la implementació de les opcions d'emmagatzematge proposades també us sembla injustificada (complicada, cara...), val la pena mirar la situació des de l'altra banda. És a dir, aprofundir en l'arquitectura del projecte i solucionar el problema al codi, lligat a alguna estructura de dades estàtiques a la imatge, una definició inequívoca del contingut o procediment per "escalfar" i/o precompilar actius en l'etapa de muntatge de la imatge. D'aquesta manera obtenim un comportament absolutament previsible i el mateix conjunt de fitxers per a tots els entorns i rèpliques de l'aplicació en execució.

Si tornem a l'exemple concret amb el framework Yii i no aprofundim en la seva estructura (que no és l'objectiu de l'article), n'hi ha prou amb assenyalar dos enfocaments populars:

  1. Canvieu el procés de creació de la imatge per col·locar els recursos en una ubicació previsible. Això es suggereix/implementa en extensions com yii2-actius-estàtics.
  2. Definiu hash específics per als directoris d'actius, tal com s'explica a, p. aquesta presentació (a partir de la diapositiva núm. 35). Per cert, l'autor de l'informe en última instància (i no sense motiu!) aconsella que després d'acoblar els actius al servidor de compilació, els pugeu a un emmagatzematge central (com S3), davant del qual col·loqueu un CDN.

Arxius descarregables

Un altre cas que definitivament entrarà en joc quan es migra una aplicació a un clúster de Kubernetes és emmagatzemar fitxers d'usuari al sistema de fitxers. Per exemple, tornem a tenir una aplicació PHP que accepta fitxers mitjançant un formulari de càrrega, fa alguna cosa amb ells durant l'operació i els torna.

A Kubernetes, la ubicació on s'han de col·locar aquests fitxers hauria de ser comuna a totes les rèpliques de l'aplicació. Depenent de la complexitat de l'aplicació i de la necessitat d'organitzar la persistència d'aquests fitxers, les opcions de dispositius compartits esmentades anteriorment poden ser aquest lloc, però, com veiem, tenen els seus inconvenients.

Recomanació

Una solució és utilitzant emmagatzematge compatible amb S3 (encara que es tracti d'algun tipus de categoria autoallotjada com minio). Canviar a S3 requerirà canvis a nivell de codi, i com es lliurarà el contingut al front end, ja ho tenim писали.

Sessions d'usuari

Per separat, val la pena destacar l'organització de l'emmagatzematge de les sessions d'usuari. Sovint també són fitxers en disc, que en el context de Kubernetes donaran lloc a peticions constants d'autorització per part de l'usuari si la seva sol·licitud acaba en un altre contenidor.

El problema es resol en part engegant-lo stickySessions a l'entrada (la funció és compatible amb tots els controladors d'entrada populars; per obtenir més detalls, vegeu la nostra ressenya)per vincular l'usuari a un pod específic amb l'aplicació:

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

Però això no eliminarà els problemes amb desplegaments repetits.

Recomanació

Una manera més correcta seria transferir l'aplicació a emmagatzemar sessions en memcached, Redis i solucions similars - en general, abandonar completament les opcions de fitxer.

Conclusió

Les solucions d'infraestructura comentades al text són dignes d'utilitzar-se només en el format de "mulletes" temporals (que sona més bonic en anglès com a solució alternativa). Poden ser rellevants en les primeres etapes de la migració d'una aplicació a Kubernetes, però no haurien d'arrelar.

La via general recomanada és desfer-se'n a favor de la modificació arquitectònica de l'aplicació d'acord amb el que ja és conegut per molts. Aplicació de 12 factors. Tanmateix, això, portar l'aplicació a una forma sense estat, significa inevitablement que es requeriran canvis en el codi, i aquí és important trobar un equilibri entre les capacitats/requisits de l'empresa i les perspectives d'implementació i manteniment del camí escollit. .

PS

Llegeix també al nostre blog:

Font: www.habr.com

Afegeix comentari