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:

  1. Hilft bei der Durchsetzung von Best Practices und bewährten Entwurfsmustern für verteilte Systeme,
  2. Reduziert die Betriebskosten eines komplexen Systems wie Solr und
  3. 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).

Bildschirmfoto Kubernetes-Cluster erstellen

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:

Solr Bildschirmfoto

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

Bildschirmfoto Google Cloud Platform

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:

Prometheus Bildschirmfoto

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:

Bildschirmfoto Solr Dashboard

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.

You Might Also Like

Analytics Studio: Verwandeln Sie Ihre E-Commerce-Daten in verwertbare Einblicke

Entdecken Sie, wie Analytics Studio Teams in die Lage versetzt, datengestützte Entscheidungen...

Read More

Wenn KI schief geht: Fehlschläge in der realen Welt und wie man sie vermeidet

Lassen Sie nicht zu, dass Ihr KI-Chatbot einen 50.000 Dollar teuren Tahoe...

Read More

Lucidworks Kernpakete: Branchenoptimierte KI-Such- und Personalisierungslösungen

Entdecken Sie unsere umfassenden Core Packages, die Analytics Studio, Commerce Studio und...

Read More

Quick Links

Diese Site ist auf wpml.org als Entwicklungssite registriert. Wechseln Sie zu einer Produktionssite mit dem Schlüssel remove this banner.