Friday, May 28, 2021

Hello, Java + Kubernetes!

Nesse artigo  fizemos um Hello World de Docker com Java e abordamos os conceitos de imagens e containers. Entendemos que aplicações na forma de containers possuem caracteristicas peculiares que possibilitam sua replicação e distribuição entre os diferentes ambientes com muito mais agilidade. Porém elas também apresentam novos desafios.

Containers no mundo real

Com o comando docker run podemos criar e iniciar um novo container e nossa aplicação está no ar. Porém temos apenas uma única instancia da aplicação em um único docker host. O que acontece se:

  • o número de usuários crescer acima da capacidade suportada pela aplicação?
  • precisarmos monitorar o estado de dezenas ou centenas de containers?
  • um ou vários containers param por qualquer motivo?
  • o host no qual rodam os containers pára?
Esses são apenas alguns dos desafios introduzidos pelo uso massivo de aplicações em containers.

Essas questões são específicas de um problema mais genérico conhecido como Orquestração de Containers.

Neste artigo exploramos uma das soluções mais usadas para o problema da Orquestração de Container, que é o Kubernetes. Criamos uma aplicação Java e vamos containerizá-la e em seguida replicar o container via kubernetes.

Kubernetes (K8)

Aplicação que iremos conteinerizar já está pronta. É a que usamos nesse artigo que apresenta uma introdução à autenticação JWT em Java. O que a aplicação faz não é revelevante por agora. O que importa é que ela já possui uma suite testes de integração automatizados apontando para localhost

Após conteinerizá-la e replicar os containers, vamos rodar os testes apontando para o load-balancer do cluster kubernetes, o qual irá distribuir a carga entre as réplicas criadas.

Para realizar esse teste precisamos instalar o minikube (utilitário que simula um cluster na sua máquina) com o seguinte comando:


curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

Agora instalamos o utilitário kubectl que permite gerencial o cluster kubernetes por linha de comando:


$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
$ sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
$ kubectl version --client

O primeiro passo para conteinerizar a aplicação é criar seu Dockerfile, no qual definimos todo o script para seu build e deploy. 

Nosso Dockerfile começa declarando que o build começa em uma imagem que possui o JDK 11 e o maven instalados:

  • copiamos a pasta /src e o arquivo pom.xml para uma imagem que já contenha JDK 11 e Maven configurados (maven:3.6.3-openjdk-11-slim)
  • buildamos a aplicação dentro da imagem com mvn clean package (esse passo gera o .war)
  • copiamos o artefato gerado (.war) no passo anterior para uma outra imagem jboss/wildfly e descartamos a imagem do Maven
  • expomos a porta 8080 do container jboss/wildfly

FROM maven:3.6.3-openjdk-11-slim AS Builder
LABEL key="https://finalexception.blogspot.com"

COPY ./pom.xml /app/pom.xml
COPY ./src /app/src
WORKDIR /app
RUN mvn clean package

FROM jboss/wildfly
EXPOSE 8080
COPY --from=Builder /app/target/hello-authentication.war /opt/jboss/wildfly/standalone/deployments/

Com o Dockerfile pronto, criamos a imagem:


$ docker build -t rafaelnasc1mento/hello-authentication:latest .

Em seguida publico a imagem em alguma conta docker-hub com o padrão <prefixo-conta>/hello-authentication:latest:

$ docker login
$ docker push rafaelnasc1mento/hello-authentication:latest

Agora que temos a imagem devidamente publicada no hub, é hora do kubernetes entrar em ação.

Deployment.yaml

Este arquivo contem a descrição de como nosso cluster irá operar e qual é o estado desejado do mesmo:

  1. Nosso serviço se chama hello-authentication-service
  2. Queremos um Load-Balancer para distribuir a carga entre os containers
  3. Queremos 3 réplicas do container
Como os containers não poderão ser acessados diretamente, o Load-Balancer expõe a porta 30001 para a qual serão encaminhados os requests, também chamada de nodePort

Como o container expoe a porta 8080, então definimos a targetPort para 8080, assim o Load-Balancer encaminhará as requisições que chegam até ele para a porta 8080 de algumas das réplicas. Abaixo o script completo:

---
kind: Service
apiVersion: v1
metadata:
  name: hello-authentication-service
spec:
  selector:
    app: hello-authentication
  ports:
    - protocol: "TCP"
      # port accessible inside cluster
      port: 8081
      # port to forward to inside pod
      targetPort: 8080
      # port accessible outside the cluster
      # whenever I hit this port, it will forward the request to the 'targetPort', which is the port to forward to the pod specified in 'selector.app'
      nodePort: 30001
  type: LoadBalancer

---
apiVersion: apps/v1
# a deployment defines the desired state of our application
kind: Deployment
metadata:
  name: hello-authentication-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      # name that's refereed by the load-balancer or other components inside the cluster
      app: hello-authentication
      tier: backend
  template:
    metadata:
      labels:
        app: hello-authentication
        tier: backend
    spec:
      containers:
        # container name
        - name: hello-authentication
          # docker image at docker-hub
          image: rafaelnasc1mento/hello-authentication:1.0
          ports:
            - containerPort: 8080


Isso é tudo! Para subirmos o cluster basta iniciar o minikube e criar o deployment:


$ minikube start
$ kubectl create -f deployment.yml

Use apply ao invez de create se o cluster já existe e você quer apenas atualizar alguma diretriz, editando o deployment.yaml.

Nesse ponto nosso cluster minimamente resiliente com 3 réplicas do container Hello-Authentication e um load balancer está totalmente operacional. Para confirmar, execute:

$ kubectl get pods


PODs são a unidade básica de deploy em um cluster. Neste caso podemos ver 3 instâncias de cada um dos containers.

Conforme dito, o Load Balancer é a porta de entrada dos serviços, umas vez que os containers não ficam expostos diretamente ao mundo exterior. Para descobrir o IP do Load Balancer execute:

$ minikube ip


Pelo output do comando, podemos ver que o IP do seriço Load Balancer, através do qual acessamos o cluster é 192.168.49.2.


Testes Automatizados

No pacote de testes do projeto Hello Atuhtentication, temos a classe Constants.java e nela o campo BASE_URI. Vamos colocar o IP do minikube como valor desse campo:


Agora, ao rodar os testes, as requisições serão direcionadas para o IP do cluster 192.168.49.2 na porta 30001. Depois da porta, colocamos a url dos serviços conforme definido na aplicação java.

 Clique com o botão direito na classe ServiceApiTest.java para rodar os testes:



Podemos ver que as replicas recebem as requisições normalmente.

Outro recurso interessante do minikube é o dashborad. Execute:

$ minikube addons enable metrics-server

$ minikube dashboard




O dashboard é uma interface web de administração que te permite ter uma visão geral do cluster, podemos inclusive remover ou adicionar mais containers, ver o consumo de CPU e memória em cada réplica e muito mais!


       

No comments:

Post a Comment