Compartilhe:

Publicado em 23 de May de 2022

Observability as code

Introdução

Muitas organizações possuem um ambiente distribuído e adotam tecnologias distribuídas em nuvem para entregar uma melhor experiência para o usuário, com escalabilidade e eficiência. Porém, embora adotar tecnologias em nuvem e de microsserviços trará agilidade na entrega de funcionalidades, também irá aumentar consideravelmente a complexidade operacional de sustentar a operação.

Por isso foram criados processos e ferramentas como infrastructure as code, que permitem às equipes de SRE (Software Reliabiliy Engineering) automatizar a criação e o gerenciamento de ambientes. Contudo, entregar somente a infraestrutura não é suficiente. Isso porque com a entrega de cada microsserviço também é necessário acompanhar a saúde e o desempenho daquela aplicação no ambiente produtivo. Equipes de SRE e de desenvolvimento precisam criar dashboards e alarmes para acompanhar as aplicações e usá-los de forma proativa para mitigar possíveis problemas que possam impactar a experiência do usuário. E, quando ocorrem incidentes em produção, essas ferramentas devem auxiliar na correção rápida dos problemas.

Criar esses dashboards de acompanhamento e alarmes para cada microsserviço não é uma tarefa simples e requer muito cuidado, porque “buracos” na monitoração podem causar um desastre. Ambientes distribuídos podem falhar de maneiras imprevisíveis; por isso é necessária a instrumentação de todo o ambiente, facilitando a identificação de problemas de performance e falhas de forma proativa.

Por isso a observabilidade do ambiente produtivo é muito importante e não pode ser deixada de lado. Mas como fazer isso de maneira rápida, padronizada e evolutiva?

Spoiler alert: observability as code (observabilidade como código)!

Mas o que é observabilidade como código?

Assim como infraestrutura como código aplica práticas de engenharia de software para configurar e gerenciar infraestruturas, a observabilidade como código permite a times de SRE e de desenvolvimento criarem dashboards de acompanhamento e alarmes de forma consistente e acompanharem o estado e o comportamento das aplicações em todos os ambientes.

Ou seja, a criação de dashboards e alarmes não será feita manualmente após a entrega em produção do microsserviço. A monitoração codificada segue os ciclos de desenvolvimento. Ao entregar uma aplicação, o código da observabilidade é ativado, criando dashboards e alarmes. Usando boas práticas de programação, é possível e recomendável reutilizar o código fonte para várias aplicações. Assim, toda vez que for necessário evoluir o dashboard ou alarme para adicionar novas informações, basta evoluir o código e aplicá-lo. Pronto, todas as aplicações serão contempladas com as novas ferramentas de observabilidade! Simples, não é mesmo?

Como fazer?

Um ponto importante antes de começar a explicar como utilizar é escolher uma ferramenta de monitoração que tenha uma API que permita você criar e editar itens de monitoração de forma automática.

Nesse artigo irei demonstrar o básico da observabilidade com código. Para isso irei usar uma ferramenta muito popular: Terraform. A vantagem de usar essa ferramenta é que ela é amplamente utilizada como infraestrutura como código. Ou seja, se você já criou alguma infraestrutura com o Terraform não terá nenhum problema em criar observabilidade com código.

Se você nunca usou Terraform recomendo ler esses artigos:

Quando trabalhamos com sistemas distribuídos, nem todos os microsserviços possuem o mesmo grau de maturidade de monitoração. Isso deixa lacunas de monitoração, nas quais os desenvolvedores, SREs e plantonistas às cegas. Em um ambiente distribuído é impossível prever onde podem acontecer falhas e degradação. Caso ocorra um problema em um serviço que possui lacunas de monitoração (por exemplo, não tem monitorada sua taxa de erros, ou seu throughput), isso pode ter consequências desastrosas para o ambiente de produção, onde a queda de um serviço pode levar a degradação ou até mesmo queda de outros serviços.

Para me ajudar nisso, eu apliquei a observabilidade como código, criei com Terraform dashboards e alarmes padrões que eu quero que todas as aplicações possuam. Seguindo o framework SRE, criei códigos Terraform baseados nos Golden Signals da observabilidade. Criei um job parametrizável no Jenkins e o adicionei no job padrão de deploy. Com isso, todo serviço, desde o primeiro delivery, já possui um dashboard padrão e os alarmes principais. Mas isso ainda não é o suficiente. A observabilidade é uma escada sem fim. Mas com a observabilidade como código dei um primeiro e importante passo para a padronização e redução de lacunas de monitoração.

Hands-on

Para começar a usar observabilidade como código o primeiro passo é criar um arquivo provider.tf Como o exemplo abaixo:

provider "newrelic" {
  account_id = var.newrelic_account_id
  api_key = var.newrelic_api_key
}
 
terraform {
  required_providers {
	newrelic = {
  	version = "~> 2.30.0"
	}
  }
}

No código acima estamos declarando o provider do New Relic e o configurando com a conta correspondente à chave de acesso. O provider é responsável por transformar o código Terraform nas chamadas de API do New Relic para criar os recursos declarados.

Depois disso, por boa prática de desenvolvimento, é importante salvarmos o tfsate do Terraform em um bucket do S3.

terraform {
  backend "s3" {
	bucket     	= "nome_do_bucket"
	key        	= "caminho_tfstate/terraform.state"
	region     	= "sa-east-1"
	profile    	= "profile"
  }
}

Com isso pronto, agora iremos criar um arquivo chamado dashboard.tf que será responsável em criar os dashboards de monitoração da aplicação.

resource "newrelic_one_dashboard" "app_dashboard" {
 
  name        	= "App GoldenSignals" //aqui declaramos o nome do dashboard no new relic
 
  account_id  	= var.newrelic_account_id //Id da conta onde será criado o dashboard
  permissions 	= "public_read_write" #permissões de leitura escrita no dash board.
  page { //aqui criamos uma aba dentro do dashboard
	name = "Application Monitoring" # nome da aba
	widget_line {# criamos um gráfico de linha
  	title = "APDEX Score"#nome do gráfico
  	row = 1 #quantidade de linhas
  	column = 1 #quantidade de colunas
  	width = 3 # comprimento
 
  	nrql_query { # Consulta NRQL que irá popular o gráfico, aqui um exemplo para mostrar o apdex da aplicação
    	query   	= format("SELECT apdex(apm.service.apdex) as 'App server', apdex(apm.service.apdex.user) as 'End user' FROM Metric WHERE (entity.guid = '%s') SINCE 1 hour AGO TIMESERIES", data.newrelic_entity.app_APM.guid)
  	}
	}
 
	widget_billboard { #Criando um gráfico que mostra a disponibilidade
  	title = "Availability"
  	row = 1
  	column = 4
  	width = 3
 
  	nrql_query {
    	query   	= format("SELECT percentage(count(*), WHERE httpResponseCode <= '500') as '%s Availability' FROM Transaction WHERE appName = '%s' AND httpResponseCode IS NOT NULL AND request.headers.userAgent NOT IN ('ELB-HealthChecker/2.0')", "%", var.newrelic_application_name)
  	}
	}
  }
}

Pronto, criamos nosso primeiro dashboard. Agora é importante criarmos um alarme para nos avisar quando a aplicação não está saudável. Para isso vamos criar um arquivo alert.tf.

 
resource "newrelic_alert_policy" "policy_error_rate" {
  name = "policy error"
  incident_preference = "PER_CONDITION_AND_TARGET" #política irá alarmar para cada condition
}
 
resource "newrelic_alert_channel" "slack_channel" { #criando canal do slack que será enviando notificação em caso o gatilho desse alarme seja acionado
  name = "slack-channel-example"
  type = "slack"
 
  config {
	channel = "#example-channel"
	url 	= "http://example-org.slack.com"
  }
}
 
resource "newrelic_alert_policy_channel" "error_rate_alarm_channel" { #nesse block fazemos associação do canal do slack com a policy criada
  policy_id  = newrelic_alert_policy.policy_error_rate.id
  channel_ids = [
	newrelic_alert_channel.slack_channel.id
  ]
}
 
 
resource "newrelic_nrql_alert_condition" "alarm_error_rate" { # nesse block criamos o alarme
  policy_id     	= newrelic_alert_policy.policy_error_rate.id
 
  name          	= "nome do alarme" # nome do alarme
  enabled       	= true  # alarme habilitado
 
  type                       	= "static" #tipo do alarme, pode ser static ou baseline
  description                	= "descrição do alarme"
  runbook_url                	= "https://www.example.com" #url de runbook
  violation_time_limit_seconds   = 3600
  fill_option                	= "static"
  fill_value                 	= 1.0
  aggregation_window         	= 60
  aggregation_method         	= "event_flow"
  aggregation_delay          	= 120
  expiration_duration        	= 120
  open_violation_on_expiration   = true
  close_violations_on_expiration = true
  slide_by                   	= 30
 
  nrql {# consulta nrql que irá alarmar
	query = "format("SELECT percentage(count(*), WHERE httpResponseCode >= '500') FROM Transaction WHERE appName = '%s' AND httpResponseCode IS NOT NULL",  var.newrelic_application_name)"
  }
  critical { #nesse block é declarado os thresholds  para alarmar em estado critico
	operator          	= "above"
	threshold         	= 5.5
	threshold_duration	= 300
	threshold_occurrences = "ALL"
  }
 
  warning { #nesse block é declarado os thresholds  para alarmar em estado de atenção
	operator          	= "above"
	threshold         	= 3.5
	threshold_duration	= 600
	threshold_occurrences = "ALL"
  }
 
}

Pronto, agora temos o canal do alarme criado e o dashboard. Esse foi um “Hello World” de observabilidade como código. Muita coisa pode ser feita usando recursos avançados do Terraform, como módulos, blocos dinamicos, etc.

Referências

Tags:monitoraçãoterraformautomatizaçãoMicro-serviçosnew relic

Publicado por: PagBank Engineering