Java na Nuvem: Tutorial de Configuração de Integração Contínua

Publicados: 2022-03-11

Ano após ano, testemunhamos a evolução cada vez mais rápida da indústria de TI. Já se passaram mais de duas décadas desde que o slogan inovador "Escreva uma vez, execute em qualquer lugar" estabeleceu um novo nível de expectativa para a comunidade de desenvolvimento de software. E aqui estamos hoje, com um conjunto de ferramentas resultante e em constante expansão que, coletivamente, levaram o desenvolvimento Java em particular, e o desenvolvimento de software em geral, a um universo totalmente novo de possibilidades.

Metodologias como Agile, DevOps e Integração e Implantação Contínua – juntamente com a evolução dos microsserviços – impulsionaram coletivamente a produtividade do processo de desenvolvimento de software a um ponto em que é um prazer desenvolver software mais do que nunca. Utilizar a automação e configurar o conjunto correto de ferramentas pode tornar o desenvolvimento e a entrega de produtos de software surpreendentemente simples.

Este artigo analisará esse novo universo a partir da perspectiva de um desenvolvedor Java que cruza o DevOps e busca otimizar o desenvolvimento e a entrega de produtos ao máximo.

Hoje, termos como Spring Boot, Docker, Cloud, Amazon Web Services, Continuous Delivery são amplamente utilizados, mas menos compreendidos. Este artigo seguirá o caminho mais fácil possível para apresentar todas essas tecnologias e explicar esses termos, e resumi-lo na forma de um tutorial onde desenvolveremos um pequeno software e o prepararemos para entrega em produção usando todas as ferramentas mencionadas.

Java, Spring Boot, Docker, Nuvem, Amazon Web Services, Integração Contínua

Cansado de lidar com problemas de JVM? Dockerize seu aplicativo Java.
Tweet

Por que essas ferramentas?

Simplifique as implantações com o Docker

"Escreva uma vez, execute em qualquer lugar" foi o avanço conceitual que rendeu tecnologias como a Java Virtual Machine (JVM), que permitiu que seu código fosse executado em qualquer lugar. E agora aqui estamos, algumas décadas depois, com algo chamado Docker sendo apresentado à comunidade de TI. O Docker é uma ferramenta de contenção na qual você pode colocar seu software e executá-lo sem problemas, praticamente em qualquer lugar que desejar.

No entanto, um desenvolvedor Java pode olhar para o Docker e dizer “Por que precisamos disso, já temos a JVM, que é bem reconhecida como a solução portátil mestre”. Mas é?

"Escreva uma vez, execute em qualquer lugar" soa bem e funciona bem... pelo menos na maioria das vezes. Até encontrar vários fornecedores de JVM, várias versões de Java, vários sistemas operacionais e várias permutações e combinações de todos os itens acima. Você então se vê mudando do paradigma elegante "Escreva uma vez, execute em qualquer lugar" para a armadilha contraproducente "Escreva uma vez, depure em todos os lugares".

E é aí que o Docker entra para ajudar a salvar o dia.

O Docker simplifica o desenvolvimento, teste e envio de software. Se você tiver o software que deseja testar, coloque-o no contêiner do Docker e ele será executado e será fácil de instalar para todas as partes envolvidas.

Relacionado: Introdução ao Docker: simplificando o Devops

Acelere o Desenvolvimento com Spring Boot

Menos de uma década após a introdução do slogan “correr em qualquer lugar”, o framework Spring apareceu em cena. Hoje, o ecossistema Spring continua a florescer e produziu muitos projetos valiosos baseados em Spring, talvez mais notavelmente Spring Boot. Conforme declarado no site do Spring Boot:

O Spring Boot facilita a criação de aplicativos independentes baseados em Spring de nível de produção que você pode simplesmente executar.

O Spring Boot permite que você coloque o aplicativo em execução em questão de minutos. Os desenvolvedores de software podem se concentrar no desenvolvimento de software e, em seguida, podem se beneficiar de uma ferramenta que faz toda a configuração para eles.

Neste tutorial, usaremos o Spring Boot para desenvolver nosso microsserviço.

Integração Contínua (CI) com Jenkins

DevOps é um movimento de rápido crescimento que está integrando fortemente as equipes de desenvolvimento de software e administração de sistemas, com o objetivo de tornar um ciclo de vida de desenvolvimento e entrega de software o mais fácil, contínuo e produtivo possível para todas as partes envolvidas: desenvolvedores, administradores de sistemas, testadores e, finalmente, , usuários finais.

A integração contínua (CI) é um dos pilares da revolução DevOps. A ideia é que sempre que um desenvolvedor enviar código para o repositório de código, ele seja automaticamente testado e empacotado para entrega (implantação) para produção.

CI anda de mãos dadas com:

  • Entrega Contínua – Entrega automática do pacote preparado para teste de negócios do usuário final com acionamento manual para implantação em produção.
  • Implantação Contínua – Implantação automática do produto embalado diretamente na produção.

Existem mais do que algumas ferramentas que podem ser utilizadas para implementar o processo de IC. Um dos mais populares é o Jenkins, uma ferramenta de CI de código aberto. Com mais de mil plugins e uma enorme comunidade por trás dele, Jenkins é uma escolha fácil quando se começa a pensar em implementar integração, entrega ou implantação contínuas.

Em nosso tutorial, Jenkins será usado para entregar nosso produto na nuvem, mais especificamente, a nuvem Amazon (AWS).

Computação em nuvem com AWS

Se você tem alguma experiência em administração de sistemas, imagine remover algumas das preocupações da administração do sistema de seus ombros. Você tem alguns aplicativos; você tem uma ideia de quantos recursos eles exigirão, mas não sabe exatamente o dimensionamento de hardware necessário. Você faz a estimativa, os recursos são comprados e o sistema vai para a produção. Se você tiver sorte, descobrirá que superestimou e tem mais recursos do que precisa. Mas, dada a Lei de Murphy, você provavelmente descobrirá que subestimou os requisitos de recursos e acabará lutando para obter um pouco mais de memória ou poder de processamento sob uma tremenda pressão de tempo. Por outro lado, se você estiver implantando na nuvem, basta colocar seu sistema no mercado e dimensioná-lo conforme necessário, com a flexibilidade oferecida pelos provedores de nuvem. Com a nuvem, você não precisa se preocupar em ficar sem recursos do sistema, nem precisa se preocupar em ter 90% de sua memória ou CPU ociosa.

Claro, há o desafio de decidir qual provedor escolher. As guerras na nuvem ainda estão em andamento. Clash of Microsoft, Amazon e Google for the future of computing é um título de exemplo que você pode encontrar ultimamente nas notícias do mundo da tecnologia. Para este blog, escolhi a Amazon Web Services (AWS), em grande parte com base em sua popularidade atual e participação de mercado.

Uma das vantagens da AWS é que a Amazon oferece muitos serviços após você se inscrever:

AWS

Neste tutorial, usaremos os dois serviços da AWS a seguir: Elastic Compute Cloud EC2 (mais especificamente, Amazon EC2 Container Registry ou Amazon ECR) e Amazon S3 (Simple Storage Services).

Amazon ECR

Precisaremos armazenar nossas imagens do Docker em algum lugar. O Amazon ECR é um serviço de registro gerenciado do AWS Docker. Conforme declarado no site da Amazon ECR:

…facilita para os desenvolvedores armazenar, gerenciar e implantar imagens de contêiner do Docker. O Amazon ECR é integrado ao Amazon EC2 Container Service (ECS), simplificando o fluxo de trabalho de desenvolvimento para produção. O Amazon ECR elimina a necessidade de operar seus próprios repositórios de contêiner ou se preocupar em dimensionar a infraestrutura subjacente.

Amazon S3

Como mencionado, o aplicativo que desenvolvemos será um microsserviço Spring Boot que fará upload de arquivos para o Amazon S3. Conforme declarado no site do Amazon S3:

…fornece aos desenvolvedores e equipes de TI armazenamento em nuvem seguro, durável e altamente escalável. O Amazon S3 é um armazenamento de objetos fácil de usar, com uma interface de serviço web simples para armazenar e recuperar qualquer quantidade de dados de qualquer lugar na web.

Um tutorial prático de “como fazer”

O objetivo é preparar para a implantação um microsserviço Spring Boot que fará upload de arquivos para o Amazon S3. Os passos são os seguintes:

  • Desenvolver o microsserviço
  • Defina o processo de compilação no qual o serviço será dockerizado
  • Use o Bitbucket para hospedar o repositório de código Git
  • Integre o Bitbucket ao Jenkins para empacotar o aplicativo usando o Gradle
  • Envie-o para um Amazon ECR remoto

O que se segue é um tutorial para configurar todos os componentes necessários:

  • Aplicativo de exemplo Spring Boot – microsserviço empacotado e dockerizado usando Gradle
  • Instalação do Jenkins em um novo servidor Ubuntu
  • Integração do Bitbucket com Jenkins via webhook
  • Configuração de trabalho do Jenkins
  • Amazon ECR para armazenar as imagens do Docker contendo nosso aplicativo

Pré-requisitos

Para poder usar os recursos da nuvem AWS, precisamos primeiro nos registrar na Amazon. Ao se registrar, obteremos uma conta com benefícios imediatos de uso do Nível Gratuito, para permitir a experiência prática durante os 12 meses após o registro.

Conforme mencionado, neste tutorial usaremos o Amazon S3 e o Amazon ECR. Para ambos, precisaremos de chaves de acesso para nos conectarmos aos serviços.

Depois de se cadastrar na AWS, vamos para nossa conta Credenciais de segurança , onde escolhemos Chaves de acesso e clicamos em “Criar nova chave de acesso”. Após clicar, uma chave é gerada junto com seu ID. Você precisa armazená-lo em algum lugar seguro, pois o usaremos mais tarde ao configurar a integração do AWS Jenkins e desenvolver nosso upload de arquivo S3.

Credenciais de segurança da AWS

O próximo pré-requisito é que precisamos de um bucket do Amazon S3 (contêiner de armazenamento). Nosso Spring Boot Service fará upload e download de arquivos de e para o armazenamento do Amazon S3. A criação do bucket é bastante simples e requer apenas alguns cliques. Uma descrição completa de como fazer isso é fornecida na documentação Criar um bucket.

Também usaremos o Bitbucket para hospedar nosso código e acionar solicitações ao Jenkins, portanto, também é necessária uma conta Bitbucket. O Bitbucket é uma ótima opção para desenvolvedores, tendo como um de seus principais benefícios a quantidade ilimitada de repositórios privados que você pode criar.

Desenvolvimento de aplicações

Em vez de entrar em todos os detalhes das anotações do Spring e como elas funcionam, vou focar, de uma perspectiva pura do desenvolvedor, na parte mais desafiadora de toda a configuração; ou seja, instalar e configurar Linux, Jenkins e outras ferramentas necessárias para CI. Todos os exemplos de código usados ​​neste tutorial, incluindo o aplicativo de microsserviço Spring Boot, estão disponíveis no repositório do Bickbucket para o projeto.

Nossa composição de aplicativos é simples. Temos um ponto de entrada do aplicativo Spring Boot em nosso arquivo StorageWebserviceApplication.java . A lógica para fazer upload e download de arquivos está em StorageService.java . StorageController.java é um controlador Rest, contendo terminais de API usados ​​para upload e download de arquivos. Aqui está a hierarquia do projeto:

Hierarquia do projeto

Escolhemos o Gradle como uma ferramenta de compilação e ele empacotará nosso aplicativo e comporá a imagem do Docker. Então, a seguir, discutiremos o arquivo de compilação do Gradle, o componente de serviço e o Dockerfile.

Para poder usar a API da AWS, precisamos incluir dependências em nosso arquivo de compilação, conforme definido na documentação da AWS para usar o Gradle.

Resumindo, a parte de configuração de dependência da AWS do nosso script Gradle terá a seguinte aparência:

 buildscript { ... repositories { mavenCentral() } dependencies { ... classpath("io.spring.gradle:dependency-management-plugin:0.5.4.RELEASE") } } .. apply plugin: "io.spring.dependency-management" dependencyManagement { imports { mavenBom ('com.amazonaws:aws-java-sdk-bom:1.10.47') } } dependencies { .. compile ('com.amazonaws:aws-java-sdk-s3') }

Conforme declarado anteriormente, ao fazer upload de arquivos para o Amazon S3, fazemos isso fazendo upload de arquivos para um bucket do S3.

Para se conectar ao bucket, nosso cliente Amazon S3 precisa ter credenciais fornecidas. As credenciais são as chaves de acesso que criamos anteriormente. Definimos o ID e o valor da chave de acesso no arquivo application.properties ; nomeamos nosso bucket toptal-s3-example .

propriedade.aplicativo

Nosso principal componente de serviço agora é o seguinte:

 @Service public class StorageService { @Value("${aws.accesKeyId}") private String awsAccessKeyId; @Value("${aws.secretKey}") private String awsSecretKey; @Value("${aws.bucketName}") private String awsBucketName; private AWSCredentials credentials; private AmazonS3 s3client;; @PostConstruct public void init(){ credentials = new BasicAWSCredentials(awsAccessKeyId, awsSecretKey); s3client = new AmazonS3Client(credentials); } public void uploadFile(MultipartFile file) throws IOException { File fileForUpload = transformMultipartToFile(file); s3client.putObject(new PutObjectRequest(awsBucketName, file.getOriginalFilename(), fileForUpload)); } public InputStream downloadFile(String amazonFileKey) throws IOException { S3Object fetchFile = s3client.getObject(new GetObjectRequest(awsBucketName, amazonFileKey)); InputStream objectData = fetchFile.getObjectContent(); return objectData; } …

StorageService lê as credenciais do arquivo application.properties e as usa para instanciar o objeto BasicAWSCredentials e, posteriormente, o objeto AmazonS3Client . O que segue é uma simples questão de invocar putObject para upload de arquivo e getObject para download de arquivo, no objeto cliente do Amazon S3.

Executaremos o serviço dentro de um contêiner do Docker e, durante o processo de compilação do Gradle, construiremos a imagem do Docker. Faremos isso configurando adicionalmente o arquivo build.gradle , da seguinte forma:

 buildscript { ... dependencies { ... classpath('se.transmode.gradle:gradle-docker:1.2') } } ..... apply plugin: 'docker' ... task buildDocker(type: Docker, dependsOn: build) { push = false applicationName = "storageservice" dockerfile = file('src/main/docker/Dockerfile') doFirst { copy { from jar into stageDir } } }

A parte Buildscript e o apply plugin são bastante padrão. Também definimos uma tarefa buildDocker que lê a configuração do Docker armazenada no src/main/docker/Dockerfile e copia o arquivo JAR para a compilação do Docker.

O Dockerfile contém uma lista de comandos puros do Docker com os quais prepararemos nossa imagem:

 FROM frolvlad/alpine-oraclejdk8 ADD storageWebService-0.0.1-SNAPSHOT.jar storageService.jar EXPOSE 8080 CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/storageService.jar"]

Um pré-requisito para executar nosso aplicativo é ter instalado uma Java Virtual Machine (JVM). O Docker fornece uma lista de imagens com Java instalado e escolheremos uma das menores, com base em um Alpine Linux mínimo de 5 MB. frolvlad/alpine-oraclejdk8 tem tudo o que precisamos e é bem pequena (apenas 170 MB).

O comando FROM define a imagem mencionada como a base sobre a qual a nossa será construída. ADD o arquivo JAR construído ao sistema de arquivos do contêiner sob o nome storageService.jar . Em seguida, definimos o contêiner do Docker para escutar na porta 8080 em tempo de execução com o comando EXPOSE . Isso, no entanto, não permitirá a comunicação com 8080 do host. Quando a imagem estiver pronta e quisermos executá-la, também precisaremos publicar a porta no container com o seguinte comando docker run -p 8080:8080 amazonRepository/storageservice , onde amazonRepository é um repositório que configuraremos posteriormente neste tutorial. Com o CMD , definimos quais comandos serão executados quando executarmos o container. Os valores entre colchetes do comando CMD significam simplesmente que o seguinte será executado quando executarmos o contêiner:

 java -Djava.security.egd=file:/dev/./urandom -jar /storageService.jar

A opção -Djava.security.egd=file:/dev/./urandom é necessária para ajudar a mitigar os atrasos da JVM durante a inicialização. Se omitido, fará com que o aplicativo inicialize extremamente lento devido a um processo de geração de números aleatórios necessário durante o processo de inicialização.

Isso resume a parte "Desenvolvimento de aplicativos". Feito isso, o serviço que criamos aqui será iniciado automaticamente quando executarmos um container Docker posteriormente. Então, vamos iniciar a instalação e configuração das outras ferramentas necessárias para configurar o processo de integração contínua.

Operações de aplicativos e sistemas

Primeiro, precisamos de um servidor Linux limpo para configurar a ferramenta Jenkins CI. Observe que as instruções a seguir são especificamente para o Ubuntu 14.04. Tenha em mente que as instruções podem ser ligeiramente diferentes para outras distribuições Linux. A versão do Jenkins usada é 2.7.1 e as telas e instruções podem diferir um pouco dependendo da versão do Jenkins que está sendo usada.

Então, vamos ao nosso console do servidor Linux e começamos a instalar os pré-requisitos.

Pré-requisito do JDK

Precisamos ter um JDK instalado. A seguir estão as instruções para instalar o JDK8.

 sudo add-apt-repository ppa:webupd8team/java sudo apt-get install python-software-properties sudo apt-get update sudo apt-get install oracle-java8-installer java -version

Instalar o Docker

Para que o Jenkins possa acionar as compilações do Docker, precisamos instalar docker-engine da seguinte forma:

 sudo apt-get install apt-transport-https ca-certificates sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D #create a docker list file sudo vi /etc/apt/sources.list.d/docker.list #add the following entry in the docker.list file (change trusty to #the release you are running on if you are running on different, ie. #xenial, precise...): deb https://apt.dockerproject.org/repo ubuntu-trusty main #save and exit the file sudo apt-get update apt-cache policy docker-engine sudo apt-get install docker-engine

Como agora instalamos o mecanismo Docker, com o comando a seguir, iniciaremos uma imagem do Docker hello-world para confirmar que o Docker funciona corretamente.

 sudo docker run hello-world

A saída da imagem Hello-world terá a seguinte aparência e, com isso, podemos confirmar que o mecanismo está funcionando bem.

Motor Docker Olá mundo

Instalar AWS Command Line Interface (CLI)

Em seguida, instalaremos a AWS CLI. Mais tarde, na configuração do trabalho do Jenkins, usaremos a CLI para executar comandos para autenticação da AWS e push de imagem do Docker para o registro de contêiner do Amazon EC2.

Para instalar a AWS CLI, seguimos as diretrizes descritas em detalhes na documentação da Amazon CLI.

Das duas opções de instalação, escolheremos a instalação usando o Pip, um sistema de gerenciamento de pacotes usado para instalar e gerenciar programas Python. Instalaremos o Pip e a AWS CLI simplesmente executando os três comandos a seguir:

 #install Python version 2.7 if it was not already installed during the JDK #prerequisite installation sudo apt-get install python2.7 #install Pip package management for python sudo apt-get install python-pip #install AWS CLI sudo pip install awscli

AWS ECR

Como última etapa do processo de compilação, enviaremos nossa imagem do Docker para o registro de contêiner da Amazon. No console do Amazon web services, encontramos o AWS EC2 Container Service.

Serviço de contêiner AWS EC2

Selecionamos o submenu Repositórios à esquerda e clicamos em Começar .

Serviço de contêiner AWS EC2

Em seguida, somos apresentados à primeira tela para configurar o repositório onde inserimos o nome do repositório e clicamos no botão Next Step .

Serviço de contêiner AWS EC2

Clicar em Next Step nos mostra uma tela com instruções sobre como enviar imagens para o repositório.

Serviço de contêiner AWS EC2

Somos apresentados a um exemplo de como construir e enviar uma imagem do Docker para o registro, mas não precisamos nos preocupar com isso agora. Com isso, criamos um repositório.

Instalar e configurar o Jenkins

Para instalar o Jenkins, inserimos os seguintes comandos no shell:

 #Download Jenkins key and pipe it to apt-key tool, apt-key command #add will read from input stream, as defined by „–„. When added #apt will be able to authenticate package to be installed. wget -q -O - https://jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - #create a sources list for jenkins sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list' #update your local package list sudo apt-get update #install jenkins sudo apt-get install jenkins

Quando a instalação termina, o Jenkins é iniciado automaticamente. Verifique o status do serviço com o seguinte comando:

 sudo service jenkins status

O Jenkins se conectará ao repositório Bitbucket Git e, para isso, precisamos instalar o Git.

 #install Git sudo apt-get install git

Jenkins acionará o processo de compilação do Gradle, durante o qual uma imagem do Docker será criada. Para poder fazer isso, o usuário Jenkins precisa ser adicionado ao grupo de usuários do docker :

 #add Jenkins user to docker user group sudo usermod -aG docker jenkins

Durante o processo de compilação, o Jenkins enviará imagens do Docker para o Amazon ECR. Para habilitar isso, precisamos configurar a AWS para o usuário Jenkins.

Primeiro, precisamos mudar para o usuário jenkins . Para isso, precisamos definir uma senha.

 #change Jenkins password sudo passwd jenkins #switch to Jenkins user su – jenkins #configure AWS aws configure

Depois de inserir o comando aws configure , começamos a inserir a chave de acesso secreta gerada e o ID da chave (essas são as credenciais que geramos anteriormente no processo). No meu caso, a região da conta é us-west-2 , então eu insiro isso. Também definimos o formato de saída padrão para comandos da AWS como JSON.

Formato de saída padrão AWS JSON

Agora podemos seguir para a configuração do Jenkins por meio do console da Web acessível na porta 8080.

Quando acessamos a URL, somos apresentados à seguinte tela de introdução .

Introdução ao Jenkins

Conforme indicado na tela, precisamos inserir a senha. Feito isso, o assistente de configuração nos pede para fazer o seguinte:

  • Escolha quais plugins instalar - nós escolheremos Instalar plugins sugeridos .
  • Crie o primeiro usuário administrador inserindo as credenciais do usuário

Quando terminar, clique em Salvar e Concluir . Com isso, finalizamos a configuração do Jenkins.

Antes de começarmos a definir o trabalho de construção, precisamos adicionar alguns plugins adicionais. Iremos para Gerenciar Jenkins e clicar em Gerenciar plugins . Na guia Disponível , primeiro encontramos o plugin Bitbucket , marque a caixa e clique em Baixar e instalar após reiniciar .

Jenkins

Em seguida, você será apresentado a algo como a tela a seguir.

Instalando plugins Jenkins

Após a instalação do plug-in, repetimos o processo para os seguintes plug-ins adicionais que serão necessários para configurar o trabalho:

  • Plug-in do Gradle
  • Plug-in de etapa de compilação do Docker
  • Plug-in do ambiente de compilação personalizado do Cloudbees Docker
  • Plug-in Amazon ECR

O plug-in da etapa de compilação do Docker que usamos enviará solicitações ao daemon do Docker. Para isso, precisamos habilitar o socket TCP na porta 2375. Para isso, entramos no arquivo de configuração do Docker localizado em etc/default/docker .

 sudo vi /etc/default/docker

Aqui adicionamos a seguinte linha na configuração:

 DOCKER_OPTS='-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock'

Salvamos e saímos do arquivo e reiniciamos o serviço Docker e Jenkins.

 sudo service docker restart sudo service jenkins restart

Após a reinicialização do Jenkins, vamos ao console do Jenkins e, em Manage Jenkins , escolhemos Configure System .

Configurar o sistema Jenkins

Encontramos a seção do construtor Docker e inserimos http://localhost:2375 para a URL da API REST, clique em Aplicar para confirmar a alteração. Em seguida, clicamos na conexão de teste para confirmar que tudo está ok.

Construtor do Docker

Salvamos a configuração e prosseguimos para a configuração do trabalho do Jenkins.

Configuração do trabalho

Vamos para a página inicial do Jenkins e criamos um New Item .

Configuração de trabalho do Jenkins

Escolhemos um projeto Freestyle e inserimos o nome do projeto conforme mostrado na tela a seguir:

Projeto Jenkins Freestyle

Clicando em OK , é apresentada a página de configuração do trabalho. Queremos que o projeto seja compilado em cada push para nosso repositório Bitbucket Git. Para conseguir isso, primeiro precisamos definir o repositório ao qual estamos nos conectando.

Etapa 1: gerenciamento de código-fonte

No gerenciamento de código-fonte, escolhemos Git e inserimos a URL do nosso repositório Bitbucket. A URL tem o formato https://bitbucket.org/bitbucketUsername/repositoryName .

Depois de inserir a URL, o Jenkins tentará automaticamente testar a conexão. Como ainda não inserimos as credenciais, ele mostrará um erro indicando que não pode se conectar.

Erro do Jenkins

Abra a lista suspensa Add e clique em Credentials Jenkins Provider.

Credenciais Jenkins

Somos apresentados à tela a seguir, onde inserimos o nome de usuário e a senha da nossa conta Bitbucket.

Bitbucket e Jenkins

Depois de adicionar o novo registro de credenciais, certifique-se de selecioná-lo na lista suspensa de credenciais e isso conclui a configuração do gerenciamento de código-fonte .

Etapa 2: criar acionadores

Verifique as compilações do Trigger remotamente e defina um token de autenticação. Certifique-se de definir um token aleatório e seguro.

Construir gatilhos

Etapa 3: Webhook do Bitbucket

Jenkins já forneceu a URL para nós que usaremos no Bitbucket. Vamos para a página do nosso repositório Bitbucket e, no menu de configurações, clique em Web hooks . Em seguida, clicar em Adicionar webhook nos apresenta a seguinte tela, que preenchemos da seguinte forma:

Webhook do Bitbucket

A URL tem a seguinte estrutura: http://JENKINS_URL _HOST:PORT/job/JOB_NAME/build?token=TOKEN .

Insira os valores acima, respectivamente, com o URL do Jenkins, a porta em que está sendo executado, o nome do trabalho que você criou e o token que você definiu anteriormente.

Depois de salvar o Webhook, você receberá a tela a seguir, que poderá editar, se necessário, ou visualizar solicitações geradas sempre que enviarmos um novo código.

Webhook do Bitbucket

Com essa configuração, o webhook é acionado em cada push do repositório, independentemente do branch. No lado do Jenkins, podemos definir qual push de branch acionará a compilação.

Para que o Bitbucket possa enviar o código para o Jenkins, precisamos reconfigurar a segurança global do Jenkins para permitir o acesso de leitura anônimo. Além disso, para nossa configuração, temos que desabilitar a opção padrão do Jenkins, que impede a falsificação de solicitações entre sites. Para fazer isso, vá para Manage Jenkins e escolha Configure global security . Marque Permitir acesso de leitura anônimo e marque Impedir explorações de falsificação entre sites . Em seguida, salve a configuração.

Configurar segurança global

Observe que isso é feito apenas por motivos de simplicidade. A configuração completa supera a cobertura deste tutorial e incluiria proteger ainda mais o Jenkins por trás de um proxy reverso, na conexão TLS e habilitar a prevenção de CSRF.

Etapa 4: compilação do Gradle

Agora podemos retornar ao trabalho do Jenkins e continuar configurando-o. Na seção de compilação, adicionamos uma etapa de compilação: Invoke gradle script .

Invocar script Gradle

Neste formulário, inserimos o seguinte:

Invocar script Gradle

Conforme mostrado na tela, usaremos o wrapper Gradle, um recurso conveniente do Gradle que não exige que você tenha o Gradle instalado no host. Certifique-se de marcar a caixa Tornar gradlew executável .

Nas tarefas, especificamos build e buildDocker .

Etapa 5: imagem da etiqueta do Docker

Esta parte da compilação marca uma imagem do Docker previamente preparada pela tarefa dockerBuild do Gradle. Para isso, adicionamos uma nova etapa de build ao job: Execute Docker command . Escolhemos o comando Tag image e definimos o nome da imagem, o repositório de destino onde enviaremos a imagem e a tag:

Imagem do Docker Tag

Etapa 6: Docker Push para o Amazon ECR

Por fim, precisamos definir como enviar nossa imagem para o Amazon ECR. Para isso, adicionamos uma nova etapa Execute shell build e definimos os comandos para autenticar na AWS e enviar a imagem para o Amazon ECR:

 #region for our account is us-west-2 aws ecr get-login --region us-west-2 | bash #push the previously tagged image docker push 058432294874.dkr.ecr.us-west-2.amazonaws.com/springbootdocker:${BUILD_NUMBER} 

Docker Push para Amazon ECR

Com isso, finalizamos nosso processo de construção. Depois de enviar o novo código para o repositório, esse trabalho será ativado e teremos uma nova imagem do Docker carregada no registro do Docker “automaticamente”.

A imagem pode ser então puxada para onde quer que tenhamos o docker-engine instalado e pode ser executado com o seguinte comando:

 docker run -p 8080:8080 amazonRepository/springbootdocker

Este comando iniciará nosso microsserviço Spring Boot, com os seguintes endpoints para fazer upload e download de nossos arquivos no bucket do S3:

  • http://hostnameURL:8080/api/storage/upload
  • http://hostnameURL:8080/api/storage/download?fileName=xyz

Outras etapas com Java e integração contínua

Há sempre mais coisas para fazer. Muito terreno foi abordado neste tutorial, mas eu consideraria isso apenas um ponto de partida para aprender mais. Colocar o Jenkins atrás de um servidor proxy da Web, como o Nginx, e estabelecer uma conexão TLS, são apenas dois exemplos do que mais poderia e provavelmente deveria ser feito.

Nossa imagem do Docker está disponível no Amazon ECR e pronta para implantação. Agora podemos pegá-lo e implantá-lo manualmente. No entanto, uma solução melhor seria automatizá-lo ainda mais. CI é apenas o primeiro passo, e o próximo passo é a Entrega Contínua. Que tal uma alta disponibilidade? O Amazon AWS EC2 fornece recursos para registrar contêineres na nuvem em um ambiente em cluster que é obrigatório para serviço baseado em produção. Um bom exemplo de trabalho de desenvolvimento de um processo de entrega contínua pode ser encontrado na seguinte postagem do blog da AWS.

Conclusão

Em suma, implementamos um processo de desenvolvimento de software suave e limpo. Utilizando as ferramentas disponíveis, criamos uma infraestrutura que ajuda a maximizar nossa produtividade. Agora, não precisamos nos preocupar com a configuração do nosso serviço Java, que é um serviço web simples com um endpoint REST. Deixamos a convenção Spring Boot cuidar de tudo e focar apenas na lógica do serviço. Utilizamos Jenkins para criar uma nova imagem do Docker toda vez que enviamos nosso código para nosso repositório Bitbucket Git e, no final, definimos a nuvem para ser responsável por armazenar nossas imagens e arquivos do Docker. Quando implantarmos nosso serviço contido em uma imagem do Docker, estaremos livres de quaisquer restrições do sistema operacional (desde que o sistema operacional tenha um docker-engine instalado).