Alura > Cursos de DevOps > Cursos de Confiabilidade > Conteúdos de Confiabilidade > Primeiras aulas do curso Observabilidade: coletando métricas de uma aplicação com Prometheus

Observabilidade: coletando métricas de uma aplicação com Prometheus

Tornando uma aplicação observável - Apresentação

Olá! Boas-vindas ao curso de Observabilidade: coletando métricas de uma aplicação com Prometheus. Sou o Kleber Costa, instrutor responsável pelo curso.

Audiodescrição: Kleber é um homem de pele clara com cabelos curtos, ondulados e pretos. Tem olhos castanhos claros, barba grisalha e usa óculos de grau com armação retangular cinza. Está vestindo uma camiseta azul. Ao fundo, uma parede branca.

Neste vídeo, o conteúdo abordado será:

Público-alvo

Este curso destina-se a pessoas desenvolvedoras, de operação de infraestrutura, pessoas, de DevOps e de engenharia de confiabilidade de sites. Mesmo que você ainda não integre uma dessas equipes, esse conteúdo te possibilitará adquirir diferencial para adentrar em um desses times.

Observabilidade e Monitoramento

A observabilidade consiste em acompanhar o estado de execução de um sistema através da externalização do seu comportamento em tempo de execução. Ou seja, o objetivo é tornar a aplicação observável para uma fonte externa. O que deve ser observável na aplicação são os comportamentos que importam para a experiência do usuário final.

O monitoramento consiste em acompanhar o estado de um sistema através de eventos registrados e executar ações como resposta a esses eventos. Essas respostas podem ser derivadas de ações reativas e proativas.

A observabilidade faz parte do escopo de monitoramento. Com aplicações complexas e distribuídas, não é possível mais ter um monitoramento efetivo sem a observabilidade. Portanto, a observabilidade é fundamental para entendermos como um sistema tem se comportando diante do consumo.

Métricas

Se estamos lidando com observabilidade e monitoramento, precisamos de algum indicador para trazer os dados importantes. Uma métrica é basicamente um indicador de nível de serviço, coletado dentro de uma série temporal.

As métricas são utilizadas para a medição de performance, disponibilidade, saturação (uso dos recursos que ultrapassa o previsto), erros e qualquer informação útil para o negócio. Portanto, a métrica é uma medição em uma janela de tempo que foca em uma propriedade do sistema.

Uma métrica sempre vai possuir um objetivo específico. Para cada ponto de atenção do sistema deve existir uma métrica correspondente. Significa que podemos extrair métricas da infraestrutura, da execução da aplicação e também de regras de negócio. Isso é possível através da instrumentação.

A partir das métricas, obteremos informações úteis tanto para a equipe de desenvolvimento, para a operação de infraestrutura e também para a área de negócios.

Cenário do curso e pré-requisitos

Entendendo o que é observabilidade, monitoramento e o que são métricas, podemos avançar para o cenário do curso.

Diagrama de arquitetura de sistema mostrando a comunicação entre diferentes componentes de um serviço web. No canto superior esquerdo, há um ícone de usuário ligado a uma nuvem rotulada "Internet" com uma seta apontando para um ícone de proxy dentro de um círculo com o logo do NGINX, o qual está conectado a um ícone rotulado "api". Existem também conexões com um ícone de cache "redis-forum-api" e um banco de dados "MySQL". À esquerda, existe um sistema de monitoramento com ícones representando "grafana-forum-api", "prometheus-forum-api" e "alertmanager-forum-api". No canto inferior direito, uma nuvem conectada a um ícone do Slack representa integração para notificações, e abaixo, um grupo de usuários é simbolizado como time de suporte. Uma linha tracejada mostra a separação de containers gerenciados pelo Docker Compose.

Este diagrama que mostra o ambiente que vamos utilizar. Este ambiente sobe uma stack do Docker Compose. Então, é pré-requisito para esse curso ter o Docker e Docker Compose instalados, além do Maven. Será necessário ter também o Java. Recomendo a versão 1.8 do Java, OpenJDK.

Além disso, recomendo que você utilize alguma IDE que possa trazer dependências de forma automatizada. Nós vamos trabalhar com o Eclipse, então, fica a seu critério adotar esta ou outra IDE que cumpra esse requisito.

Nossa jornada do cliente para consumir essa aplicação começa com um cliente sintético, que vai representar os nossos usuários e executará um acesso não previsível à aplicação, gerando tanto acessos em rotas com êxito, como erros de autenticação, logins, busca por conteúdos que não existem. Mas, a maior parte dos acessos terão êxito.

Este cliente inicia a sua jornada passando por um proxy reverso, um container em GeneXus que redireciona para as rotas da API. Essa API tem uma rota principal, que no curso vamos chamar de endpoint, para não causar nenhuma dificuldade para quem não entende o mundo de desenvolvimento.

Essa rota principal tem seu conteúdo armazenado em um cache, que é um container rodando o redis. Na subida da aplicação, esse conteúdo não estará cacheado, portanto, o primeiro acesso não terá Cache Hit, seguirão para o container que é a base de dados, o mysql, retornarão o conteúdo para o cliente e o armazenará em cache.

Também existe uma passagem de parâmetros que busca por um ID específico nesta rota principal que se chama Topicus. Essas consultas sempre chegarão ao database. Existe a autenticação que validará, via regras de negócio, o que for inserido pelo usuário com dados contidos no database.

Além disso, contamos com a função de delete da API, mas ela não será utilizada. Nosso foco é específico em relação à instrumentalização e das métricas.

Nesse primeiro passo, vamos instrumentar essa API, externalizar suas métricas com seu comportamento e subir o Prometheus, que está em nossa stack de monitoramento. O Prometheus será utilizado para entendermos quais são os tipos de métricas default que ele utiliza e começarmos a nossa imersão na linguagem PromQL.

Na plataforma Alura, você encontra um segundo curso que dá sequência a essa atividade e tem como foco atividades no grafana e no alertmanager integrado ao Slack para fazer a camada final de monitoramento em cima da observabilidade que configuraremos.

Conclusão

Finalizamos a apresentação do curso. Nos encontramos no próximo vídeo!

Tornando uma aplicação observável - Configurando o ambiente

Olá, pessoal, tudo bem? Boas-vindas! Neste vídeo, vamos configurar e entender algumas peculiaridades do ambiente.

Estamos utilizando o Eclipse, que é uma dependência para seguir com o curso. Se quiser, você pode usar outra IDE, porém, recomendamos o Eclipse, porque ele tem a funcionalidade de trazer as dependências que precisamos para essa aplicação, como o Maven.

Então, vamos importar o projeto selecionando "Import projects". Na próxima tela, buscaremos por um projeto Maven já existente, "Existing Maven Projects", e apertaremos "Next". Na outra tela, apertaremos "Browse" e encontraremos o conteúdo descompactado do pacote que está disponível na plataforma.

Por conveniência, estamos usando o path do Workdir do Eclipse. Você pode utilizar outro lugar sem problemas. Então, vamos selecionar "prometheus-grafana". Dentro desse conteúdo, acessaremos o subdiretório "app". Nesse subdiretório, existe um arquivo pom.xml que o Eclipse vai reconhecer automaticamente como o arquivo de um projeto.

No canto superior direito, apertaremos o botão "Open". Na outra tela, verificaremos que esse arquivo está selecionado e pressionaremos "Finish" para que o projeto seja importado. Feito isso, na lateral esquerda da próxima tela, visualizaremos a nossa API: forum. O primeiro passo foi realizado com êxito!

Agora, vamos para o segundo passo. Vamos abrir o terminal. Estamos usando o Linux, mas você pode cumprir as dependências em qualquer sistema operacional, seja o Windows, o Linux ou o MacOS.

Vamos entrar onde a aplicação está descompactada, onde está toda a stack.

cd eclipse-workspace/prometheus-grafana/

Em seguida, passaremos ls.

ls

app docker-compose.yaml grafana mysql nginx

Encontraremos o diretório MySQL, um docker-compose com Pose e o app. Não usaremos o grafana ou o nginx no momento. Não precisamos nos preocupar com eles, são só lixo residual que ficou nesse pacote que estamos utilizando. O que será disponibilizado na plataforma não terá esse conteúdo.

Vamos começar analisando o conteúdo que está dentro de mysql.

cat mysql/database.sql

Este script será executado quando o container do MySQL subir para popular a base. Agora, vamos analisar o docker-compose. Ele é quem vai subir a stack que utilizaremos.

vim docker-compose.yaml

Retorno:

version: '3'

networks:
  local:
services:
  redis-forum-api:
    image: redis
    container_name: redis-forum-apt
    restart: unless-stopped
    ports:
      - 6379:6379
    networks:
      - local
            
mysql-forum-apt:
  image: mysql:5.7
  container_name: mysql-forum-api
  restart: unless-stopped
  environment:
    MYSQL_DATABASE: forum
    MYSQL_USER: 'forum'
    MYSQL PASSWORD: 'Bk55yclu@elqgabe'
    MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
    MYSQL_ROOT_HOST: '%'
volumes:
  - ./mysql:/docker-entrypoint-initdb.d
ports:
 - 3306:3306
networks
 - local
depends on:
 - redis-forum-api
"docker-compose.yaml" 33L, 641c

Temos dois serviços: o redis-forum-api e o mysql-forum-api. Esses dois containers farão bind de porta com a máquina. Então, se tivermos uma instância local rodando MySQL, geraremos conflito. Se tivermos qualquer serviço ocupando a porta 3306 TCP, devemos derrubá-lo para que o bind ocorra sem gerar nenhum problema.

O mesmo vale para o Redis, que vai rodar na 6379, porta padrão do Redis, fazendo bind para 6379 da nossa máquina. Ele estará numa rede local do docker-compose, mas vai bindar na nossa porta. Em seguida, temos configurações do MySQL e sua dependência. Ele precisa que o Redis suba primeiro para que ele possa subir depois.

Essas são as dependências básicas de comunicação da API. Ela depende disso. Por isso, essa stack foi disponibilizada nesta aula.

Antes de subir a stack, vamos fazer uma equivalência. É importante que o docker esteja instalado. Então, vamos rodar um docker --version, para realizar uma equivalência de versão.

docker --version

Docker version 20.10.7, build 20.10.7-0ubuntu5~18.04.3

Essa é a versão do Docker que estamos utilizando. E também temos a versão do docker-compose.

docker-compose --version

docker-compose version 1.29.2, build 5becea4c

É importante que você tenha uma versão igual ou superior à nossa. Se for muito superior, talvez aconteça algum conflito. Se isso acontecer, basta comunicar o fórum ou fazer downgrade da versão. É um problema bastante simples de resolver e, de qualquer maneira, é bastante difícil acontecer essa quebra de compatibilidade.

Também vamos rodar o java -version e conferir a versão.

java -version

Picked up JAVA_OPTIONS: -Djdk.net.URLClassPath.disableClassPathURLCheck=true

openjdk version "1.8.0_312"

OpenJDK Runtime Environment (build 1.8.0_312-8u312-b07-0ubuntu1-18.04-b07)

OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)

Estamos na versão 1.8. Feito isso, podemos subir a stack:

docker-compose up

Retorno:

  File "http/client.py", line 1032, in send_output
  File "http/client.py", line 972, in send
  File "docker/transport/unixconn.py", line 43, in connect
urllib3.exceptions.ProtocolError: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "docker/api/client.py", line 214, in_retrieve_server_version
  File "docker/api/daemon.py", line 181, in version
  File "docker/utils/decorators.py", line 46, in inner
  File "docker/api/client.py", line 237, inget File "requests/sessions.py", line 543, in get
  File "requests/sessions.py", line 530, in request File "requests/sessions.py", line 643, in send
  File "requests/adapters.py", line 498, in send

// Retorno omitido. 

Não vamos subir de forma em in daemon agora. Tivemos um erro previsível. Não subimos o serviço do Docker e, sem ele, o docker-compose não vai conseguir subir os containers. Vamos fazer isso.

sudo systemctl start docker.socket

No Windows, o processo é outro. Ele tem, inclusive, um cliente de interface gráfica, que facilita a operação. Basta dar "start" no serviço.

Seguindo, basta aguardar. Vamos verificar o status do serviço.

sudo systemctl status docker docker.socket

Retorno:

docker.service Docker Application Container Engine
  Loaded: loaded (/lib/systemd/system/docker.service; disabled; vendor preset: enabled) Active: active (running) since Thu 2022-01-06 17:10:22 -03; 5s ago
  Docs: https://docs.docker.com
  Main PID: 22256 (dockerd)
  Tasks: 13
  CGroup: /system.slice/docker.service
            22256 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
                        
// Retorno omitido. 

Apareceu active running, então, subiu tudo. Agora podemos passar o docker-compose up. Então, ele vai criar as redes e os containers. O processo de criação dos containers é um pouco mais demorado, porque o Docker vai fazer download do Redis, na última versão, e do MySQL 5.7, das imagens base.

Com isso, temos a base necessária para rodar a nossa aplicação. Terminou de rodar e apresentou até o versionamento. E ele está esperando por conexões, isto é, ready for connections.

Vamos abrir o terminal e acessar app.

cd app/

Em app, vamos rodar o comando mvn clean package, que é o Maven. Então, estamos chamando o Maven para fazer o build da aplicação e verificar se está tudo certo com o pacote que baixamos.

mvn clean package

É possível fazer isso pelo Eclipse, pois ele também tem essa funcionalidade, mas preferimos fazer no terminal. Ele vai executar uma série de testes da aplicação. E, se tudo passar, vai concluir o build e gerar um artefato JAR. Passou e ele rodou quatro testes, com nenhuma falha ou erro.

Results: 

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

// Retorno omitido. 

Feito isso, vamos ao Eclipse. Temos um script chamado start.sh. Basta executar esse script para que a aplicação possa subir. Por enquanto, estamos fazendo algo bem básico, porque vamos alterar poucas coisas no código da aplicação. Mas, esse processo evitar termos que ficar rebuildando containers várias vezes.

Quando chegarmos ao status final da aplicação, com ela instrumentada, com o Application Properties devidamente configurado, ela se transformará em um container e subirá diretamente via Docker Compose.

Então, subimos a aplicação. Agora, vamos ao Firefox e acessaremos localhost:8080/topicos. Está rodando!

content:
  0:
     id:            3
      titulo:        "Duvida 3"
      mensagem:      "2019-05-05T17:00:00
   1:
      id:             1
      titulo:         "Projeto nao compila"
      dataCriacao:    "2019-05-05T16:00:00"

// API omitida. 

Essa é a nossa API, ela responde por topicos e também aceita um ID específico em topicos. Por exemplo, vamos passar a URL localhost:8080/topicos/1. O resultado será:

id:            1
titulo:        "Duvida 1"
mensagem:      "Erro ao criar projeto"
dataCriacao:   "2019-05-05T15:00:00
nomeAutor:     "Aluno"
status:        "NAO RESPONDIDO"
respostas:     []

Na base, o script SQL populou 3 IDs. Então, temos 1, 2 e o 3, são endpoints. Para cada inserção de tópico que ocorrer nessa aplicação, um endpoint com um número de ID será gerado.

Então, configuramos a stack, já temos tudo pronto para seguirmos com os próximos passos! Temos que resolver um problema: se observarmos nossa aplicação hoje, ela não está instrumentalizada, ela não tem observabilidade. Então, não sabemos como está seu funcionamento de fato.

Ela está respondendo, mas, quanto está consumindo de CPU? Qual o tempo de resposta para os clientes? Quanto está usando de memória? Como está a conexão com a base de dados? Qual é a duração de uma requisição? Estamos com algum erro? Não sabemos essas informações.

Portanto, vamos criar a primeira parte da camada de observabilidade para compreendermos o funcionamento da aplicação, começando pelo Actuator. Até mais!

Tornando uma aplicação observável - Externalizando métricas com Actuator

Olá, pessoal! Agora, vamos iniciar a configuração do Actuator para tornar nossa aplicação observável.

Na aula anterior, conseguimos implementar o ambiente e subir a aplicação. É importante que você consulte a documentação do Actuator. Ela é facilmente encontrada no Google. Basta pesquisar "Actuator" e um dos primeiros resultados será a documentação do Spring, explicando como habilitar o Actuator.

O Actuator atua como uma dependência. No nosso caso, estamos utilizando o Maven. Então, vamos copiar essa dependência.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Se você estiver usando o Gradle, a implantação será outra. Mas, no nosso caso, é o Maven.

Agora, acessaremos o Eclipse e abriremos o arquivo forum/pom.xml. Neste arquivo, há uma série de dependências já configuradas. Vamos encontrar um espaço para colar a dependência que copiamos, tomando cuidado para manter a identação.

Com isso, a dependência já está configurada. Vamos salvar e, automaticamente, o Maven vai baixar essa dependência através do Eclipse. O processo é bem simples!

Terminada esta etapa, vamos realizar alguns ajustes na nossa aplicação. Porém, não alteraremos o código da aplicação agora. Ainda no Ecplise, lateral esquerda da tela, acessaremos "src/main/resources" e abriremos o arquivo applicationprod.properties.

Nele, encontraremos diversas configurações: a porta onde a aplicação sobe; a configuração do Redis, MySQL e JPA para conexão; e o token JWT, porque essa aplicação usa um token. Para algumas ações, como excluir ou criar um tópico, é necessário ter um token gerado através de uma autenticação. Estamos falando de uma API REST.

Vamos inserir algumas linhas neste arquivo.

#actuator
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=*

A primeira linha que inserimos está como always, logo, mostrará todos os detalhes. Na segunda, temos o web.exposure. Mantendo o asterisco, receberemos todas as informações relacionadas à JVM e tudo que estiver relacionado ao gerenciamento da aplicação pela JVM. Para o nosso caso, isso não é muito interessante.

Sendo assim, apagaremos o asterísco e acessaremos alguns endpoints: health; info; e metrics. Esses três já são suficientes. Nosso foco será o metrics.

#actuator
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=health,info,metrics

O health é útil por poder ser usado para health check, dependendo de onde a aplicação estiver rodando. E o info é útil porque nos permite acessar as informações da aplicação importantes para nós e não para o público externo.

Os endpoints Health, info e metrics são internos, eles não devem ser expostos publicamente. Quando a aplicação estiver rodando na stack do Docker Compose, estes endpoints estarão fechados. Para acessá-la, teremos que passar por um proxy que também será implantado mais adiante.

Com esses pré-requisitos cumpridos, vamos ao terminal. A aplicação está rodando e podemos fechar a janela de /bin/bash. Como já estamos no diretório da aplicação, vamos rodar um mvn clean package para recompilar essa aplicação. O processo é bem simples e, como dito na aula anterior, pode ser feito diretamente no Eclipse.

Nos containers, não é necessário realizar nenhuma alteração. Não subimos em modo daemon, então, ele está trazendo o status dos containers. Vamos localizar o ponto em que a aplicação está sendo buildada. Rodou corretamente. Vamos agora para o Eclipse.

No Eclipse, vamos acessar o start.sh novamente e rodar a aplicação. Se você estiver usando o Windows, provavelmente o /bin/bash não funcionará. Neste caso, você pode rodar o código a seguir na linha de comando, no cmd do próprio Windows, contanto que você esteja posicionado no path, onde está o app. Você tem que estar dentro do diretório app.


java -jar -Xms128M -Xmx128M -XX:PermSize=64m -XX:MaxPermSize=128m -Dspring.profiles.active=prod target/forum.jar

Lembrando que, no Windows, é necessário usar "contra barra" / ao invés de barra \, porque ele não entende a barra. No Linux, ela faz distinção entre um diretório e outro. No MacOS não muda nada.

A aplicação foi iniciada. Temos topicos, topocos/1 e de outros ids, e agora, temos /actuator. Então, vamos acessar http://localhost:8080/actuator.

Temos o health e um path específico, mas ele é herdado do health que foi configurado no application.properties.

Então, temos o endpoint health, o endpoint health-path, que veio herdado do health. Temos o info e o metrics com uma métrica específica e o metrics puro. Vamos abrir o health.

O health pode ser usado como health check. O status indica "up". Ele apresenta, inclusive, a comunicação da qual a aplicação depende, no caso, o MySQL (que stá conectado). Apresenta também o espaço em disco e o Redis.

Então, esse health é importantíssimo para a questão de health check. Ele nos ajuda a entender, de maneira imediata, se alguma dependência está com problema. Se ele não se conectar com o Redis, essa informação aparecerá aqui. Se não se conectar com o MySQL, também, pois isso não depende da aplicação.

Agora, vamos analisar o info. Nele, econtramos o app, o nome, a descrição, a versão e como ele está codificado. Em seguida, temos a versão do Java. O ponto alto é que, acessando metrics, temos a exposição de todas as métricas da JVM.

Dentre essas métricas, é possível consultar o uso de CPU, o log em "logback", as threads, memória, garbage collector, pool de conexões da JDBC com o banco, conexões pendentes, timeout utilizado, tempo de criação, entre outras.

Selecionando um dos nomes dessas métricas, conseguimos chegar no endpoint e acessar a métrica específica. Por exemplo, vamos selecionar Hikaricp.connections e chamar o endpoint passando a métrica:

localhost:8080/actuator/metrics/hikaricp.connections 

Com isso, abrimos a métrica Hikaricp.connections e encontramos o valor 10.

Conseguimos expor as métricas da JVM, porém, elas não estão no formato esperado. Não conseguimos identificar e usar essas métricas através do Prometheus, que é o nosso objetivo.

A seguir, vamos configurar o Micrometer, que tornará essas métricas legíveis para o Prometheus. Até logo!

Sobre o curso Observabilidade: coletando métricas de uma aplicação com Prometheus

O curso Observabilidade: coletando métricas de uma aplicação com Prometheus possui 160 minutos de vídeos, em um total de 23 atividades. Gostou? Conheça nossos outros cursos de Confiabilidade em DevOps, ou leia nossos artigos de DevOps.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Confiabilidade acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas