Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Kubernetes HPA mit Stackdriver-Metriken automatisch skalieren

By Eran ChetzroniNov 5, 20185 min read

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

1 ce4zye4gpmytsbthtlntwq

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.

1 ce4zye4gpmytsbthtlntwq

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-autorepair

Wichtig 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.yaml

Ein 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/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
selector:
app: nginx

Und dann ausrollen:

kubectl apply -f deployment.yaml

LoadBalancer Ingress anlegen

Legen Sie die Ingress-Datei an: ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: basic-ingress
spec:
backend:
serviceName: nginx
servicePort: 80

Und wenden Sie den Ingress an:

kubectl apply -f ingress.yaml

HorizontalPodAutoscaler-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-ingress
Name: basic-ingress
Namespace: default
Address: 35.190.3.165
Default 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--ffd629d77b6630de

Jetzt 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--ffd629d77b6630de

Die fertige Datei sieht so aus: hpa.yaml

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: nginx
spec:
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: nginx

Beachten 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/ ; done

Und schauen wir, ob unser HorizontalPodAutoscaler darauf reagiert:

kubectl describe hpa nginx-hpa

An 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-hpa
Namespace: default
Labels: <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 +0200
Reference: Deployment/nginx
Metrics: ( current / target )
"loadbalancing.googleapis.com|https|request_count" (target average value): 1034m / 1
Min replicas: 1
Max replicas: 5

Und 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 target

Und Liftoff! 🚀

Troubleshooting

  1. 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=8080

Dann können wir lokal darauf zugreifen:

http://localhost:8080/apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com%7Chttps%7Crequest_count

Hier 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-metrics
NAME READY
custom-metrics-stackdriver-adapter-c4d98dc54-2n4jz 1/1

Und 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.