In dieser Anleitung wird gezeigt, wie Sie einen Cluster verteilter Memcached-Server in Google Kubernetes Engine (GKE) mit Kubernetes, Helm und Mcrouter bereitstellen. Memcached zählt zu den beliebtesten Open-Source-Multifunktions-Caching-Systemen. Es dient in der Regel als Zwischenspeicher für häufig verwendete Daten, um Webanwendungen zu beschleunigen und Datenbanklasten zu reduzieren.
Eigenschaften von Memcached
Memcached hat zwei wesentliche Designziele:
- Einfachheit: Memcached funktioniert wie eine große Hash-Tabelle. Beliebig gestaltete Objekte können mit einer einfachen API gespeichert und nach Schlüssel abgerufen werden.
- Geschwindigkeit: Memcached speichert Daten ausschließlich in RAM und ermöglicht dadurch einen extrem schnellen Datenzugriff.
Memcached ist ein verteiltes System, das die horizontale Skalierung seiner Hash-Tabellen-Kapazität über einen Pool von Servern ermöglicht. Jeder Memcached-Server wird komplett von den anderen Servern im Pool isoliert betrieben. Das Routing und Load-Balancing zwischen den Servern muss daher auf der Clientebene erfolgen. Memcached-Clients wenden ein konsistentes Hash-Schema an, um die entsprechenden Zielserver auszuwählen. Dieses Schema garantiert die folgenden Bedingungen:
- Es wird immer derselbe Server für denselben Schlüssel ausgewählt.
- Die Speichernutzung wird gleichmäßig auf die Server verteilt.
- Bei einer Reduzierung oder Erweiterung des Serverpools wird eine Mindestanzahl von Schlüsseln verlagert.
Im folgenden Diagramm erhalten Sie einen Überblick über die Interaktion zwischen einem Memcached-Client und einem verteilten Pool von Memcached-Servern.
Ziele
- Machen Sie sich mit den Eigenschaften der verteilten Memcached-Architektur vertraut.
- Erstellen Sie mit Kubernetes und Helm einen Memcached-Dienst in GKE.
- Erstellen Sie zur Verbesserung der Systemleistung den Open-Source-Memcached-Proxy Mcrouter.
Kosten
In diesem Dokument verwenden Sie die folgenden kostenpflichtigen Komponenten von Google Cloud:
- Compute Engine
Mit dem Preisrechner können Sie eine Kostenschätzung für Ihre voraussichtliche Nutzung vornehmen.
Hinweise
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Compute Engine and GKE APIs.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Compute Engine and GKE APIs.
- Starten Sie eine Cloud Shell-Instanz.
Zu Cloud Shell
Memcached-Dienst bereitstellen
Eine einfache Möglichkeit, um einen Memcached-Dienst in GKE zu erstellen, ist die Verwendung eines Helm-Diagramms. Führen Sie zum Deployment die folgenden Schritte in Cloud Shell aus:
Erstellen Sie einen neuen GKE-Cluster mit drei Knoten:
gcloud container clusters create demo-cluster --num-nodes 3 --zone us-central1-f
Laden Sie das Binärarchiv
helm
herunter:HELM_VERSION=3.7.1 cd ~ wget https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz
Entpacken Sie die Archivdatei in Ihr lokales System:
mkdir helm-v${HELM_VERSION} tar zxfv helm-v${HELM_VERSION}-linux-amd64.tar.gz -C helm-v${HELM_VERSION}
Fügen Sie das Verzeichnis der
helm
-Binärdatei IhrerPATH
-Umgebungsvariablen hinzu:export PATH="$(echo ~)/helm-v${HELM_VERSION}/linux-amd64:$PATH"
Dadurch wird die
helm
-Binärdatei während der aktuellen Cloud Shell-Sitzung von jedem beliebigen Verzeichnis aus sichtbar. Damit diese Konfiguration über mehrere Sitzungen hinweg bestehen bleibt, müssen Sie den Befehl in die Datei~/.bashrc
des Cloud Shell-Nutzers einfügen.Installieren Sie eine neue Memcached-Helm-Diagrammversion mit der Hochverfügbarkeitsarchitektur:
helm repo add bitnami https://charts.bitnami.com/bitnami helm install mycache bitnami/memcached --set architecture="high-availability" --set autoscaling.enabled="true"
Das Memcached-Helm-Diagramm verwendet einen StatefulSet-Controller. Ein Vorteil bei der Verwendung eines StatefulSet-Controllers ist, dass die Namen der Pods geordnet und vorhersagbar sind. In diesem Fall lauten die Namen
mycache-memcached-{0..2}
. Diese Reihenfolge erleichtert Memcached-Clients den Verweis auf die Server.Führen Sie den folgenden Befehl aus, um die aktiven Pods anzeigen zu lassen:
kubectl get pods
Die Ausgabe der Google Cloud Console sieht so aus:
NAME READY STATUS RESTARTS AGE mycache-memcached-0 1/1 Running 0 45s mycache-memcached-1 1/1 Running 0 35s mycache-memcached-2 1/1 Running 0 25s
Memcached-Dienstendpunkte ermitteln
Das Memcached-Helm-Diagramm verwendet einen monitorlosen Dienst. Ein monitorloser Dienst stellt IP-Adressen für alle seine Pods bereit, sodass diese einzeln ermittelt werden können.
Überprüfen Sie, ob der bereitgestellte Dienst monitorlos ist:
kubectl get service mycache-memcached -o jsonpath="{.spec.clusterIP}"
Durch die Ausgabe
None
wird bestätigt, dass der Dienst keineclusterIP
hat und daher monitorlos ist.Der Dienst erstellt einen DNS-Eintrag für einen Hostnamen in folgendem Format:
[SERVICE_NAME].[NAMESPACE].svc.cluster.local
In dieser Anleitung wird als Dienstname
mycache-memcached
verwendet. Da kein Namespace explizit definiert wurde, wird der Standard-Namespace verwendet. Somit lautet der gesamte Hostnamemycache-memcached.default.svc.cluster.local
. Dieser Hostname wird in eine Gruppe von IP-Adressen und Domains für alle drei vom Dienst bereitgestellten Pods aufgelöst. Wenn später weitere Pods im Pool hinzugefügt oder entfernt werden, wird der DNS-Eintrag automatisch durchkube-dns
aktualisiert.Für die Ermittlung der Memcached-Dienstendpunkte ist der Client zuständig. Die Vorgehensweise wird in den folgenden Schritten erläutert.
Rufen Sie die IP-Adressen der Endpunkte ab:
kubectl get endpoints mycache-memcached
Die Ausgabe sieht etwa so aus:
NAME ENDPOINTS AGE mycache-memcached 10.36.0.32:11211,10.36.0.33:11211,10.36.1.25:11211 3m
Beachten Sie, dass jeder Memcached-Pod eine separate IP-Adresse hat:
10.36.0.32
,10.36.0.33
bzw.10.36.1.25
. Diese IP-Adressen können bei Ihren eigenen Serverinstanzen anders sein. Alle Pods hören den Memcached-Standardport11211
ab.Alternativ zu Schritt 2 können Sie eine DNS-Prüfung mithilfe einer Programmiersprache wie Python ausführen:
Starten Sie in Ihrem Cluster eine interaktive Python-Konsole:
kubectl run -it --rm python --image=python:3.10-alpine --restart=Never python
Führen Sie in der Python-Konsole folgende Befehle aus:
import socket print(socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local')) exit()
Die Ausgabe sieht etwa so aus:
('mycache-memcached.default.svc.cluster.local', ['mycache-memcached.default.svc.cluster.local'], ['10.36.0.32', '10.36.0.33', '10.36.1.25'])
Starten Sie eine
telnet
-Sitzung mit einem der ausgeführten Memcached-Server an Port11211
, um das Deployment zu testen:kubectl run -it --rm busybox --image=busybox:1.33 --restart=Never telnet mycache-memcached-0.mycache-memcached.default.svc.cluster.local 11211
Führen Sie an der
telnet
-Eingabeaufforderung folgende Befehle mit dem Memcached-ASCII-Protokoll aus:set mykey 0 0 5 hello get mykey quit
Die Ausgabe ist hier fett dargestellt:
set mykey 0 0 5 hello STORED get mykey VALUE mykey 0 5 hello END quit
Diensterkennungslogik implementieren
Sie können jetzt die im folgenden Diagramm gezeigte grundlegende Diensterkennungslogik implementieren.
Auf übergeordneter Ebene besteht die Diensterkennungslogik aus den folgenden Schritten:
- Die Anwendung fragt den DNS-Eintrag von
mycache-memcached.default.svc.cluster.local
beikube-dns
ab. - Sie ruft die damit verknüpften IP-Adressen ab.
- Die Anwendung instanziiert einen neuen Memcached-Client und stellt diesen mit den abgerufenen IP-Adressen zur Verfügung.
- Der in den Memcached-Client integrierte Load-Balancer stellt über die gegebenen IP-Adressen eine Verbindung zu den Memcached-Servern her.
Diese Diensterkennungslogik implementieren Sie jetzt mithilfe von Python:
Erstellen Sie in Ihrem Cluster einen neuen Python-fähigen Pod und starten Sie eine Shell-Sitzung im Pod:
kubectl run -it --rm python --image=python:3.10-alpine --restart=Never sh
Installieren Sie die Bibliothek
pymemcache
:pip install pymemcache
Starten Sie mit dem Befehl
python
eine interaktive Python-Konsole.Führen Sie in der Python-Konsole folgende Befehle aus:
import socket from pymemcache.client.hash import HashClient _, _, ips = socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local') servers = [(ip, 11211) for ip in ips] client = HashClient(servers, use_pooling=True) client.set('mykey', 'hello') client.get('mykey')
Die Ausgabe sieht so aus:
b'hello'
Das Präfix
b
kennzeichnet ein Byte-Literal. Dies ist das Format, in dem Daten in Memcached gespeichert werden.Beenden Sie die Python-Konsole:
exit()
Drücken Sie
Control
+D
, um die Shell-Sitzung des Pods zu beenden.
Verbindungs-Pooling aktivieren
Mit steigenden Caching-Anforderungen und einem auf Dutzende, Hunderte oder Tausende Memcached-Server anwachsenden Pool stoßen Sie möglicherweise an gewisse Grenzen. Insbesondere die große Anzahl offener Verbindungen von Memcached-Clients kann für die Server zu einer starken Belastung werden, wie im folgenden Diagramm zu sehen ist.
Führen Sie einen Proxy ein, der ein Verbindungs-Pooling wie im folgenden Diagramm ermöglicht, um die Anzahl der offenen Verbindungen zu reduzieren.
Mcrouter (gesprochen "Mick Router"), ein leistungsfähiger Open Source Memcached-Proxy, ermöglicht das Verbindungs-Pooling. Die Integration von Mcrouter erfolgt aufgrund des Memcached-ASCII-Standardprotokolls nahtlos. Aus Sicht eines Memcached-Clients verhält sich Mcrouter wie ein normaler Memcached-Server. Aus Sicht eines Memcached-Servers verhält sich Mcrouter wie ein normaler Memcached-Client.
Führen Sie die folgenden Befehle in Cloud Shell aus, um Mcrouter bereitzustellen.
Löschen Sie die zuvor installierte
mycache
-Helm-Diagrammversion:helm delete mycache
Erstellen Sie neue Memcached-Pods und Mcrouter-Pods, indem Sie eine neue Mcrouter-Helm-Diagrammversion installieren:
helm repo add stable https://charts.helm.sh/stable helm install mycache stable/mcrouter --set memcached.replicaCount=3
Die Proxy-Pods können jetzt Anfragen von Clientanwendungen annehmen.
Testen Sie diese Einrichtung, indem Sie eine Verbindung zu einem der Proxy-Pods herstellen. Führen Sie den Befehl
telnet
am Mcrouter-Standardport5000
aus.MCROUTER_POD_IP=$(kubectl get pods -l app=mycache-mcrouter -o jsonpath="{.items[0].status.podIP}") kubectl run -it --rm busybox --image=busybox:1.33 --restart=Never telnet $MCROUTER_POD_IP 5000
Geben Sie folgende Befehle in die
telnet
-Eingabeaufforderung ein:set anotherkey 0 0 15 Mcrouter is fun get anotherkey quit
Mit den Befehlen wird der Wert Ihres Schlüssels festgelegt und zurückgegeben.
Sie haben jetzt einen Proxy bereitgestellt, der ein Verbindungs-Pooling ermöglicht.
Latenz reduzieren
Die Robustheit wird in der Regel durch einen Cluster mit mehreren Knoten gesteigert. In dieser Anleitung verwenden wir einen Cluster mit drei Knoten. Werden mehrere Knoten verwendet, kann sich jedoch aufgrund des erhöhten Netzwerkverkehrs zwischen den Knoten auch die Latenz erhöhen.
Proxy-Pods am selben Standort platzieren
Wenn Sie Clientanwendungs-Pods nur mit einem Memcached-Proxy-Pod auf demselben Knoten verbinden, können Sie die Latenz verringern. Diese Konfiguration wird im folgenden Diagramm dargestellt.
So erstellen Sie diese Konfiguration:
- Wichtig ist, dass jeder Knoten einen aktiven Proxy-Pod enthält. Ein gängiger Ansatz besteht darin, die Proxy-Pods mit einem DaemonSet-Controller zu erstellen. Mit weiteren Knoten werden dem Cluster auch automatisch neue Proxy-Pods hinzugefügt. Diese Pods werden beim Entfernen von Knoten aus dem Cluster bereinigt. In dieser Anleitung wird für das zuvor erstellte Mcrouter-Helm-Diagramm standardmäßig ein DaemonSet-Controller verwendet. Dieser Schritt ist somit bereits erledigt.
- Legen Sie in den Kubernetes-Parametern des Proxycontainers den Wert
hostPort
fest, damit der Knoten diesen Port überwacht und Traffic an den Proxy weiterleitet. In dieser Anleitung wird der Parameter im Mcrouter-Helm-Diagramm standardmäßig für Port5000
verwendet. Dieser Schritt ist somit ebenfalls bereits erledigt. Stellen Sie den Knotennamen innerhalb der Anwendungs-Pods als Umgebungsvariable bereit. Verwenden Sie hierfür den Eintrag
spec.env
und wählen Sie den Wertspec.nodeName
fieldRef
aus. Weitere Informationen zu dieser Methode finden Sie in der Kubernetes-Dokumentation.Stellen Sie einige Anwendungs-Pod-Beispiele bereit:
cat <<EOF | kubectl create -f - apiVersion: apps/v1 kind: Deployment metadata: name: sample-application spec: selector: matchLabels: app: sample-application replicas: 9 template: metadata: labels: app: sample-application spec: containers: - name: busybox image: busybox:1.33 command: [ "sh", "-c"] args: - while true; do sleep 10; done; env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName EOF
Überprüfen Sie durch einen Blick in eines der Anwendungs-Pod-Beispiele, ob der Knotenname bereitgestellt wurde:
POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}") kubectl exec -it $POD -- sh -c 'echo $NODE_NAME'
Mit diesem Befehl wird der Knotenname in folgendem Format ausgegeben:
gke-demo-cluster-default-pool-XXXXXXXX-XXXX
Pods verbinden
Die Anwendungs-Pod-Beispiele können jetzt mit dem Mcrouter-Pod verbunden werden, der auf den jeweils gegenseitigen Knoten am Mcrouter-Standardport 5000
ausgeführt wird.
Starten Sie eine
telnet
-Sitzung, um für einen der Pods eine Verbindung herzustellen:POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}") kubectl exec -it $POD -- sh -c 'telnet $NODE_NAME 5000'
Geben Sie folgende Befehle in die
telnet
-Eingabeaufforderung ein:get anotherkey quit
Ausgabe:
Mcrouter is fun
Der folgende Python-Code ist ein Beispielprogramm, das diese Verbindung durch Abrufen der Variablen NODE_NAME
aus der Umgebung und mithilfe der Bibliothek pymemcache
ausführt:
import os
from pymemcache.client.base import Client
NODE_NAME = os.environ['NODE_NAME']
client = Client((NODE_NAME, 5000))
client.set('some_key', 'some_value')
result = client.get('some_key')
Bereinigen
Damit Ihrem Google Cloud-Konto die in dieser Anleitung verwendeten Ressourcen nicht in Rechnung gestellt werden, löschen Sie entweder das Projekt, das die Ressourcen enthält, oder Sie behalten das Projekt und löschen die einzelnen Ressourcen.
Führen Sie diesen Befehl aus, um den GKE-Cluster zu löschen:
gcloud container clusters delete demo-cluster --zone us-central1-f
Löschen Sie optional die Helm-Binärdatei:
cd ~ rm -rf helm-v3.7.1 rm helm-v3.7.1-linux-amd64.tar.gz
Weitere Informationen
- Erkunden Sie die zahlreichen über das einfache Verbindungs-Pooling hinaus gehenden Mcrouter-Funktionen, wie Failover-Replikate, zuverlässige Lösch-Streams, Cold-Cache-Aufwärmphase und Multi-Cluster-Übertragung.
- Weitere Details zu den jeweiligen Kubernetes-Konfigurationen erhalten Sie über die Quelldateien des Memcached-Diagramms und des Mcrouter-Diagramms.
- Hier finden Sie weitere Informationen zu effektiven Techniken für die Verwendung von Memcached in App Engine. Manche davon gelten auch für andere Plattformen wie GKE.