
Meist skalieren wir Kubernetes-Deployments über Metriken wie CPU- oder Speicherverbrauch – manchmal müssen wir aber auf externe Metriken zurückgreifen. In diesem Beitrag zeige ich Schritt für Schritt, wie Sie den Horizontal Pod Autoscaler (HPA) mit einer beliebigen Stackdriver-Metrik einrichten; konkret nutzen wir die Requests pro Sekunde eines Google Cloud HTTP/S Load Balancers.

Autoscaling für den Kubernetes Horizontal Pod Autoscaler mit Stackdriver-Metriken
Los geht's!
Legen wir zunächst einen neuen Cluster in der Google Kubernetes Engine (GKE) an:
gcloud beta container clusters create "hpa-with-stackdriver-metrics" --zone "us-central1-a" \--username "admin" \--cluster-version "1.10.7-gke.6" \--machine-type "n1-standard-1" \--image-type "COS" \--disk-type "pd-standard" \--disk-size "100" --scopes \ "https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append"--num-nodes "3" \--enable-cloud-logging \--enable-cloud-monitoring \--addons HorizontalPodAutoscaling,HttpLoadBalancing \--enable-autoupgrade --enable-autorepairWichtig ist die Option `enable-cloud-monitoring` – nur damit können wir die Metriken aus Stackdriver Monitoring auslesen.
Custom Metrics Stackdriver Adapter bereitstellen
Der Custom Metrics Adapter sorgt dafür, dass Stackdriver-Metriken in die Kubernetes-API importiert werden. Erst dadurch kann der HPA diese Metriken auswerten und entsprechend reagieren. Mehr Details dazu finden Sie weiter unten im Abschnitt Troubleshooting.
Damit GKE-Objekte auf die in Stackdriver gespeicherten Metriken zugreifen können, müssen Sie den Custom Metrics Stackdriver Adapter in Ihrem Cluster bereitstellen.
Damit der Custom Metrics Adapter überhaupt laufen kann, brauchen Sie als User die Berechtigung, die nötigen Authorization Roles anzulegen. Dazu führen Sie folgenden Befehl aus:
kubectl create clusterrolebinding cluster-admin-binding \--clusterrole cluster-admin \--user "$(gcloud config get-value account)"Und jetzt deployen wir den eigentlichen Adapter, mit dem wir Metriken aus Stackdriver auslesen können:
kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter.yamlEin Deployment anlegen
Jetzt deployen wir eine einfache nginx-Anwendung, die später anhand der vom HTTP/S Load Balancer gemessenen RPS skaliert wird.
Legen Sie folgende Datei an: deployment.yaml
apiVersion: apps/v1kind: Deploymentmetadata: name: nginxspec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.8 ports: - containerPort: 80---apiVersion: v1kind: Servicemetadata: name: nginx labels: app: nginxspec: type: NodePort ports: - port: 80 protocol: TCP selector: app: nginxUnd dann ausrollen:
kubectl apply -f deployment.yamlLoadBalancer Ingress anlegen
Legen Sie die Ingress-Datei an: ingress.yaml
apiVersion: extensions/v1beta1kind: Ingressmetadata: name: basic-ingressspec: backend: serviceName: nginx servicePort: 80Und wenden Sie den Ingress an:
kubectl apply -f ingress.yamlHorizontalPodAutoscaler-Objekt erstellen
Hier passiert die eigentliche Magie:
Wir verwenden eine externe Metrik*, mit folgendem metricName:
loadbalancing.googleapis.com|https|Hinweis: Eine Liste aller Stackdriver-Metriken finden Sie hier – alternativ können Sie auch den Metrics Explorer nutzen.
Außerdem sollten wir einen metricSelector setzen, damit wirklich nur die Metriken unseres spezifischen Load Balancers ausgewertet werden.
Ermitteln wir die Forwarding Rule unseres LB:
$ kubectl describe ingress basic-ingressName: basic-ingressNamespace: defaultAddress: 35.190.3.165Default backend: nginx:80 (10.48.2.11:80)Rules: Host Path Backends ---- ---- -------- * * nginx:80 (10.48.2.11:80)Annotations: backends: {"k8s-be-32432--ffd629d77b6630de":"HEALTHY"} forwarding-rule: k8s-fw-default-basic-ingress--ffd629d77b6630de target-proxy: k8s-tp-default-basic-ingress--ffd629d77b6630de url-map: k8s-um-default-basic-ingress--ffd629d77b6630deJetzt können wir das passende Label-Match in unsere Konfiguration aufnehmen (achten Sie auf das Label "forwarding_rule_name"):
metricSelector: matchLabels: resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630deDie fertige Datei sieht so aus: hpa.yaml
apiVersion: autoscaling/v2beta1kind: HorizontalPodAutoscalermetadata: name: nginxspec: minReplicas: 1 maxReplicas: 5 metrics: - external: metricName: loadbalancing.googleapis.com|https|request_count metricSelector: matchLabels: resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630de targetAverageValue: "1" type: External scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginxBeachten Sie, dass wir targetAverageValue verwenden. Damit legen wir fest, welchen Anteil am Gesamtwert der Metrik jede Replica verarbeiten kann. Praktisch ist das bei Metriken, die Last oder Ressourcen beschreiben, die sich auf mehrere Replicas verteilen lassen – in unserem Fall verarbeitet jede Replica genau einen (also 1) RPS. Den Wert passen Sie natürlich an Ihre Anforderungen an.
Alles testen
Schicken wir zunächst Traffic auf unseren Load Balancer.
Wie Sie an der Ausgabe des obigen Befehls sehen:
kubectl describe ingress basic-ingress… lautet die öffentliche IP-Adresse unseres Ingress 35.190.3.165.
Jetzt feuern wir 🥊 ein paar Requests auf diesen Endpoint ab:
while true ; do curl -Ss -k --write-out '%{http_code}\n' --output /dev/null http://35.190.3.165/ ; doneUnd schauen wir, ob unser HorizontalPodAutoscaler darauf reagiert:
kubectl describe hpa nginx-hpaAn dieser Stelle sehen Sie eventuell noch Warnungen, weil die Metrik noch keine Werte enthält. Nach ein paar Minuten ist der Metrics-Bereich aber befüllt:
Name: nginx-hpaNamespace: defaultLabels: <none>Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"autoscaling/v2beta1","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"name":"nginx-hpa","namespace":"default"},"spec":{"ma...CreationTimestamp: Wed, 31 Oct 2018 18:18:28 +0200Reference: Deployment/nginxMetrics: ( current / target )"loadbalancing.googleapis.com|https|request_count" (target average value): 1034m / 1Min replicas: 1Max replicas: 5Und im Bereich "Events" sehen wir:
Events:Type Reason Age From Message...Normal SuccessfulRescale 2m horizontal-pod-autoscaler New size: 2; reason: external metric loadbalancing.googleapis.com|https|request_count(&LabelSelector{MatchLabels:map[string]string{resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630de,},MatchExpressions:[],}) above targetUnd Liftoff! 🚀
Troubleshooting
- Eine einfache Möglichkeit zu prüfen, ob die Metrik tatsächlich in die External-Metrics-API von Kubernetes importiert wird, ist der direkte Aufruf der API. Damit lässt sich auch verifizieren, ob Sie den metricSelector korrekt verwendet haben.
Zuerst starten wir den Kubernetes-Proxy:
kubectl proxy --port=8080Dann können wir lokal darauf zugreifen:
http://localhost:8080/apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com%7Chttps%7Crequest_countHier ein Auszug aus der Antwort:
{ "kind": "ExternalMetricValueList", "apiVersion": "external.metrics.k8s.io/v1beta1", "metadata": { "selfLink": "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com%7Chttps%7Crequest_count" }, "items": [\ {\ "metricName": "loadbalancing.googleapis.com|https|request_count",\ "metricLabels": {\ "metric.labels.cache_result": "DISABLED",\ "resource.labels.backend_target_type": "BACKEND_SERVICE",\ "resource.labels.backend_name": "k8s-ig--ffd629d77b6630de",\ ...\ "resource.labels.forwarding_rule_name": "k8s-fw-default-basic-ingress--ffd629d77b6630de",\ ...\ },\ "timestamp": "2018-11-01T08:41:30Z",\ "value": "2433m"\ }\ ]}Voilà!
2. Eine weitere Möglichkeit, das Verhalten des Adapters nachzuvollziehen, ist ein Blick in seine Logs. Listen wir zunächst die Custom-Metrics-Pods auf:
$ kubectl get pods -n custom-metricsNAME READYcustom-metrics-stackdriver-adapter-c4d98dc54-2n4jz 1/1Und nun die Logs ansehen:
$ kubectl logs custom-metrics-stackdriver-adapter-c4d98dc54-2n4jz -n custom-metrics...I1104 06:42:11.125627 1 trace.go:76] Trace[1192308782]: "List /apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com|https|request_count" (started: 2018-11-04 06:42:08.155209905 +0000 UTC m=+311951.293335726) (total time: 2.970372027s):Trace[1192308782]: [2.97027864s] [2.970185564s] Listing from storage done...Diese Logs liefern viele wertvolle Informationen – gerade wenn Fehlermeldungen auftauchen.
Fazit
Externe Metriken wie die in Stackdriver erfassten und gespeicherten lassen sich erfreulich unkompliziert nutzen. Auf die gleiche Weise können Sie auch eigene Custom Metrics verwenden, die Sie über die Monitoring API an Stackdriver senden.
Ich habe ein GitHub-Repo mit allen Ressourcen aus diesem Beitrag angelegt – Sie finden es hier.
Lust auf mehr? Schauen Sie in unserem Blog vorbei oder folgen Sie Eran auf Twitter.