Implante um aplicativo Micronaut conteinerizado com o Jib no Google Kubernetes Engine

1. Visão geral

Sobre o Micronaut

O Micronaut é um framework moderno de pilha completa baseado em JVM para criar microsserviços modulares e aplicativos sem servidor fáceis de testar. O Micronaut tem como objetivo oferecer um ótimo tempo de inicialização, capacidade de processamento rápida e um consumo mínimo de memória. Os desenvolvedores podem desenvolver com o Micronaut em Java, Groovy ou Kotlin.

O Micronaut oferece:

  • Tempo de inicialização rápido e baixo consumo de memória: os frameworks IoC baseados em reflexão carregam e armazenam em cache dados de reflexão para cada campo, método e construtor no seu código. Já com o Micronaut, o tempo de inicialização do aplicativo e o consumo de memória não estão vinculados ao tamanho da base de código.
  • Cliente HTTP declarativo, reativo e de tempo de compilação: crie clientes HTTP reativos de forma declarativa, que são implementados no tempo de compilação, reduzindo o consumo de memória.
  • Servidor HTTP não bloqueador criado no Netty: com uma curva de aprendizado suave, o servidor HTTP do Micronaut facilita ao máximo a exposição de APIs que podem ser consumidas por clientes HTTP.
  • Teste rápido e fácil: crie servidores e clientes com facilidade nos seus testes de unidade e execute-os instantaneamente.
  • Injeção de dependência e AOP eficientes em tempo de compilação: o Micronaut oferece uma API de programação orientada a aspectos simples em tempo de compilação que não usa reflexão.
  • Crie apps totalmente reativos e sem bloqueio: o Micronaut é compatível com qualquer framework que implemente Reactive Streams, incluindo RxJava e Reactor.

Para mais informações, acesse o site do Micronaut.

Sobre o Kubernetes

O Kubernetes é um projeto de código aberto que pode ser executado em diversos ambientes, de laptops a clusters com vários nós de alta disponibilidade, de nuvens públicas a implantações locais e de máquinas virtuais a bare metal.

Neste laboratório, você vai implantar um microsserviço simples do Micronaut baseado em Groovy no Kubernetes executado no Kubernetes Engine.

O objetivo deste codelab é executar seu microsserviço como um serviço replicado no Kubernetes. Você pega o código que desenvolveu na sua máquina, transforma-o em uma imagem de contêiner do Docker e executa essa imagem no Kubernetes Engine.

Confira um diagrama das várias partes deste codelab para entender como as peças se encaixam. Use isso como referência à medida que você avança no codelab. Tudo fará sentido quando você chegar ao fim. Você pode ignorar isso por enquanto.

Kubernetes Codelab Diagram 1 (2).png

Neste codelab, o uso de um ambiente gerenciado como o Kubernetes Engine, uma versão do Kubernetes hospedada pelo Google em execução no Compute Engine, permite que você se concentre mais na experiência do Kubernetes, em vez de configurar a infraestrutura subjacente.

Se você estiver interessado em executar o Kubernetes no computador, como um laptop de desenvolvimento, conheça o Minikube. Ele oferece uma configuração simples de um cluster do Kubernetes de nó único para desenvolvimento e teste. Você pode usar o Minikube durante este codelab, se quiser.

Sobre o Jib

O Jib é uma ferramenta de código aberto que permite criar imagens do Docker e do OCI para seus aplicativos Java. Ele está disponível como plug-ins para Maven e Gradle e como uma biblioteca Java.

O Jib tem como objetivo ser:

  • Rápido: implante suas mudanças rapidamente. O Jib separa seu aplicativo em várias camadas, dividindo dependências de classes. Agora você não precisa esperar que o Docker recrie todo o aplicativo Java. Basta implantar as camadas que foram alteradas.
  • Reproduzível: recriar a imagem do contêiner com o mesmo conteúdo sempre gera a mesma imagem. Nunca mais acione uma atualização desnecessária.
  • Sem daemon: reduza as dependências da CLI. Crie sua imagem Docker no Maven ou Gradle e envie para qualquer registro de sua escolha. Não é mais necessário escrever Dockerfiles e chamar docker build/push.

Saiba mais sobre o Jib na página do projeto no GitHub.

Sobre este tutorial

Neste tutorial, usamos o exemplo de código da ferramenta Jib para criar contêineres para aplicativos Java.

A amostra é um serviço simples de hello world, usando o framework Micronaut e a linguagem de programação Apache Groovy.

O que você vai aprender

  • Como empacotar um aplicativo Java simples como um contêiner do Docker usando o Jib
  • Como criar um cluster do Kubernetes no Kubernetes Engine
  • Como implantar seu serviço do Micronaut no Kubernetes no Kubernetes Engine
  • Como escalonar verticalmente o serviço e fazer um upgrade
  • Como acessar o painel gráfico do Kubernetes

O que é necessário

  • Um projeto do Google Cloud Platform
  • Um navegador, como o Chrome ou o Firefox
  • Conhecer os editores de texto padrão do Linux, como vim, emacs ou nano

Como você usará este tutorial?

Apenas leitura Leitura e exercícios

Como você classificaria sua experiência com a criação de apps da Web em HTML/CSS?

Iniciante Intermediário Proficiente

Como você classificaria sua experiência com o uso dos serviços do Google Cloud Platform?

Iniciante Intermediário Proficiente

2. Configuração e requisitos

Configuração de ambiente autoguiada

  1. Faça login no Console do Cloud e crie um novo projeto ou reutilize um existente. Crie uma se você ainda não tiver uma conta do Gmail ou do G Suite.

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

Lembre-se do código do projeto, um nome exclusivo em todos os projetos do Google Cloud. O nome acima já foi escolhido e não servirá para você. Faremos referência a ele mais adiante neste codelab como PROJECT_ID.

  1. Em seguida, será necessário ativar o faturamento no Console do Cloud para usar os recursos do Google Cloud.

A execução deste codelab não será muito cara, se for o caso. Siga todas as instruções na seção "Limpeza", que orienta você sobre como encerrar recursos para não incorrer em cobranças além deste tutorial. Novos usuários do Google Cloud estão qualificados para o programa de US$ 300 de avaliação sem custos.

3. Acessar o código-fonte de exemplo do Micronaut

Depois que o Cloud Shell for iniciado, use a linha de comando para clonar o código-fonte de exemplo no diretório principal e cd no diretório que contém nosso serviço de exemplo:

$ git clone https://github.com/GoogleContainerTools/jib.git
$ cd jib/examples/micronaut/

4. Uma olhada rápida no código

Nosso serviço simples do Micronaut é composto por um controlador que gera a famosa mensagem "Hello World":

@Controller("/hello")
class HelloController {
    @Get("/")
    String index() {
        "Hello World"
    }
}

O controlador HelloController está respondendo a solicitações no caminho /hello, e o método index() aceita as solicitações HTTP GET.

Uma classe de teste Spock também está disponível para verificar se a mensagem correta é exibida na saída.

class HelloControllerSpec extends Specification {
    @Shared
    @AutoCleanup
    EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)

    @Shared
    @AutoCleanup
    RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL()) 

    void "test hello world response"() {
        when:
        HttpRequest request = HttpRequest.GET('/hello')
        String rsp  = client.toBlocking().retrieve(request)

        then:
        rsp == "Hello World"
    }
}

Mais do que um simples teste de unidade, esse teste executa a mesma pilha de servidor Micronaut (baseada no framework Netty) que é executada em produção. Assim, o comportamento do código será o mesmo no produto e nos testes.

Para executar os testes, execute o seguinte comando para verificar se está tudo bem:

./gradlew test

5. execute o aplicativo no local

É possível iniciar o serviço do Micronaut normalmente com o seguinte comando do Gradle:

$ ./gradlew run

Depois que o aplicativo for iniciado, abra outra instância do Cloud Shell usando o pequeno ícone de mais (+) e verifique com curl se você recebe a saída esperada:

$ curl localhost:8080/hello

Uma mensagem simples "Hello World" vai aparecer.

6. Empacotar o aplicativo como um contêiner do Docker com o Jib

Em seguida, prepare o aplicativo para ser executado no Kubernetes. Para isso, vamos aproveitar o Jib para fazer o trabalho pesado, já que não vamos precisar tocar em um Dockerfile.

Vamos executar o comando para criar nosso contêiner:

$ ./gradlew jibDockerBuild

Esta é a saída que você vai ver:

Tagging image with generated image reference micronaut-jib:0.1. If you'd like to specify a different tag, you can set the jib.to.image parameter in your build.gradle, or use the --im
age=<MY IMAGE> commandline flag.

Containerizing application to Docker daemon as micronaut-jib:0.1...
warning: Base image 'gcr.io/distroless/java' does not use a specific image digest - build may not be reproducible
Getting base image gcr.io/distroless/java...
Building dependencies layer...
Building resources layer...
Building classes layer...
Finalizing...

Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, example.micronaut.Application]
Loading to Docker daemon...

Built image to Docker daemon as micronaut-jib:0.1

Agora que a imagem foi criada, vamos verificar se podemos ver a mensagem de olá executando a imagem Docker na primeira guia do Cloud Shell:

$ docker run -it -p 8080:8080 micronaut-jib:0.1
16:57:20.255 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cloud, gcp]
16:57:23.203 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 2926ms. Server Running: http://97b7d76ccf3f:8080

Nosso serviço está em execução. Agora podemos iniciar o comando curl na segunda guia do Cloud Shell para verificar se ele está funcionando como esperado:

$ curl localhost:8080/hello
Hello World

Para interromper o contêiner, pressione Ctrl+C no Cloud Shell.

7. Como enviar nosso serviço em contêiner para o registro

Agora que a imagem funciona como esperado, envie-a para o Google Container Registry, um repositório particular para suas imagens Docker, acessível a partir de qualquer projeto na nuvem do Google (mas também de fora do Google Cloud Platform).

Antes de enviar para o registro, verifique se o Container Registry está ativado para o projeto. Para isso, acesse Ferramentas > Container Registry. Se ela não estiver ativada, a caixa de diálogo a seguir vai aparecer. Clique em Ativar a API Container Registry para ativar:

ac812e6260ac7dfb.png

Quando o registro estiver pronto, execute os seguintes comandos para enviar a imagem a ele:

$ gcloud auth configure-docker
$ docker tag micronaut-jib:0.1 \
         gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1
$ docker push gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1

Os comandos acima permitem que o SDK gcloud configure e autorize o Docker a enviar imagens para sua instância do Container Registry, marque a imagem para apontar para a localização dela no registro e, em seguida, envie-a para o registro.

Se tudo der certo, depois de um tempo, a imagem do contêiner vai aparecer no console: Ferramentas > Container Registry. Agora você tem uma imagem do Docker disponível para todo o projeto que pode ser acessada e orquestrada pelo Kubernetes, como você verá em alguns minutos.

12224c4e42183b4e.png

8. Crie o cluster

Agora você está pronto para criar seu cluster do Kubernetes Engine. Antes disso, navegue até a seção do Google Kubernetes Engine no console da Web e aguarde a inicialização do sistema (isso leva apenas alguns segundos).

20c0587c0108b8ba.png

Um cluster consiste em um servidor de API mestre do Kubernetes administrado pelo Google e um conjunto de nós de trabalho. Os nós de trabalho são máquinas virtuais do Compute Engine. Vamos usar a CLI gcloud da sua sessão do Cloud Shell para criar um cluster com dois nós n1-standard-1. Esse processo vai levar alguns minutos:

$ gcloud container clusters create hello-cluster \
  --num-nodes 2 \
  --machine-type n1-standard-1 \
  --zone us-central1-c

No final, você verá o cluster criado.

Creating cluster hello-cluster in us-central1-c...done.
Created [https://container.googleapis.com/v1/projects/mn-gke-test/zones/us-central1-c/clusters/hello-cluster].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-c/hello-cluster?project=mn-gke-test
kubeconfig entry generated for hello-cluster.
NAME           LOCATION       MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
hello-cluster  us-central1-c  1.9.7-gke.7     35.239.224.115  n1-standard-1  1.9.7-gke.7   2          RUNNING

Agora você tem um cluster do Kubernetes totalmente funcional com o Google Kubernetes Engine:

d9e1e314769753e7.png

Agora é hora de implantar o aplicativo em um contêiner no cluster do Kubernetes. A partir de agora, use a linha de comando kubectl (já configurada no ambiente shell do Cloud Shell). O restante deste codelab exige que a versão do cliente e do servidor do Kubernetes seja 1.2 ou superior. O kubectl version vai mostrar a versão atual do comando.

9. Implante o aplicativo no Kubernetes

Uma implantação do Kubernetes pode criar, gerenciar e escalonar várias instâncias do aplicativo usando a imagem do contêiner que você acabou de criar. Vamos criar uma implantação do aplicativo no Kubernetes usando o comando kubectl create deployment:

$ kubectl create deployment hello-micronaut \
  --image=gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1

Para ver a implantação que você acabou de criar, execute:

$ kubectl get deployments
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-micronaut   1         1         1            1           5m

Para ver as instâncias de aplicativo criadas pela implantação, execute este comando:

$ kubectl get pods
NAME                               READY     STATUS    RESTARTS   AGE
hello-micronaut-5647fb98c5-lh5h7   1/1       Running   0          5m

Agora, você tem o contêiner em exibição sob o controle do Kubernetes, mas ainda precisa torná-lo acessível ao mundo exterior.

10. permitir o tráfego externo

Por padrão, o pod é acessível somente do seu IP interno dentro do cluster. Para tornar o contêiner hello-micronaut acessível de fora da rede virtual do Kubernetes, é necessário expor o pod como um serviço do Kubernetes.

Com o Cloud Shell, podemos expor o pod à Internet pública com o comando kubectl expose, combinado com a flag --type=LoadBalancer. A sinalização é obrigatória para a criação de um IP acessível externamente:

$ kubectl expose deployment hello-micronaut --type=LoadBalancer --port=8080

A flag usada nesse comando especifica que você vai usar o balanceador de carga fornecido pela infraestrutura subjacente (nesse caso, o balanceador de carga do Compute Engine). Você expôs a implantação, não o pod diretamente. Isso fará com que o serviço resultante faça o balanceamento de carga do tráfego em todos os pods gerenciados pela implantação (nesse caso, apenas um pod, mas você adicionará mais réplicas posteriormente).

O mestre do Kubernetes cria o balanceador de carga e as regras de encaminhamento do Compute Engine relacionadas, pools de destino e regras de firewall para tornar o serviço totalmente acessível de fora do Google Cloud Platform.

Para encontrar o endereço IP público do serviço, basta solicitar que o kubectl liste todos os serviços de cluster:

$ kubectl get services
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
hello-micronaut   LoadBalancer   10.39.243.251   aaa.bbb.ccc.ddd 8080:30354/TCP   1m
kubernetes        ClusterIP      10.39.240.1     <none>          443/TCP          31m

Há dois endereços IP listados para o serviço, ambos usando a porta 8080. Um é o IP interno que é visível apenas dentro da sua rede virtual na nuvem. Outro é o IP com balanceamento de carga externo. Neste exemplo, o endereço IP externo é aaa.bbb.ccc.ddd.

Agora, você deve conseguir acessar o serviço com este endereço no navegador: http://<EXTERNAL_IP>:8080/hello

11. escalonar o serviço verticalmente

Um dos poderosos recursos oferecidos pelo Kubernetes é a facilidade de escalonar seu aplicativo. Suponha que, de repente, você precise de mais capacidade para seu aplicativo. Basta dizer ao controlador de replicação para gerenciar um novo número de réplicas para as instâncias do aplicativo:

$ kubectl scale deployment hello-micronaut --replicas=3
deployment.extensions "hello-micronaut" scaled

$ kubectl get deployment
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-micronaut   3         3         3            3           16m

Observe a abordagem declarativa aqui: em vez de iniciar ou interromper novas instâncias, você declara quantas instâncias precisam estar em execução o tempo todo. Os loops de reconciliação do Kubernetes garantem que a realidade corresponda ao solicitado e entre em ação, se necessário.

12. implementar uma atualização no serviço

Em algum momento, o aplicativo implantado na produção exigirá correções de bugs ou recursos adicionais. O Kubernetes ajuda você a implantar uma nova versão na produção sem afetar seus usuários.

Primeiro, vamos modificar o aplicativo. Abra o editor de código no Cloud Shell.

5aee8f3d1e003571.png

Navegue até /jib/examples/micronaut/src/main/groovy/example/micronaut/HelloController.groovy e atualize o valor da resposta:

@Controller("/hello")
class HelloController {
    @Get("/")
    String index() {
        "Hello Kubernetes World"
    }
}

No /jib/examples/micronaut/build.gradle, vamos fazer upgrade da versão da nossa imagem de 0.1 para 0.2 atualizando esta linha:

version '0.2'

Em seguida, recrie e empacote o aplicativo com as mudanças mais recentes:

$ ./gradlew jibDockerBuild

E marque e envie a imagem para o registro de imagens de contêiner:

$ docker tag micronaut-jib:0.2 \
         gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2
$ docker push gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2

Agora já está tudo pronto para o Kubernetes atualizar o controlador de replicação para a nova versão do aplicativo. Para mudar o rótulo da imagem do contêiner em execução, edite o hello-micronaut deployment e mude a imagem de gcr.io/PROJECT_ID/micronaut-jib:0.1 para gcr.io/PROJECT_ID/micronaut-jib:0.2.

Use o comando kubectl set image para pedir ao Kubernetes que implante a nova versão do aplicativo em todo o cluster, uma instância por vez, com a atualização gradual:

$ kubectl set image deployment/hello-micronaut \
          micronaut-jib=gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2

deployment.apps "hello-micronaut" image updated

Verifique http://EXTERNAL_IP:8080 novamente para conferir se ele está retornando a nova resposta.

13. Reverter

Ops, você cometeu um erro com uma nova versão do aplicativo? Talvez a nova versão tenha um erro e você precise fazer um rollback rapidamente. Com o Kubernetes, é fácil reverter para o estado anterior. Vamos reverter o aplicativo executando:

$ kubectl rollout undo deployment/hello-micronaut

Se você observar a saída do serviço, vai ver que voltamos à mensagem inicial "Hello World".

14. Resumo

Nesta etapa, você configurou um serviço simples de hello world do Micronaut baseado em Apache Groovy e o executou diretamente no Cloud Shell, empacotou como um contêiner com Jib e implantou no Google Kubernetes Engine.

15. Parabéns!

Você aprendeu a criar e implantar um novo microsserviço baseado na Web do Apache Groovy / Micronaut no Kubernetes no Google Kubernetes Engine.

Saiba mais

Licença

Este conteúdo está sob a licença Atribuição 2.0 Genérica da Creative Commons.