
Bei der Einrichtung eines CI/CD-Prozesses mit Kubernetes gibt es manchmal Probleme mit der KompatibilitĂ€t zwischen den Anforderungen der neuen Infrastruktur und der darauf portierten Anwendung. Insbesondere ist es wichtig, in der Phase der Anwendungsentwicklung ein eins Image zu erhalten, das in alle den Umgebungen und Clustern des Projekts verwendet wird. Dieses Prinzip liegt der korrekten laut Google. Und unser Technologiedirektor hat oft darĂŒber gesprochen.
Es gibt jedoch Situationen, in denen ein fertiger Framework im Code der Website verwendet wird, dessen Nutzung EinschrĂ€nkungen fĂŒr die weitere Nutzung mit sich bringt. WĂ€hrend dies in einer 'normalen Umgebung' leicht zu bewĂ€ltigen ist, kann ein solches Verhalten in Kubernetes problematisch werden, insbesondere wenn man damit zum ersten Mal konfrontiert wird. Obwohl ein einfallsreicher Geist in der Lage ist, infrastrukturelle Lösungen vorzuschlagen, die auf den ersten Blick offensichtlich und sogar gut erscheinen, ist es wichtig, sich daran zu erinnern, dass die meisten Situationen architektonisch gelöst werden mĂŒssen..
Wir werden populĂ€re Workaround-Lösungen fĂŒr die Speicherung von Dateien untersuchen, die zu unangenehmen Folgen bei der Nutzung des Clusters fĂŒhren können, und auf den korrekteren Weg hinweisen.
Statische Speicherung
Zur Veranschaulichung betrachten wir eine Webanwendung, die einen statischen Generator verwendet, um eine Sammlung von Bildern, Styles und anderem zu erhalten. Zum Beispiel gibt es im PHP-Framework Yii einen integrierten Asset-Manager, der einzigartige Verzeichnisnamen generiert. So ergibt sich ein Satz von eindeutig nicht ĂŒberschneidenden Pfaden fĂŒr die Website-Statische (dies geschieht aus mehreren GrĂŒnden â beispielsweise zur Vermeidung von Duplikaten bei der Nutzung des gleichen Ressourcen durch viele Komponenten). Daher wird beim ersten Zugriff auf das Modul der Web-Ressource die Statische generiert und abgelegt (in der Regel handelt es sich hĂ€ufig um Symlinks, aber darauf kommen wir spĂ€ter zurĂŒck), wobei ein einzigartiger Stammverzeichnis fĂŒr dieses Deployment verwendet wird:
-
webroot/assets/2072c2df/css/⊠-
webroot/assets/2072c2df/images/⊠-
webroot/assets/2072c2df/js/âŠ
Welche Risiken birgt dies im Hinblick auf den Cluster?
Ein einfaches Beispiel
Nehmen wir einen ziemlich hĂ€ufigen Fall an, bei dem nginx vor PHP steht, um statische Dateien auszuliefern und einfache Anfragen zu bearbeiten. Die einfachste Methode istâ Deployment mit zwei Containern:
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.confIn vereinfachter Form lÀsst sich die nginx-Konfiguration wie folgt zusammenfassen:
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;
}
} Beim ersten Zugriff auf die Website im PHP-Container erscheinen Assets. Wenn jedoch zwei Container innerhalb eines Pods verwendet werden, ist nginx sich dieser statischen Dateien nicht bewusst, die - gemÀà der Konfiguration - an ihn ausgegeben werden sollten. Infolgedessen wird der Kunde bei allen Anfragen an CSS- und JS-Dateien einen 404-Fehler sehen. Die einfachste Lösung besteht darin, ein gemeinsames Verzeichnis fĂŒr die Container einzurichten. Eine primitive Variante wĂ€re ein gemeinsamer. 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.confJetzt werden die im Container generierten statischen Dateien korrekt von nginx bereitgestellt. Ich möchte jedoch daran erinnern, dass dies eine primitive Lösung ist, und somit ist sie weit entfernt von der idealen Umsetzung und hat ihre eigenen Nuancen und Verbesserungspotenziale, die ich weiter unten erlÀutern werde.
Fortschrittlicherer Speicher
Stellen wir uns nun vor, der Benutzer hat die Website betreten, die Seite mit den im Container vorhandenen Stilen geladen, und wĂ€hrend er diese Seite liest, haben wir den Container erneut bereitgestellt. Im Asset-Verzeichnis ist es leer geworden, und es ist ein PHP-Request erforderlich, um die Generierung neuer Assets zu starten. Selbst danach werden die Links zur alten Statische nicht mehr aktuell sein, was zu Anzeige-Fehlern fĂŒhrt.
DarĂŒber hinaus haben wir wahrscheinlich ein mehr oder weniger stark belastetes Projekt, was bedeutet, dass eine einzige Kopie der Anwendung nicht ausreicht:
- Wir skalieren Deployment auf zwei Replikate.
- Bei der ersten Anfrage an die Website wurden in einer Replik Assets erstellt.
- Irgendwann hat der Ingress (zu Lastenverteilungszwecken) beschlossen, die Anfrage an die zweite Replik zu senden, und dort sind diese Assets noch nicht vorhanden. Vielleicht sind sie dort bereits nicht mehr vorhanden, weil wir gerade
RollingUpdateund im Moment ein Deployment durchfĂŒhren.
Zusammenfassend ergibt sich â erneut Fehler.
Um alte Assets nicht zu verlieren, kann man Ă€ndern emptyDir findet man hostPath, indem man die Statische physisch an einen Knoten des Clusters speichert. Dieser Ansatz hat den Nachteil, dass wir faktisch an einen bestimmten Knoten des Clusters gebunden sein mĂŒssen. mit Ihrer Anwendung, denn im Falle eines Wechsels zu anderen Knoten wird das Verzeichnis möglicherweise nicht die erforderlichen Dateien enthalten. Alternativ ist eine Art von Hintergrundsynchronisierung des Verzeichnisses zwischen den Knoten erforderlich.
Welche LösungsansÀtze gibt es?
- Wenn die Hardware und die Ressourcen es zulassen, kann man verwenden, um ein gleichberechtigtes Verzeichnis fĂŒr statische Inhalte zu organisieren. empfiehlt SSD-Laufwerke, mindestens dreifache Replikation und eine stabile "dicke" Verbindung zwischen den Knoten des Clusters.
- Eine weniger anspruchsvolle Option wĂ€re die Einrichtung eines NFS-Servers. Dabei sollte jedoch das mögliche erhöhte Antwortzeit bei der Bearbeitung von Anfragen durch den Webserver berĂŒcksichtigt werden, und die Ausfallsicherheit lĂ€sst zu wĂŒnschen ĂŒbrig. Die Folgen eines Ausfalls können catastrophal sein: der Verlust des Mounts fĂŒhrt dazu, dass der Cluster durch die aufkommende LA-Last, die in die Höhe strebt, zum Scheitern verurteilt ist.
Neben allem, um alle Optionen fĂŒr die Erstellung eines dauerhaften Speichers zu berĂŒcksichtigen, wird eine Hintergrundbereinigung veralteter DateisĂ€tze erforderlich sein, die ĂŒber einen bestimmten Zeitraum angesammelt wurden. Vor den PHP-Containern kann man DaemonSet von caching-nginx, die Kopien von Assets fĂŒr eine begrenzte Zeit speichern. Dieses Verhalten lĂ€sst sich leicht mit proxy_cache in Tagen oder Gigabyte Speicherplatz anpassen.
Die Kombination dieser Methode mit den oben genannten verteilten Dateisystemen bietet enorme Möglichkeiten, wobei das einzige Limit das Budget und die technischen Möglichkeiten derjenigen sind, die dies umsetzen und pflegen werden. Aus Erfahrung können wir sagen, dass je einfacher das System ist, desto stabiler funktioniert es. Mit der HinzufĂŒgung solcher Schichten wird die Infrastruktur viel schwieriger zu warten, und gleichzeitig erhöht sich die Zeit, die fĂŒr Diagnosen und Wiederherstellungen im Falle von AusfĂ€llen benötigt wird.
Empfehlung
Wenn die Umsetzung der vorgeschlagenen Speicheroptionen auch fĂŒr Sie nicht gerechtfertigt erscheint (zu kompliziert, zu teuer ...), sollten Sie die Situation aus einer anderen Perspektive betrachten. NĂ€mlich â die Architektur des Projekts zu durchleuchten und das Problem im Code zu beheben., indem man sich an eine statische Datenstruktur im Image bindet, wird der Inhalt oder das Verfahren zur âAufwĂ€rmungâ und/oder Vor-Compilierung von Assets wĂ€hrend des Build-Prozesses eindeutig definiert. So erhalten wir absolut vorhersehbares Verhalten und einen einheitlichen Satz von Dateien fĂŒr alle Umgebungen und Replikate der gestarteten Anwendung.
Wenn wir zu einem konkreten Beispiel mit dem Yii-Framework zurĂŒckkehren und nicht auf seine Struktur eingehen (was nicht das Ziel des Artikels ist), reicht es aus, auf zwei gĂ€ngige AnsĂ€tze hinzuweisen:
- Den Build-Prozess so Àndern, dass Assets an einem vorhersehbaren Ort abgelegt werden. So wird in Erweiterungen wie .
- konkrete Hashes fĂŒr Asset-Verzeichnisse zu definieren, wie zum Beispiel in (ab Folie â35). Ăbrigens empfiehlt der Autor des Vortrags letztendlich (und nicht ohne Grund!), die Assets nach dem Build-Prozess auf dem Build-Server in ein zentrales Repository (wie S3) hochzuladen und davor ein CDN zu setzen.
Hochgeladene Dateien
Ein weiterer Fall, der beim Transfer einer Anwendung in einen Kubernetes-Cluster unbedingt funktionieren wird, ist die Speicherung von Benutzerdaten im Dateisystem. Zum Beispiel haben wir wieder eine PHP-Anwendung, die Dateien ĂŒber ein Hochladeformular entgegennimmt, wĂ€hrend der Verarbeitung etwas damit macht und sie zurĂŒckgibt.
Der Ort, an dem diese Dateien abgelegt werden sollen, muss in der Kubernetes-RealitĂ€t fĂŒr alle Replikate der Anwendung gemeinsam sein. Je nach KomplexitĂ€t der Anwendung und der Notwendigkeit zur Organisation der Persistenz dieser Dateien können dies die oben genannten Optionen fĂŒr gemeinsame GerĂ€te sein, aber wie wir sehen, haben sie ihre eigenen Nachteile.
Empfehlung
Eine der Lösungsmöglichkeiten ist die Verwendung eines S3-kompatiblen Speichers (sei es auch nur eine Art selbst gehosteten Typs wie minio). Der Ăbergang zur Arbeit mit S3 erfordert Ănderungen auf der Code-Ebene, und wie die Bereitstellung von Inhalten im Frontend erfolgen wird, haben wir bereits .
Benutzersitzungen
Es ist wichtig, die Organisation der Speicherung von Benutzersitzungen gesondert zu erwĂ€hnen. Oft sind auch das Dateien auf der Festplatte, was in Bezug auf Kubernetes zu stĂ€ndigen Authentifizierungsanfragen an den Benutzer fĂŒhrt, wenn seine Anfrage in einen anderen Container gelangt.
Ein Teil des Problems wird durch die Aktivierung von stickySessions in der Ingress (dieses Feature wird von allen gĂ€ngigen Ingress-Controllern unterstĂŒtzt â weitere Informationen finden Sie in ), um den Benutzer an einen bestimmten Pod mit der Anwendung zu binden:
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: /Aber das wird nicht alle Probleme bei wiederholten Deployments lösen.
Empfehlung
Eine bessere Lösung wĂ€re es, die Anwendung auf Sessionspeicherung in memcached, Redis und Ă€hnlichen Lösungen zu migrieren â im Grunde genommen, vollstĂ€ndig auf dateibasierte Optionen zu verzichten.
Fazit
Die hier besprochenen infrastrukturellen Lösungen sollten nur als vorĂŒbergehende âNotlösungenâ (was im Englischen eleganter als "workaround" klingt) betrachtet werden. Sie können in den frĂŒhen Phasen der Migration der Anwendung nach Kubernetes relevant sein, sollten jedoch keine dauerhafte Lösung werden.
Der allgemein empfohlene Weg besteht darin, diese durch die architektonische Weiterentwicklung der Anwendung zu ersetzen, gemÀà dem, was bereits vielen bekannt ist. Die Anpassung der Anwendung an einen stateless Zustand erfordert jedoch zwangslĂ€ufig Ănderungen am Code. Hier ist es wichtig, ein Gleichgewicht zwischen den geschĂ€ftlichen Anforderungen und den Möglichkeiten der Umsetzung und Wartung des gewĂ€hlten Ansatzes zu finden.
P.S.
Lesen Sie auch in unserem Blog:
- «»;
- «»;
- «» (von Red Hat);
- «».
Quelle: habr.com
