Jenkins to popularne narzędzie do automatyzacji zadań w modelu Continuous Integration/Continuous Deployment (CI/CD), które dzięki wielu rozszerzeniom skutecznie może być wykorzystywane m.in. do realizacji zadań związanych z budową i utrzymaniem aplikacji. Powszechnie jest on używany do budowy infrastruktury zwirtualizowanej zarówno tej serwerowej, jak i sieciowej, a także do przeprowadzania testów funkcjonalnych czy nawet utrzymywania spójnej i aktualnej dokumentacji.

Jenkins

Działanie Jenkinsa oparte jest o scenariusze (pipelines), czyli sekwencje zadań do wykonania. Scenariusze mogą być liniowe lub zawierać rozgałęzienia, w których zadania wykonywane są równolegle. Przejście do kolejnego zadania może być uwarunkowane nie tylko poprawnym wykonaniem zadania poprzedzającego, lecz sprawdzeniem szeregu innych czynników. Każdego z zadań Jenkins nie wykonuje samodzielnie, lecz wykorzystuje do tego dostępne lokalnie lub zdalnie inne narzędzia, takie jak Ansible, Docker, Git, kompilatory a nawet parsery języka LaTeX do tworzenia dokumentacji. Jenkins jest spoiwem pozwalającym na zbudowanie i wykonanie określonych scenariuszy.

Jenkins został napisany w języku Java, co czyni go produktem łatwym do uruchomienia na prawie dowolnym systemie operacyjnym. Do działania wymagane jest środowisko Java w wersji 8 lub 11 (inne wersje nie są wspierane) w wersji wydawanej przez Oracle lub w ramach projektu OpenJDK. Ponadto dodatkowe moduły mogą wymagać do poprawnego działania odpowiednich bibliotek. Na stronie projektu znajdziemy co tydzień nową wersję zawierającą poprawki błędów i nowe funkcje. Równolegle utrzymywana jest tak zwana stabilna linia Jenkinsa przeznaczona do instalacji w systemach produkcyjnych – oznaczona jest jako LTS (Long-Term Support) i jej wydania następują co 12 tygodni.

Budowa kontenerów z Jenkins

Jenkins możemy zainstalować jako natywną usługę systemu operacyjnego. Na stronie projektu znajdziemy repozytoria pakietów dla różnych systemów. Ten sposób instalacji będzie od nas jednak wymagał spełnienia zgodnych wersji pakietów z zależnymi komponentami – jeżeli serwer nie jest przeznaczony jedynie pod Jenkinsa może to rodzić problemy. Dlatego preferowaną przeze mnie metodą jest uruchomienie Jenkinsa w kontenerach Dockera. Obrazy kontenerów znajdziemy w oficjalnych repozytoriach Docker Hub. Oznaczone są one różnymi tagami, w zależności od wybranego obrazu bazowego, wersji JDK czy linii samego Jenkinsa. Wydania linii stabilnej są oznaczone tagami zawierającymi „lts” w swojej nazwie.

Ja jednak rekomenduję zbudowanie własnego obrazu kontenera samodzielnie. W ten sposób uzyskujemy pełną kontrolę zarówno nad wersją samego Jenkinsa, którą uruchomimy, jak i wszystkimi bibliotekami zależnymi, które będą wymagane przez nasze moduły. Oficjalny obraz kontenerów nie zawiera żadnych dodatkowych bibliotek i część modułów domyślnie nie będzie działać.

Jako obrazu bazowego możemy użyć albo oficjalnego obrazu kontenera, albo zbudować wszystko samodzielnie od podstaw. W tym drugim przypadku zaczynamy od pobrania receptury budowy kontenera z oficjalnego repozytorium projektu na GitHubie (https://github.com/jenkinsci/docker). W plikach o nazwach zaczynających się na Dockerfile znajdują się instrukcje budowy oficjalnych obrazów. Pamiętajmy, że do procesu budowy musimy przekazać poprawne wartości parametrów JENKINS_VERSION oraz JENKINS_SHA, oznaczające odpowiednio numer wydania oraz sumę kontrolą pliku .war zawierającego oprogramowanie Jenkinsa. Pierwszy z nich znajdziemy w pliku https://repo.jenkins-ci.org/releases/org/jenkins-ci/main/jenkins-war/maven-metadata.xml. Aby odczytać sumę kontrolną pliku .war wchodzimy na stronę https://repo.jenkins-ci.org/releases/org/jenkins-ci/main/jenkins-war/ . Następnie przechodzimy do folderu z wybranym przez nas numerem wersji i odczytujemy go z pliku, którego nazwa zakończona jest rozszerzeniem .war.asc. Modyfikujemy plik Dockerfile w taki sposób, by dodać do niego wszystkie wymagane przez nasze moduły biblioteki.

Budowanie własnego obrazu od podstaw jest bardziej skomplikowane i czasochłonne, lecz w wielu firmach ze względu na polityki bezpieczeństwa niezbędne. Wykorzystanie oficjalnego kontenera jako bazy jest o wiele prostsze. W takim wypadku musimy jedynie stworzyć własny plik Dockerfile. Mój znajdziesz w moim repozytorium GitHub-a do tego artykułu: https://github.com/SzkolaDevNet/WladcySieci/blob/master/Jenkins-w-kontenerze-Docker/Dockerfile

Do budowy mojego obrazu wykorzystałem oficjalny obraz wersji rozwojowej Jenkinsa z tagiem alpine, czyli zbudowanej na bazie systemu Alpine Linux. Jest to bardzo lekka, minimalistyczna wręcz dystrybucja Linuksa. Zyskujemy dzięki temu obraz o wiele mniejszych rozmiarów, gdyż jest on pozbawiony wszystkich niepotrzebnych bibliotek domyślnie instalowanych w dystrybucjach Linuksa. Obraz kontenera z artykułu ma rozmiar 656MB lub 360MB, jeżeli jako bazy użyjemy Alpine Linux. Jest to też obraz bardziej bezpieczny i zawiera w sobie mniej znanych podatności.

Aby zbudować obraz kontenera, musimy w katalogu, w którym stworzyliśmy plik konfiguracyjny Dockerfile wydać polecenie:

docker build -t jenkins:latest

Raport skanu bezpieczeństwa dla wersji 2.235 zbudowanej w oparciu o domyślny obraz jenkins/jenkins:latest z dodatkowymi bibliotekami opisanymi w artykule
Raport skanu bezpieczeństwa dla wersji 2.235 zbudowanej w oparciu o obraz jenkins/jenkins:alpine z dodatkowymi bibliotekami opisanymi w artykule

Uruchomenie Jenkins

Mimo, iż Jenkins składa się z pojedynczej aplikacji wygodnie odpalić go za pomocą Docker Compose, korzystając z załączonego pliku konfiguracyjnego. W jego konfiguracji zdefiniowane są dwa wolumeny o nazwach jenkins_data oraz jenkins_home, dzięki czemu zainstalowane biblioteki i konfiguracja nie będą usuwane przy każdej aktualizacji kontenerów aplikacji. Ponadto, na potrzeby Dockera zainstalowanego w kontenerze, który musi mieć dostęp do systemowego gniazda usługi, podłączony został do niego plik z gniazdem lokalnego systemu operacyjnego.

Plik docker-compose.yml: https://github.com/SzkolaDevNet/WladcySieci/blob/master/Jenkins-w-kontenerze-Docker/docker-compose.yml

Kontenery uruchamiamy poleceniem:

docker-compose up -d

wydanym w katalogu, w którym znajduje się plik docker-compose.yml. Usługa Jenkins-a nasłuchuje na porcie 8080. Pierwsze uruchomienie wymagać będzie od nas podania wygenerowanego losowo hasła administratora. Znajduje się ono w pliku /var/jenkins_home/secrets/initialAdminPassword wewnątrz działającego kontenera usługi. Aby odczytać jego zawartość, wydajemy zatem polecenie:

docker exec -it jenkins_jenkins_1 cat /var/jenkins_home/secrets/initialAdminPassword

Uzyskane hasło wpisujemy w przeglądarce. Zostaniemy następnie poproszeni o wybór, czy chcemy zainstalować domyślny rekomendowany zestaw wtyczek czy wybrać je manualnie. Wybieramy pierwszą z opcji i czekamy na zainstalowanie niezbędnych komponentów. Następnie tworzymy pierwszego użytkownika i potwierdzamy adres URL, pod którym Jenkins jest osiągalny. Jenkins został zainstalowany i możemy przystąpić do jego konfiguracji i używania.

Pierwsze uruchomienie Jenkinsa wymaga podania wygenerowanego hasła administratora, które odczytamy z pliku znajdującego się wewnątrz kontenera
Początkujący użytkownicy powinny wybrać domyślny zestaw wtyczek
Instalacja domyślnego zestawu wtyczek