Back-End

11 jan, 2019

Utilizando o Health Check do ASP.NET Core 2.2 com Kubernetes

Publicidade

Fala, galera! Tudo certo?

Hoje vamos ver rapidamente uma das novidades do ASP.NET Core 2.2, que é a disponibilização de um mecanismo de Health Check com um endpoint para acessarmos o status dessas verificações, e como podemos construir um Check customizado.

Veremos também, no detalhe, como utilizar isso em conjunto com o Kubernetes, configurando um cluster, fazendo deployment e atualizando este que possa avaliar a saúde dos containers em execução e fazer a reciclagem deles quando necessário.

A demonstração está sendo feita no Azure com o serviço AKS, mas nada impede de executarem em uma instalação local do Kubernetes.

Motivação

Antes de colocar as mãos na massa, por que fazer esse tipo de verificação?

Já não estamos mais na época que verificávamos logs e status da aplicação manualmente – quando recebíamos algum chamado do usuário questionando a disponibilidade ou reportando erros.

Hoje, quem ainda faz desta forma simplesmente perde usuários, pois estamos vivendo uma fase onde nossas aplicações/ambientes devem ser inteligentes o suficiente para nos alertar de falhas antes mesmo dos usuários as reportarem, e até mesmo, se recuperarem sem a necessidade de intervenção manual.

Avaliando a situação da aplicação ao receber um chamado do usuário. Bom, pode ser um pouco tarde.

Habilitando o Health Check

Vamos criar um novo projeto Web MVC com o ASP.NET Core 2.2. Para isso, vamos utilizar o comando “dotnet new mvc -n DemoHealthCheck”. Para isso devemos ter instalado o SDK do .NET Core 2.2. Para verificar a versão do CLI, basta executar o comando “dotnet — version”, conforme também mostro na tela abaixo:

Criando um novo projeto ASP.NET MVC onde vamos habilitar o Health Check.

Vamos abrir o diretório/projeto no Visual Studio Code e acessar a classe Startup.

Visual Studio Code com a classe Startup aberta.

Agora adicionaremos no método ConfigureServices a chamada para adicionar o serviço de Health Check ao ServiceProvider do ASP.NET Core, adicionando a linha conforme o destaque na imagem abaixo.

Por enquanto estamos apenas habilitando o serviço. Logo veremos como adicionar verificações específicas que dirão se nossa aplicação está saudável ou não. Para habilitar o Middleware que responderá pelo serviço de Health Check, devemos editar o método Configure da classe Startup, adicionando a linha em destaque na imagem a seguir.

Notem que este método recebe uma string com o endereço a ser utilizado e, opcionalmente, um objeto de opções para configurar detalhes da rota e visualização dos dados, como mapear códigos de resposta HTTP para determinados estados de saúde retornados, se vamos utilizar cache e entre outras opções. Para esta demonstração informaremos apenas a string com a rota “/health”.

Ao executar a aplicação, ela será carregada normalmente.

Acessando a rota /health, por acessar diretamente no Browser, teremos uma resposta de texto indicando que a aplicação encontra-se saudável, visto que ainda não configuramos nenhuma verificação extra.

Retorno do endpoint de verificação de “saúde” de nossa aplicação.

Neste momento temos o serviço de Health Check ativo, mas ele não está efetivamente verificando nada além da aplicação estar em execução sem nenhum erro interno.

Agora adicionaremos a verificação que acessará um MockRest do site mockable.io. Após se cadastrar no site, criaremos um endpoint que responde com HTTP 200.

Endpoint Mock configurado para retornar um HTTP 200, com a URL obtida no botão Actions, Copy Live URL

Vamos criar uma nova classe implementando a interface de IHealthCheck na raiz de nossa aplicação.

Classe MockHealthCheck com a implementação da interface IHealthCheck.

Também editaremos o método CheckHealthAsync para realizar um HTTP GET para nossa API Mock e, caso o resultado seja OK (200), vamos retornar o resultado como saudável, ou, caso contrário, como não saudável.

Para isso, precisamos implementar o código abaixo. Note que adicionei o async no método CheckHealthAsync.

Agora precisamos registrar nossa classe MockHealthCheck no serviço de monitoração. Para isso vamos editar o método ConfigureServices da classe Startup, adicionando os métodos a seguir como sequência do método AddHealthChecks (temos uma DSL onde vamos configurando diferentes itens um após o outro), conforme a imagem em destaque a seguir.

Adicionando nossa classe de Health Check a lista de verificações do serviço.

Notem que é um método genérico onde informamos o tipo da classe de verificação e um parâmetro string, que é a chave de identificação daquele Check.

Agora, ao executar novamente nossa aplicação e acessar o endpoint /health, temos o resultado a seguir.

Aplicação retornando status de saudável, visto que o Mock está respondendo com OK (200).

Se alterarmos no mockable.io para retornar um status diferente de 2xx, conforme a imagem a seguir e verificarmos novamente a página de Health Check, teremos o status informado como não saudável, ou seja, nossa aplicação encontra-se com problemas, mas não completamente indisponível, visto que ela ainda está em execução. Isto já é o suficiente para disparar um mecanismo de alerta no Kubernetes, como veremos a seguir.

Retornando um HTTP 503 do mockable.io para forçar um status não saudável.

Antes de continuar, vamos voltar o mockable.io para retornar um status 200, assim poderemos prosseguir com o uso do Kubernetes para deploy da apliação.

Integrando nossa aplicação ao Kubernetes

Antes de colocar nossa aplicação em um cluster do Kubernetes, precisamos construir a imagem Docker dela, já com os comandos de Health Check do Docker prontos na imagem.

Para isso adicionaremos na raiz do projeto um arquivo chamado Dockerfile, com o conteúdo a seguir.

Arquivo Dockerfile

Apenas explicando rapidamente o que faz o arquivo acima, ele utiliza a imagem base do .NET Core 2.2, realiza o restore, build e publish da aplicação Web. Ao final ele configura o entry point como sendo a execução da DLL de nossa aplicação pelo CLI do .NET Core.

Notem que utilizamos como definição final a imagem de Runtime, gerando assim uma imagem menor para a publicação, comparado quando utilizamos apenas a imagem com o SDK.

Com o arquivo configurado, vamos executar o comando “docker build -t demo-health-check .”, onde “demo-health-check” é o nome da imagem que vamos criar.

Isso pode demorar de alguns segundos até alguns minutos, dependendo da sua conexão de internet e cache de imagens do docker, pois ele fará o download das imagens necessárias e realizará o build de nossa imagem.

Build em execução – Parte 1
Build em execução –  Parte 2

Agora temos a imagem pronta, podendo confirmar com o comando “docker images”.

Imagem pronta disponível em nosso repositório local.

Configurando o Contêiner Registry

Vamos criar um repositório para nossas imagens Docker no Azure. Para isto, vamos acessar o Portal do Azure, clicar no menu de Adicionar recursos, navegar até a opção Contêineres e escolher o item Container Registry.

Acessando a opção para criar um Container Registry.

Vamos dar um nome para nosso contêiner, escolher a assinatura em uso e um grupo de recursos, sendo que criei um novo para agrupar os recursos deste artigo, facilitando a remoção posteriormente.

Escolhi a região Sul do Brasil por questões de menor latência de rede. Habilitamos o Admin para poder publicar e alterei a camada de preços para a camada mais básica, por se tratar de uma demonstração. Após isto, basta clicar em Criar e esperar a criação do recurso ser concluída.

Configurando as opções do novo registro de contêiner.

Agora que temos nosso registro de contêiners criado, vamos publicar nossa imagem nele. Para isso, precisamos gerar uma tag de nossa imagem com a referência para publicação no registro de contêiners e fazer a publicação com os comandos apresentados nas imagens a seguir. Onde estiver gbbigardi.azurecr.io, troque pelo nome de registro que você utilizou em sua conta, ok?

Gerando a tag para publicação da imagem.

Para fazer o push da imagem precisamos fazer login no registro de contêiner. Para obter o usuário e senha, acesse o item no portal do Azure. Os dados encontram-se no menu “Access Keys” do recurso.

Tela de chaves de acesso, onde podemos obter o usuário e senha para login e publicação de nossa imagem.
Realizando login com o usuário e senha obtidos no portal do Azure.
Executando o push da imagem para o registro de containers.

Agora que temos a imagem pronta e disponível em nosso registro de containers, vamos criar um cluster Kubernetes para podermos fazer o deployment.

Criando um cluster Kubernetes no Azure

Para criar nosso cluster, vamos utilizar o terminal de linha de comando do portal do Azure. Para isso, basta clicar no ícone da barra de ferramentas como indicado na imagem a seguir, e em seguida escolher o bash. Este último é minha preferência. Podem utilizar o Powershell, se quiserem.

Acessando o terminal de comandos no Portal do Azure.

Caso seja a primeira vez acessando este recurso, será solicitada a criação de um recurso de Storage, pois é necessário para uso do terminal. O custo é em torno de centavos.

Vamos executar primeiro o comando a seguir para carregar o provedor que permite criar e gerenciar o Kubernetes via CLI.

Regsitrando o provider Microsoft.ContainerService para trabalharmos com o AKS via CLI.

Criaremos um novo grupo de recursos, visto que o serviço AKS ainda não está disponível no Sul do Brasil, impossibilitando a utilização do grupo que criei para o registro de contêiners. Para isto, basta executar o comando da imagem a seguir, trocando o valor do parâmetro name conforme desejar.

Criando um grupo de recursos no Azure, com uma região onde o AKS encontra-se disponível.

Agora geraremos um recurso do AKS, o nosso cluster Kubernetes, tendo estes dois nodes vinculados ao grupo que criamos no passo anterior. Para isto basta executar o comando conforme a imagem a seguir. O valor do parâmetro name pode ser trocado conforme a preferência de vocês, ok?

Criando um novo cluster do AKS com 2 nós.

Finalizados os procedimentos, podemos verificar que foram criados dois grupos de recursos: um com o serviço do AKS, TesteKubernetes, e outro com todos os recursos que compõem nosso cluster,

MC_TesteKubernetes_HealthCheckService_eastus, com as máquinas virtuais dos nós, rotas de rede, storage, etc.

Grupos de recursos criados durante a construção do cluster AKS.

Realizando deploy da aplicação

Agora que temos nosso cluster criado, precisamos fazer o deployment de nossa aplicação. Vamos primeiro configurar o deployment e em seguida alterar o mesmo para habilitar o Health Check.

Para poder fazer deployment da aplicação, precisamos do CLI kubectl do Kubernetes. Se você tem o Docker instalado localmente e habilitou o Kubernetes, ele já vai estar disponível no prompt de comando. Caso contrário, pode encontrar como instalar o CLI no link a seguir:

Pra quem não quiser instalar, como vou fazer aqui no artigo, vamos mais uma vez ao terminal no Portal do Azure para configurar e obter as credenciais de acesso ao nosso cluster AKS. Para isso, executaremos o comando a seguir.

Configurando o acesso ao Kubernetes

O comando acima, conforme visto na resposta dele, gerou um arquivo de configuração para acesso. Vamos agora habilitar o acesso ao registro de contêiners com o comando a seguir.

kubectl create secret docker-registry healthcheck-registrykey — docker-server=https://gbbigardi.azurecr.io — docker-username=<username> — docker-password=”<chave_acesso>” — docker-email=”<email>”
Executando comando para criação de chave de acesso ao registro de contêiners pelo AKS.

Agora precisamos criar o arquivo com as definições de deployment da aplicação. Chamaremos este de healthcheck.yml e vamos adicionar o conteúdo a seguir.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: health-check-deployment
spec:
  replicas: 2
  minReadySeconds: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: demohealthcheck
    spec:
      containers:
      - name: demohealthcheck
        image: gbbigardi.azurecr.io/demo-health-check:latest
        ports:
        - containerPort: 80
      imagePullSecrets:
        - name: healthcheck-registrykey

Podemos observar neste arquivo que a propriedade replicas está configurada para dois. Ou seja, dois pods e que estou indicando o uso da porta 80 como saída, além da imagem gbbigardi.azurecr.io/demo-health-check:latest, já disponível no registro de contêiners que criamos.

Agora vamos executar o comando a seguir para realizar o deployment com a configuração que criamos.

Executando o comando kubectl create com o arquivo de configuração para configurar o deploy da aplicação.

Se executarmos o comando “kubectl get pods” no terminal, vamos ver que temos agora dois pods (containers) em execução.

Verificando nossos pods em execução.

Se executarmos o comando “kubectl get services”, vamos ver que temos apenas o serviço de cluster em execução e sem nenhum IP externo. Ou seja, temos nossos pods em execução, mas não temos como acessá-los.

Verificando os serviços em execução no Kubernetes.

Precisamos criar um endpoint de entrada, e dado que temos dois pods em execução, nada mais justo que criarmos um load-balancer. Vamos criar agora um arquivo loadbalancer.yml com as configurações para deploy de um load-balancer no Kubernetes, direcionando o tráfego para nossos pods.

apiVersion: v1
kind: Service
metadata:
  name: healthcheck-balance-service
  labels:
    version: test
spec:
  selector:
    app: demohealthcheck
  ports:
  - port: 80
  type: LoadBalancer

Podemos verificar de especial neste arquivo o nó spec / selector / app com o nome de nossa aplicação nos pods. Em seguida, a porta em uso e o tipo (type) de serviço, que é um LoadBalancer.

Após criar o arquivo, vamos executar o comando a seguir para carregar o load-balancer.

Criando um load-balancer no Kubernetes com o arquivo de configuração.

Executando novamente o comando “kubectl get services”, podemos agora verificar nosso load-balancer com um IP externo. Pode demorar algum tempo para que o IP externo apareça, enquanto o AKS configura o roteador virtual.

Verificando o serviço de load-balancer criado, já com IP externo disponível.

Faremos um teste acessando o endereço http://157.56.179.189 (utilizando o IP fornecido em seu ambiente) no navegador.

Aplicação em execução.
API de Health Check respondendo como saudável, visto que no mackable.io ainda está retornando HTTP 200.

Vamos habilitar o Dashboard do Kubernetes para ter um “visual” melhor para ver nossa aplicação em execução, status e também validarmos o controle de Health Check.

Antes, precisamos configurar o usuário de acesso ao Dashboard. Vamos executar o comando a seguir no terminal do Azure para isso.

Habilitando o acesso de admin para podermos carregar e acessar o Dashboard do Kubernetes.

Agora executaremos o comando a seguir para carregar o Dashboard.

Carregando o Dashboard do Kubernetes.

Neste momento, ele carregou a aplicação de Dashboard e já criou uma rota para expor ela externamente. Para acessar, basta abrir a URL apresentada pelo CLI em uma janela do Browser.

Dashboard do Kubernetes carregado no browser.
Visualizando nossos dois pods em execução pelo Dashboard.

O próximo passo é configurar o Kubernetes para monitorar nosso endpoint, fazendo com que o pod seja reciclado caso nossa API reporte que não está saudável.

Poderemos ver nos logs do Kubernetes que os pods foram finalizados e substituídos por novas instâncias, quando alterarmos o retorno no mockable.io.

Para configurar o Kubernetes, vamos alterar o arquivo de deploy de nossa aplicação, o arquivo healthcheck.yml.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: health-check-deployment
spec:
  replicas: 2
  minReadySeconds: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    metadata:
      labels:
        app: demohealthcheck
    spec:
      containers:
      - name: demohealthcheck
        image: gbbigardi.azurecr.io/demo-health-check:latest
        ports:
        - containerPort: 80
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /health
            port: 80
            scheme: HTTP
          periodSeconds: 5
          initialDelaySeconds: 5
          successThreshold: 1
          timeoutSeconds: 30
      imagePullSecrets:
        - name: healthcheck-registrykey

Notem que adicionamos entre os nós ports e imagePullSecrets o nó livenessProbe, onde temos a configuração de validação da aplicação. Informamos que vamos utilizar um httpGet, no path /health, porta 80 a cada 5 segundos, com um delay de 5 segundos para a primeira verificação.

Feito isso, salvar o arquivo e atualizar nosso deployment com o comando a seguir:

Atualizando o deployment com o comando kubectl apply -f healthcheck.yml.

Em seguida, carregaremos novamente o dashboard.

Iniciando novamente o proxy para o serviço de Dashboard.

No dashboard, no menu à esquerda, clicaremos em Pods, e em um de nossos pods ver os detalhes do mesmo. Na tela do Pod vamos navegar até o painel de Events. Podemos ver que nosso contêiner foi iniciado e está em execução normalmente. Nenhum evento de erro sendo reportado.

Log de eventos de um dos Pods.

Vamos agora alterar o retorno do mockable.io para que nossa aplicação comece a retornar o status de não saudável no endpoint health através do Health Check do ASP.NET Core.

Configurando o mockable.io para retornar falha, simulando um serviço que nossa aplicação depende com problemas.

Como nosso Health Check está reportando Unhealthy (HTTP 503, alguns serviços indisponíveis), já temos nos logs do Kubernetes o alerta.

Evento de falha na verificação de saúde da aplicação.

Como a alteração no mockable.io reflete em todos os pods, podemos ver que todos estão com alertas. E verificando no detalhe, podemos notar que eles tiveram uma quantidade de restarts aplicados.

Pods com alertas de problemas reportados pelo serviço de verificação do Kubernetes.
Verificando que nossos pods reiniciaram em tentativa de recuperar a aplicação.

Vamos agora voltar ao mockable.io e voltar a resposta para 200, para que o Health Check do ASP.NET volte a reportar que a aplicação está saudável. Após alguns segundos veremos a quantidade final de restarts que ocorreram na aplicação, mas que a mesma já encontra-se disponível novamente.

Alicação novamente disponível após recuperação do serviço.

Notem que no arquivo .yml de deployment, utilizamos o nó liveness para configurar a verificação de saúde da aplicação que gerou restarts de nossos pods.

E se, por acaso, fosse apenas report de um serviço indisponível, como foi nesta situação, em que nossos pods apenas deveriam aguardar seu retorno sendo isolados do ambiente? Basta trocar o nó liveness por readiness.

Este nó terá o mesmo comportamento, exceto que os pods não serão reiniciados, apenas isolados do load-balancer até que se recuperem e reportem estarem saudáveis novamente.

Concluindo

Para finalizar a demonstração, caso queira, pode remover todo o ambiente que criamos com o comando a seguir, evitando surpresas com a cobrança no final do mês ou esgotamento de seus créditos de avaliação.

Removendo o ambiente de cluster do Kubernetes.

Hoje aprendemos mais sobre uma novidade que chegou na versão 2.2 do ASP.NET Core e vimos em detalhes como configurar e realizar o deploy de uma aplicação em um cluster AKS no Azure.

O código fonte da aplicação com o Custom Health Check encontra-se disponível no meu GitHub. Os arquivos de deployment estão no GIST, exibidos no conteúdo do artigo.

Espero que tenham gostado do artigo e caso tenham dúvidas ou se quiserem deixar um feedback, podem entrar em contato comigo aqui no iMasters pela sessão de comentários abaixo ou pelas redes sociais.

Um abraço a todos e até a próxima!