Back-End

12 fev, 2019

ASP.NET Core 2.2 + Application Insights: monitorando a disponibilidade dos BDs de uma aplicação

172 visualizações
Publicidade

Muitas aplicações Web dependem, quase que invariavelmente, de bases de dados relacionais e/ou NoSQL para a manipulação de informações. O fato de um projeto deste tipo se encontrar operante não significa, necessariamente, que tudo esteja em ordem.

Alguns dos bancos de dados utilizados podem não se encontrar acessíveis em determinado momento, levando assim a eventuais falhas.

Considerando o caso específico de aplicações construídas com o ASP.NET Core 2.2, como conseguiríamos efetuar o monitoramento de dependências como bancos de dados e empregando, para isto, um esforço mínimo de codificação?

A resposta a esse questionamento passa pela utilização de Health Checks (novo recurso que integra a versão 2.2 do ASP.NET Core), bem como do Application Insights (serviço de monitoramento que integra o Microsoft Azure).

É justamente o que farei nas próximas seções, demonstrando a implementação deste tipo de prática através do uso de Health Checks e do Azure Application Insights.

Os fontes do projeto descrito neste artigo já estão, inclusive, no seguinte repositório do GitHub:

E aproveito este artigo para deixar um convite:

No dia 14/02/2019 (quinta-feira), às 21:30 ( horário de Brasília), teremos mais um evento online no Canal .NET. Desta vez, será abordado o uso do NGINX, solução open source utilizada como Load Balancer, Web Server, Proxy Reverso, etc.

Ao longo do evento, eu e o MVP LuizCarlosFaria abordaremos o uso do NGINX em conjunto com tecnologias como ASP.NET Core, Linux, Node, Docker, Docker Compose e Microsoft Azure.

Para efetuar a sua inscrição acesse a página do evento no Meetup. A transmissão acontecerá via YouTube, em um link a ser divulgado em breve.

Criando um novo recurso do Application Insights

A criação de um novo recurso do Application Insights por meio do Portal do Azure é uma tarefa extremamente simples, bastando, para isso, acionar a opção New e localizar este serviço entre as opções oferecidas:

A seguir estão os campos a serem informados para a criação de tal recurso:

No caso específico do campo Application Type, será utilizada a opção ASP.NET Web Application em virtude do projeto empregado neste artigo:

Uma Chave de Instrumentação (Instrumentation Key) aparecerá ao acessar o recurso do Application Insights. A adição desta configuração a um projeto permitirá que o mesmo envie informações para o monitoramento a partir do Portal do Azure:

Os slides a seguir trazem uma visão geral das diferentes possibilidades de monitoramento oferecidas pelo Application Insights:

Configurando o uso do Application Insights e de Health Checks em um projeto ASP.NET Core

A Chave de Instrumentação para uso do Application Insights deverá ser especificada no arquivo appsettings.json, através dos itens ApplicationInsights e InstrumentationKey:

{
  "ApplicationInsights": {
    "InstrumentationKey": "CHAVE INSTRUMENTAÇÃO"
  },
  "ConnectionStrings": {
    "BaseIndicadores": "Data Source=?;Initial Catalog=?;User Id=?;Password=?;",
    "CacheRedis": "STRING REDIS"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Será necessário, também, adicionar ao projeto os packages Microsoft.ApplicationInsights.AspNetCore, AspNetCore.HealthChecks.Redis e AspNetCore.HealthChecks.SqlServer, que permitirão a ativação da utilização do Application Insights e dos Health Checks de monitoramento:

Os pacotes AspNetCore.HealthChecks.Redis e AspNetCore.HealthChecks.SqlServer fazem parte do projeto Xabaril/AspNetCore.Diagnostics.HealthChecks. Essa iniciativa reúne uma série de extensões para o ASP.NET Core 2.2, de forma a simplificar a implementação de Health Checks envolvendo verificações de disponibilidade de diversos tipos de serviços consumidos por uma aplicação.

Alterações também serão realizadas na classe Startup:

  • O método AddApplicationInsightsTelemetry será acionado em ConfigureServices, de forma a ativar o uso do Application Insights no projeto considerado;
  • Os métodos AddSqlServer e AddRedis foram acionados logo após a chamada a AddHealthChecks em ConfigureServices, recebendo como parâmetro as Connection Strings das bases a serem testadas;

Ao invocar o método UseHealthChecks no método Configure da classe Startup, foi informado como parâmetro uma instância do tipo HealthCheckOptions (namespace Microsoft.AspNetCore.Diagnostics.HealthChecks), a qual permitirá formatar o retorno do middleware de Health Check com informações como o status geral da aplicação e a situação de cada checagem previamente configurada.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Mime;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.EntityFrameworkCore;
using APIIndicadores.Data;
using Newtonsoft.Json;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace APIIndicadores
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            // Configurando o uso do Entity Framework Core
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("BaseIndicadores")));

            // Ativando o uso de cache via Redis
            services.AddDistributedRedisCache(options =>
            {
                options.Configuration =
                    Configuration.GetConnectionString("CacheRedis");
                options.InstanceName = "Cache-APIIndicadores-";
            });

            // Verificando a disponibilidade dos bancos de dados
            // da aplicação através de Health Checks
            services.AddHealthChecks()
                .AddSqlServer(Configuration.GetConnectionString("BaseIndicadores"), name: "baseSql")
                .AddRedis(Configuration.GetConnectionString("CacheRedis"), name: "cacheRedis");

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            // Ativando o Application Insights
            services.AddApplicationInsightsTelemetry(Configuration);
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            // Ativando o middlweare de Health Check
            app.UseHealthChecks("/status",
               new HealthCheckOptions()
               {
                   ResponseWriter = async (context, report) =>
                   {
                       var result = JsonConvert.SerializeObject(
                           new
                           {
                               statusApplication = report.Status.ToString(),
                               healthChecks = report.Entries.Select(e => new
                               {
                                   check = e.Key,
                                   ErrorMessage = e.Value.Exception?.Message,
                                   status = Enum.GetName(typeof(HealthStatus), e.Value.Status)
                               })
                           });
                       context.Response.ContentType = MediaTypeNames.Application.Json;
                       await context.Response.WriteAsync(result);
                   }
                });

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

É importante destacar que o uso de Health Checks no ASP.NET Core 2.2 contribui para um menor esforço em termos de codificação, além de uma maior simplicidade no projeto. Isto se deve ao fato desse novo tipo de recurso evitar a necessidade de implementação de Actions e Controllers para fins de monitoramento.

Concluída a implementação desta aplicação, o próximo passo será realizar o deployment da mesma. Para os testes descritos neste artigo, o projeto em questão foi publicado através do Azure App Service.

Verificando a disponibilidade das bases de dados via Health Checks

A ativação dos Health Checks via método UseHealthChecks irá gerar um endpoint para o endereço /status, o qual retornará uma resposta com um conteúdo JSON indicando a saúde geral da aplicação.

A seguir temos um exemplo indicando normalidade, com os status da aplicação e de suas dependências estando como Healthy e um código de retorno 200 (OK):

Caso algum dos Health Checks apresente problema, o mesmo aparecerá como Unhealthy, com o status da aplicação também assumindo tal valor e o código 503 (Service Unavailable) estando associado à resposta produzida.

É o que demonstra a próxima imagem, com a verificação da base de dados do Redis indicando problemas de conexão:

Este endpoint que permite analisar o estado dos Health Checks será utilizado em conjunto com o Application Insights, na configuração de um teste de disponibilidade executado de forma periódica e automática por este serviço de monitoramento.

A criação deste tipo de checagem acontecerá em Availability, através da opção Add test:

Em Create test deverão ser preenchidos:

  • O nome do teste de disponibilidade (campo Test name);
  • O tipo do teste como URL ping test (em Test type);
  • Em URL será informado o endereço do endpoint que retornará o resultado dos Health Checks;
  • Uma frequência de execução de cinco minutos em Test frequency;
  • Localidades (data centers do Azure) a partir das quais serão disparados os testes em Test locations;
  • Em Success criteria está definido que a resposta esperada deve possuir o código HTTP 200, bem como foi configurado um timeout de 120 segundos;
  • Um alerta será gerado se três ou mais das requisições enviadas por diferentes localizações falharem durante um conjunto de testes (campo Alerts).

Para confirmar a criação do teste, acionar então o botão Create:

Nas próximas imagens é possível observar o resultado da execução de vários testes de disponibilidade:

Referências