Solr auf Kubernetes ausführen
Lernen Sie die Grundlagen der Ausführung von Apache Solr auf Kubernetes für Suchanwendungen.
Wichtiger Hinweis: Bitte besuchen Sie die Seite Solr Operator für die neuesten Updates zu Solr auf Kubernetes.
Dies ist der erste Teil einer zweiteiligen Serie, in der wir die Grundlagen des Betriebs von Solr auf Kubernetes (k8s) für Suchtechniker vorstellen. Im Einzelnen behandeln wir die folgenden Themen:
- Erste Schritte mit Google Kubernetes Engine (GKE)
- Steuerkarten
- StatefulSets, initContainers, ConfigMaps und persistente Volumes
- Aktivitäts- und Bereitschaftstests
- Pod-übergreifende Synchronisierung
- Lastausgleichsdienste und Pod-Selektoren
- Upgrade von Solr mit Zero-Downtime-Bereitstellungen im Canary-Stil
- Leistungs- und Belastungstests
- Überwachung von Solr-Metriken mit Prometheus und Grafana
- Verschlüsselung des Datenverkehrs zwischen Solr-Instanzen mit TLS
Im nächsten Beitrag werden wir uns mit Fragen der automatischen Skalierung, Performance- und Lasttests und anderen fortgeschrittenen Operationen befassen. Bevor wir uns mit den Details befassen, lassen Sie uns der Frage nachgehen, warum Sie Solr auf Kubernetes betreiben sollten. k8s bietet Solr-Betreibern vor allem drei Vorteile:
- Hilft bei der Durchsetzung von Best Practices und bewährten Entwurfsmustern für verteilte Systeme,
- Reduziert die Betriebskosten eines komplexen Systems wie Solr und
- Führt Solr in der gleichen Betriebsumgebung aus, in der Benutzer Microservice-basierte Anwendungen ausführen möchten.
In Bezug auf Best Practices und Design Patterns gibt uns Kubernetes eine gemeinsame Sprache, um zu erklären, wie eine verteilte Anwendung in der Produktion installiert, konfiguriert und gewartet werden sollte. Betriebsingenieure lernen, wie Solr durch die Verwendung von Kubernetes-eigenen Ressourcen wie Diensten, StatefulSets und Volume Claims verwaltet wird, anstatt sich um interne Implementierungsdetails zu kümmern. Mit Kubernetes können sich die Betriebsteams auf die Dimensionierung von Clustern, die Überwachung und die Messung der Leistung konzentrieren, indem sie ihre Standardtools für Metriken, Protokollierung, Warnmeldungen usw. verwenden.
Was die Senkung der Betriebskosten angeht, so ermöglicht es Kubernetes den allgemeinen Betriebsingenieuren, Solr zu betreiben, ohne dass unsere Kunden in Schulungen oder die Einstellung von Spezialisten investieren müssen. Dies ist besonders wichtig für Solr, wo der Betrieb eines großen Solr-Clusters traditionell sehr spezielle Fähigkeiten erfordert. Auf dem heutigen Arbeitsmarkt ist es ein echtes Risiko, dass Ihr Solr-Experte abwandert, um ein besseres Angebot zu erhalten. Natürlich beseitigt k8s nicht die gesamte Komplexität, die mit dem Betrieb von Solr im großen Maßstab verbunden ist, aber es geht einen sehr großen Schritt in diese Richtung.
Kubernetes ist speziell für die Verwaltung von Cloud-nativen Microservice-basierten Anwendungen konzipiert. Dank der Fähigkeit von Solr, riesige Datensätze in Sekundenschnelle zu durchsuchen und Ad-hoc-Analysen mit niedriger Latenzzeit und Streaming-Ausdrücken durchzuführen, ist Solr ein attraktives Backend für datenintensive Anwendungen.
Es wäre jedoch unproduktiv, Ihre Microservices in Sekundenschnelle in Kubernetes bereitzustellen, dann aber einen komplexen Bereitstellungsprozess für Solr außerhalb von k8s durchlaufen zu müssen. Im Idealfall können Ops-Teams Solr einfach zusammen mit ihren Microservice-Anwendungen, die davon abhängen, bereitstellen. Das von Lucidworks bereitgestellte Solr-Steuerungsdiagramm macht dies möglich.
Da Sie nun wissen, warum es eine gute Idee ist, Solr auf Kubernetes laufen zu lassen, lassen Sie uns die Ärmel hochkrempeln und einen Solr-Cluster in der Cloud starten.
Voraussetzungen
In diesem Abschnitt erfahren Sie, wie Sie sich mit Kubernetes einrichten und Ihren ersten Cluster in GKE starten. Wenn Sie bereits mit kubectl, helm, gcloud und GKE vertraut sind, können Sie getrost zum nächsten Abschnitt übergehen.
Kubernetes
In diesem Dokument zeigen wir Ihnen, wie Sie einen Google Kubernetes Engine (GKE) basierten Cluster bereitstellen. Die GKE-Option wird empfohlen, da Sie schnell mehrere Knoten bereitstellen können, GKE eine unterhaltsame Umgebung ist, um k8s-Konzepte zu lernen, und Google Ihnen für den Anfang 300 $ an kostenlosen Credits zur Verfügung stellt. Bevor Sie fortfahren, richten Sie Ihren Google Cloud-Zugang und Ihr SDK ein, indem Sie die Anweisungen hier befolgen: https://cloud.google.com/sdk/docs/quickstarts. Sie können auch einen Solr-Cluster mit nur einem Knoten auf minikube lokal betreiben, aber darauf gehen wir hier nicht ein.
kubectl
kubectl ist das Kommandozeilen-Tool für die Interaktion mit einem Kubernetes-Cluster. Es sollte mit minikube oder dem gcloud SDK installiert worden sein. Um zu überprüfen, ob kubectl verfügbar ist, führen Sie aus: `kubectl Version`. Wenn es nicht installiert ist, führen Sie einfach aus:
gcloud components install kubectl
Irgendwann werden Sie es leid sein, „kubectl“ einzugeben. Tun Sie Ihrem zukünftigen Ich also einen Gefallen und fügen Sie den folgenden Alias zu Ihrem Shell-Init-Skript hinzu (z.B. ~/.bash_profile):
alias k=kubectl
GKE-Cluster starten
Sie können den Cluster von der Befehlszeile aus starten, aber wenn Sie dies zum ersten Mal tun, empfehlen wir Ihnen, die GCloud-Konsolen-Benutzeroberfläche zu verwenden. Die vorgeschlagene Standard-Master-Version und die Instanztypen n1-standard-4 sollten für den Moment ausreichen. Klicken Sie auf die Option Erweiterte Bearbeitung, um die automatische Skalierung des k8s-Clusters auf bis zu 5 Knoten zu aktivieren (siehe Screenshot).
Notieren Sie sich den Namen Ihres Clusters und die Projekt-ID. In den folgenden Beispielen verwenden wir die Werte solr-perf-1 bzw. solr-dev. Sobald Ihr Cluster läuft, wechseln Sie in die Befehlszeile, um die Standardeinstellungen für Compute/Zone, Core/Account und Projekt-ID für die Arbeit mit Ihrem Cluster festzulegen, z. B:
gcloud config set compute/zone us-west2-b gcloud config set core/account some-account@lucidworks.com gcloud config set project solr-dev
Als Nächstes aktualisieren wir die kubectl-Konfigurationsdatei, damit sie mit Ihrem neuen Cluster zusammenarbeitet, indem wir dies tun:
gcloud container clusters get-credentials solr-perf-1
Um zu überprüfen, ob kubectl für Ihren Cluster richtig konfiguriert ist, tun Sie dies:
kubectl config current-context
Sie sollten eine Ausgabe wie diese sehen:
gke_solr-dev_us-west2-b_solr-perf-1
Geben Sie Ihrem Konto Cluster-Administratorrechte, indem Sie dies tun:
kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value core/account)
Profi-Tipp: Halten Sie den kubectl-Spickzettel bereit: https://kubernetes.io/docs/reference/kubectl/cheatsheet/
Helm
Helm ist ein beliebtes Tool im k8s Ökosystem für die Bereitstellung von Anwendungen. Wir verwenden Helm für die Bereitstellung von Solr. Folgen Sie also den Anweisungen hier, um Helm einzurichten: https://github.com/helm/helm. Die Installation von Tiller ist die gebräuchlichste Art, Helm zu verwenden, aber sie ist nicht erforderlich, um diesem Beitrag zu folgen. Versuchen Sie es kurz auf dem Mac:
brew install kubernetes-helm kubectl create serviceaccount --namespace kube-system tiller kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller helm init --service-account tiller helm version
Solr auf GKE bereitstellen
Beginnen wir mit der Bereitstellung eines 3-Knoten-Solr-Clusters mit Zookeeper auf GKE. Klonen Sie das Repo oder laden Sie die Zip-Datei herunter von: https://github.com/lucidworks/solr-helm-chart. Wir haben das Helm-Diagramm an https://github.com/helm/charts geschickt, aber es muss noch genehmigt werden.
Eine der schönen Funktionen von Helm ist, dass ein Diagramm dynamisch mit anderen Diagrammen verknüpft werden kann. Zum Beispiel hängt das Solr-Diagramm vom Zookeeper-Diagramm ab. Lassen Sie uns das Zookeeper-Diagramm mit dem Solr-Diagramm verknüpfen:
cd solr-helm-chart/solr helm repo add incubator https://storage.googleapis.com/kubernetes-charts-incubator helm dependency update
Nehmen Sie sich vor der Bereitstellung einen Moment Zeit, um die in values.yaml definierten Konfigurationsvariablen durchzusehen. In dieser Datei können Sie die gängigsten Variablen für eine Solr-Bereitstellung anpassen, z. B. die Ressourcenzuweisung, die an Solr übergebenen JVM-Args und die Solr-Version (derzeit 7.6.0).
In der Produktion ist es üblich, Helm-Charts an den Helm Tiller Service zu senden, der in k8s läuft. Für diese Übung überspringen wir Tiller und verwenden den Befehl helm template, um ein Kubernetes-Manifest aus den Solr- und Zookeeper-Helm-Charts zu erstellen. Lassen Sie uns außerdem die Solr-Version auf 7.5.0 ändern, damit wir später in der Übung auf 7.6.0 aktualisieren können:
helm template . --set image.tag=7.5.0 --name solr > solr.yaml
Stellen Sie nun das Solr-Manifest (solr.yaml) in Kubernetes bereit:
kubectl apply -f solr.yaml
Haben Sie Geduld, während die Zookeeper- und Solr-Pods initialisiert werden. Kubernetes muss möglicherweise die Docker-Images von Docker Hub abrufen und persistente Volumes bereitstellen. Machen Sie sich auch nicht zu viele Gedanken über etwaige Warnungen, die Sie in der GCloud Console UI sehen, während die Pods initialisiert werden. Unserer Erfahrung nach ist die Cluster Workload UI etwas zu aggressiv mit ihren Warnungen, während die Pods bereitgestellt werden, und kann ein falsches Gefühl von Problemen vermitteln. Wenn Solr und Zookeeper nicht innerhalb von 3-4 Minuten laufen, wenn Sie dies zum ersten Mal tun, können Sie mit der Fehlersuche beginnen.
Um den Status der Pods zu sehen, tun Sie dies:
kubectl get pods
Wenn sie alle fertig sind, sehen Sie eine Ausgabe wie diese:
NAME READY STATUS RESTARTS AGE solr-0 1/1 Running 0 38m solr-1 1/1 Running 0 35m solr-2 1/1 Running 0 34m solr-zookeeper-0 1/1 Running 0 38m solr-zookeeper-1 1/1 Running 0 37m solr-zookeeper-2 1/1 Running 0 36m
Wenn ein Pod Schwierigkeiten hat, in den Status Ausgeführt zu gelangen oder nur langsam online geht, verwenden Sie den Befehl describe, um podspezifische Aktivitäten zu sehen, z.B. `kubectl describe pod solr-0`. Die Ausgabe des describe-Befehls enthält die Ereignisse, die Kubernetes zum Starten des Pods benötigt. Nehmen Sie sich einen Moment Zeit und sehen Sie sich die Ereignisse an, die für den Pod solr-0 gemeldet werden.
Es sieht so aus, als ob alles läuft. Was nun? Die meisten Anwendungen, die Solr als Backend verwenden, werden es nicht dem Internet aussetzen und stattdessen mit einer zustandslosen Microservice-Suchanwendung wie Lucidworks Fusion versehen. Lassen Sie uns also einfach einen lokalen Port an den Cluster weiterleiten: kubectl port-forward solr-0 28983:8983
Rufen Sie nun Ihren Browser auf: http://localhost:28983/solr/#/~cloud?view=nodes
Sie sollten etwas sehen wie:
Da wir nun eine Möglichkeit haben, Solr von unserem lokalen Arbeitsplatz aus zu kontaktieren, lassen Sie uns eine Sammlung wie folgt erstellen:
curl -v "http://localhost:28983/solr/admin/collections?action=CREATE&name=perf3x1&numShards=3&replicationFactor=1&maxShardsPerNode=1&collection.configName=_default"
Und fügen Sie einige Testdaten hinzu:
wget https://raw.githubusercontent.com/apache/lucene-solr/master/solr/example/exampledocs/books.json curl "http://localhost:28983/solr/perf3x1/update/json/docs?commit=true" -H "Content-Type: application/json" --data-binary @books.json
Jetzt haben Sie einen Solr-Cluster mit 3 Knoten, der in Kubernetes läuft. Gute Arbeit! Jetzt gehen wir ins Detail, wie die Bereitstellung funktioniert, und behandeln grundlegende Vorgänge wie die Aktivierung von TLS zwischen den Solr-Instanzen.
Kubernetes Schrauben und Muttern
In diesem Abschnitt gehen wir auf einige der interessanten Aspekte der Solr-Bereitstellung ein. Aus Zeitgründen gehen wir nicht näher auf Zookeeper ein und verweisen Sie stattdessen auf den folgenden Leitfaden zur Funktionsweise von Zookeeper in Kubernetes: https://kubernetes.io/docs/tutorials/stateful-application/zookeeper/
Außerdem gibt es eine Reihe von wichtigen Kubernetes-Konzepten, die wir hier nicht behandeln. Eine ausführlichere Beschreibung der k8s-Konzepte finden Sie unter: https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/
Pod
Ein Pod ist eine Gruppe von einem oder mehreren Containern (in der Regel Docker), die Netzwerk und Speicher gemeinsam nutzen. Der Einfachheit halber können Sie sich einen Pod als eine Gruppe von zusammenhängenden Prozessen auf einem anwendungsspezifischen logischen Host vorstellen. Die Container in einem Pod teilen sich dieselbe IP-Adresse und denselben Portbereich. Sie können also über localhost kommunizieren, sich aber nicht an denselben Port binden.
Da es sich bei k8s um ein Framework zur Container-Orchestrierung handelt, fragen Sie sich vielleicht, warum ein neuer Begriff erfunden wurde, anstatt einfach „Container“ zu verwenden? Es hat sich herausgestellt, dass viele Implementierungen zwar nur einen einzigen Container in einem Pod haben, was bei unserer Solr-Implementierung der Fall ist, dass es aber nicht ungewöhnlich ist, Pods mit mehreren Containern zu implementieren.
Ein hervorragendes Beispiel hierfür ist der von Istio eingesetzte Sidecar Envoy Proxy. Das klassische Beispiel für einen Pod mit mehreren zusammenhängenden Containern ist der Betrieb von Apache httpd und Memcached im selben Pod. Es gibt im Internet eine Reihe großartiger Ressourcen über Pods, also lassen Sie uns zu interessanteren Konzepten übergehen und wir werden bei Bedarf wichtige Aspekte der Solr-Pods nennen.
StatefulSet
Wenn Sie neu in Kubernetes sind, müssen Sie als Erstes lernen, dass sich Pods im Cluster bewegen und dass Sie darauf keinen Einfluss haben! Eigentlich sollte es Ihnen egal sein, ob sich ein Pod im Cluster bewegt, denn dieser Prozess ist ein zentraler Bestandteil des Designs von Kubernetes.
Eine der Hauptaufgaben von Kubernetes besteht darin, die Auslastung der Cluster-Ressourcen auszugleichen. Im Rahmen dieses Prozesses kann k8s beschließen, einen Pod auf einen anderen Knoten zu verschieben. Oder ein Knoten kann aus verschiedenen Gründen ausfallen und k8s muss diese ausgefallenen Pods auf einem anderen gesunden Knoten im Cluster ersetzen.
Stellen Sie sich also einmal vor, was passieren würde, wenn k8s einen Solr-Pod auf einen anderen Knoten verschiebt. Wenn die Festplatte, die Solr verwendet hat, nicht mitgenommen wurde, hat Solr bei der Initialisierung auf dem neuen Knoten keine Kerne (Lucene-Indizes) zur Verfügung und müsste eine potenziell teure Snapshot-Replikation von einem anderen Replikat im Cluster durchführen. Und woher soll er wissen, welche Kerne er replizieren muss, da diese Informationen ebenfalls auf der Festplatte gespeichert sind? Noch schlimmer wäre es bei Sammlungen, die einen Replikationsfaktor von eins verwenden, da es kein anderes Replikat gäbe, mit dem es sich synchronisieren könnte.
Dieses Problem besteht nicht nur bei Solr. Glücklicherweise hat Kubernetes eine elegante Lösung für Systeme wie Solr, die den Status auf der Festplatte speichern und diesen Status wiederherstellen müssen, wenn der Pod verschoben wird (oder abstürzt und neu gestartet wird), nämlich StatefulSets.
Wir könnten einen ganzen Blog damit verbringen, uns mit den Details eines StatefulSet zu beschäftigen, aber es gibt bereits eine große Anzahl von Ressourcen, die das tun, angefangen mit https://cloud.google.com/kubernetes-engine/docs/concepts/statefulset.
Wir möchten mit einem Missverständnis aufräumen, das wir in den Gängen gehört haben, wenn es um die Ausführung von Solr auf Kubernetes ging, nämlich dass k8s nicht gut für zustandsabhängige Anwendungen geeignet ist. Es stimmt, dass k8s eine gemischte Geschichte mit zustandsbehafteten Anwendungen hat, aber das ist ein alter Hut. StatefulSets sind eine erstklassige Funktion in k8s und es gibt viele Beispiele für erfolgreiche zustandsbehaftete Anwendungen; eine schnelle Suche auf der helm github Seite für Diagramme zeigt 110 Treffer bei der Suche nach StatefulSet: https://github.com/helm/charts/search?l=YAML&q=StatefulSet.
Sehen wir uns nun ein StatefulSet in Aktion an. Wenn Sie die Pods auflisten(kubectl get pods -l ), erhalten Sie die folgende Ausgabe:
solr-0 1/1 Running 0 1h solr-1 1/1 Running 0 1h solr-2 1/1 Running 0 1h
Dies sind die Pods im StatefulSet namens „solr“. Beachten Sie, dass jeder einen stabilen Hostnamen mit einem Ordnungsindex erhält, der mit 0 beginnt. Wenn der Pod verschwindet, kommt er mit demselben Hostnamen, aber einer anderen IP-Adresse zurück. Obwohl es für Solr unwichtig ist, da es Zookeeper zur Koordinierung der Clusteraktivitäten verwendet, werden die Replikate im Set in aufsteigender Reihenfolge initialisiert und in absteigender Reihenfolge entfernt.
Persistente Volumes
Um zu beweisen, dass ein Replikat in einem StatefulSet mit demselben Hostnamen und demselben Speicherplatz zurückkommt, müssen wir einen Pod töten. Bevor wir mit dem Töten von Pods im Cluster beginnen, lassen Sie uns einen wichtigen Aspekt von Solr StatefulSets behandeln, nämlich PersistentVolumes. Wenn Sie einen Blick in die Solr-Steuerkarte werfen, werden Sie feststellen, dass das StatefulSet den folgenden volumeMount hat:
volumeMounts: - name: solr-pvc mountPath: /opt/solr/server/home
Melden wir uns bei solr-0 an und sehen wir uns an, was das ist:
kubectl exec -it solr-0 --container solr -- /bin/bash
solr@solr-0:/opt/solr/server$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 34G 0 disk ├─sda1 8:1 29.9G 0 part /etc/hosts ... sdb 8:16 0 5G 0 disk sdc 8:32 0 20G 0 disk /opt/solr/server/home
Dies zeigt, dass wir eine 20G-Platte unter /opt/solr/server/home eingebunden haben. Wie ist das passiert? Um jedem Replikat im Set ein dauerhaftes Volume zuzuordnen, benötigen Sie eine Vorlage für den Volume-Anspruch, in der die Gruppen-ID (gid=8983 für Solr) und die gewünschte Größe (20 Gigabyte) festgelegt sind:
volumeClaimTemplates: - metadata: name: solr-pvc annotations: pv.beta.kubernetes.io/gid: "8983" spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi
Natürlich benötigen Sie für einen echten Solr-Einsatz mehr Festplattenspeicher. Diesen können Sie erhöhen, indem Sie den Parameter `volumeClaimTemplates.storageSize` in der Datei values.yaml ändern. Hinter den Kulissen weist GKE Festplatten von Google Compute Engine zu. Sie können Details über den für die persistenten Volumes zugewiesenen Speicherplatz über die Benutzeroberfläche abrufen (siehe unten):
kubectl describe PersistentVolumeClaim solr-pvc-solr-0
Verwendung eines initContainers für Bootstrap Solr Home
Im Verzeichnis /opt/solr/server/home finden Sie eine Datei solr.xml. Hier gibt es ein paar interessante Dinge zu sehen. Erstens übergibt die Pod-Spezifikation für das StatefulSet das Folgende über eine Umgebungsvariable an Solr:
- name: "SOLR_HOME" value: "/opt/solr/server/home"
Solr 7.x erfordert, dass das Verzeichnis SOLR_HOME eine Datei solr.xml enthält. Wenn k8s das Volume `solr-pvc` einbindet, ist es (zunächst) ein leeres Verzeichnis. Daher nutzen wir ein weiteres nützliches Kubernetes-Tool namens initContainer, um die Datei solr.xml in das leere Verzeichnis des persistenten Volumes einzubinden.
initContainers: - name: check-zk ... - name: "cp-solr-xml" image: busybox:latest command: ['sh', '-c', 'cp /tmp/solr.xml /tmp-config/solr.xml'] volumeMounts: - name: "solr-xml" mountPath: "/tmp" - name: "solr-pvc" mountPath: "/tmp-config"
Der initContainer cp-solr-xml kopiert einfach die Datei solr.xml von /tmp nach /tmp-config, das zufällig das gleiche persistente Volume (solr-pvc) ist, das der Solr-Container unter /opt/solr/server/home sieht. Aber Moment, wie ist solr.xml in /tmp des initContainers gelangt? Dies geschieht mit Hilfe einer Kubernetes ConfigMap und einem Volume Mount in der StatefulSet-Definition:
apiVersion: "v1" kind: "ConfigMap" metadata: name: "solr-config-map" labels: app: solr chart: solr-1.0.0 release: solr heritage: Tiller data: solr.xml: | <?xml version="1.0" encoding="UTF-8" ?> <solr> ... </solr>
Die ConfigMap enthält nun eine solr.xml-Datei im Schlüssel solr.xml. Um sie für unsere Pods im StatefulSet verfügbar zu machen, mounten wir die ConfigMap als Volume mit:
volumes: - name: solr-xml configMap: name: solr-config-map items: - key: solr.xml path: solr.xml
Zugegebenermaßen ist es ziemlich mühsam, die solr.xml mithilfe von initContainers und ConfigMaps in das Home-Verzeichnis von Solr zu booten. Es ist jedoch ein gutes Beispiel für die Verwendung von initContainers, um den Pod in einen guten Zustand zu bringen, bevor der primäre Container gestartet wird. In Zukunft sollte Solr eine bessere Lösung für dieses Problem eingebaut haben, siehe: https://issues.apache.org/jira/browse/SOLR-13035.
Zusammenfassend lässt sich sagen, dass das Solr StatefulSet jedem Knoten im Cluster einen Hostnamen zugewiesen hat, der auf dem Namen des Sets und der Ordnungszahl der Replikate basiert, z. B. solr-0, solr-1 usw., und jedem Pod ein 20G persistentes Volume unter /opt/solr/server/home zugewiesen hat.
Ersetzen verlorener Stateful Replicas
Lassen Sie uns feststellen, auf welchem Knoten der solr-2 Pod läuft:
kubectl get pod -o=custom-columns=NODE:.spec.nodeName,NAME:.metadata.name
Lassen Sie uns eine Schote töten und sehen, was passiert:
kubectl delete po solr-2 --force --grace-period 0
Wait a few seconds and then list out the pods: kubectl get pods NAME READY STATUS RESTARTS AGE solr-0 1/1 Running 0 19m solr-1 1/1 Running 0 16m solr-2 0/1 PodInitializing 0 7s solr-zookeeper-0 1/1 Running 0 19m solr-zookeeper-1 1/1 Running 0 18m solr-zookeeper-2 1/1 Running 0 17m
Nachdem Sie eine Weile gewartet haben, stellen Sie fest, dass der verlorene solr-2-Pod wieder zum Cluster hinzugefügt wurde. Wenn Sie die Auflistung der Knotennamen erneut ausführen, werden Sie sehen, dass der solr-2-Pod zurückkam und auf demselben Knoten wie zuvor neu erstellt wurde. Das liegt daran, dass k8s bestrebt ist, einen ausgewogenen Cluster zu erhalten.
Aktivitäts- und Bereitschaftstests
Kubernetes überwacht den Status Ihrer Pods aktiv mit Liveness- und Readiness-Probes. Im Moment trifft die Solr-Helmdiagramm auf den Endpunkt /solr/admin/info/system mit:
livenessProbe: initialDelaySeconds: 20 periodSeconds: 10 httpGet: scheme: "HTTP" path: /solr/admin/info/system port: 8983 readinessProbe: initialDelaySeconds: 15 periodSeconds: 5 httpGet: scheme: "HTTP" path: /solr/admin/info/system port: 8983
Sobald jedoch https://issues.apache.org/jira/browse/SOLR-11126 verfügbar ist, können wir ausgefeiltere Liveness- und Readiness-Checks durchführen.
Pod-Initialisierung koordinieren
Bevor wir zum nächsten Abschnitt übergehen, sehen wir uns an, wie k8s das Timing der Pods zwischen Solr und Zookeeper koordiniert. Für Solr muss Zookeeper verfügbar sein, bevor es Anfragen vollständig initialisieren und bedienen kann. Mit k8s möchten wir jedoch unsere Pods bereitstellen können, ohne die Reihenfolge koordinieren zu müssen. In der Tat gibt es in Kubernetes kein Konzept für die Reihenfolge der Pod-Initialisierung zwischen StatefulSets. Um dies zu erreichen, verlassen wir uns auf einen initContainer, der den Zustand von ZK testet, bevor der primäre Solr-Container von k8s aufgerufen wird. Wenn ZK nicht gesund ist, schläft der initContainer ein paar Sekunden lang und versucht es dann erneut für bis zu einer Minute.
initContainers: - name: check-zk image: busybox:latest command: - 'sh' - '-c' - | COUNTER=0; while [ $COUNTER -lt 30 ]; do ... # see helm chart for script details done; echo "Did NOT see a ZK leader after 60 secs!"; exit 1;
Wenn Solr nicht online geht, überprüfen Sie den Status des initContainers mit:
kubectl describe pod <pod name>
Solr aktualisieren
Erinnern Sie sich, als wir sagten, dass Kubernetes dabei hilft, bewährte Praktiken und bewährte Designmuster durchzusetzen? Die Durchführung eines rollenden Upgrades ohne Ausfallzeiten ist eine dieser bewährten Praktiken, die in StatefulSets eingebaut sind. Um dies in Aktion zu sehen, führen Sie einfach den Befehl helm template ohne den Parameter –set image.tag erneut aus:
helm template . --name solr > solr.yaml and then do: kubectl apply -f solr.yaml
Die Ausgabe des Befehls apply zeigt die Eleganz von Kubernetes:
service "solr-zookeeper-headless" unchanged service "solr-zookeeper" unchanged statefulset.apps "solr-zookeeper" configured statefulset.apps "solr" configured configmap "solr-config-map" unchanged poddisruptionbudget.policy "solr" unchanged service "solr-headless" unchanged service "solr-svc" unchanged poddisruptionbudget.policy "solr-zookeeper" unchanged
Beachten Sie, dass sich das solr StatefulSet geändert hat, aber alle anderen Ressourcen unverändert geblieben sind. k8s führt ein rollendes Upgrade vom Solr 7.5.0 Container auf den 7.6.0 Container durch und beginnt mit solr-2. Wenn Sie nach der Initialisierung von solr-2 einen Blick in die Protokolle werfen, sehen Sie, dass jetzt Solr 7.6.0 läuft:
2019-02-04 02:07:26.059 INFO (main) [ ] o.a.s.s.SolrDispatchFilter ___ _ Welcome to Apache Solr™ version 7.6.0 2019-02-04 02:07:26.063 INFO (main) [ ] o.a.s.s.SolrDispatchFilter / __| ___| |_ _ Starting in cloud mode on port 8983 2019-02-04 02:07:26.063 INFO (main) [ ] o.a.s.s.SolrDispatchFilter __ / _ | '_| Install dir: /opt/solr 2019-02-04 02:07:26.064 INFO (main) [ ] o.a.s.s.SolrDispatchFilter |___/___/_|_| Start time: 2019-02-04T02:07:26.064447Z
Das ist alles schön und gut, aber es berücksichtigt nicht die Wiederwahl der Leader für alle Leader, die auf dem Knoten gehostet werden, der aktualisiert wird. Kube hält uns auch in diesem Fall den Rücken frei, indem es ein SIGTERM an den Solr-Prozess sendet, was Solr dazu veranlasst, die Kerne zu entladen und sich ordnungsgemäß herunterzufahren. k8s wartet bis zu 30 Sekunden, bis Solr ein ordnungsgemäßes Herunterfahren durchführt, was für die meisten Anwendungsfälle ausreichend sein sollte. Sie können diese Wartezeit mit `terminationGracePeriodSeconds` in der Pod-Spezifikation bei Bedarf erhöhen.
Kanarienvogel-Freigabe
Das Rolling von Updates in einem StatefulSet erfordert, dass Sie alle Pods aktualisieren. Aber was ist, wenn Sie mit einem Solr-Update experimentieren möchten, bevor Sie es im gesamten Cluster ausrollen, d.h. Sie möchten ein so genanntes „Canary Release“ durchführen.
Nehmen wir zum Beispiel an, wir möchten Solr 8.0.0 (noch nicht veröffentlicht) ausprobieren, aber nur einen bestimmten Prozentsatz der Anfragen dorthin schicken, nur für den Fall, dass unser Experiment schief geht. Oder es könnte etwas weniger Aufwändiges sein, wie das Ausprobieren einer anderen Mischung von Solr-Konfigurationsparametern. Der Punkt ist, dass Ihr Canary-Pod eine Änderung aufweist, die Sie überprüfen möchten, bevor Sie sie auf den gesamten Cluster ausweiten.
Für dieses Experiment wollen wir nur einen einzigen Canary-Pod in den Mix einführen. Bevor wir diese Lösung implementieren, sollten wir uns ansehen, wie Kubernetes-Dienste mit einer Reihe von Pods funktionieren. Werfen Sie zunächst einen Blick auf die Dienste, die für den Solr-Cluster definiert wurden:
kubectl get svc -l app=solr
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE solr-headless ClusterIP None <none> 8983/TCP 59m solr-svc ClusterIP 10.63.248.4 <none> 8983/TCP 59m
Kubernetes-Dienste verteilen die Last auf eine Reihe von Pods mithilfe von Pod-Selektor-Labels. Der Dienst solr-svc wählt beispielsweise Pods mit den Bezeichnungen: app=solr, release=solr und component=server:
apiVersion: "v1" kind: "Service" metadata: name: "solr-svc" spec: type: "ClusterIP" ports: - port: 8983 name: "solr-client" selector: app: "solr" release: "solr" component: "server"
Es spielt also keine Rolle, aus welchem StatefulSet (oder Deployment) die Pods stammen, solange die Labels eines Pods mit dem Selektor des Dienstes übereinstimmen. Das bedeutet, dass wir mehrere StatefulSets im Cluster einsetzen können, die jeweils auf verschiedene Versionen von Solr verweisen, und der Dienst leitet den Datenverkehr problemlos an beide weiter.
Wir überlassen es dem Leser als Übung, ein anderes StatefulSet mit einem einzigen Replikat und einer anderen Solr-Version einzusetzen. Nachdem der Canary-Pod online ist, müssen Sie die Solr-Sammlungen-API verwenden, um der Canary-Solr-Instanz ein Replikat aus Ihrer Sammlung hinzuzufügen.
Leistung Rauchtest
Wir werden jetzt nicht viel Zeit auf Leistungs- und Lasttests verwenden, da wir im nächsten Beitrag ausführlicher darauf zurückkommen werden. Eine der Fragen, die wir vorerst beantworten wollen, ist, ob Solr in Kubernetes langsamer ist oder nicht.
Für den Anfang müssen wir Daten mit einem ziemlich hohen Volumen indizieren. Daher haben wir uns für Spark in Dataproc und die spark-solr-Bibliothek von Lucidworks entschieden. Das folgende Scala-Skript, das von der Spark-Shell ausgeführt wird, indiziert ~7,5 Mio. Dokumente, die im Google Cloud Storage (GCS) gespeichert sind:
val df = spark.read.parquet("gs://solr-perf-data/test7m") val zkhost = "35.236.83.52:2181" val collection = "perf3x1" val writeOpts = Map("zkhost" -> zkhost, "collection" -> collection, "batch_size" -> "10000", "commit_within" -> "30000") df.write.format("solr").options(writeOpts).save com.lucidworks.spark.util.SolrSupport.getCachedCloudClient(zkhost).commit(collection)
Mit diesem Skript können wir es auf so viele gleichzeitige Indizierungskerne unter Verwendung von Spark skalieren, wie wir benötigen, so dass wir massive Solr-Cluster und in GCS gespeicherte Datensätze jeder Größe testen können. Die Indizierung in unserem 3-Knoten-Cluster mit den Instanztypen `n1-standard-4` führte zu 16.800 Dokumenten/Sekunde (3 Shards / 1 Replikat pro Shard). Wir verwendeten 12 gleichzeitige Executor-Kerne auf der Spark-Seite.
Zum Vergleich haben wir denselben Test mit Solr auf GCE (VMs, nicht Container) durchgeführt und ~15.000 Dokumente/Sek. erreicht. In diesem Fall war der Kube-Betrieb also schneller, aber es handelt sich um einen relativ kleinen Datensatz und die Leistung von Cloud-VMs kann leicht variieren. Wichtig ist, dass Kube mit denselben n1-Standard-4-Instanztypen mit der VM-basierten Leistung in GCE gleichauf lag. Im nächsten Beitrag werden wir längere Leistungs- und Lasttests mit größeren Datensätzen und aktivierter Solr-Replikation durchführen.
Solr Metriken in Prometheus
Prometheus(https://prometheus.io/) ist eine Überwachungslösung, die über eine erstklassige Integration mit Kubernetes verfügt. Das Solr-Helmdiagramm bietet die Möglichkeit, Prometheus über den prometheus-exporter, der als Teil des Solr-Docker-Images verteilt wird, zu integrieren. Dieser ist standardmäßig ausgeschaltet, kann aber über den Wert `exporter.enabled` aktiviert werden.
Die folgenden Anweisungen gehen davon aus, dass Sie bereits über eine laufende Instanz von Prometheus verfügen. Der einfachste Weg, eine Instanz einzurichten, ist über das Prometheus-Helmdiagramm, das hier dokumentiert ist: https://github.com/helm/charts/tree/master/stable/prometheus
Um den Exporter zu unserem laufenden Solr hinzuzufügen, erstellen Sie zunächst eine Datei namens solr_exporter.yaml mit dem Inhalt:
--- exporter: enabled: true service: annotations: prometheus.io/scrape: "true" prometheus.io/port: "9983"
Diese Anmerkungen gehen davon aus, dass Ihre Prometheus-Instanz so konfiguriert ist, dass sie nach den Standardannotationen zu Kubernetes-Diensten sucht. Wenn Sie diese geändert haben, passen Sie die Anmerkungen entsprechend an.
Anschließend können wir unsere solr.yaml-Datei mit dem folgenden Befehl neu generieren:
helm template --name "solr" . --values solr_exporter.yaml > solr.yaml
Die Anwendung der neuen Vorlage zeigt uns, dass eine neue Bereitstellung und ein neuer Dienst erstellt wurden:
kubectl apply -f solr.yaml poddisruptionbudget.policy "solr-zookeeper" unchanged service "solr-zookeeper-headless" unchanged service "solr-zookeeper" unchanged statefulset.apps "solr-zookeeper" configured statefulset.apps "solr" configured configmap "solr-config-map" unchanged poddisruptionbudget.policy "solr" unchanged service "solr-exporter" created deployment.apps "solr-exporter" created service "solr-headless" unchanged service "solr-svc" unchanged
Und wir sehen, dass wir jetzt einen solr-exporter-Pod laufen haben:
kubectl get po -l "app=solr,component=exporter" NAME READY STATUS RESTARTS AGE solr-exporter-5c8d77d658-47jbp 0/1 Running 5 4m
In Prometheus können wir nun sehen, dass wir über Metriken von Solr verfügen, z.B. können wir die Anzahl der Dokumente in jedem Shard sehen:
Es gibt eine erstaunliche Anzahl von Metriken, die der Exporter bereitstellt, fast 1400 für einen Cluster mit drei Knoten und einer einzigen Sammlung. Es kann schwierig sein, zu wissen, welche Metriken wichtig sind und wie sie zusammenhängen. Zum Glück gibt es ein Grafana-Dashboard, das Solr zur Verfügung stellt und mit dem wir die wichtigsten Metriken im Laufe der Zeit sehen können.
Um dieses Dashboard verwenden zu können, müssen Sie eine Instanz von Grafana installiert haben, die auf Prometheus als eine der Datenquellen verweist. Hier finden Sie eine Hilfstafel, die Ihnen bei der Installation von Grafana helfen kann: https://github.com/helm/charts/tree/master/stable/grafana.
Sobald Grafana läuft, können Sie das Grafana-Diagramm von hier importieren: https://github.com/apache/lucene-solr/blob/master/solr/contrib/prometheus-exporter/conf/grafana-solr-dashboard.json indem Sie auf Erstellen -> Importieren gehen und dann das JSON einfügen.
Das Dashboard sieht dann etwa so aus:
Oben finden Sie eine Reihe von Filtern, mit denen Sie die angezeigten Kerne, Sammlungen oder Repliken einschränken können. Probieren Sie es aus und sehen Sie, was Sie über Ihre Solr-Instanz erfahren.
Verschlüsselung des Datenverkehrs zwischen Solr-Instanzen mit TLS
Um den Datenverkehr zwischen den Solr-Instanzen zu verschlüsseln, müssen wir einen privaten Schlüssel, eine CSR, generieren und die CSR anschließend signieren. Wir werden das Kubernetes CA-Zertifikat verwenden, um unsere CSR zu signieren.
Hierfür verwenden wir das Dienstprogramm cfssl
(https://github.com/cloudflare/cfssl). Folgen Sie den Installationsanweisungen, um es einzurichten.
Zunächst müssen wir ein Zertifikat für die Hosts in unserem Solr-Cluster generieren, also erstellen Sie eine Datei namens ssl_config.json mit dem Inhalt:
{ "hosts": [ "solr-svc.default.svc.cluster.local", "solr-0.solr-headless.default.svc.cluster.local", "solr-0.solr-headless", "solr-1.solr-headless.default.svc.cluster.local", "solr-1.solr-headless", "solr-2.solr-headless.default.svc.cluster.local", "Solr-2.solr-headless" ], "key": { "algo": "rsa", "size": 2048 } }
Mit dieser Konfiguration können wir dann einen privaten Schlüssel und eine csr erzeugen, indem wir sie ausführen:
cfssl genkey ssl_config.json | cfssljson -bare server
Dadurch werden die Dateien server-key.pem und server.csr in Ihrem Arbeitsverzeichnis erstellt. Dann bringen wir Kubernetes dazu, unsere csr zu signieren. Ausführlichere Informationen dazu finden Sie hier: https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/, aber jetzt können wir erst einmal loslegen:
export MY_CSR_NAME="solr-certificate" cat <<EOF | kubectl apply -f - apiVersion: certificates.k8s.io/v1beta1 kind: CertificateSigningRequest metadata: name: ${MY_CSR_NAME} spec: groups: - system:authenticated request: $(cat server.csr | base64 | tr -d 'n') EOF
Wir können nun die CSR in einem ausstehenden Status in Kubernetes sehen:
kubectl get csr NAME AGE REQUESTOR CONDITION solr-certificate 1m <requestor_username> Pending
Wir können das Zertifikat dann genehmigen:
kubectl certificate approve solr-certificate certificatesigningrequest.certificates.k8s.io "solr-certificate" approved
Der CSR ist jetzt genehmigt worden, wie wir durch Laufen sehen können:
kubectl get csr NAME AGE REQUESTOR CONDITION solr-certificate 4m <requestor_username> Approved,Issued kubectl get csr solr-certificate -o jsonpath='{.status.certificate}' | base64 --decode > server-cert.pem
Wir können dann ein Kubernetes-Geheimnis erstellen, das das Zertifikat und den privaten Schlüssel enthält.
kubectl create secret tls solr-certificate --cert server-cert.pem --key server-key.pem
Jetzt konfigurieren wir solr für die Verwendung dieses Zertifikats. Dazu erstellen wir zunächst die solr.yaml neu:
helm template --name "solr" . --set tls.enabled=true,tls.certSecret.name=solr-certificate,tls.importKubernetesCA=true > solr.yaml
Und wenden Sie dann die Vorlage an
kubectl apply -f solr.yaml
Solr läuft jetzt mit einem selbstsignierten Zertifikat. Um dies zu testen, können wir Solr portieren:
kubectl port-forward solr-0 28983:8983
Der Besuch von https://localhost:28983 ermöglicht uns den Zugriff auf die Solr-Knoten, wenn auch mit einem Zertifikatsfehler, da wir dem Kubernetes ca. nicht vertraut haben.
Um TLS einzurichten, wird auf den Solr-Knoten ein Init-Container konfiguriert, der einen Keystore erstellt, der den privaten Schlüssel und das Zertifikat aus dem konfigurierten Geheimnis enthält. Außerdem wird ein Truststore eingerichtet, der das Kubernetes-Zertifikat oder ein anderes Zertifikat aus einem Geheimnis importiert und Solr für die Verwendung dieses Truststores konfiguriert.
Einpacken
Wenn Sie bis zu diesem Punkt durchgehalten haben, dann können Sie sich selbst auf die Schulter klopfen! Sie sollten nun genau wissen, wie Sie Solr in Kubernetes einsetzen und betreiben. Im zweiten Teil dieser Serie werden wir uns eingehender mit der automatischen Skalierung befassen, z.B. mit dem Hinzufügen von Replikaten als Reaktion auf einen erhöhten Abfrageverkehr sowie mit der Durchführung von Lasttests für größere Datensätze. Probieren Sie doch erst einmal das Solr Helm-Diagramm aus(https://github.com/lucidworks/solr-helm-chart oder https://github.com/helm/charts) und lassen Sie uns wissen, wie es funktioniert. Da wir Sie mit einer Vielzahl von Befehlen überschüttet haben, finden Sie hier einen Spickzettel, der Ihnen bei gängigen Aufgaben hilft:
Command |
Notes |
---|---|
gcloud container clusters get-credentials <cluster> |
Update kubectl config to connect to the GKE cluster named <cluster> |
gcloud container clusters describe <cluster> |
Get details about your GKE cluster |
kubectl config current-context |
See which cluster kubectl is configured to send commands to |
gcloud config list |
See your current GCloud config settings |
kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value core/account) |
Get your current user cluster admin privileges |
helm template . --name solr > solr.yaml |
Generate the K8s manifest file locally from a Helm chart |
helm template . --set key=value ... |
Override a Helm chart config property when generating the template |
helm lint solr.yaml |
Run the Helm linter on a manifest |
kubectl apply -f solr.yaml |
Apply the k8s manifest to the cluster; k8s performs a diff and only applies changes |
kubectl get pods |
Get a list of pods from the default namespace |
kubectl get pods -l app=solr |
Get a list of pods using a label selector, such as app=solr |
kubectl logs <pod> |
Log from the pod named <pod> |
kubectl port-forward solr-0 28983:8983 |
Setup a port forward for local port 28983 to the solr-0 pod port 8983 |
kubectl get service <svc> -o |
Get the external IP address for a |
jsonpath='{.status.loadBalancer.ingress[0].ip}' |
LoadBalancer service named <svc> |
kubectl exec -it solr-0 --container solr -- /bin/bash |
Get a BASH shell on the solr-0 pod |
kubectl describe po <pod> |
Get details about a specific pod named <pod> |
kubectl get pods -o wide |
Get a list of pods with more details, such as the nodeName |
kubectl delete po <pod> --force --grace-period 0 |
Forcefully kill a pod named <pod>; be careful with this! |
kubectl get svc -l app=solr |
Get a list of services with label app=solr; use any valid label selector |
gcloud auth configure-docker |
Allow your GCloud user to upload Docker images to your GKE cluster |
kubectl apply -f install/kubernetes/helm/helm-service-account.yaml helm init --service-account tiller |
Install Tiller component of Helm on your GKE cluster |
Sehen Sie sich auch diese Ressourcen an:
Wenn Sie mehr über den Aufbau einer absichtsgesteuerten Suche mit Fusion auf Kubernetes erfahren möchten, kontaktieren Sie uns noch heute.