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

1. Visão geral

Sobre a Micronaut

O Micronaut é um framework de pilha completa moderno baseado em JVM para a criação de aplicativos sem servidor e microsserviços modulares e facilmente testáveis. A 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:

  • Inicialização rápida e Baixo consumo de memória: frameworks de IoC baseados em reflexões carregam e armazenam em cache os dados de reflexão para cada campo, método e construtor no seu código, enquanto que, com o Micronaut, o tempo de inicialização e o consumo de memória do aplicativo não estão vinculados ao tamanho da sua base de código.
  • Cliente HTTP declarativo, reativo e em tempo de compilação: crie clientes HTTP reativos de forma declarativa, que são implementados no momento da compilação, reduzindo o consumo de memória.
  • Servidor HTTP sem bloqueios integrado ao Netty: com uma curva de aprendizado suave, o servidor HTTP da Micronaut facilita ao máximo a exposição de APIs que podem ser consumidas por clientes HTTP.
  • Testes rápidos e fáceis: ative com facilidade servidores e clientes nos seus testes de unidade e execute-os instantaneamente.
  • Injeção eficiente de dependências no tempo de compilação e AOP: o Micronaut oferece uma API de programação simples orientada a aspectos em tempo de compilação que não usa reflexão.
  • Criar apps totalmente reativos e sem bloqueio: o Micronaut oferece suporte a qualquer framework que implemente streams reativos, incluindo RxJava e Reactor.

Para mais informações, acesse o site da 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 baseado no Groovy no Kubernetes em execução no Kubernetes Engine.

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

Veja um diagrama das várias partes deste codelab para ajudar você a entender como as peças se encaixam. Use-o como referência ao longo do codelab. tudo deverá fazer sentido quando você chegar ao fim (mas sinta-se à vontade para ignorar isso por enquanto).

Diagrama 1 do codelab do Kubernetes (2).png

Para os fins deste codelab, o uso de um ambiente gerenciado como o Kubernetes Engine (uma versão hospedada pelo Google do Kubernetes em execução no Compute Engine) permite que você se concentre mais em testar o 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. Se quiser, use o Minikube para fazer este codelab.

Sobre o Jib

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

O objetivo do Jib é:

  • Rápido: implante as alterações rapidamente. Ela separa seu aplicativo em várias camadas, dividindo as dependências das classes. Agora você não precisa esperar que o Docker recrie todo o seu aplicativo Java, basta implantar as camadas que mudaram.
  • Reprodutí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.
  • Daemonless: reduza suas dependências de CLI. Crie sua imagem Docker no Maven ou Gradle e envie por push para qualquer registro de sua escolha. Chega de escrever Dockerfiles e chamar build/push do Docker.

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

Sobre este tutorial

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

O exemplo é um serviço hello world simples que usa o framework Micronaut e a linguagem de programação Apache Groovy (links em inglês).

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 um serviço do Micronaut no Kubernetes com o 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 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. Fazer o download do código-fonte de amostra 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 use cd no diretório que contém o serviço de exemplo:

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

4. Uma rápida olhada no código

Nosso serviço simples do Micronaut é composto por um controlador que emite a infame 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 foi enviada 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, o teste executa a mesma pilha de servidores Micronaut (com base no framework Netty) que é executada na produção. Portanto, o comportamento do código será o mesmo no produto e nos testes.

Execute o comando a seguir para verificar se está tudo bem:

./gradlew test

5. execute o aplicativo no local

Inicie o serviço Micronaut normalmente com o seguinte comando do Gradle:

$ ./gradlew run

Depois que o aplicativo for iniciado, abra outra instância do Cloud Shell graças ao pequeno ícone "+" e use o comando curl para conferir se o resultado esperado é o resultado esperado:

$ curl localhost:8080/hello

E vai aparecer um simples "Hello World" 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 e fazer o trabalho pesado para nós, já que não precisamos tocar em uma Dockerfile.

Vamos executar o comando para criar o contêiner:

$ ./gradlew jibDockerBuild

Você verá esta saída:

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 conferir se a mensagem de boas-vindas aparece. Para isso, execute 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

Como o serviço está em execução, inicie o comando curl na segunda guia do Cloud Shell para conferir se ele está funcionando conforme o esperado:

$ curl localhost:8080/hello
Hello World

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

7. Como enviar nosso serviço conteinerizado para o registro

Agora que a imagem funciona conforme o esperado, envie-a para o Google Container Registry, um repositório particular para suas imagens do Docker acessível a partir de todos os projetos do Google Cloud, mas também de fora do Google Cloud Platform.

Antes de enviar para o registro, confira se o Container Registry está ativado no nosso projeto. Acesse Tools > Container Registry. Se ela não estiver ativada, você verá a caixa de diálogo a seguir. Clique em Ativar API Container Registry. para ativá-lo:

ac812e6260ac7dfb.png

Quando o registro estiver pronto, envie a imagem para ele executando os seguintes comandos:

$ 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 da gcloud configure e autorize o Docker a enviar imagens para sua instância do Container Registry, marcar a imagem para apontar para o local dela no registro e, em seguida, enviá-la para o registro.

Se tudo correr bem e depois de um tempo, você verá a imagem do contêiner listada no console: Tools > 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 está tudo pronto para criar o cluster do Kubernetes Engine. Antes disso, acesse a seção "Google Kubernetes Engine" no console da Web e aguarde a inicialização do sistema. Leva só 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. Use a CLI gcloud da sessão do Cloud Shell para criar um cluster com dois nós do n1-standard-1. Esse processo leva 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 a tecnologia do Google Kubernetes Engine:

d9e1e314769753e7.png

Agora é hora de implantar o aplicativo em um contêiner no cluster do Kubernetes. De agora em diante, você usará a linha de comando kubectl, que já está configurada no ambiente do Cloud Shell. O restante deste codelab exige que o cliente e o servidor do Kubernetes sejam a 1.2 ou mais recente. kubectl version vai mostrar a versão atual do comando.

9. Implantar o aplicativo no Kubernetes

Uma implantação do Kubernetes pode criar, gerenciar e escalonar várias instâncias do aplicativo usando a imagem de contêiner recém-criada. Crie 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 do 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

Neste ponto, o contêiner já deve estar em execução sob o controle do Kubernetes, mas você 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.

No Cloud Shell, é possível expor o pod à Internet pública com o comando kubectl expose combinado com a sinalização --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

O sinalizador usado nesse comando especifica que você 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 publicamente acessível do serviço, basta solicitar que 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 seu 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 ao apontar seu navegador para este endereço: 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. você pode simplesmente dizer ao controlador de replicação para gerenciar um novo número de réplicas para suas instâncias de 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, declare quantas instâncias precisam estar em execução ao mesmo tempo. Os loops de reconciliação do Kubernetes simplesmente garantem que a realidade corresponda ao que você solicitou e tomam medidas, 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"
    }
}

Em /jib/examples/micronaut/build.gradle, vamos fazer o 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 alterações mais recentes:

$ ./gradlew jibDockerBuild

Adicione uma tag e envie a imagem ao registro de imagens do 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, é necessário editar o hello-micronaut deployment atual e mudar a imagem de gcr.io/PROJECT_ID/micronaut-jib:0.1 para gcr.io/PROJECT_ID/micronaut-jib:0.2.

É possível usar o comando kubectl set image para pedir que o Kubernetes 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

Acesse http://EXTERNAL_IP:8080 de novo para conferir se a nova resposta está sendo retornada.

13. Reverter

Ops! Você cometeu um erro com uma nova versão do aplicativo? Talvez a nova versão contenha um erro e você precise reverter rapidamente. Com o Kubernetes, é possível reverter para o estado anterior facilmente. Reverta o aplicativo executando:

$ kubectl rollout undo deployment/hello-micronaut

Se você observar a saída do serviço, voltaremos para a versão inicial do "Hello World" mensagem.

14. Resumo

Nesta etapa, você configurou um serviço "Hello World" do Micronaut simples baseado no Apache Groovy e o executou diretamente no Cloud Shell. Em seguida, você o empacotou como um contêiner com o Jib e o implantou no Google Kubernetes Engine.

15. Parabéns!

Você aprendeu a criar e implantar um novo microsserviço baseado na Web no 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.