
Na maioria das vezes, escalamos nossos deployments do Kubernetes com base em métricas como consumo de CPU ou memória, mas às vezes é preciso escalar a partir de métricas externas. Neste post, vou te mostrar o passo a passo para configurar o autoscaling do Horizontal Pod Autoscaler (HPA) usando qualquer métrica do Stackdriver — mais especificamente, o Request Per Second de um Google Cloud HTTP/S Load Balancer.

Autoscaling do Horizontal Pod Autoscaler do Kubernetes com métricas do Stackdriver
Bora lá!
Primeiro, vamos criar um cluster novo no Google Kubernetes Engine (GKE):
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-autorepairRepare na flag `enable-cloud-monitoring` — é ela que vai permitir a leitura das métricas do Stackdriver Monitoring.
Faça o deploy do Custom Metrics Stackdriver Adapter
O custom metrics adapter é o responsável por levar as métricas do Stackdriver até a API do Kubernetes, o que permite ao HPA consumir essas métricas e agir com base nelas. Há mais detalhes na seção de troubleshooting, mais abaixo.
Para que os objetos do GKE tenham acesso às métricas armazenadas no Stackdriver, você precisa fazer o deploy do Custom Metrics Stackdriver Adapter no seu cluster.
Para rodar o Custom Metrics Adapter, você precisa dar ao seu usuário permissão para criar as roles de autorização necessárias. Basta executar o comando a seguir:
kubectl create clusterrolebinding cluster-admin-binding \--clusterrole cluster-admin \--user "$(gcloud config get-value account)"Agora vamos ao deploy do adapter propriamente dito, que é o que vai nos permitir ler as métricas do Stackdriver:
kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter.yamlCrie um Deployment
Agora, vamos subir uma aplicação nginx simples, que mais para frente vai escalar com base no RPS medido pelo HTTP/S Load Balancer.
Crie este arquivo: 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: nginxAgora é só fazer o deploy:
kubectl apply -f deployment.yamlCrie o Ingress do LoadBalancer
Crie o arquivo de ingress: ingress.yaml
apiVersion: extensions/v1beta1kind: Ingressmetadata: name: basic-ingressspec: backend: serviceName: nginx servicePort: 80E aplique o ingress:
kubectl apply -f ingress.yamlCrie o objeto HorizontalPodAutoscaler
É aqui que a mágica acontece.
Vamos usar uma métrica externa*, com o metricName:
loadbalancing.googleapis.com|https|Observação: a lista completa de métricas do Stackdriver está aqui, e você também pode usar o Metrics Explorer.
Também precisamos usar um metricSelector, para garantir que estamos capturando apenas as métricas do nosso load balancer específico.
Vamos descobrir a forwarding rule do nosso 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--ffd629d77b6630deAgora podemos adicionar o label match na nossa configuração (repare no label "forwarding_rule_name"):
metricSelector: matchLabels: resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630deO arquivo final fica assim: 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: nginxNote que usamos targetAverageValue, que define quanto do valor total da métrica cada réplica consegue suportar. Isso é útil quando trabalhamos com métricas que descrevem algum trabalho ou recurso que pode ser dividido entre réplicas — no nosso caso, cada réplica dá conta de uma única (ou seja, 1) RPS. Você deve, claro, ajustar esse valor conforme a sua necessidade.
Vamos testar tudo
Vamos começar gerando tráfego para o nosso load balancer.
Como você viu no comando acima:
kubectl describe ingress basic-ingressO IP público do nosso Ingress é: 35.190.3.165
Agora vamos meter a porrada nesse endpoint 🥊 com algumas requisições:
while true ; do curl -Ss -k --write-out '%{http_code}\n' --output /dev/null http://35.190.3.165/ ; doneAgora vamos ver se o nosso HorizontalPodAutoscaler reagiu:
kubectl describe hpa nginx-hpaNesse momento, podem aparecer alguns avisos, já que a métrica ainda não foi populada. Mas, depois de alguns minutos, a seção Metrics aparece preenchida:
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: 5E na seção "Events" dá para ver:
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 targetDecolamos! 🚀
Troubleshooting:
- Uma forma simples de checar se a métrica está sendo importada para a API de external metrics do Kubernetes é navegar pela API manualmente. Isso também ajuda a confirmar se você usou o metricSelector corretamente.
Primeiro, rodamos o proxy do Kubernetes:
kubectl proxy --port=8080Em seguida, dá para acessar pelo localhost:
http://localhost:8080/apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com%7Chttps%7Crequest_countE este é um trecho do resultado:
{ "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. Outro jeito de ver como o adapter está se comportando é acompanhar os logs dele. Primeiro, vamos listar os pods de custom-metrics:
$ kubectl get pods -n custom-metricsNAME READYcustom-metrics-stackdriver-adapter-c4d98dc54-2n4jz 1/1Por fim, vamos olhar os logs:
$ 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...Esses logs trazem muita informação útil, principalmente quando há mensagens de erro.
Conclusão
Usar métricas externas como essas, coletadas e armazenadas pelo Stackdriver, é bem direto e tranquilo. Da mesma forma, você também pode aproveitar suas próprias custom metrics publicadas no Stackdriver via Monitoring API.
Criei um repositório no GitHub com os recursos que usei neste post aqui.
Quer mais conteúdo? Confira nosso blog ou siga o Eran no Twitter.