Java in der Cloud: Tutorial zur Einrichtung der kontinuierlichen Integration
Veröffentlicht: 2022-03-11Jahr für Jahr werden wir Zeugen der immer schnelleren Entwicklung der IT-Branche. Es ist nun mehr als zwei Jahrzehnte her, seit der bahnbrechende Slogan „Write once, run anywhere“ ein völlig neues Maß an Erwartungen an die Softwareentwickler-Community gesetzt hat. Und hier sind wir heute mit einem sich daraus ergebenden, ständig wachsenden Satz von Tools, die gemeinsam die Java-Entwicklung im Besonderen und die Softwareentwicklung im Allgemeinen in ein völlig neues Universum voller Möglichkeiten geführt haben.
Methoden wie Agile, DevOps und Continuous Integration and Deployment – zusammen mit der Entwicklung von Microservices – haben gemeinsam die Produktivität des Softwareentwicklungsprozesses so weit gesteigert, dass es mehr denn je eine Freude ist, Software zu entwickeln. Der Einsatz von Automatisierung und die Einrichtung der richtigen Tools können die Entwicklung und Bereitstellung von Softwareprodukten überraschend einfach machen.
Dieser Artikel wirft einen Blick auf dieses neue Universum aus der Perspektive eines Java-Entwicklers, der zu DevOps wechselt und versucht, die Produktentwicklung und -bereitstellung maximal zu optimieren.
Heute sind Begriffe wie Spring Boot, Docker, Cloud, Amazon Web Services, Continuous Delivery weit verbreitet, werden aber weniger verstanden. Dieser Artikel wird den einfachsten Weg einschlagen, um all diese Technologien vorzustellen und diese Begriffe zu erklären, und ihn in Form eines Tutorials zusammenfassen, in dem wir ein kleines Stück Software entwickeln und es mit allen genannten Tools für die Produktionsbereitstellung vorbereiten.
Warum diese Tools?
Vereinfachen Sie Bereitstellungen mit Docker
„Write once, run anywhere“ war der konzeptionelle Durchbruch, der Technologien wie die Java Virtual Machine (JVM) hervorbrachte, mit denen Ihr Code überall ausgeführt werden konnte. Und jetzt sind wir hier, ein paar Jahrzehnte später, mit etwas namens Docker, das der IT-Community präsentiert wird. Docker ist ein Containment-Tool, in dem Sie Ihre Software platzieren und problemlos ausführen können, fast überall, wo Sie möchten.
Ein Java-Entwickler kann sich jedoch Docker ansehen und sagen: „Warum sollten wir das brauchen, wir haben bereits die JVM, die als die tragbare Masterlösung anerkannt ist.“ Aber ist es?
„Write once, run anywhere“ klingt nett und funktioniert gut… zumindest meistens. Bis Sie auf mehrere JVM-Anbieter, mehrere Java-Versionen, mehrere Betriebssysteme und verschiedene Permutationen und Kombinationen aus all dem stoßen. Sie wechseln dann vom eleganten Paradigma „Einmal schreiben, überall ausführen“ zum kontraproduktiven Fallstrick „Einmal schreiben, überall debuggen “.
Und hier kommt Docker ins Spiel, um den Tag zu retten.
Docker vereinfacht die Entwicklung, das Testen und den Versand von Software. Wenn Sie die Software haben, die Sie testen möchten, legen Sie sie in den Docker-Container, und sie wird ausgeführt und ist für alle Beteiligten problemlos zu installieren.
Beschleunigen Sie die Entwicklung mit Spring Boot
Weniger als ein Jahrzehnt nach der Einführung des Slogans „Run Anywhere“ erschien das Spring-Framework auf der Bildfläche. Heute blüht das Spring-Ökosystem weiter und hat viele wertvolle Spring-basierte Projekte hervorgebracht, vielleicht am bemerkenswertesten Spring Boot. Wie auf der Spring Boot-Website angegeben:
Spring Boot macht es einfach, eigenständige Spring-basierte Anwendungen in Produktionsqualität zu erstellen, die Sie einfach ausführen können.
Mit Spring Boot können Sie die Anwendung in wenigen Minuten zum Laufen bringen. Softwareentwickler können sich auf die Softwareentwicklung konzentrieren und dann von einem Tool profitieren, das die gesamte Konfiguration für sie übernimmt.
In diesem Tutorial verwenden wir Spring Boot, um unseren Microservice zu entwickeln.
Kontinuierliche Integration (CI) mit Jenkins
DevOps ist eine schnell wachsende Bewegung, die Softwareentwicklungs- und Systemadministrationsteams eng integriert, mit dem Ziel, einen Softwareentwicklungs- und -bereitstellungslebenszyklus für alle Beteiligten so schmerzlos, nahtlos und produktiv wie möglich zu gestalten: Entwickler, Systemadministratoren, Tester und letztendlich , Endnutzer.
Continuous Integration (CI) ist einer der Eckpfeiler der DevOps-Revolution. Die Idee ist, dass jedes Mal, wenn ein Entwickler Code in das Code-Repository überträgt, dieser automatisch getestet und für die Lieferung (Bereitstellung) an die Produktion gepackt wird.
CI geht Hand in Hand mit:
- Kontinuierliche Bereitstellung – Automatische Bereitstellung des für Endbenutzer-Geschäftstests vorbereiteten Pakets mit manuellem Auslöser für die Bereitstellung in der Produktion.
- Kontinuierliche Bereitstellung – Automatische Bereitstellung des verpackten Produkts direkt in der Produktion.
Es gibt nicht wenige Tools, die zur Implementierung des CI-Prozesses verwendet werden können. Eines der beliebtesten ist Jenkins, ein Open-Source-CI-Tool. Mit mehr als tausend Plugins und einer riesigen Community dahinter ist Jenkins eine einfache Wahl, wenn Sie anfangen, über die Implementierung von Continuous Integration, Delivery oder Deployment nachzudenken.
In unserem Tutorial wird Jenkins verwendet, um unser Produkt in die Cloud zu liefern, genauer gesagt in die Amazon (AWS) Cloud.
Cloud-Computing mit AWS
Wenn Sie etwas Erfahrung mit Systemadministratoren haben, stellen Sie sich vor, einige der Sorgen der Systemadministration von Ihren Schultern zu nehmen. Sie haben einige Anwendungen; Sie haben eine Vorstellung davon, wie viel Ressourcen sie benötigen, aber Sie wissen nicht genau, welche Hardwaregröße Sie benötigen. Sie machen die Schätzung, die Ressourcen werden gekauft und das System geht in Produktion. Wenn Sie Glück haben, werden Sie feststellen, dass Sie sich überschätzt haben und mehr Ressourcen haben, als Sie benötigen. Aber angesichts von Murphys Gesetz werden Sie eher feststellen, dass Sie den Ressourcenbedarf unterschätzt haben und sich unter enormem Zeitdruck bemühen, etwas mehr Speicher oder Rechenleistung zu bekommen. Im Gegensatz dazu stellen Sie bei einer Bereitstellung in der Cloud Ihr System einfach dort draußen bereit und passen es nach Bedarf an, mit der Flexibilität, die die Cloud-Anbieter bieten. Mit der Cloud müssen Sie sich weder Sorgen machen, dass Ihnen die Systemressourcen ausgehen, noch müssen Sie sich Sorgen machen, dass 90 Prozent Ihres Arbeitsspeichers oder Ihrer CPU im Leerlauf sind.
Natürlich gibt es die Herausforderung, sich für einen Anbieter zu entscheiden. Wolkenkriege sind noch im Gange. Clash of Microsoft, Amazon and Google for the future of computing ist ein Beispieltitel, den Sie in letzter Zeit in den Nachrichten der Tech-Welt finden können. Für diesen Blog habe ich mich für Amazon Web Services (AWS) entschieden, hauptsächlich basierend auf seiner aktuellen Popularität und seinem Marktanteil.
Einer der Vorteile von AWS ist, dass Amazon viele Dienste anbietet, nachdem Sie sich angemeldet haben:
In diesem Tutorial verwenden wir die folgenden zwei AWS-Services: Elastic Compute Cloud EC2 (genauer gesagt Amazon EC2 Container Registry oder Amazon ECR) und Amazon S3 (Simple Storage Services).
Amazon ECR
Wir müssen unsere Docker-Images irgendwo speichern. Amazon ECR ist ein verwalteter AWS Docker-Registrierungsservice. Wie auf der Amazon ECR-Website angegeben:
…erleichtert Entwicklern das Speichern, Verwalten und Bereitstellen von Docker-Container-Images. Amazon ECR ist in Amazon EC2 Container Service (ECS) integriert und vereinfacht Ihren Arbeitsablauf von der Entwicklung bis zur Produktion. Amazon ECR beseitigt die Notwendigkeit, eigene Container-Repositories zu betreiben oder sich Gedanken über die Skalierung der zugrunde liegenden Infrastruktur zu machen.
Amazon S3
Wie bereits erwähnt, handelt es sich bei der von uns entwickelten Anwendung um einen Spring Boot-Microservice, der Dateien auf Amazon S3 hochlädt. Wie auf der Amazon S3-Website angegeben:
…bietet Entwicklern und IT-Teams sicheren, langlebigen und hochskalierbaren Cloud-Speicher. Amazon S3 ist ein benutzerfreundlicher Objektspeicher mit einer einfachen Webservice-Schnittstelle zum Speichern und Abrufen beliebiger Datenmengen von überall im Internet.
Ein praktisches „How To“-Tutorial
Das Ziel besteht darin, einen Spring Boot-Microservice für die Bereitstellung vorzubereiten, der Dateien auf Amazon S3 hochlädt. Schritte sind die folgenden:
- Entwickeln Sie den Microservice
- Definieren Sie den Build-Prozess, in dem der Dienst angedockt wird
- Verwenden Sie Bitbucket zum Hosten des Git-Code-Repositorys
- Integrieren Sie Bitbucket in Jenkins, um die Anwendung mit Gradle zu packen
- Pushen Sie es zu einem entfernten Amazon ECR
Was folgt, ist ein Tutorial zum Einrichten aller erforderlichen Komponenten:
- Spring Boot-Beispielanwendung – Microservice gepackt und dockerisiert mit Gradle
- Jenkins-Installation auf einem neuen Ubuntu-Server
- Bitbucket-Integration mit Jenkins über Webhook
- Jenkins-Auftragskonfiguration
- Amazon ECR zum Speichern der Docker-Images, die unsere Anwendung enthalten
Voraussetzungen
Um AWS-Cloud-Ressourcen nutzen zu können, müssen wir uns zunächst bei Amazon registrieren. Durch die Registrierung erhalten wir ein Konto mit sofortigen Nutzungsvorteilen des kostenlosen Kontingents, um in den 12 Monaten nach der Registrierung praktische Erfahrungen zu ermöglichen.
Wie bereits erwähnt, verwenden wir in diesem Tutorial Amazon S3 und Amazon ECR. Für beide benötigen wir Zugriffsschlüssel, um eine Verbindung zu den Diensten herzustellen.
Nachdem wir uns bei AWS angemeldet haben, gehen wir zu unserem Konto Sicherheitsanmeldeinformationen , wo wir Zugangsschlüssel auswählen und auf „Neuen Zugangsschlüssel erstellen“ klicken. Nach dem Klicken wird ein Schlüssel zusammen mit seiner ID generiert. Sie müssen diese an einem sicheren Ort aufbewahren, da wir sie später bei der Konfiguration der AWS Jenkins-Integration und der Entwicklung unseres S3-Datei-Uploads verwenden werden.
Die nächste Voraussetzung ist, dass wir einen Amazon S3-Bucket (Speichercontainer) benötigen. Unser Spring Boot Service lädt Dateien in den und aus dem Amazon S3-Speicher hoch und herunter. Die Bucket-Erstellung ist einfach genug und erfordert nur wenige Klicks. Eine vollständige Beschreibung der Vorgehensweise finden Sie in der Dokumentation zum Erstellen eines Buckets.
Wir werden Bitbucket auch zum Hosten unseres Codes und zum Auslösen von Anfragen an Jenkins verwenden, daher ist auch ein Bitbucket-Konto erforderlich. Bitbucket ist eine großartige Option für Entwickler, wobei einer der Hauptvorteile die unbegrenzte Anzahl privater Repositories ist, die Sie erstellen können.
Anwendungsentwicklung
Anstatt auf alle Details der Spring-Annotationen und ihrer Funktionsweise einzugehen, werde ich mich stattdessen aus reiner Entwicklerperspektive auf den anspruchsvolleren Teil des gesamten Setups konzentrieren; nämlich das Installieren und Konfigurieren von Linux, Jenkins und anderen Tools, die für CI benötigt werden. Alle in diesem Tutorial verwendeten Codebeispiele, einschließlich der Spring Boot-Microservice-Anwendung, sind im Bickbucket-Repository für das Projekt verfügbar.
Unsere Bewerbungszusammenstellung ist einfach. Wir haben einen Einstiegspunkt für Spring Boot-Anwendungen in unserer Datei „ StorageWebserviceApplication.java
“. Die Logik zum Hoch- und Herunterladen von Dateien befindet sich in StorageService.java
. StorageController.java
ist ein Rest-Controller, der API-Endpunkte enthält, die für das Hochladen und Herunterladen von Dateien verwendet werden. Hier ist die Projekthierarchie:
Wir haben Gradle als Build-Tool ausgewählt, das unsere Anwendung verpackt und das Docker-Image erstellt. Als Nächstes werden wir die Gradle-Build-Datei, die Dienstkomponente und die Docker-Datei besprechen.
Um die AWS-API verwenden zu können, müssen wir Abhängigkeiten in unsere Build-Datei aufnehmen, wie in der AWS-Dokumentation zur Verwendung von Gradle definiert.
Zusammengefasst sieht der AWS-Abhängigkeitskonfigurationsteil unseres Gradle-Skripts wie folgt aus:
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') }
Wie bereits erwähnt, tun wir dies beim Hochladen von Dateien auf Amazon S3, indem wir Dateien in einen S3- Bucket hochladen.
Um eine Verbindung mit dem Bucket herzustellen, muss unser Amazon S3-Client über Anmeldeinformationen verfügen. Anmeldeinformationen sind die zuvor erstellten Zugriffsschlüssel. Wir definieren die Zugriffsschlüssel-ID und den Wert in der Datei application.properties
; wir haben unseren Bucket toptal-s3-example
genannt.
Unsere Hauptdienstleistungskomponente sieht nun wie folgt aus:
@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
liest die Anmeldeinformationen aus der Datei „ application.properties
“ und verwendet sie zum Instanziieren des BasicAWSCredentials
Objekts und anschließend des AmazonS3Client
Objekts. Was folgt, ist eine einfache Frage des Aufrufens von putObject
für das Hochladen von Dateien und getObject
für das Herunterladen von Dateien auf dem Amazon S3-Client-Objekt.
Wir werden den Dienst in einem Docker-Container ausführen und während des Gradle-Build-Prozesses das Docker-Image erstellen. Dazu konfigurieren wir zusätzlich die Datei build.gradle
wie folgt:
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 } } }
Der Buildscript
Teil und das Apply- apply plugin
sind ziemlich Standard. Wir haben auch eine buildDocker
Aufgabe definiert, die die in src/main/docker/Dockerfile
gespeicherte Docker-Konfiguration liest und die JAR-Datei in den Docker-Build kopiert.
Dockerfile enthält eine Liste reiner Docker-Befehle, mit denen wir unser Image vorbereiten:
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"]
Voraussetzung für den Betrieb unserer Anwendung ist die Installation einer Java Virtual Machine (JVM). Docker stellt eine Liste von Images mit installiertem Java bereit, und wir werden eines der kleinsten auswählen, basierend auf Alpine Linux mit mindestens 5 MB. frolvlad/alpine-oraclejdk8
Image hat alles, was wir brauchen, und ist ziemlich klein (nur 170 MB).
Der FROM
-Befehl legt das erwähnte Bild als Basis fest, auf der unser eigenes aufgebaut wird. Wir ADD
die erstellte JAR-Datei dem Container-Dateisystem unter dem Namen storageService.jar
. Als Nächstes definieren wir, dass der Docker-Container zur Laufzeit mit dem Befehl EXPOSE
auf Port 8080
lauschen soll. Dies ermöglicht jedoch keine Kommunikation zu 8080
vom Host. Wenn das Image fertig ist und wir es ausführen möchten, müssen wir auch den Port auf dem Container mit dem folgenden Befehl veröffentlichen docker run -p 8080:8080 amazonRepository/storageservice
, wobei amazonRepository
ein Repository ist, das wir später konfigurieren werden Lernprogramm. Mit CMD
definieren wir, welche Befehle ausgeführt werden, wenn wir den Container ausführen. Werte in den Klammern des CMD
Befehls bedeuten einfach, dass Folgendes ausgeführt wird, wenn wir den Container ausführen:
java -Djava.security.egd=file:/dev/./urandom -jar /storageService.jar
Die Option -Djava.security.egd=file:/dev/./urandom
wird benötigt, um JVM-Verzögerungen während des Starts abzumildern. Wenn es weggelassen wird, wird die Anwendung aufgrund eines Zufallszahlengenerierungsprozesses, der während des Startvorgangs benötigt wird, extrem langsam gestartet.
Damit ist der Teil „Anwendungsentwicklung“ zusammengefasst. Damit wird der hier erstellte Dienst automatisch gestartet, wenn wir später einen Docker-Container ausführen. Beginnen wir also mit der Installation und Konfiguration der anderen Tools, die zum Einrichten des kontinuierlichen Integrationsprozesses erforderlich sind.
Anwendungs- und Systembetrieb
Zunächst einmal brauchen wir einen sauberen Linux-Server, auf dem wir das Jenkins CI-Tool einrichten. Beachten Sie, dass die folgenden Anweisungen speziell für Ubuntu 14.04 gelten. Beachten Sie, dass die Anweisungen für andere Linux-Distributionen leicht abweichen können. Die verwendete Jenkins-Version ist 2.7.1 und Bildschirme und Anweisungen können je nach verwendeter Jenkins-Version geringfügig abweichen.

Also gehen wir zu unserer Linux-Serverkonsole und beginnen mit der Installation der Voraussetzungen.
JDK-Voraussetzung
Wir müssen ein JDK installiert haben. Im Folgenden finden Sie Anweisungen zur Installation von 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
Installieren Sie Docker
Damit Jenkins Docker-Builds auslösen kann, müssen wir docker-engine
wie folgt installieren:
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
Da wir jetzt die Docker-Engine installiert haben, starten wir mit dem folgenden Befehl ein hello-world
Docker-Image, um zu bestätigen, dass Docker korrekt funktioniert.
sudo docker run hello-world
Die Bildausgabe von Hello-world
sieht wie folgt aus, und damit können wir bestätigen, dass die Engine ordnungsgemäß funktioniert.
AWS-Befehlszeilenschnittstelle (CLI) installieren
Als Nächstes installieren wir die AWS CLI. Später werden wir in der Jenkins-Auftragskonfiguration die CLI verwenden, um Befehle für die AWS-Authentifizierung und Docker-Image-Push an die Amazon EC2-Containerregistrierung auszuführen.
Um die AWS CLI zu installieren, befolgen wir die ausführlich in der Amazon CLI-Dokumentation beschriebenen Richtlinien.
Von den beiden Installationsoptionen wählen wir die Installation mit Pip, einem Paketverwaltungssystem, das zum Installieren und Verwalten von Python-Programmen verwendet wird. Wir werden Pip und AWS CLI installieren, indem wir einfach die folgenden drei Befehle ausführen:
#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
Als letzten Schritt des Build-Prozesses werden wir unser Docker-Image in die Amazon-Container-Registry übertragen. In der Amazon Web Services-Konsole finden wir den AWS EC2 Container Service.
Wir wählen das Untermenü Repositories auf der linken Seite und klicken auf Get started .
Uns wird dann der erste Bildschirm zum Konfigurieren des Repositorys angezeigt, wo wir den Repository-Namen eingeben und auf die Schaltfläche „ Nächster Schritt “ klicken.
Wenn Sie auf Next Step klicken, wird uns ein Bildschirm mit Anweisungen zum Übertragen von Bildern in das Repository angezeigt.
Uns wird ein Beispiel gezeigt, wie ein Docker-Image erstellt und in die Registrierung übertragen wird, aber wir müssen uns jetzt nicht damit befassen. Damit haben wir ein Repository erstellt.
Jenkins installieren und konfigurieren
Um Jenkins zu installieren, geben wir folgende Befehle in die Shell ein:
#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
Wenn die Installation abgeschlossen ist, startet Jenkins automatisch. Überprüfen Sie den Dienststatus mit dem folgenden Befehl:
sudo service jenkins status
Jenkins stellt eine Verbindung zum Bitbucket-Git-Repository her, und dazu müssen wir Git installieren.
#install Git sudo apt-get install git
Jenkins löst den Gradle-Build-Prozess aus, während dessen ein Docker-Image erstellt wird. Dazu muss der Jenkins-Benutzer zur docker
-Benutzergruppe hinzugefügt werden:
#add Jenkins user to docker user group sudo usermod -aG docker jenkins
Während des Build-Prozesses überträgt Jenkins Docker-Images an Amazon ECR. Um dies zu ermöglichen, müssen wir AWS für den Jenkins-Benutzer konfigurieren.
Zuerst müssen wir zum Benutzer jenkins
wechseln. Dazu müssen wir ein Passwort festlegen.
#change Jenkins password sudo passwd jenkins #switch to Jenkins user su – jenkins #configure AWS aws configure
Nach Eingabe des Befehls aws configure
beginnen wir mit der Eingabe des generierten geheimen Zugriffsschlüssels und der Schlüssel-ID (dies sind die Anmeldeinformationen, die wir zuvor im Prozess generiert haben). In meinem Fall ist die Region des Kontos us-west-2
, also gebe ich das ein. Wir legen auch das Standardausgabeformat für AWS-Befehle auf JSON fest.
Wir können jetzt mit der Konfiguration von Jenkins über die Webkonsole fortfahren, auf die über Port 8080 zugegriffen werden kann.
Wenn wir auf die URL zugreifen, wird uns der folgende Erste -Schritte-Bildschirm angezeigt.
Wie auf dem Bildschirm angegeben, müssen wir das Passwort eingeben. Danach fordert uns der Setup-Assistent auf, Folgendes zu tun:
- Wählen Sie aus, welche Plugins installiert werden sollen – wir wählen Vorgeschlagene Plugins installieren .
- Erstellen Sie den ersten Administratorbenutzer, indem Sie Benutzeranmeldeinformationen eingeben
Wenn Sie fertig sind, klicken Sie auf Speichern und fertig stellen . Damit haben wir die Konfiguration des Jenkins-Setups abgeschlossen.
Bevor wir mit der Definition des Build-Jobs beginnen, müssen wir einige zusätzliche Plugins hinzufügen. Wir gehen zu Manage Jenkins und klicken auf Manage plugins . Auf der Registerkarte Verfügbar finden wir zuerst das Bitbucket-Plugin , aktivieren das Kontrollkästchen und klicken auf Nach dem Neustart herunterladen und installieren .
Sie werden dann mit etwas wie dem folgenden Bildschirm konfrontiert.
Nach der Plugin-Installation wiederholen wir den Vorgang für die folgenden zusätzlichen Plugins, die zum Einrichten des Jobs benötigt werden:
- Gradle-Plugin
- Docker-Build-Step-Plugin
- Cloudbees Docker benutzerdefiniertes Build-Umgebungs-Plugin
- Amazon ECR-Plugin
Das von uns verwendete Docker-Build-Step-Plugin sendet Anfragen an den Docker-Daemon. Dazu müssen wir den TCP-Socket auf Port 2375 aktivieren. Dazu geben wir die Docker-Konfigurationsdatei ein, die sich unter etc/default/docker
befindet.
sudo vi /etc/default/docker
Hier fügen wir in der Konfiguration folgende Zeile hinzu:
DOCKER_OPTS='-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock'
Wir speichern und beenden die Datei und starten sowohl den Docker- als auch den Jenkins-Dienst neu.
sudo service docker restart sudo service jenkins restart
Nach dem Neustart von Jenkins gehen wir zur Jenkins-Konsole und wählen unter Manage Jenkins die Option Configure System .
Wir finden den Docker-Builder- Bereich und geben http://localhost:2375
für die REST-API-URL ein, klicken auf Übernehmen , um die Änderung zu bestätigen. Wir klicken dann auf Test Connection , um hoffentlich zu bestätigen, dass alles in Ordnung ist.
Wir speichern die Konfiguration und fahren mit der Jenkins-Jobkonfiguration fort.
Jobkonfiguration
Wir gehen zur Jenkins-Homepage und erstellen ein New Item .
Wir wählen ein Freestyle-Projekt und geben den Projektnamen ein, wie auf dem folgenden Bildschirm gezeigt:
Wenn Sie auf OK klicken, wird uns die Auftragskonfigurationsseite angezeigt. Wir möchten, dass das Projekt bei jedem Push in unser Bitbucket-Git-Repository aufgebaut wird. Um dies zu erreichen, müssen wir zuerst das Repository definieren, mit dem wir uns verbinden.
Schritt 1: Quellcodeverwaltung
Unter Quellcodeverwaltung wählen wir Git und geben die URL unseres Bitbucket-Repositorys ein. Die URL hat die Form https://bitbucket.org/bitbucketUsername/repositoryName
.
Nachdem wir die URL eingegeben haben, versucht Jenkins automatisch, die Verbindung zu testen. Da wir noch keine Anmeldeinformationen eingegeben haben, wird ein Fehler angezeigt, der darauf hinweist, dass keine Verbindung hergestellt werden kann.
Öffnen Sie die Dropdown-Liste Add und klicken Sie auf Credentials Jenkins provider.
Uns wird der folgende Bildschirm angezeigt, in dem wir den Benutzernamen und das Passwort für unser Bitbucket-Konto eingeben.
Nachdem Sie den neuen Anmeldedatensatz hinzugefügt haben, stellen wir sicher, dass Sie ihn in der Dropdown-Liste für Anmeldeinformationen auswählen, und dies schließt die Einrichtung der Quellcodeverwaltung ab.
Schritt 2: Trigger erstellen
Check Trigger baut remote auf und definiert ein Authentifizierungstoken. Stellen Sie sicher, dass Sie ein zufälliges und sicheres Token definieren.
Schritt 3: Bitbucket-Webhook
Jenkins hat uns bereits die URL bereitgestellt, die wir auf Bitbucket verwenden werden. Wir gehen auf unsere Bitbucket-Repository-Seite und klicken im Einstellungsmenü auf Web-Hooks . Ein anschließender Klick auf Webhook hinzufügen präsentiert uns den folgenden Bildschirm, den wir wie folgt ausfüllen:
Die URL hat die folgende Struktur: http://JENKINS_URL _HOST:PORT/job/JOB_NAME/build?token=TOKEN
.
Geben Sie die obigen Werte jeweils mit der Jenkins-URL, dem Port, auf dem sie ausgeführt wird, dem Namen des von Ihnen erstellten Jobs und dem zuvor definierten Token ein.
Nach dem Speichern des Webhook wird Ihnen der folgende Bildschirm angezeigt, den Sie bei Bedarf bearbeiten oder Anforderungen anzeigen können, die jedes Mal generiert werden, wenn wir neuen Code übertragen.
Bei dieser Konfiguration wird der Webhook unabhängig vom Branch bei jedem Repository-Push ausgelöst. Auf der Jenkins-Seite können wir definieren, welcher Branch-Push den Build auslöst.
Damit Bitbucket Code an Jenkins übertragen kann, müssen wir die globale Sicherheit von Jenkins neu konfigurieren, um anonymen Lesezugriff zu ermöglichen. Außerdem müssen wir für unser Setup die standardmäßige Jenkins-Option deaktivieren, die eine Cross-Site-Request-Fälschung verhindert. Gehen Sie dazu zu Manage Jenkins und wählen Sie Configure global security . Aktivieren Sie Anonymen Lesezugriff zulassen und Cross-Site Forgery Exploits verhindern . Speichern Sie anschließend die Konfiguration.
Bitte beachten Sie, dass dies nur der Einfachheit halber erfolgt. Die vollständige Einrichtung geht über die Abdeckung dieses Tutorials hinaus und würde die weitere Sicherung von Jenkins hinter einem Reverse-Proxy auf einer TLS-Verbindung und die Aktivierung der CSRF-Prävention umfassen.
Schritt 4: Gradle-Build
Wir können jetzt zum Jenkins-Job zurückkehren und mit der Konfiguration fortfahren. Im Build-Abschnitt fügen wir einen Build-Schritt hinzu: Invoke gradle script .
Auf diesem Formular tragen wir Folgendes ein:
Wie auf dem Bildschirm gezeigt, verwenden wir den Gradle-Wrapper, eine praktische Gradle-Funktion, für die Sie Gradle nicht auf dem Host installiert haben müssen. Stellen Sie sicher, dass Sie das Kontrollkästchen Gradlew ausführbar machen aktiviert haben .
In den Tasks geben wir build
und buildDocker
.
Schritt 5: Docker-Tag-Bild
Dieser Teil des Builds markiert ein Docker-Image, das zuvor von der dockerBuild
-Aufgabe von Gradle vorbereitet wurde. Dazu fügen wir dem Job einen neuen Build-Step hinzu: Execute Docker command . Wir wählen den Befehl Tag image und legen den Image-Namen, das Ziel-Repository, in das wir das Image verschieben, und das Tag fest:
Schritt 6: Docker-Push an Amazon ECR
Zuletzt müssen wir definieren, wie wir unser Image an den Amazon ECR übertragen. Dazu fügen wir einen neuen Build-Schritt Execute Shell hinzu und legen die Befehle fest, um sich bei AWS zu authentifizieren und das Image an Amazon ECR zu pushen:
#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}
Damit haben wir unseren Bauprozess abgeschlossen. Nachdem neuer Code in das Repo gepusht wurde, wird dieser Job aktiviert und wir haben ein neues Docker-Image, das „automatisch“ in die Docker-Registrierung hochgeladen wird.
Das Image kann dann dorthin gezogen werden, wo wir die docker-engine
installiert haben, und kann mit dem folgenden Befehl ausgeführt werden:
docker run -p 8080:8080 amazonRepository/springbootdocker
Dieser Befehl startet unseren Spring Boot-Microservice mit den folgenden Endpunkten zum Hoch- und Herunterladen unserer Dateien in den S3-Bucket:
-
http://hostnameURL:8080/api/storage/upload
-
http://hostnameURL:8080/api/storage/download?fileName=xyz
Weitere Schritte mit Java und Continuous Integration
Es gibt immer noch mehr zu tun. In diesem Tutorial wurde viel Boden behandelt, aber ich würde dies nur als Ausgangspunkt betrachten, um weiter zu lernen. Jenkins hinter einen Web-Proxy-Server wie Nginx zu stellen und eine TLS-Verbindung herzustellen, sind nur zwei Beispiele dafür, was noch getan werden könnte und sollte.
Unser Docker-Image ist auf Amazon ECR verfügbar und kann bereitgestellt werden. Wir können es jetzt nehmen und manuell bereitstellen. Eine feinere Lösung wäre jedoch, es weiter zu automatisieren. CI ist nur der erste Schritt, und der nächste Schritt ist Continuous Delivery. Wie sieht es mit einer hohen Verfügbarkeit aus? Amazon AWS EC2 bietet Funktionen zum Registrieren von Containern in der Cloud in einer geclusterten Umgebung, was für produktionsbasierte Dienste obligatorisch ist. Ein gutes Arbeitsbeispiel für die Entwicklung eines Continuous-Delivery-Prozesses finden Sie im folgenden AWS-Blogbeitrag.
Fazit
Alles in allem haben wir einen reibungslosen und sauberen Softwareentwicklungsprozess eingerichtet. Mit den verfügbaren Tools haben wir eine Infrastruktur geschaffen, die uns hilft, unsere Produktivität zu maximieren. Jetzt müssen wir uns keine Gedanken mehr über die Konfiguration unseres Java-Dienstes machen, der ein einfacher Webdienst mit einem REST-Endpunkt ist. Wir überlassen alles der Spring Boot-Konvention und konzentrieren uns nur auf die Dienstlogik. Wir verwenden Jenkins, um jedes Mal, wenn wir unseren Code in unser Bitbucket-Git-Repository übertragen, ein neues Docker-Image zu erstellen, und am Ende haben wir die Cloud so eingestellt, dass sie für das Speichern unserer Docker-Images und -Dateien verantwortlich ist. Wenn wir unseren in einem Docker-Image enthaltenen Dienst bereitstellen, sind wir frei von Einschränkungen des Betriebssystems (solange auf dem Betriebssystem eine docker-engine
installiert ist).