20
Escalando sua aplicação utilizando K8S e Prometheus
Hoje em dia é comum termos a necessidade de trabalhar com várias instâncias de um serviço para atender cargas de trabalho que em determinados momentos uma única pode não ser o suficiente.
O Kubernetes possui o componente HorizontalPodAutoscaler(HPA) que nos permite de forma automática controlar a quantidade de replicas do serviço baseado em métricas, ou seja permite definir o momento em que deve ser adicionado replicas ao seu "pool" de instâncias do seu serviço, evitando assim desperdício de recurso que seria o caso de um gerência estática.
Por padrão só conseguimos utilizar o HPA com métricas de CPU ou memoria, porém com o Prometheus podemos obter métricas da aplicação que por sua vez serão enviadas para a API Kubernetes.
Para seguir artigo será necessário ter instalado o Helm e o nosso cluster que sugiro utilizar opções como Kind, Minikube e afin.
Este é uma coleção de documentos para configuração do seu Prometheus para o Kubernetes, além de já instalar o Grafana que também sera utilizado aqui.
kubectl create namespace monitoring
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --set grafana.service.type=NodePort --set prometheus.service.type=NodePort
Pode ser observado que foi adicionado argumentos definindo os tipos de serviço como NodePort tendo em vista facilitar nosso acesso as interfaces fora do cluster, porém em um ambiente de produção o recomendado é utilizar Ingress.
Apos a execução dos comandos é esperado esse resultado ao listar nossos deployments
~$ kubectl get deployments -n monitoring
NAME READY UP-TO-DATE AVAILABLE AGE
prometheus-grafana 1/1 1 1 19h
prometheus-kube-prometheus-operator 1/1 1 1 19h
prometheus-kube-state-metrics 1/1 1 1 19h
Esse é o resposável por ser conectar ao nosso Prometheus e obter as metricas coletadas pelo mesmo e envia-las para a API Metricas do Kubernetes disponibilizando o uso das mesmas no nosso HPA.
Crie o arquivo customValues.yaml
, pois nele iremos configurar a url para nosso prometheus e configuração adicional para obter métricas requisições por segundo que iremos utilizar logo mais a frente.
prometheus:
url: "http://prometheus-kube-prometheus-prometheus"
rules:
custom:
- seriesQuery: '{container!="POD",namespace!="",pod!=""}'
resources:
template: <<.Resource>>
name:
matches: "^(.*)_seconds_count"
as: "${1}_per_second"
metricsQuery: (sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>))
_A url do prometheus definida é baseada no DNS interno do serviço visto que o mesmo foi também na namespace monitoring _
helm install prometheus-adapter prometheus-community/prometheus-adapter -n monitoring -f customValues.yaml
Para este exemplo utilizei de uma aplicação SpringBoot+Kotlin que está disponível em GitHub.
Toda a especificação do chart dessa aplicação se encontra em /chart, que segue a seguinte estrutura:
chart/
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── servicemonitor.yaml
│ └── service.yaml
└── values.yaml
Dentre os yaml's acima o servicemonitor.yaml
é responsável por definir a captura de metricas do serviço, que no nosso caso é no path /actuator/prometheus
.
Para gerar a imagem da aplicação iremos usar o seguinte comando gradle:
gradle bootBuildImage
Após isso já teremos disponível em docker local a imagem spring-auto-scaling:0.0.1
, feito isso poderemos agora intalar nosso chart no cluster.
helm install article chart/
Podemos fazer isso com kubectl:
~$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
article-spring-auto-scaling Deployment/article-spring-auto-scaling 418m/1 1 5 1 65m
A Coluna TARGETS representa a métrica alvo, onde temos 418m(O K8's utiliza 'm' para unidades fracionarias da métrica que nesse caso seria 0,4/1) de 1 como media entre as quantidade de replicas que também se encontra em 1. A carga atual se deve ao fato de termos o Prometheus obtendo as métricas do nosso serviço através do ServiceMonitor.
Iremos executar um pod com busybox para fazer chamadas á nossa aplicação em loop, irá tentar gerando uma carga de 10 requisições por segundo.
kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.1; do wget -q -O- http://article-spring-auto-scaling/users/latest; done"
Devemos levar em conta o tempo de resposta da aplicação, pois as chamadas são sequencias e não paralelas, porém já é o suficiente para vermos funcionar.
kubectl describe hpa article-spring-auto-scaling
Como podemos para que para tentar atingir a media máxima de 1 req/s enviou eventos para aumentar o numero replicas para nosso deployment e chegou ao número de 4 replicas sendo 5 o número máximo de replicas permitidas.
Uma excelente boa prática é definição de valores limites, pois não queremos que um serviço escale infinitamente dentro nosso cluster.
Podemos ver que o escalonamento de automático de aplicações é grande aliado, juntamente com a possibilidade de usar métricas customizadas que podem estar bem alinhadas a necessidade do negócio.As configurações e definições de escalonamento também devem ser constantemente monitoradas e refinidas, pois os requisitos sempre mudam e afinal não existe bala de prata.
20