HPA usando Custom metrics com Prometheus Adapter

iti
ititech
Published in
5 min readJun 29, 2020

Bom, estava estudando mais a fundo o tema, e resolvi escrever, tanto para repassar o conhecimento, quanto pra fixar e melhorar meus estudos.

Não sou muito de escrever, porém fico a disposição para tirar duvidas, caso surja alguma durante a leitura… e caso eu não saiba, aprenderemos juntos :)

O Problema:

Hoje, se você implanta o Kubernetes na sua empresa, um dos primeiros desafios que surgem é: como faço scaling das minhas aplicações? essa parte é muito bem resolvida com o HPA padrão do kubernetes, e funciona muito bem quando suas aplicações escalam por memória e/ou por CPU.

Mas e se nenhuma dessa métricas faça sentido para sua aplicação escalar? e se o problema da sua aplicação não é memória e nem CPU, e é, por exemplo, IO Bound?

Pode ser, que neste caso o melhor cenário seria fazer o scaling por quantidade de requests recebidas nos pods, mas como fazer isso se hoje o HPA só suporta Memória e CPU?

A Solução:

Para estes casos, existe varias formas de se conseguir usar métricas customizadas para fazer o scaling de sua aplicação, e uma delas é o prometheus-adapter, ele é um componente que, basicamente faz queries no seu prometheus e expõe essa métrica em uma api de custom metrics, para que o HPA faça o scaling através desta métrica!

fonte: xuyasong.com/?p=1781

Como é possível ver, o HPA vai ler as métricas tanto do metrics server, quanto da api de custom metrics.

Mão na massa:

Bom, o cenário é o seguinte: temos um cluster de Kubernetes provisionado com Kops, utilizando o nginx ingress para controlar o trafego de entrada e com toda parte de métrica sendo coletada pelo prometheus-operator, mas por enquanto, apenas com a api padrão de metrics disponível, por enquanto o scaling está sendo feito apenas por CPU ou memória:

kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods | jq -r 'first(.items[])'
{ "metadata": { "name": "kube-proxy-pq2f4", "namespace": "kube-system", "selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-proxy-pq2f4", "creationTimestamp": "2020-03-23T00:38:29Z" }, "timestamp": "2020-03-23T00:37:34Z", "window": "30s", "containers": [ { "name": "kube-proxy", "usage": { "cpu": "4316495n", "memory": "13340Ki" } } ]}

Coletando métricas

Bom, o primeiro passo, é coletar as métricas de requests no prometheus, essa métrica vai vir do nginx ingress, que é quem efetivamente vai receber as requests.

Como usamos o prometheus operator, essa parte vai ser fácil, o próprio nginx ingress tem um endpoint que disponibiliza todas essas métricas de requests e muitas outras.

O precisamos fazer é configurar o scrape dessas métricas pelo prometheus, o caminho é simples, basta adicionar no service do nginx-controller, a porta de métricas (default: 10254), adicionar o seguinte trecho:

k edit svc nginx-ingress-controller   ports:
- name: metrics
port: 10254
protocol: TCP
targetPort: 10254

agora vamos criar o servicemonitor para scrapear essa porta pelo prometheus:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: nginx-ingress-sm
labels:
name: nginx-ingress
spec:
selector:
matchLabels:
name: nginx-ingress
endpoints:
- port: metrics

pronto! só vamos confirmar no prometheus se essas métricas estão indo pra lá:

Tudo certo!

Configurando o prometheus-adapter

Para nossa alegria, o prometheus-adapter tem seu chart do helm bonitão, pra facilitar muito nosso trabalho, então é só o rodar esse comando pra ter ele rodando em nosso cluster:

helm install --name prometheus-adapter stable/prometheus-adapter --set prometheus.url="http://prometheus",prometheus.port="9090" --set rbac.create="true" --namespace monitoring

nesse caso só alterar o endereço e porta do seu prometheus, e esperar a mágica acontecer…

para validar que o prometheus adapter foi deployado com sucesso, precisamos fazer um get na api de custom-metrics:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | jq

se retornar uma porrada de métrica, é porque ta funcionando :), também da pra filtrar apenas as métricas de pods:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | jq | grep -i "pods/"

Nesse caso vai exibir todas as métricas disponiveis pra fazer o scale de pods…

no nosso caso, essa métrica de requests no nginx-controller ja deve vir pré configurada, para confirmar se está funcionando é só executar:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/monitoring/services/*/nginx_ingress_controller_requests"

Deve ter retornado um erro, certo? isso pq precisamo fazer alguns ajustes para tornar essa métrica, “namespaced”, todas essas métricas estão configuradas em um configmap do prometheus adapter, vamos editar ele:

kubectl edit configmap prometheus-adapter

e vamos adicionar o seguinte trecho:

rules:
- seriesQuery: '{__name__=~"^nginx_ingress_.*",namespace!=""}'
seriesFilters: []
resources:
template: <<.Resource>>
overrides:
exported_namespace:
resource: "namespace"
exported_service:
resource: "services"
name:
matches: ""
as: ""
metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)

isso foi apenas para mapear as labels padrões que estão nas metricas que o nginx exporta com as métricas que o prometheus adapter organiza na api de custom metrics, agora o get deve funcionar :D

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/monitoring/services/*/nginx_ingress_controller_requests"{
"kind": "MetricValueList",
"apiVersion": "custom.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/monitoring/services/grafana/nginx_ingress_controller_requests"
},
"items": [
{
"describedObject": {
"kind": "Service",
"namespace": "monitoring",
"name": "grafana",
"apiVersion": "/v1"
},
"metricName": "nginx_ingress_controller_requests",
"timestamp": "2020-03-23T01:37:02Z",
"value": "15",
"selector": null
}
]
}

Agora falta pouco …

Configurando o HPA da sua aplicação:

kind: HorizontalPodAutoscaler
apiVersion: autoscaling/v2beta1
metadata:
name: hpa-demo-grafana
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: grafana
minReplicas: 1
maxReplicas: 10
metrics:
- type: Object
object:
target:
kind: Service
name: grafana
metricName: nginx_ingress_controller_requests
targetValue: 100m

Neste caso eu fiz o teste do HPA com o grafana, e logo depois é só gerar trafego na sua aplicação, dar um kubectl get hpa e ver o autoscaling funcionando :D

hpa-demo-grafana   Deployment/grafana   15/100m   1         10        10         5m45s

Como eu disse, esse é um caso de estudo, e é bem legal pra ter uma idéia do que da pra fazer com o autoscaling, inclusive escalar suas aplicações com métricas personalizadas e que fazendo sentido escalar.

A imaginação é o limite…

Thats All Folks!

--

--

iti
ititech
Editor for

como o time de tecnologia do iti trabalha para construir uma plataforma simples e acessível