Deploy de aplicação Quarkus no Kubernetes com LoadBalancer
Tags: kubernetes, java, cloud
Published: 2021-08-14 04:21:52

Hoje em dia muito se fala em aplicações cloud native, quando falamos sobre este conceito nos referimos a aplicações containerizadas que são executadas em um ambiente de nuvem, escalável, com alta disponibilidade e tolerância a falhas. 
Muita das vezes nós desenvolvedores não estamos inseridos no contexto de infraestrutura da empresa em que trabalhamos, e com isso não temos a visão de como este universo funciona por debaixo dos panos, mas é de grande importância conhecermos estes conceitos, para desenvolver aplicações que sejam realmente escaláveis e de alta disponibilidade.


Neste artigo vamos rodar uma aplicação com Framework Quarkus, utilizando Minikube para rodar Kubernetes localmente, e utilizar Nginx Ingress Controller para fazer o balanceamento de carga das requisições que nossa aplicação receber.

Para este artigo é necessário que já tenha o conhecimento sobre o que são containers.

## Tecnologias

Falando um pouco sobre a stack que utilizaremos neste artigo:

### [Kubernetes] (https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/)

Kubernetes ou k8s, é um orquestrador de container, ou seja, é o encarregado pelo gerenciamento dos containers da nossa aplicação, sendo responsável pela execução e monitoração dos nossos containers, e quando necessário, levantar novas instâncias da nossa aplicação. Neste artigo utilizaremos o Minikube que funciona como um Kubernetes local na sua máquina.

### [Quarkus] (https://www.redhat.com/en/topics/cloud-native-apps/what-is-quarkus)

Quarkus é um framework open source desenvolvido pela Red Hat. Pensado para aplicações Cloud native, o Quarkus trouxe inovações possibilitando o desenvolvimento de Aplicações em Java com baixo consumo de memória e rápida inicialização.

### [Helm] (https://helm.sh/)

Helm é um gerenciador de pacotes para aplicações projetadas para executar em cluster Kubernetes.


## Estrutura do Projeto

{% github Jonathas-garcia/demo-quarkus-k8s no-readme %}

O projeto que executaremos possui uma proposta bem simples. Recebe uma requisição HTTP GET em um endpoint "/host" e responde um objeto contendo o nome da máquina em que ele está sendo executado, e a data e hora atual. Dessa forma conseguiremos verificar se o balanceamento de carga é realizado corretamente entre as instâncias que estarão em execução.

Os arquivos referente a configuração do Helm estão dentro da pasta *"Helm"*, nele temos os seguintes arquivos:

- **Chart.yaml** - Contém as informações da aplicação como nome, descrição, tipo e versão.

- **values.yaml** - Arquivo que contém os valores dos parâmetros que utilizaremos nos arquivos de "configuração" do Kubernetes. Nesse caso estamos especificando "replicaCount" que é a quantidade de instâncias da nossa aplicação, algumas informações referente ao container e seu repositório no dockerhub, e também informações de porta da aplicação. Essas informações são injetadas pelo Helm nos arquivos de template do Kubernetes que falaremos abaixo.

Para configurar nosso deploy temos os arquivos de templates do Kubernetes que está localizado na pasta *"helm/templates"*, que contém a seguinte estrutura.

- **deployment** - Contém o arquivo dp.yaml, que é um template do Kubernetes que utilizamos para especificar as informações da aplicação que será executada, como por exemplo, o nome da imagem, quantidade de instâncias e alguns metadados, como por exemplo a utilização de labels para identificar aplicações. Neste caso estamos definindo que nosso container executará a imagem jonathasgarcia/demo-quarkus-k8s, terá 4 instâncias e também definimos alguns outros metadados.

{% gist https://gist.github.com/Jonathas-garcia/693f0dfdf94097351ef421a09b505260 file=dp.yaml %}

- **service** - Contém o arquivo service.yaml, template do Kubernetes para criar um service, ou seja, estamos expondo nossa aplicação para acesso interno/externo. Neste caso, estamos especificando que quando nosso cluster Kubernetes receber uma requisição na porta 30081, a chamada será redirecionada para a porta 8080, que é a porta da nossa aplicação. 

{% gist https://gist.github.com/Jonathas-garcia/693f0dfdf94097351ef421a09b505260 file=service.yaml %}

- **ingress** - Contém o arquivo ingress.yaml, arquivo para especificar o objeto ingress, responsável por gerenciar acessos externos as services que estão executando no nosso Kubernetes. Neste arquivo estamos definindo que as chamadas para a rota /app serão redirecionadas para a service que criamos acima. O Ingress também fica responsável por realizar o balanceamento de carga entre as instâncias disponíveis.

{% gist https://gist.github.com/Jonathas-garcia/693f0dfdf94097351ef421a09b505260 file=ingress.yaml %}

*Importante relembrar neste ponto que estes arquivos no github não estão recebendo os valores diretamente, eles estão configurados para serem preenchidos pelo Helm de acordo com o arquivo values.yaml*.


## Executando

Inicialização do Minikube

```
minikube start
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2gd60c6fzzo9vn4upymu.png)
 

Habilitando ingress controller no Minikube

```
minikube addons enable ingress
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mrn89bky6jg19gejkgrg.png)
 
Criação namespace em que será executado nossa aplicação.

```
kubectl create namespace backend
```

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v65ma3suppavm51wat1x.png)
 
Execução da aplicação pelo Helm

```
helm install --namespace backend demo-quarkus ./helm 
```
Nesse comando definimos a instalação da nossa aplicação, passando em qual namepace será instalada, o nome da aplicação, e caminho da pasta que estão os arquivos de configuração.


![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jos376d60m7glx5hocta.png)

Podemos verificar as pods executando através do comando

```
kubectl get pods -n backend
```

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofai25zwqd4560js2kzj.png)

Para tornar o minikube acessível rode o seguinte comando em outro terminal, e o minikube disponibilizará um IP para acesso.

```
minikube tunnel
```
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8dgx6ubkxwrfy2jcsyj.png)
 
Acessando 172.17.171.187:30081/host já temos acesso a aplicação via browser, mas nesse caso estamos acessando diretamente a service, sem passar pelo ingress controller, portanto se dermos alguns F5 na tela veremos que a informação de "hostname" não é alterada, pois o balanceamento de carga é realizado pelo ingress.
 
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h9vtkl9ratc88adiouw8.png)

Acessando 172.17.171.187/app/host temos acesso a aplicação passando pelo ingress controller, lembrando que configuramos o path "/app" para redirecionar para o service da nossa aplicação. Dessa forma então o ingress recebe a requisição e redireciona para nossa service, distribuindo cada requisição para cada uma das instâncias que estão em execução.

![gif-loadbalancer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/caj3k4uirbqneppnsxbj.gif)
 
## Atualizando configurações via Helm

Vamos supor que queremos diminuir a quantidade de instâncias de 4 para apenas 2. Essa informação é determinada pelo parâmetro "replicaCount" no nosso *values.yaml*.
 
```
helm upgrade demo-quarkus -n backend --set replicaCount=2 ./helm
```

 ![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lzve8ljqz584gh0337d3.png)

Dessa forma duas instâncias são finalizadas, e o balanceamento de carga é realizado apenas entre as duas que restaram. Podemos 
utilizar o *helm upgrade* em qualquer cenário que nossa aplicação sofrerá alterações, seja em quantidades de instâncias ou até mesmo versão da imagem docker que será executada.

## Conclusão

Neste artigo tivemos o intuito de mostrar uma rápida configuração e deploy no Kubernetes. Não passamos por alguns conceitos básicos, que são primordiais que você aprenda nos estudos desta ferramenta. Essa é apenas a ponta do iceberg do que o Kubernetes e outras ferramentas podem nos proporcionar no contexto de aplicações cloud native, porém já vimos como ele pode nos ajudar a rodar aplicações escaláveis, com alta disponibilidade e tolerante a falhas