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.
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:
Vamos abrir o diretório/projeto no Visual Studio Code e acessar a classe Startup.
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.
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.
Vamos criar uma nova classe implementando a interface de IHealthCheck na raiz de nossa aplicação.
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.
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.
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.
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.
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.
Agora temos a imagem pronta, podendo confirmar com o comando “docker images”.
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.
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.
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?
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.
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.
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.
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.
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?
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.
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.
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>”
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.
Se executarmos o comando “kubectl get pods” no terminal, vamos ver que temos agora dois pods (containers) 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.
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.
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.
Faremos um teste acessando o endereço http://157.56.179.189 (utilizando o IP fornecido em seu ambiente) no navegador.
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.
Agora executaremos o comando a seguir para carregar o Dashboard.
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.
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:
Em seguida, carregaremos novamente o 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.
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.
Como nosso Health Check está reportando Unhealthy (HTTP 503, alguns serviços indisponíveis), já temos nos logs do Kubernetes o alerta.
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.
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.
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.
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!