
Bei der Erstellung des CI/CD-Prozesses mit Kubernetes gibt es manchmal Probleme mit der InkompatibilitĂ€t der Anforderungen an die neue Infrastruktur und der zu ĂŒbertragenden Anwendung. Insbesondere ist es in der Phase des Anwendungsbaus wichtig, eine Abbildung zu erhalten, die in allen Umgebungen und Clustern des Projekts verwendet wird. Dieses Prinzip bildet die Grundlage fĂŒr ein gutes Container-Management, nach Ansicht von Google und auch unser technischer Direktor dazu sagte. Schauen wir uns beliebte Workaround-Lösungen zur Dateispeicherung an, die bei der Nutzung eines Clusters zu unangenehmen Konsequenzen fĂŒhren können, und zeigen wir auch den richtigen Weg auf.
Speicherung von Statischen Inhalten Zur Veranschaulichung betrachten wir eine Webanwendung, die einen Statik-Generator verwendet, um eine Reihe von Bildern, Stilen und anderem zu erstellen. Zum Beispiel verfĂŒgt das PHP-Framework Yii ĂŒber einen integrierten Asset-Manager, der eindeutige Verzeichnisnamen generiert. Daher entsteht als Ergebnis eine Sammlung von eindeutig nicht ĂŒberschneidenden Pfaden fĂŒr die Statik der Website (dies wurde aus mehreren GrĂŒnden getan â zum Beispiel zur Vermeidung von Duplikaten bei der Verwendung derselben Ressource durch mehrere Komponenten). So geschieht es standardmĂ€Ăig, dass beim ersten Zugriff auf das Modul der Webressource die Statik generiert und verteilt wird (in Wirklichkeit sind dies hĂ€ufig symbolische Links, aber dazu spĂ€ter mehr) mit einem fĂŒr dieses Deployment eindeutigen gemeinsamen Stammverzeichnis:.
webroot/assets/2072c2df/css/âŠ
webroot/assets/2072c2df/images/âŠ
webroot/assets/2072c2df/js/âŠ
-
Welche Folgen hat dies im Kontext eines Clusters? -
Ein einfaches Beispiel -
Nehmen wir den recht gĂ€ngigen Fall, dass vor PHP nginx steht, um Statische Inhalte auszuliefern und einfache Anfragen zu bearbeiten. Der einfachste Weg ist â
Deployment
mit zwei Containern:
Nehmen wir einen recht verbreiteten Anwendungsfall, bei dem nginx vor PHP steht, um statische Dateien bereitzustellen und einfache Anfragen zu verarbeiten. Der einfachste Weg 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 besteht die nginx-Konfiguration aus Folgendem:
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 erscheinen im PHP-Container die Assets. Bei zwei Containern innerhalb eines Pods weiĂ nginx jedoch nichts ĂŒber diese statischen Dateien, die gemÀà der Konfiguration genau an ihn geliefert werden sollten. Infolgedessen sieht der Client auf alle Anfragen zu CSS- und JS-Dateien einen 404-Fehler. Das einfachste Lösung hier 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. Aber ich möchte daran erinnern, dass dies eine primitive Lösung ist, was bedeutet, dass sie weit vom Ideal entfernt ist und ihre eigenen Nuancen und UnzulĂ€nglichkeiten hat, ĂŒber die ich spĂ€ter berichten werde.
Ein fortschrittlicherer Speicher
Stellen wir uns nun die Situation vor, in der der Benutzer die Website aufgerufen hat, die Seite mit den im Container vorhandenen Stilen geladen hat, und wĂ€hrend er diese Seite liest, haben wir den Container erneut bereitgestellt. Im Asset-Verzeichnis ist es leer geworden und eine Anfrage an PHP ist erforderlich, um die Generierung neuer Assets zu starten. Selbst nach diesem Vorgang werden die Links zur alten Statik nicht mehr aktuell sein, was zu Anzeigeproblemen der Statik fĂŒhren wird.
AuĂerdem haben wir höchstwahrscheinlich ein mehr oder weniger stark ausgelastetes Projekt, was bedeutet, dass eine Kopie der Anwendung nicht ausreichen wird:
- Wir skalieren Deployment auf zwei Replikate.
- Bei der ersten Anfrage an die Website wurden in einer Replikate Assets erstellt.
- Irgendwann beschloss der Ingress (zum Zwecke der Lastverteilung), eine Anfrage an die zweite Replikate zu senden, und dort sind diese Assets noch nicht vorhanden. Vielleicht sind sie dort auch nicht mehr vorhanden, weil wir
RollingUpdateverwendet werden und derzeit die Bereitstellung durchfĂŒhren.
Insgesamt das Ergebnis â wieder Fehler.
Um alte Assets nicht zu verlieren, kann man Ă€ndern emptyDir auf hostPath, indem man die Statik physisch auf dem Knoten des Clusters speichert. Dieser Ansatz hat den Nachteil, dass wir praktisch uns an einen bestimmten Knoten des Clusters mit unserer Anwendung binden mĂŒssen, denn im Falle eines Umzugs zu anderen Knoten wird das Verzeichnis die erforderlichen Dateien nicht enthalten. Alternativ ist eine Art Hintergrundsynchronisierung des Verzeichnisses zwischen den Knoten erforderlich.
Welche Lösungen gibt es?
- Wenn die Hardware und die Ressourcen es zulassen, kann man verwenden, um ein verteiltes Verzeichnis fĂŒr die Belange der Statik einzurichten. empfiehlt SSD-Laufwerke, mindestens dreifache Replikation und eine stabile "fette" Verbindung zwischen den Knoten des Clusters.
- Eine weniger anspruchsvolle Option wĂ€re die Einrichtung eines NFS-Servers. Allerdings sollte man dabei das mögliche Ansteigen der Reaktionszeit bei der Verarbeitung von Anfragen durch den Webserver berĂŒcksichtigen, und auch die Fehlertoleranz lĂ€sst zu wĂŒnschen ĂŒbrig. Die Konsequenzen eines Ausfalls sind katastrophal: Der Verlust des Mounts verurteilt den Cluster zum Untergang unter dem Druck der Last, die in die Höhe strebt.
Neben allem anderen benötigen alle Optionen zur Einrichtung eines permanenten Speichers eine Hintergrundreinigung veralteter DateisĂ€tze, die ĂŒber einen bestimmten Zeitraum angesammelt wurden. Vor den PHP-Containern kann man ein DaemonSet aus zwischen speichernden Nginx einrichten, die Kopien von Assets fĂŒr eine begrenzte Zeit speichern werden. Dieses Verhalten lĂ€sst sich leicht ĂŒber konfigurieren. Proxy-Cache mit einer Speicherdauer in Tagen oder Gigabyte Festplattenspeicher.
Die Kombination dieser Methode mit den oben genannten verteilten Dateisystemen eröffnet enormes Potenzial, wobei das einzige Limit das Budget und das technische Know-how derjenigen ist, die dies umsetzen und unterstĂŒtzen werden. Aus Erfahrung können wir sagen, dass je einfacher das System, desto stabiler funktioniert es. Bei der HinzufĂŒgung solcher Schichten wird die Wartung der Infrastruktur erheblich komplizierter, was auch die Zeit erhöht, die fĂŒr Diagnosen und Wiederherstellungen bei AusfĂ€llen benötigt wird.
Empfehlung
Wenn die Implementierung der vorgeschlagenen Speicheroptionen Ihnen auch als unangemessen (komplex, teuer...) erscheint, sollten Sie die Situation aus einer anderen Perspektive betrachten. NĂ€mlich â die Architektur des Projekts zu durchdringen und das Problem im Code zu beseitigen, indem Sie sich an eine bestimmte statische Datenstruktur im Image anhĂ€ngen, das eindeutige Bestimmen des Inhalts oder das Verfahren "AufwĂ€rmen" und\/oder die Vorkompilierung von Assets wĂ€hrend der Build-Phase des Images. So erhalten wir ein absolut vorhersehbares Verhalten und denselben Satz von Dateien fĂŒr alle Umgebungen und Replikate der laufenden Anwendung.
Wenn wir zum konkreten Beispiel mit dem Yii-Framework zurĂŒckkehren und nicht tief in seine Struktur eintauchen (was nicht Ziel des Artikels ist), reicht es aus, auf zwei beliebte AnsĂ€tze hinzuweisen:
- Den Build-Prozess des Images so zu Àndern, dass Assets an einem vorhersehbaren Ort platziert werden. So schlagen Erweiterungen wie .
- vor, spezifische Hashes fĂŒr die Asset-Verzeichnisse zu definieren, wie beispielsweise in (beginnend mit Folie Nr. 35). Ăbrigens empfiehlt der Referent am Ende (und nicht ohne Grund!), nach dem Build der Assets auf dem Build-Server diese in ein zentrales Repository (wie S3) hochzuladen, vor dem ein CDN platziert wird.
Hochgeladene Dateien
Ein weiterer Anwendungsfall, der beim Umzug einer Anwendung in ein Kubernetes-Cluster unbedingt erfolgreich sein wird, ist die Speicherung von Benutzerdaten in einem Dateisystem. Zum Beispiel haben wir wieder eine PHP-Anwendung, die Dateien ĂŒber ein Upload-Formular annimmt, etwas damit wĂ€hrend der Verarbeitung macht und sie zurĂŒckgibt.
Der Ort, an dem diese Dateien abgelegt werden sollten, muss in der Kubernetes-RealitĂ€t fĂŒr alle Replikate der Anwendung gemeinsam sein. Je nach KomplexitĂ€t der Anwendung und dem Bedarf an der Organisation der Persistenz dieser Dateien kann dieser Ort die oben erwĂ€hnten Varianten von Shared-GerĂ€ten sein, aber wie wir sehen, haben diese ihre eigenen Nachteile.
Empfehlung
Eine der Möglichkeiten zur Lösung ist die Verwendung eines S3-kompatiblen Speichers (selbst wenn es eine Art von Self-hosted Variante wie minio ist). Der Ăbergang zur Arbeit mit S3 erfordert Ănderungen auf Code-Ebene, und wie der Inhalt im Frontend ausgeliefert wird, haben wir bereits .
Benutzersitzungen
Es ist erwĂ€hnenswert, wie die Speicherung von Benutzersitzungen organisiert wird. Oftmals handelt es sich dabei auch um Dateien auf der Festplatte, was im Kontext von Kubernetes zu stĂ€ndigen Autorisierungsanforderungen an den Benutzer fĂŒhren kann, wenn seine Anfrage in einen anderen Container gelangt.
Teilweise wird das Problem durch die Aktivierung von stickySessions auf dem Ingress (dieses Feature wird in allen gĂ€ngigen Ingress-Controllern unterstĂŒtzt â weitere Informationen finden Sie in ), um den Benutzer an ein bestimmtes Pod 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 die Probleme bei wiederholten Deployments nicht lösen.
Empfehlung
Eine geeignetere Vorgehensweise wĂ€re es, die Anwendung auf die Speicherung von Sitzungen in memcached, Redis und Ă€hnlichen Lösungen umzustellen â insgesamt also vollstĂ€ndig auf dateibasierten Varianten zu verzichten.
Fazit
Die im Text betrachteten InfrastrukturmaĂnahmen sind nur als temporĂ€re "Notlösungen" (was auf Englisch schöner als "workaround" klingt) geeignet. Sie können zu Beginn der Migration der Anwendung nach Kubernetes relevant sein, sollten jedoch nicht "Wurzeln schlagen".
Der allgemeine empfohlene Weg fĂŒhrt dazu, sich von ihnen zugunsten einer architektonischen Ăberarbeitung der Anwendung zu verabschieden, die bereits vielen gut bekannt ist. . Dies bedeutet jedoch, dass die Anwendung in einen Stateless-Zustand ĂŒberfĂŒhrt werden muss, was zwangslĂ€ufig Ănderungen im Code erfordert. Hier ist es wichtig, ein Gleichgewicht zwischen den Möglichkeiten/Anforderungen des Unternehmens und den Perspektiven der Umsetzung und Wartung des gewĂ€hlten Weges zu finden.
P.S.
Lesen Sie auch in unserem Blog:
- «»;
- «»;
- «» (von Red Hat);
- «».
Quelle: habr.com
