Java w chmurze: samouczek konfiguracji ciągłej integracji
Opublikowany: 2022-03-11Z roku na rok jesteśmy świadkami coraz szybszej ewolucji branży IT. Minęły już ponad dwie dekady, odkąd przełomowe hasło „Napisz raz, uruchom wszędzie” wyznaczyło zupełnie nowy poziom oczekiwań społeczności programistów. I oto jesteśmy dzisiaj, z wynikającym z tego, stale rozszerzającym się zestawem narzędzi, które wspólnie przeniosły rozwój Javy, w szczególności tworzenie oprogramowania, do zupełnie nowego wszechświata możliwości.
Metodologie takie jak Agile, DevOps oraz Continuous Integration and Deployment – wraz z ewolucją mikrousług – wspólnie zwiększyły produktywność procesu tworzenia oprogramowania do punktu, w którym tworzenie oprogramowania jest przyjemnością bardziej niż kiedykolwiek wcześniej. Wykorzystanie automatyzacji i skonfigurowanie odpowiedniego zestawu narzędzi może sprawić, że tworzenie i dostarczanie oprogramowania będzie zaskakująco bezbolesne.
W tym artykule przyjrzymy się temu nowemu światu z perspektywy programisty Java, który wkracza w DevOps i szuka w celu zoptymalizowania rozwoju i dostarczania produktów do maksimum.
Obecnie terminy takie jak Spring Boot, Docker, Cloud, Amazon Web Services, Continuous Delivery są powszechnie używane, ale mniej rozumiane. W tym artykule obierzemy najłatwiejszą możliwą drogę, aby przedstawić wszystkie te technologie i wyjaśnić te pojęcia, a następnie podsumować to w formie samouczka, w którym opracujemy mały kawałek oprogramowania i przygotujemy go do dostarczenia produkcyjnego przy użyciu wszystkich wymienionych narzędzi.
Dlaczego te narzędzia?
Uprość wdrożenia dzięki Dockerowi
„Napisz raz, uruchom wszędzie” było przełomem koncepcyjnym, który przyniósł technologie, takie jak wirtualna maszyna Java (JVM), które umożliwiły uruchamianie kodu w dowolnym miejscu. A teraz oto jesteśmy, kilka dekad później, z czymś, co nazywa się Dockerem, przedstawianym społeczności IT. Docker to narzędzie ograniczające, w którym możesz umieścić swoje oprogramowanie i uruchomić je bezboleśnie, prawie w dowolnym miejscu.
Jednak programista Java może spojrzeć na Dockera i powiedzieć „Po co nam tego potrzebować, mamy już JVM, który jest dobrze rozpoznawany jako główne rozwiązanie przenośne”. Ale czy tak jest?
„Napisz raz, biegnij gdziekolwiek” brzmi ładnie i dobrze się gra… przynajmniej przez większość czasu. Dopóki nie napotkasz wielu dostawców JVM, wielu wersji Java, wielu systemów operacyjnych oraz różnych kombinacji i kombinacji wszystkich powyższych. Następnie przechodzisz z eleganckiego paradygmatu „napisz raz, uruchom wszędzie” na przynoszącą efekt odwrotny do zamierzonych pułapkę „napisz raz, debuguj wszędzie”.
I właśnie tam Docker pomaga uratować sytuację.
Docker upraszcza tworzenie, testowanie i dostarczanie oprogramowania. Jeśli masz oprogramowanie, które chcesz przetestować, umieść je w kontenerze Docker, a instalacja będzie działać i będzie bezbolesna dla wszystkich zaangażowanych stron.
Przyspiesz rozwój dzięki Spring Boot
Niespełna dekadę po wprowadzeniu hasła „biegnij gdziekolwiek” na scenie pojawił się framework Spring. Dziś ekosystem Spring nadal się rozwija i wyprodukował wiele cennych projektów opartych na Spring, być może przede wszystkim Spring Boot. Jak podano na stronie Spring Boot:
Spring Boot ułatwia tworzenie samodzielnych, produkcyjnych aplikacji opartych na Spring, które można po prostu uruchomić.
Spring Boot umożliwia uruchomienie aplikacji w ciągu kilku minut. Twórcy oprogramowania mogą skupić się na tworzeniu oprogramowania, a następnie skorzystać z narzędzia, które wykona za nich całą konfigurację.
W tym samouczku użyjemy Spring Boot do opracowania naszego mikroserwisu.
Ciągła integracja (CI) z Jenkins
DevOps to szybko rozwijający się ruch, który ściśle integruje zespoły zajmujące się tworzeniem oprogramowania i administracją systemami, w celu uczynienia cyklu życia oprogramowania i dostarczania tak bezbolesnym, bezproblemowym i produktywnym, jak to tylko możliwe dla wszystkich zaangażowanych stron: programistów, administratorów, testerów i ostatecznie , użytkownicy końcowi.
Ciągła integracja (CI) jest jednym z fundamentów rewolucji DevOps. Pomysł polega na tym, że za każdym razem, gdy programista wprowadza kod do repozytorium kodu, jest on automatycznie testowany i pakowany w celu dostarczenia (wdrożenia) do produkcji.
CI idzie w parze z:
- Ciągłe dostarczanie — automatyczne dostarczanie pakietu przygotowanego do testów biznesowych użytkownika końcowego z ręcznym wyzwalaniem do wdrożenia produkcyjnego.
- Continuous Deployment – Automatyczne wdrażanie spakowanego produktu bezpośrednio na produkcję.
Istnieje więcej niż kilka narzędzi, które można wykorzystać do realizacji procesu CI. Jednym z najpopularniejszych jest Jenkins, narzędzie CI o otwartym kodzie źródłowym. Dzięki ponad tysiącowi wtyczek i ogromnej społeczności, Jenkins jest łatwym wyborem, gdy zaczynasz myśleć o wdrażaniu ciągłej integracji, dostarczania lub wdrażania.
W naszym samouczku Jenkins zostanie wykorzystany do dostarczenia naszego produktu do chmury, a dokładniej do chmury Amazon (AWS).
Przetwarzanie w chmurze z AWS
Jeśli masz jakieś doświadczenie w sysadminach, wyobraź sobie, że zdejmujesz ze swoich barków niektóre zmartwienia związane z administrowaniem systemem. Masz kilka aplikacji; masz pojęcie, ile zasobów będą wymagały, ale nie znasz dokładnie rozmiaru sprzętu, którego będziesz potrzebować. Dokonujesz oszacowania, surowce są kupowane, a system trafia do produkcji. Jeśli będziesz miał szczęście, przekonasz się, że przeceniłeś i masz więcej zasobów, niż potrzebujesz. Ale biorąc pod uwagę prawo Murphy'ego, z większym prawdopodobieństwem stwierdzisz, że nie doceniłeś wymagań dotyczących zasobów i będziesz szukał trochę więcej pamięci lub mocy obliczeniowej pod ogromną presją czasu. W przeciwieństwie do tego, jeśli wdrażasz w chmurze, po prostu umieszczasz tam swój system i dostosowujesz go do potrzeb, z elastycznością oferowaną przez dostawców chmury. Dzięki chmurze nie musisz się martwić o wyczerpanie zasobów systemowych ani o to, że 90 procent pamięci lub procesora będzie bezczynne.
Oczywiście istnieje wyzwanie, jakim jest podjęcie decyzji o wyborze dostawcy. Wojny w chmurach wciąż trwają. Clash of Microsoft, Amazon and Google for the future of computing to przykładowy tytuł, który można ostatnio znaleźć w wiadomościach ze świata technologii. Na potrzeby tego bloga wybrałem Amazon Web Services (AWS), w dużej mierze w oparciu o jego aktualną popularność i udział w rynku.
Jedną z zalet AWS jest to, że Amazon oferuje wiele usług po rejestracji:
W tym samouczku użyjemy następujących dwóch usług AWS: Elastic Compute Cloud EC2 (dokładniej, Amazon EC2 Container Registry lub Amazon ECR) oraz Amazon S3 (Simple Storage Services).
Amazon ECR
Będziemy musieli gdzieś przechowywać nasze obrazy Dockera. Amazon ECR to zarządzana usługa rejestru AWS Docker. Jak podano na stronie internetowej Amazon ECR:
…ułatwia programistom przechowywanie, zarządzanie i wdrażanie obrazów kontenerów Docker. Amazon ECR jest zintegrowany z Amazon EC2 Container Service (ECS), co upraszcza proces opracowywania i produkcji. Amazon ECR eliminuje potrzebę obsługi własnych repozytoriów kontenerów lub martwienia się o skalowanie infrastruktury bazowej.
Amazonka S3
Jak wspomniano, aplikacja, którą tworzymy, będzie mikroserwisem Spring Boot, który będzie przesyłał pliki do Amazon S3. Jak podano na stronie internetowej Amazon S3:
…zapewnia programistom i zespołom IT bezpieczną, trwałą i skalowalną pamięć masową w chmurze. Amazon S3 to łatwa w użyciu obiektowa pamięć masowa z prostym interfejsem usługi sieciowej do przechowywania i pobierania dowolnej ilości danych z dowolnego miejsca w sieci.
Praktyczny samouczek „Jak”
Celem jest przygotowanie do wdrożenia mikrousługi Spring Boot, która będzie przesyłać pliki do Amazon S3. Kroki są następujące:
- Opracuj mikroserwis
- Zdefiniuj proces kompilacji, w którym usługa będzie dokeryzowana
- Użyj Bitbucket do hostowania repozytorium kodu Git
- Zintegruj Bitbucket z Jenkins, aby spakować aplikację za pomocą Gradle
- Prześlij go do zdalnego Amazon ECR
Poniżej znajduje się samouczek dotyczący konfiguracji wszystkich potrzebnych komponentów:
- Przykładowa aplikacja Spring Boot – mikroserwis spakowany i dokowany przy użyciu Gradle
- Instalacja Jenkinsa na nowym serwerze Ubuntu
- Integracja Bitbucket z Jenkins za pomocą webhooka
- Konfiguracja pracy Jenkinsa
- Amazon ECR do przechowywania obrazów Dockera zawierających naszą aplikację
Warunki wstępne
Aby móc korzystać z zasobów chmury AWS, musimy najpierw zarejestrować się w Amazon. Rejestrując się, otrzymamy konto z natychmiastowymi korzyściami z korzystania z poziomu bezpłatnego, w celu umożliwienia praktycznego doświadczenia w ciągu 12 miesięcy od rejestracji.
Jak wspomniano, w tym samouczku użyjemy Amazon S3 i Amazon ECR. W obu przypadkach będziemy potrzebować kluczy dostępu, aby połączyć się z usługami.
Po zarejestrowaniu się w AWS przechodzimy do poświadczeń bezpieczeństwa naszego konta, gdzie wybieramy Klucze dostępu i klikamy „Utwórz nowy klucz dostępu”. Po kliknięciu generowany jest klucz wraz z jego identyfikatorem. Musisz przechowywać to w bezpiecznym miejscu, ponieważ użyjemy go później podczas konfigurowania integracji AWS Jenkins i opracowywania przesyłania plików S3.
Następnym warunkiem jest to, że potrzebujemy wiadra Amazon S3 (pojemnika do przechowywania). Nasza usługa Spring Boot będzie przesyłać i pobierać pliki do iz pamięci Amazon S3. Tworzenie zasobnika jest dość proste i wymaga tylko kilku kliknięć. Pełny opis, jak to zrobić, znajduje się w dokumentacji tworzenia zasobnika.
Będziemy również używać Bitbucket do hostowania naszego kodu i wysyłania żądań do Jenkins, więc potrzebne jest również konto Bitbucket. Bitbucket to świetna opcja dla programistów, a jedną z jej głównych zalet jest nieograniczona liczba prywatnych repozytoriów, które możesz tworzyć.
Rozwój aplikacji
Zamiast zagłębiać się we wszystkie szczegóły adnotacji Spring i jak one działają, skupię się, z czystej perspektywy programisty, na trudniejszej części całej konfiguracji; mianowicie instalowanie i konfigurowanie Linuksa, Jenkinsa i innych narzędzi potrzebnych do CI. Wszystkie przykłady kodu użyte w tym samouczku, w tym aplikacja mikrousługi Spring Boot, są dostępne w repozytorium Bickbucket dla projektu.
Nasz skład aplikacji jest prosty. W naszym pliku StorageWebserviceApplication.java
znajduje się punkt wejścia aplikacji Spring Boot. Logika przekazywania i pobierania plików znajduje się w StorageService.java
. StorageController.java
to kontroler Rest zawierający punkty końcowe interfejsu API używane do przesyłania i pobierania plików. Oto hierarchia projektu:
Wybraliśmy Gradle jako narzędzie do budowania, które spakuje naszą aplikację i skomponuje obraz Dockera. Następnie omówimy plik kompilacji Gradle, komponent usługi i plik Dockerfile.
Aby móc korzystać z AWS API, musimy uwzględnić zależności w naszym pliku kompilacji, zgodnie z definicją w dokumentacji AWS dotyczącej korzystania z Gradle.
Podsumowując, część konfiguracji zależności AWS naszego skryptu Gradle będzie wyglądać następująco:
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') }
Jak wspomniano wcześniej, przesyłając pliki do Amazon S3, robimy to, przesyłając pliki do zasobnika S3 .
Aby połączyć się z zasobnikiem, nasz klient Amazon S3 musi mieć podane poświadczenia. Poświadczenia to klucze dostępu, które stworzyliśmy wcześniej. Definiujemy identyfikator i wartość klucza dostępu w pliku application.properties
; nazwaliśmy nasze wiadro toptal-s3-example
.
Nasz główny składnik usług jest teraz następujący:
@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; } …
Usługa StorageService
odczytuje poświadczenia z pliku application.properties
i używa ich do utworzenia wystąpienia obiektu BasicAWSCredentials
, a następnie obiektu AmazonS3Client
. Poniżej znajduje się prosta sprawa wywołania putObject
w celu załadowania pliku i getObject
w celu pobrania pliku na obiekcie klienta Amazon S3.
Uruchomimy usługę wewnątrz kontenera Docker, a podczas procesu budowania Gradle zbudujemy obraz Dockera. Zrobimy to dodatkowo konfigurując plik build.gradle
w następujący sposób:
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 } } }
Część Buildscript
i apply plugin
Apply są dość standardowe. Zdefiniowaliśmy również zadanie buildDocker
, które odczytuje konfigurację Dockera zapisaną w src/main/docker/Dockerfile
i kopiuje plik JAR do kompilacji Dockera.
Dockerfile zawiera listę czystych poleceń Dockera, za pomocą których przygotujemy nasz obraz:
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"]
Warunkiem uruchomienia naszej aplikacji jest zainstalowanie wirtualnej maszyny języka Java (JVM). Docker udostępnia listę obrazów z zainstalowaną Javą, a my wybierzemy jeden z najmniejszych, oparty na minimalnym 5MB Alpine Linux. frolvlad/alpine-oraclejdk8
ma wszystko, czego potrzebujemy i jest dość mały (tylko 170 MB).
Polecenie FROM
ustawia wspomniany obraz jako bazę, na której zostanie zbudowany nasz własny. Zbudowany plik JAR ADD
do systemu plików kontenera pod nazwą storageService.jar
. Następnie definiujemy, aby kontener Docker nasłuchiwał na porcie 8080
w czasie wykonywania za pomocą polecenia EXPOSE
. To jednak nie umożliwi komunikacji z 8080
z hosta. Gdy obraz jest gotowy, a chcemy go uruchomić, będziemy musieli również opublikować port na kontenerze za pomocą następującego polecenia docker run -p 8080:8080 amazonRepository/storageservice
, gdzie amazonRepository
to repozytorium, które skonfigurujemy później w tym instruktaż. Za pomocą CMD
definiujemy, które polecenia zostaną wykonane po uruchomieniu kontenera. Wartości w nawiasach polecenia CMD
oznaczają po prostu, że po uruchomieniu kontenera zostanie wykonane:
java -Djava.security.egd=file:/dev/./urandom -jar /storageService.jar
Opcja -Djava.security.egd=file:/dev/./urandom
jest potrzebna do zmniejszenia opóźnień JVM podczas uruchamiania. Jeśli zostanie pominięty, spowoduje to, że aplikacja uruchomi się bardzo wolno ze względu na proces generowania liczb losowych potrzebny podczas procesu uruchamiania.
To podsumowuje część „Rozwój aplikacji”. Po wykonaniu tej czynności usługa, którą tutaj utworzyliśmy, zostanie automatycznie uruchomiona, gdy później uruchomimy kontener Docker. Zacznijmy więc od instalacji i konfiguracji pozostałych narzędzi potrzebnych do skonfigurowania procesu ciągłej integracji.
Operacje aplikacji i systemu
Po pierwsze, potrzebujemy czystego serwera Linux, na którym możemy skonfigurować narzędzie Jenkins CI. Zauważ, że poniższe instrukcje dotyczą specjalnie dla Ubuntu 14.04. Pamiętaj, że instrukcje mogą się nieznacznie różnić w przypadku innych dystrybucji Linuksa. Używana wersja Jenkins to 2.7.1, a ekrany i instrukcje mogą się nieznacznie różnić w zależności od używanej wersji Jenkins.

Więc przechodzimy do naszej konsoli serwera Linux i zaczynamy instalowanie wymagań wstępnych.
Wymagania wstępne JDK
Musimy mieć zainstalowane JDK. Poniżej znajdują się instrukcje dotyczące instalowania 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
Zainstaluj Docker
Aby Jenkins mógł uruchamiać kompilacje docker-engine
w następujący sposób:
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
Ponieważ już zainstalowaliśmy silnik Docker, za pomocą następującego polecenia uruchomimy obraz Docker hello-world
aby potwierdzić, że Docker działa poprawnie.
sudo docker run hello-world
Wyjście obrazu Hello-world
będzie wyglądało następująco i dzięki temu możemy potwierdzić, że silnik działa poprawnie.
Zainstaluj interfejs wiersza poleceń AWS (CLI)
Następnie zainstalujemy AWS CLI. Później, w konfiguracji zadania Jenkins, użyjemy CLI do wykonania poleceń uwierzytelniania AWS i wypychania obrazu Docker do rejestru kontenerów Amazon EC2.
Aby zainstalować AWS CLI, kierujemy się wytycznymi opisanymi szczegółowo w dokumentacji Amazon CLI.
Spośród dwóch opcji instalacji wybierzemy instalację za pomocą Pip, systemu zarządzania pakietami używanego do instalowania i zarządzania programami w Pythonie. Zainstalujemy Pip i AWS CLI, uruchamiając następujące trzy polecenia:
#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
W ostatnim kroku procesu kompilacji przekażemy nasz obraz Dockera do rejestru kontenerów Amazon. W konsoli usług internetowych Amazon znajdziemy usługę kontenerową AWS EC2.
Wybieramy podmenu Repozytoria po lewej stronie i klikamy Rozpocznij .
Następnie pojawia się pierwszy ekran konfiguracji repozytorium, w którym wpisujemy nazwę repozytorium i klikamy przycisk Następny krok .
Kliknięcie Next Step wyświetla nam ekran z instrukcjami, jak przesłać obrazy do repozytorium.
Przedstawiono nam przykład, jak zbudować i przekazać obraz Dockera do rejestru, ale nie musimy się tym teraz zajmować. Dzięki temu stworzyliśmy repozytorium.
Zainstaluj i skonfiguruj Jenkinsa
Aby zainstalować Jenkinsa, wpisujemy w powłoce następujące polecenia:
#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
Po zakończeniu instalacji Jenkins uruchamia się automatycznie. Sprawdź stan usługi za pomocą następującego polecenia:
sudo service jenkins status
Jenkins będzie łączył się z repozytorium Bitbucket Git i aby to zrobić, musimy zainstalować Git.
#install Git sudo apt-get install git
Jenkins uruchomi proces budowania Gradle, podczas którego zostanie utworzony obraz Dockera. Aby móc to zrobić, użytkownik Jenkins musi zostać dodany do grupy użytkowników platformy docker
:
#add Jenkins user to docker user group sudo usermod -aG docker jenkins
Podczas procesu kompilacji Jenkins będzie przesyłać obrazy Docker do Amazon ECR. Aby to umożliwić, musimy skonfigurować AWS dla użytkownika Jenkins.
Najpierw musimy przełączyć się na użytkownika jenkins
. Aby to zrobić, musimy ustawić hasło.
#change Jenkins password sudo passwd jenkins #switch to Jenkins user su – jenkins #configure AWS aws configure
Po wpisaniu polecenia aws configure
zaczynamy wpisywać wygenerowany tajny klucz dostępu i identyfikator klucza (są to dane uwierzytelniające, które wygenerowaliśmy wcześniej w procesie). W moim przypadku region konta to us-west-2
, więc go wpisuję. Ustawiliśmy również domyślny format wyjściowy poleceń AWS na JSON.
Możemy teraz przejść do konfiguracji Jenkinsa poprzez konsolę internetową dostępną na porcie 8080.
Kiedy uzyskujemy dostęp do adresu URL, pojawia się następujący ekran Pierwsze kroki .
Jak podano na ekranie, musimy wprowadzić hasło. Po wykonaniu tej czynności kreator instalacji prosi nas o wykonanie następujących czynności:
- Wybierz wtyczki do zainstalowania - wybierzemy opcję Zainstaluj sugerowane wtyczki .
- Utwórz pierwszego administratora, wprowadzając dane uwierzytelniające użytkownika
Po zakończeniu kliknij Zapisz i zakończ . Na tym zakończyliśmy konfigurację instalacji Jenkinsa.
Zanim zaczniemy definiować zadanie budowania, musimy dodać kilka dodatkowych wtyczek. Przejdziemy do Manage Jenkins i klikniemy Manage plugins . W zakładce Dostępne najpierw znajdujemy wtyczkę Bitbucket , zaznaczamy pole i klikamy Pobierz i zainstaluj po ponownym uruchomieniu .
Zostanie wyświetlony ekran podobny do poniższego.
Po zainstalowaniu wtyczki powtarzamy proces dla następujących dodatkowych wtyczek, które będą potrzebne do skonfigurowania zadania:
- Wtyczka Gradle
- Wtyczka kroku kompilacji Docker
- Wtyczka niestandardowego środowiska kompilacji Cloudbees Docker
- Wtyczka Amazon ECR
Wtyczka kroku kompilacji Docker, której używamy, będzie wysyłać żądania do demona Docker. W tym celu musimy włączyć gniazdo TCP na porcie 2375. W tym celu wchodzimy do pliku konfiguracyjnego Dockera znajdującego się pod adresem etc/default/docker
.
sudo vi /etc/default/docker
Tutaj dodajemy następującą linię w konfiguracji:
DOCKER_OPTS='-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock'
Zapisujemy i zamykamy plik i ponownie uruchamiamy zarówno usługę Docker, jak i Jenkins.
sudo service docker restart sudo service jenkins restart
Po ponownym uruchomieniu Jenkinsa przechodzimy do konsoli Jenkins i z Manage Jenkins wybieramy Configure System .
Znajdujemy sekcję Docker builder i wpisujemy http://localhost:2375
dla adresu URL REST API, kliknij Apply , aby potwierdzić zmianę. Następnie klikamy opcję Testuj połączenie , aby mieć nadzieję, że wszystko jest w porządku.
Zapisujemy konfigurację i przechodzimy do konfiguracji zadania Jenkins.
Konfiguracja zadania
Przechodzimy do strony głównej Jenkins i tworzymy nowy przedmiot .
Wybieramy projekt Freestyle i wpisujemy nazwę projektu, jak pokazano na poniższym ekranie:
Klikając na OK , pojawia się strona konfiguracji zadania. Chcemy, aby projekt był budowany przy każdym wypchnięciu do naszego repozytorium Bitbucket Git. Aby to osiągnąć, najpierw musimy zdefiniować repozytorium, z którym się łączymy.
Krok 1: Zarządzanie kodem źródłowym
W ramach zarządzania kodem źródłowym wybieramy Git i wpisujemy adres URL naszego repozytorium Bitbucket. Adres URL ma postać https://bitbucket.org/bitbucketUsername/repositoryName
.
Po wprowadzeniu adresu URL Jenkins automatycznie spróbuje przetestować połączenie. Ponieważ nie wprowadziliśmy jeszcze danych logowania, wyświetli błąd wskazujący, że nie można się połączyć.
Otwórz listę rozwijaną Dodaj i kliknij Dostawca poświadczeń Jenkins .
Pojawia się następujący ekran, na którym wpisujemy nazwę użytkownika i hasło do naszego konta Bitbucket.
Po dodaniu nowego rekordu poświadczeń upewniamy się, że wybraliśmy go z listy rozwijanej poświadczeń, co kończy konfigurację zarządzania kodem źródłowym .
Krok 2: Wyzwalacze kompilacji
Sprawdź wyzwalacz kompiluje zdalnie i zdefiniuj token uwierzytelniający. Upewnij się, że zdefiniowałeś losowy i bezpieczny token.
Krok 3: Webhook Bitbucket
Jenkins już dostarczył nam adres URL, którego będziemy używać w Bitbucket. Wchodzimy na stronę naszego repozytorium Bitbucket i w menu ustawień klikamy Web hooks . Następnie kliknięcie na Dodaj webhooka przedstawia nam następujący ekran, który wypełniamy w następujący sposób:
Adres URL ma następującą strukturę: http://JENKINS_URL _HOST:PORT/job/JOB_NAME/build?token=TOKEN
.
Wprowadź powyższe wartości odpowiednio z adresem URL Jenkins, portem, na którym działa, nazwą utworzonego zadania i wcześniej zdefiniowanym tokenem.
Po zapisaniu webhooka otrzymasz następujący ekran, który możesz edytować w razie potrzeby lub przeglądać żądania generowane za każdym razem, gdy wysyłamy nowy kod.
W tej konfiguracji element webhook jest wyzwalany przy każdym wypchnięciu repozytorium, niezależnie od gałęzi. Po stronie Jenkinsa możemy zdefiniować, które wypychanie gałęzi spowoduje uruchomienie kompilacji.
Aby Bitbucket mógł przesyłać kod do Jenkins, musimy ponownie skonfigurować globalne zabezpieczenia Jenkins, aby umożliwić anonimowy dostęp do odczytu. Dodatkowo, dla naszej konfiguracji, musimy wyłączyć domyślną opcję Jenkins, która zapobiega fałszowaniu żądań między witrynami. Aby to zrobić, przejdź do Zarządzaj Jenkins i wybierz Konfiguruj globalne zabezpieczenia . Zaznacz Zezwalaj na anonimowy dostęp do odczytu i zaznacz Zapobiegaj wykorzystywaniu fałszerstw między witrynami . Następnie zapisz konfigurację.
Należy pamiętać, że odbywa się to tylko dla uproszczenia. Pełna konfiguracja wykracza poza zakres tego samouczka i obejmuje dalsze zabezpieczanie Jenkins za zwrotnym serwerem proxy, połączenie TLS i włączenie zapobiegania CSRF.
Krok 4: Budowanie Gradle
Możemy teraz wrócić do zadania Jenkinsa i kontynuować jego konfigurację. W sekcji budowania dodajemy krok budowania: Wywołaj skrypt gradle .
W tym formularzu wpisujemy:
Jak pokazano na ekranie, użyjemy Gradle wrapper, wygodnej funkcji Gradle, która nie wymaga zainstalowania Gradle na hoście. Pamiętaj, aby zaznaczyć pole Utwórz plik wykonywalny gradlew .
W zadaniach określamy build
i buildDocker
.
Krok 5: Obraz znacznika Docker
Ta część kompilacji oznacza obraz Docker przygotowany wcześniej przez zadanie dockerBuild
Gradle. W tym celu dodamy do zadania nowy krok kompilacji: Wykonaj polecenie Docker . Wybieramy polecenie Tag image i ustawiamy nazwę obrazu, docelowe repozytorium, w którym obrazek wypchniemy, oraz tag:
Krok 6: Docker Push do Amazon ECR
Na koniec musimy zdefiniować, w jaki sposób przenieść nasz wizerunek do Amazon ECR. W tym celu dodajemy nowy krok budowania powłoki Execute i ustawiamy polecenia do uwierzytelniania w AWS i wypychania obrazu do 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}
Na tym zakończyliśmy proces budowania. Po wypchnięciu nowego kodu do repozytorium to zadanie zostanie aktywowane, a nowy obraz Dockera zostanie przesłany do rejestru Dockera „automagicznie”.
Obraz można następnie ściągnąć do miejsca, w którym zainstalowaliśmy docker-engine
dockerowy, i uruchomić za pomocą następującego polecenia:
docker run -p 8080:8080 amazonRepository/springbootdocker
To polecenie uruchomi nasz mikroserwis Spring Boot z następującymi punktami końcowymi do przesyłania i pobierania naszych plików do zasobnika S3:
-
http://hostnameURL:8080/api/storage/upload
-
http://hostnameURL:8080/api/storage/download?fileName=xyz
Dalsze kroki z Javą i ciągłą integracją
Zawsze jest więcej rzeczy do zrobienia. W tym samouczku omówiono wiele zagadnień, ale uważam to za punkt wyjścia do dalszej nauki. Umieszczenie Jenkinsa za internetowym serwerem proxy, takim jak Nginx, i nawiązanie połączenia TLS to tylko dwa przykłady tego, co można i prawdopodobnie należy zrobić.
Nasz obraz platformy Docker jest dostępny w Amazon ECR i gotowy do wdrożenia. Możemy go teraz wziąć i wdrożyć ręcznie. Jednak lepszym rozwiązaniem byłaby dalsza automatyzacja. CI to tylko pierwszy krok, a następnym krokiem jest ciągłe dostarczanie. A co z wysoką dostępnością? Amazon AWS EC2 zapewnia funkcje rejestracji kontenerów w chmurze w środowisku klastrowym, co jest obowiązkowe dla usług opartych na produkcji. Dobry przykład pracy nad rozwojem procesu ciągłego dostarczania można znaleźć w następującym poście na blogu AWS.
Wniosek
Podsumowując, wdrożyliśmy płynny i czysty proces tworzenia oprogramowania. Wykorzystując dostępne narzędzia, stworzyliśmy infrastrukturę, która pomaga zmaksymalizować naszą produktywność. Teraz nie musimy martwić się konfiguracją naszej usługi Java, która jest prostą usługą internetową z punktem końcowym REST. Pozwalamy, aby konwencja Spring Boot zajęła się wszystkim i skupiła się tylko na logice obsługi. Używamy Jenkinsa do tworzenia nowego obrazu Dockera za każdym razem, gdy przesyłamy nasz kod do naszego repozytorium Bitbucket Git, a na koniec ustawiliśmy chmurę jako odpowiedzialną za przechowywanie naszych obrazów i plików Dockera. Wdrażając naszą usługę zawartą w obrazie Docker, nie będziemy się martwić o żadne ograniczenia systemu operacyjnego (o ile system operacyjny ma zainstalowany docker-engine
dockera).