Java nel cloud: tutorial per la configurazione dell'integrazione continua
Pubblicato: 2022-03-11Anno dopo anno, assistiamo all'evoluzione sempre più rapida del settore IT. Sono trascorsi più di due decenni da quando l'innovativo slogan "Scrivi una volta, corri ovunque" ha stabilito un livello completamente nuovo di aspettative per la comunità di sviluppo software. Ed eccoci qui oggi, con un insieme risultante e in continua espansione di strumenti che hanno portato collettivamente lo sviluppo Java in particolare, e lo sviluppo del software in generale, a un intero nuovo universo di possibilità.
Metodologie come Agile, DevOps e Continuous Integration and Deployment, insieme all'evoluzione dei microservizi, hanno collettivamente incrementato la produttività del processo di sviluppo software al punto che è un piacere sviluppare software più che mai. L'utilizzo dell'automazione e la configurazione del set di strumenti corretto può rendere lo sviluppo e la distribuzione di prodotti software sorprendentemente indolori.
Questo articolo esaminerà questo nuovo universo dal punto di vista di uno sviluppatore Java che entra in DevOps e cerca di ottimizzare al massimo lo sviluppo e la distribuzione del prodotto.
Oggi termini come Spring Boot, Docker, Cloud, Amazon Web Services, Continuous Delivery sono ampiamente utilizzati ma meno conosciuti. Questo articolo prenderà il percorso più semplice possibile per presentare tutte queste tecnologie e spiegare questi termini, e lo concluderà sotto forma di un tutorial in cui svilupperemo un piccolo software e lo prepareremo per la consegna in produzione utilizzando tutti gli strumenti menzionati.
Perché questi strumenti?
Semplifica le distribuzioni con Docker
"Scrivi una volta, esegui ovunque" è stata la svolta concettuale che ha prodotto tecnologie come la Java Virtual Machine (JVM) che ha consentito al tuo codice di essere eseguito ovunque. E ora eccoci qui, un paio di decenni dopo, con qualcosa chiamato Docker presentato alla comunità IT. Docker è uno strumento di contenimento in cui puoi inserire il tuo software ed eseguirlo indolore, quasi ovunque tu voglia.
Tuttavia, uno sviluppatore Java potrebbe guardare Docker e dire "Perché dovremmo averne bisogno, abbiamo già la JVM, che è ben riconosciuta come la soluzione portatile principale". Ma lo è?
"Scrivi una volta, corri ovunque" suona bene e suona bene... almeno la maggior parte delle volte. Fino a quando non incontri più fornitori JVM, più versioni Java, più sistemi operativi e varie permutazioni e combinazioni di tutto quanto sopra. Ti ritrovi quindi a passare dall'elegante paradigma "Scrivi una volta, esegui ovunque" al controproducente "Scrivi una volta, esegui il debug ovunque".
Ed è qui che entra in gioco Docker per aiutare a salvare la situazione.
Docker semplifica lo sviluppo, il test e la spedizione del software. Se disponi del software che desideri testare, inseriscilo nel contenitore Docker e verrà eseguito e sarà indolore da installare per tutte le parti coinvolte.
Accelera lo sviluppo con Spring Boot
Meno di un decennio dopo l'introduzione dello slogan "corri ovunque", il framework Spring è apparso sulla scena. Oggi, l'ecosistema Spring continua a prosperare e ha prodotto molti preziosi progetti basati su Spring, forse in particolare Spring Boot. Come affermato sul sito web di Spring Boot:
Spring Boot semplifica la creazione di applicazioni Spring stand-alone di livello produttivo che puoi semplicemente eseguire.
Spring Boot ti consente di far funzionare l'applicazione in pochi minuti. Gli sviluppatori di software possono concentrarsi sullo sviluppo del software e quindi possono beneficiare di uno strumento che esegue tutta la configurazione per loro.
In questo tutorial utilizzeremo Spring Boot per sviluppare il nostro microservizio.
Integrazione continua (CI) con Jenkins
DevOps è un movimento in rapida crescita che integra strettamente i team di sviluppo software e amministrazione dei sistemi, con l'obiettivo di rendere un ciclo di vita di sviluppo e distribuzione del software il più indolore, trasparente e produttivo possibile per tutte le parti coinvolte: sviluppatori, amministratori di sistema, tester e, infine, , utenti finali.
L'integrazione continua (CI), è uno dei capisaldi della rivoluzione DevOps. L'idea è che ogni volta che uno sviluppatore esegue il commit del codice nel repository del codice, viene automaticamente testato e impacchettato per la consegna (distribuzione) alla produzione.
CI va di pari passo con:
- Consegna continua: consegna automatica del pacco preparato per il test aziendale dell'utente finale con attivazione manuale per l'implementazione in produzione.
- Distribuzione continua: distribuzione automatica del prodotto confezionato direttamente in produzione.
Esistono più di pochi strumenti che possono essere utilizzati per implementare il processo CI. Uno dei più popolari è Jenkins, uno strumento CI open source. Con più di mille plug-in e una vasta comunità alle spalle, Jenkins è una scelta facile quando si inizia a pensare all'implementazione di integrazione, distribuzione o distribuzione continue.
Nel nostro tutorial, Jenkins verrà utilizzato per distribuire il nostro prodotto nel cloud, in particolare nel cloud Amazon (AWS).
Cloud Computing con AWS
Se hai una certa esperienza di amministratore di sistema, immagina di rimuovere alcune delle preoccupazioni dell'amministrazione del sistema dalle tue spalle. Hai alcune applicazioni; hai un'idea di quante risorse richiederanno, ma non conosci esattamente le dimensioni dell'hardware di cui avrai bisogno. Fai la stima, le risorse vengono acquistate e il sistema va in produzione. Se sei fortunato, scoprirai di aver sopravvalutato e di avere più risorse di quelle di cui hai bisogno. Ma data la legge di Murphy, molto probabilmente scoprirai di aver sottovalutato i requisiti di risorse e finirai per lottare per ottenere un po' più di memoria o potenza di elaborazione sotto un'enorme pressione di tempo. Al contrario, se stai implementando il cloud, devi semplicemente mettere il tuo sistema là fuori e ridimensionarlo secondo necessità, con la flessibilità offerta dai fornitori di cloud. Con il cloud, non devi preoccuparti di esaurire le risorse di sistema, né devi preoccuparti di avere il 90 percento della memoria o della CPU inattivo.
Naturalmente, c'è la sfida di decidere quale fornitore scegliere. Le guerre delle nuvole sono ancora in corso. Scontro tra Microsoft, Amazon e Google per il futuro dell'informatica è un titolo di esempio che puoi trovare ultimamente nelle notizie del mondo tecnologico. Per questo blog ho scelto Amazon Web Services (AWS), in gran parte in base alla sua attuale popolarità e quota di mercato.
Uno dei vantaggi di AWS è che Amazon offre molti servizi dopo la registrazione:
In questo tutorial utilizzeremo i seguenti due servizi AWS: Elastic Compute Cloud EC2 (in particolare, Amazon EC2 Container Registry o Amazon ECR) e Amazon S3 (Simple Storage Services).
Amazon ECR
Avremo bisogno di memorizzare le nostre immagini Docker da qualche parte. Amazon ECR è un servizio di registro AWS Docker gestito. Come affermato sul sito Web di Amazon ECR:
...consente agli sviluppatori di archiviare, gestire e distribuire facilmente le immagini dei container Docker. Amazon ECR è integrato con Amazon EC2 Container Service (ECS), semplificando il flusso di lavoro dallo sviluppo alla produzione. Amazon ECR elimina la necessità di gestire i propri repository di container o di preoccuparsi di ridimensionare l'infrastruttura sottostante.
Amazon S3
Come accennato, l'applicazione che svilupperemo sarà un microservizio Spring Boot che caricherà file su Amazon S3. Come affermato sul sito Web di Amazon S3:
…fornisce a sviluppatori e team IT uno storage cloud sicuro, durevole e altamente scalabile. Amazon S3 è uno storage di oggetti facile da usare, con una semplice interfaccia di servizio Web per archiviare e recuperare qualsiasi quantità di dati da qualsiasi punto del Web.
Un pratico tutorial "Come fare per".
L'obiettivo è preparare per la distribuzione un microservizio Spring Boot che caricherà i file su Amazon S3. I passaggi sono i seguenti:
- Sviluppa il microservizio
- Definisci il processo di compilazione in cui il servizio verrà inserito nella finestra mobile
- Usa Bitbucket per ospitare il repository di codice Git
- Integra Bitbucket con Jenkins per impacchettare l'applicazione usando Gradle
- Invialo a un Amazon ECR remoto
Quello che segue è un tutorial per impostare tutti i componenti necessari:
- Applicazione di esempio Spring Boot: microservizio impacchettato e dockerizzato utilizzando Gradle
- Installazione di Jenkins su un nuovo server Ubuntu
- Integrazione Bitbucket con Jenkins tramite webhook
- Configurazione del lavoro Jenkins
- Amazon ECR per archiviare le immagini Docker contenenti la nostra applicazione
Prerequisiti
Per poter utilizzare le risorse cloud di AWS, dobbiamo prima registrarci su Amazon. Registrandoti, otterremo un account con vantaggi immediati di utilizzo del piano gratuito, allo scopo di consentire un'esperienza pratica durante i 12 mesi successivi alla registrazione.
Come accennato, in questo tutorial utilizzeremo Amazon S3 e Amazon ECR. Per entrambi avremo bisogno delle chiavi di accesso per la connessione ai servizi.
Dopo esserti registrato con AWS, andiamo al nostro account Credenziali di sicurezza , dove scegliamo Chiavi di accesso e clicchiamo su "Crea nuova chiave di accesso". Dopo aver fatto clic, viene generata una chiave insieme al suo ID. Devi conservarlo in un posto sicuro, poiché lo utilizzeremo in seguito durante la configurazione dell'integrazione di AWS Jenkins e lo sviluppo del nostro caricamento di file S3.
Il prossimo prerequisito è che abbiamo bisogno di un bucket Amazon S3 (contenitore di archiviazione). Il nostro Spring Boot Service caricherà e scaricherà file da e verso lo storage Amazon S3. La creazione del bucket è abbastanza semplice e richiede solo pochi clic. Una descrizione completa di come farlo è fornita nella documentazione Crea un bucket.
Utilizzeremo anche Bitbucket per ospitare il nostro codice e attivare richieste a Jenkins, quindi è necessario anche un account Bitbucket. Bitbucket è un'ottima opzione per gli sviluppatori, con uno dei suoi principali vantaggi è la quantità illimitata di repository privati che puoi creare.
Sviluppo di applicazioni
Piuttosto che entrare in tutti i dettagli delle annotazioni primaverili e di come funzionano, mi concentrerò invece, da una prospettiva di puro sviluppatore, sulla parte più impegnativa dell'intera configurazione; vale a dire, l'installazione e la configurazione di Linux, Jenkins e altri strumenti necessari per la CI. Tutti gli esempi di codice usati in questa esercitazione, inclusa l'applicazione del microservizio Spring Boot, sono disponibili nel repository Bickbucket per il progetto.
La nostra composizione dell'applicazione è semplice. Abbiamo un punto di ingresso dell'applicazione Spring Boot nel nostro file StorageWebserviceApplication.java
. La logica per caricare e scaricare file è in StorageService.java
. StorageController.java
è un controller Rest, contenente gli endpoint API utilizzati per il caricamento e il download di file. Ecco la gerarchia del progetto:
Abbiamo scelto Gradle come strumento di compilazione e impacchetta la nostra applicazione e comporrà l'immagine Docker. Quindi, discuteremo del file di build Gradle, del componente di servizio e del Dockerfile.
Per poter utilizzare l'API AWS, dobbiamo includere le dipendenze nel nostro file di build, come definito nella documentazione AWS per l'utilizzo di Gradle.
Riassumendo, la parte di configurazione delle dipendenze AWS del nostro script Gradle sarà simile alla seguente:
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') }
Come affermato in precedenza, quando carichiamo file su Amazon S3, lo facciamo caricando i file in un bucket S3 .
Per connettersi al bucket, il nostro client Amazon S3 deve disporre delle credenziali fornite. Le credenziali sono le chiavi di accesso che abbiamo creato in precedenza. Definiamo l'ID e il valore della chiave di accesso nel file application.properties
; abbiamo chiamato il nostro bucket toptal-s3-example
.
Il nostro principale componente di servizio è ora il seguente:
@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
legge le credenziali dal file application.properties
e le utilizza per creare un'istanza dell'oggetto BasicAWSCredentials
e, successivamente, dell'oggetto AmazonS3Client
. Quello che segue è una semplice questione di richiamare putObject
per il caricamento di file e getObject
per il download di file, sull'oggetto client Amazon S3.
Eseguiremo il servizio all'interno di un container Docker e, durante il processo di compilazione Gradle, creeremo l'immagine Docker. Lo faremo configurando inoltre il file build.gradle
, come segue:
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 } } }
La parte Buildscript
e il apply plugin
sono piuttosto standard. Abbiamo anche definito un'attività buildDocker
che legge la configurazione Docker archiviata in src/main/docker/Dockerfile
e copia il file JAR nella build Docker.
Dockerfile contiene un elenco di comandi Docker puri con i quali prepareremo la nostra immagine:
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"]
Un prerequisito per eseguire la nostra applicazione è aver installato una Java Virtual Machine (JVM). Docker fornisce un elenco di immagini con Java installato e sceglieremo una delle più piccole, basata su un minimo di 5 MB di Alpine Linux. L'immagine frolvlad/alpine-oraclejdk8
ha tutto ciò di cui abbiamo bisogno ed è piuttosto piccola (solo 170 MB).
Il comando FROM
imposta l'immagine menzionata come base su cui verrà costruita la nostra. ADD
il file JAR compilato al filesystem del contenitore con il nome storageService.jar
. Successivamente, definiamo il contenitore Docker in ascolto sulla porta 8080
in fase di esecuzione con il comando EXPOSE
. Ciò, tuttavia, non consentirà la comunicazione 8080
dall'host. Quando l'immagine è terminata e vogliamo eseguirla, dovremo anche pubblicare la porta sul contenitore con il seguente comando docker run -p 8080:8080 amazonRepository/storageservice
, dove amazonRepository
è un repository che configureremo più avanti in questo tutorial. Con CMD
, definiamo quali comandi verranno eseguiti quando eseguiamo il container. I valori tra parentesi del comando CMD
significano semplicemente che quanto segue verrà eseguito quando eseguiamo il contenitore:
java -Djava.security.egd=file:/dev/./urandom -jar /storageService.jar
L'opzione -Djava.security.egd=file:/dev/./urandom
è necessaria per ridurre i ritardi della JVM durante l'avvio. Se omesso, renderà l'avvio dell'applicazione estremamente lento a causa di un processo di generazione di numeri casuali necessario durante il processo di avvio.
Questo riassume la parte "Sviluppo dell'applicazione". Fatto ciò, il servizio che abbiamo creato qui verrà avviato automaticamente quando in seguito eseguiremo un container Docker. Iniziamo quindi l'installazione e la configurazione degli altri strumenti necessari per impostare il processo di integrazione continua.
Applicazioni e operazioni di sistema
Prima di tutto, abbiamo bisogno di un server Linux pulito su cui configurare lo strumento CI Jenkins. Nota che le seguenti istruzioni sono specifiche per Ubuntu 14.04. Tieni presente che le istruzioni potrebbero differire leggermente per altre distribuzioni Linux. La versione di Jenkins utilizzata è la 2.7.1 e le schermate e le istruzioni potrebbero differire leggermente a seconda della versione di Jenkins utilizzata.

Quindi, andiamo alla nostra console del server Linux e iniziamo a installare i prerequisiti.
Prerequisito JDK
Dobbiamo avere un JDK installato. Di seguito sono riportate le istruzioni per l'installazione di 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
Installa Docker
Affinché Jenkins sia in grado di attivare le build Docker, è necessario installare docker-engine
come segue:
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
Poiché ora abbiamo installato il motore Docker, con il comando seguente avvieremo un'immagine Docker hello-world
per confermare che Docker funzioni correttamente.
sudo docker run hello-world
L'output dell'immagine Hello-world
avrà il seguente aspetto e con ciò possiamo confermare che il motore funziona correttamente.
Installa AWS Command Line Interface (CLI)
Successivamente, installeremo l'AWS CLI. Successivamente, nella configurazione del lavoro Jenkins, utilizzeremo la CLI per eseguire i comandi per l'autenticazione AWS e il push dell'immagine Docker al registro del contenitore Amazon EC2.
Per installare l'AWS CLI, seguiamo le linee guida descritte in dettaglio nella documentazione dell'Amazon CLI.
Delle due opzioni di installazione, sceglieremo l'installazione utilizzando Pip, un sistema di gestione dei pacchetti utilizzato per l'installazione e la gestione dei programmi Python. Installeremo Pip e AWS CLI semplicemente eseguendo i seguenti tre comandi:
#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
Come ultimo passaggio del processo di compilazione, eseguiremo il push della nostra immagine Docker nel registro dei container Amazon. Nella console dei servizi Web di Amazon, troviamo il servizio container AWS EC2.
Selezioniamo il sottomenu Repository a sinistra e facciamo clic su Inizia .
Ci viene quindi presentata la prima schermata per la configurazione del repository in cui inseriamo il nome del repository e facciamo clic sul pulsante Next Step .
Facendo clic su Next Step ci viene mostrata una schermata con le istruzioni su come inviare le immagini al repository.
Ci viene presentato un esempio di come creare e inviare un'immagine Docker al registro, ma non è necessario occuparsene ora. Con questo, abbiamo creato un repository.
Installa e configura Jenkins
Per installare Jenkins, inseriamo i seguenti comandi nella 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
Al termine dell'installazione, Jenkins si avvia automaticamente. Verificare lo stato del servizio con il seguente comando:
sudo service jenkins status
Jenkins si collegherà al repository Git di Bitbucket e, per farlo, dobbiamo installare Git.
#install Git sudo apt-get install git
Jenkins attiverà il processo di compilazione Gradle, durante il quale verrà creata un'immagine Docker. Per poterlo fare, l'utente Jenkins deve essere aggiunto al gruppo utenti docker
:
#add Jenkins user to docker user group sudo usermod -aG docker jenkins
Durante il processo di compilazione, Jenkins eseguirà il push delle immagini Docker su Amazon ECR. Per abilitarlo, dobbiamo configurare AWS per l'utente Jenkins.
Innanzitutto, dobbiamo passare all'utente jenkins
. Per fare ciò, dobbiamo impostare una password.
#change Jenkins password sudo passwd jenkins #switch to Jenkins user su – jenkins #configure AWS aws configure
Dopo aver inserito il comando aws configure
, iniziamo a inserire la chiave di accesso segreta generata e l'ID chiave (queste sono le credenziali che abbiamo generato in precedenza nel processo). Nel mio caso, la regione dell'account è us-west-2
, quindi l'ho inserita. Abbiamo anche impostato il formato di output predefinito per i comandi AWS su JSON.
Possiamo ora passare alla configurazione di Jenkins tramite la console web accessibile sulla porta 8080.
Quando accediamo all'URL, ci viene presentata la seguente schermata introduttiva .
Come indicato sullo schermo, dobbiamo inserire la password. Fatto ciò, la procedura guidata di installazione ci chiede di fare quanto segue:
- Scegli quali plug-in installare: sceglieremo Installa i plug-in suggeriti .
- Crea il primo utente amministratore inserendo le credenziali dell'utente
Al termine, fare clic su Salva e termina . Con questo, abbiamo terminato la configurazione dell'installazione di Jenkins.
Prima di iniziare a definire il lavoro di compilazione, è necessario aggiungere alcuni plug-in aggiuntivi. Andremo su Gestisci Jenkins e faremo clic su Gestisci plug- in. Nella scheda Disponibile , troviamo prima il plug-in Bitbucket , selezioniamo la casella e facciamo clic su Scarica e installa dopo il riavvio .
Ti verrà quindi presentato qualcosa di simile alla schermata seguente.
Dopo l'installazione del plug-in, ripetiamo la procedura per i seguenti plug-in aggiuntivi che saranno necessari per impostare il lavoro:
- Plugin Gradle
- Plugin per la fase di compilazione Docker
- Plugin per l'ambiente di build personalizzato di Cloudbees Docker
- Plugin Amazon ECR
Il plug-in per la fase di compilazione Docker che utilizziamo invierà le richieste al demone Docker. A tale scopo, dobbiamo abilitare il socket TCP sulla porta 2375. Per farlo, inseriamo il file di configurazione Docker che si trova in etc/default/docker
.
sudo vi /etc/default/docker
Qui aggiungiamo la seguente riga nella configurazione:
DOCKER_OPTS='-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock'
Salviamo ed usciamo dal file e riavviamo sia il servizio Docker che quello Jenkins.
sudo service docker restart sudo service jenkins restart
Dopo il riavvio di Jenkins, andiamo alla console Jenkins e, da Manage Jenkins , scegliamo Configure System .
Troviamo la sezione Docker builder e inseriamo http://localhost:2375
per l'URL dell'API REST, clicchiamo su Applica per confermare la modifica. Quindi facciamo clic su Test connessione per confermare, si spera, che tutto sia a posto.
Salviamo la configurazione e procediamo alla configurazione del lavoro Jenkins.
Configurazione del lavoro
Andiamo alla home page di Jenkins e creiamo un nuovo elemento .
Scegliamo un progetto Freestyle e inseriamo il nome del progetto come mostrato nella schermata seguente:
Cliccando su OK , ci viene presentata la pagina di configurazione del lavoro. Vogliamo che il progetto sia costruito su ogni push al nostro repository Bitbucket Git. Per raggiungere questo obiettivo, dobbiamo prima definire il repository a cui ci stiamo connettendo.
Passaggio 1: gestione del codice sorgente
Sotto la gestione del codice sorgente, scegliamo Git e inseriamo l'URL del nostro repository Bitbucket. L'URL ha la forma di https://bitbucket.org/bitbucketUsername/repositoryName
.
Dopo aver inserito l'URL, Jenkins proverà automaticamente a testare la connessione. Poiché non abbiamo ancora inserito le credenziali, verrà visualizzato un errore che indica che non è possibile connettersi.
Aprire l'elenco a discesa Aggiungi e fare clic su Credenziali provider Jenkins .
Ci viene presentata la schermata seguente, in cui inseriamo il nome utente e la password per il nostro account Bitbucket.
Dopo aver aggiunto il nuovo record di credenziali, ci assicuriamo di selezionarlo nell'elenco a discesa delle credenziali e questo termina la configurazione della gestione del codice sorgente .
Passaggio 2: crea trigger
Check Trigger costruisce in remoto e definisce un token di autenticazione. Assicurati di definire un token casuale e sicuro.
Passaggio 3: Webhook Bitbucket
Jenkins ci ha già fornito l'URL che useremo su Bitbucket. Andiamo nella nostra pagina del repository Bitbucket e, nel menu delle impostazioni, facciamo clic su Web hook . Successivamente facendo clic su Aggiungi webhook ci si presenta la seguente schermata, che compileremo come segue:
L'URL ha la struttura seguente: http://JENKINS_URL _HOST:PORT/job/JOB_NAME/build?token=TOKEN
.
Inserisci i valori sopra rispettivamente con l'URL Jenkins, la porta su cui è in esecuzione, il nome del lavoro che hai creato e il token che hai precedentemente definito.
Dopo aver salvato il Webhook, ti verrà fornita la seguente schermata, che puoi modificare se necessario o visualizzare le richieste generate ogni volta che inseriamo un nuovo codice.
Con questa configurazione, il webhook viene attivato su ogni push del repository, indipendentemente dal ramo. Sul lato Jenkins, possiamo definire quale branch push attiverà la build.
Affinché Bitbucket possa inviare il codice a Jenkins, è necessario riconfigurare la sicurezza globale di Jenkins per consentire l'accesso anonimo in lettura. Inoltre, per la nostra configurazione, dobbiamo disabilitare l'opzione Jenkins predefinita che impedisce la falsificazione delle richieste tra siti. Per fare ciò, vai su Gestisci Jenkins e scegli Configura sicurezza globale . Selezionare Consenti accesso in lettura anonimo e selezionare Impedisci exploit di falsificazione tra siti . Quindi salva la configurazione.
Si prega di notare che questo viene fatto solo per motivi di semplicità. La configurazione completa supera la copertura di questo tutorial e includerebbe un'ulteriore protezione di Jenkins dietro un proxy inverso, su una connessione TLS e l'abilitazione della prevenzione CSRF.
Passaggio 4: build graduale
Ora possiamo tornare al lavoro Jenkins e continuare a configurarlo. Nella sezione build, aggiungiamo un passaggio di build: Invoke gradle script .
In questo modulo inseriamo quanto segue:
Come mostrato sullo schermo, utilizzeremo il wrapper Gradle, una comoda funzione Gradle che non richiede l'installazione di Gradle sull'host. Assicurati di selezionare la casella Rendi eseguibile gradlew .
Nelle attività specifichiamo build
e buildDocker
.
Passaggio 5: immagine del tag Docker
Questa parte della build tagga un'immagine Docker precedentemente preparata dall'attività dockerBuild
di Gradle. Per questo, aggiungiamo un nuovo passaggio di compilazione al lavoro: Execute Docker command . Scegliamo il comando Tag image e impostiamo il nome dell'immagine, il repository di destinazione in cui inseriremo l'immagine e tag:
Passaggio 6: Docker Push su Amazon ECR
Infine, dobbiamo definire come inviare la nostra immagine all'Amazon ECR. Per questo, aggiungiamo un nuovo passaggio di compilazione Esegui shell e impostiamo i comandi per l'autenticazione in AWS e per inviare l'immagine ad 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}
Con questo, abbiamo terminato il nostro processo di costruzione. Dopo aver inserito il nuovo codice nel repository, questo processo si attiverà e avremo una nuova immagine Docker caricata nel registro Docker "automaticamente".
L'immagine può essere quindi trascinata ovunque sia installato docker-engine
e può essere eseguita con il comando seguente:
docker run -p 8080:8080 amazonRepository/springbootdocker
Questo comando avvierà il nostro microservizio Spring Boot, con i seguenti endpoint per caricare e scaricare i nostri file nel bucket S3:
-
http://hostnameURL:8080/api/storage/upload
-
http://hostnameURL:8080/api/storage/download?fileName=xyz
Ulteriori passaggi con Java e Integrazione Continua
Ci sono sempre più cose da fare. In questo tutorial è stato trattato molto terreno, ma lo considero solo un punto di partenza da cui imparare ulteriormente. Mettere Jenkins dietro un server proxy web, come Nginx, e stabilire una connessione TLS, sono solo due esempi di cosa si potrebbe, e probabilmente dovrebbe, essere fatto.
La nostra immagine Docker è disponibile su Amazon ECR e pronta per la distribuzione. Ora possiamo prenderlo e distribuirlo manualmente. Tuttavia, una soluzione migliore sarebbe automatizzarlo ulteriormente. CI è solo il primo passaggio e il passaggio successivo è la consegna continua. Che ne dici di una disponibilità elevata? Amazon AWS EC2 fornisce funzionalità per la registrazione di container nel cloud in un ambiente cluster, obbligatorio per il servizio basato sulla produzione. Un buon esempio funzionante di sviluppo di un processo di consegna continua può essere trovato nel seguente post del blog di AWS.
Conclusione
Tutto sommato, abbiamo messo in atto un processo di sviluppo del software fluido e pulito. Utilizzando gli strumenti disponibili, abbiamo creato un'infrastruttura che aiuta a massimizzare la nostra produttività. Ora, non dobbiamo preoccuparci della configurazione del nostro servizio Java, che è un semplice servizio Web con un endpoint REST. Lasciamo che la convenzione Spring Boot si occupi di tutto e ci concentriamo solo sulla logica del servizio. Utilizziamo Jenkins per creare una nuova immagine Docker ogni volta che inviamo il nostro codice al nostro repository Bitbucket Git e, alla fine, abbiamo impostato il cloud in modo che sia responsabile dell'archiviazione delle nostre immagini e file Docker. Quando distribuiamo il nostro servizio contenuto in un'immagine Docker, non ci occuperemo di alcuna restrizione del sistema operativo (a condizione che il sistema operativo abbia un docker-engine
Docker installato).