Do pobrania
sha1sum
51bb48ecdfb4ce210f1941e43eebe674fb524b91 Jenkins_workshop_5.ova
3c6bff4eea93c387bbdf193015cda7c04833e75f logger-plugin.hpi
a9fa67ec8bb023cf0d99eccd47b9cca96b433500 old-data-plugin.hpi
9d97f5492b3ba5c9cc502b3e26aa4b93a1c32ec5 old-data-plugin_v2.hpi
Instalacja wirtualnej maszyny
Wymagania Zainstalowany Virtual Box
W VM VirtualBox Manager wybieramy: Plik -> Importuj urządzenie wirtualne.
W kolejnym oknie wybieramy pobrany obraz.
W kolejnym oknie należy zwrócić uwagę na dwie opcje.
- Ilość pamięci (jeżeli nie mamy za dużo to można zmniejszyć do 2G)
- Wybrać opcje: Generate new MAC addresses …
Po zaimportowaniu i uruchomieniu powinniśmy otrzymać.
Proszę, zwrócić uwagę na przydzielony adres IP powinien to być w waszej sieci, a nie adres localhost (127.0.0.1)!
Użytkownicy:
jenkins:12345678
root:12345678
Można się zalogować bezpośrednio w oknie powyżej, używając programu putty lub innego klienta ssh (np. git Bash).
ssh root@192.168.0.178
or
ssh jenkins@192.168.0.178
0: Środowisko
- LDAP
- Bitbucket
- Jenkins (tylko do integracji z bitbucket)
- Każdy z uczestników dzięki LDAP ma konta na Bitbucket oraz Jenkins.
- Konwencja nazw użytkowników: pierwsza litera imienia + nazwisko bez polskich znaków diakrytycznych. Bogusław Klimas -> bklimas
Zanim przejdziemy do dalszych działań, należy wygenerować klucze ssh na naszej wirtualnej maszynie. Następnie klucz publiczny przypisać do swojego konta w Bitbuckecie.
$ ssh-keygen -t rsa -C bklimas@acme.com
$ ll ~/.ssh/
razem 12
-rw-------. 1 jenkins jenkins 1843 11-15 10:08 id_rsa
-rw-r--r--. 1 jenkins jenkins 411 11-15 10:08 id_rsa.pub
-rw-r--r--. 1 jenkins jenkins 571 11-15 09:55 known_hosts
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzsDiyJ+4WZAwaYDVz43Zrm6RCxPTzFWXnUfuBdAx6BmM98gxnveU0k+AbiiF739ZTDKxmWHyBmgGKVMzrSYoldKAPvTii4X/FtQfdsUrvl3gL9E+UM2MJ3k+yaYS88RmfDH7TTYBAwvFYqP3+F7F9tX+Te2Purf/ZwU+r5ekPMP4gtkO9Pi1XtzdOAW2vDHrXm2QJqOQPa8Ppx6SalrLOnlCjo5RtpEIBhjTN3WkwhRYHxDZedTZE0YFyn8fNna4UlZiEhPaLRu6utykDTyfOOhFiONYj9naNzv0/wIAYgYAFreD5CCNZEVMKIYwuKlS9Tb0gyzpDQYBjNGAvks2v jenkins@localhost.localdomain
Bitbucket SSH keys Dodatkowo na wirtualnej maszynie, jak i na naszym komputerze (zapis hosta do ~/.known_hosts)
Bardzo pomocnym będzie także wgranie swojego klucza (z konta, którego teraz używacie) publicznego do Bitbucketa (nie ma się co obawiać to klucz publiczny!)
cd /tmp
git clone ssh://git@ovh.klimas7.pl:7999/lab/learn.git
$ git clone ssh://git@ovh.klimas7.pl:7999/lab/learn.git
Cloning into 'learn'...
The authenticity of host '[ovh.klimas7.pl]:7999 ([51.89.23.232]:7999)' can't be established.
RSA key fingerprint is SHA256:N9yD+ozjLANIDbEfFL/zuqv1smd1sBY58NJOSakHjuo.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[ovh.klimas7.pl]:7999,[51.89.23.232]:7999' (RSA) to the list of known hosts
1: Uruchomienie
Instalacja via dnf
Definicja repozytorium i import klucza
# wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat/jenkins.repo
# rpm --import https://pkg.jenkins.io/redhat/jenkins.io.key
Instalacja
# dnf install jenkins
# dnf install java
Uruchomienie serwisu
# systemctl status jenkins.service
# systemctl enable jenkins.service
# systemctl start jenkins.service
Test: http://192.168.0.178:8080/ otwieramy w przeglądarce
Firewall
# firewall-cmd --permanent --add-service=jenkins
# firewall-cmd --zone=public --add-service=http --permanent
# firewall-cmd --reload
# firewall-cmd --list-all
Test http://192.168.0.178:8080/ otwieramy w przeglądarce
Usunięcie
# systemctl stop jenkins.service
# dnf remove jenkins
Docker
$ docker pull jenkins/jenkins
# check version
$ docker image inspect docker.io/jenkins/jenkins
# start
$ docker run -d -v jenkins_home:/var/jenkins_home --name jenkins_workshop -p 8080:8080 -p 50000:50000 jenkins/jenkins &
$ docker ps
Test http://192.168.0.178:8080/ otwieramy w przeglądarce
$ docker exec jenkins_workshop cat /var/jenkins_home/secrets/initialAdminPassword
Stop and clean
$ docker stop jenkins_workshop
$ docker rm jenkins_workshop
$ docker volume rm jenkins_home
Uruchomienie via jar
$ cd /opt/tools/jenkins
$ java -jar jenkins.war &
Test http://192.168.0.178:8080/ otwieramy w przeglądarce
Stop and clean
$ ps aux | grep java
$ kill -9 1338 #JENKINS_PID
# or
$ killall java
rm -rf ~/.jenkins
Uruchomienie ze skryptu
$ cd /opt/tools/jenkins/
$ ls
jenkins.sh jenkins.war ssh
$ cat jenkins.sh
#!/bin/bash
jh=/opt/tools/jenkins
jp=8080
mkdir -p $jh/log
mkdir -p $jh/war
command=$1
printUsage() {
echo "Usage:"
echo "jenkins.sh start"
echo "jenkins.sh stop"
}
start() {
jenkins_options="--logfile=$jh/log/jenkins.log --webroot=$jh/war"
jenkins_options="$jenkins_options --ajp13Port=-1 --debug=5 \
--handlerCountMax=100 --handlerCountMaxIdle=20"
#use http
jenkins_options="$jenkins_options --httpPort=$jp"
#use_https
#jenkins_options="$jenkins_options --httpPort=-1 --httpsPort=8443 \
--httpsCertificate=$jh/ssh/cert.pem \
--httpsPrivateKey=$jh/ssh/key.pem"
java -Dcom.sun.akuma.Daemon=daemonized -Djava.awt.headless=true -DJENKINS_HOME=$jh \
-jar $jh/jenkins.war $jenkins_options &
}
stop() {
jenkinsPID=$(ps aux | grep java | grep $jh | awk '{print $2}')
echo "kill jenkins process PID: "$jenkinsPID
kill -9 $jenkinsPID 2>&1 > /dev/null
}
case $command in
"start")
start
;;
"stop")
stop
;;
*)
printUsage
esac
winstone, winstone 1, ajp, headless, com.sun.akuma, Jenkins initial-settings
$ ./jenkins.sh
Test http://192.168.0.178:8080/ otwieramy w przeglądarce
uruchomienie https
cd /opt/tools/jenkins/ssh
$ openssl genrsa -out key.pem
Generating RSA private key, 2048 bit long modulus (2 primes)
..............................................+++++
..............+++++
e is 65537 (0x010001)
$ openssl req -new -key key.pem -out csr.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:PL
State or Province Name (full name) []:Lesser Poland
Locality Name (eg, city) [Default City]:KRK
Organization Name (eg, company) [Default Company Ltd]:K7Soft
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:192.168.0.178
Email Address []:admin@k7soft.pl
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
#### Self-signed certificate ####
$ openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
Signature ok
subject=C = PL, ST = Lesser Poland, L = KRK, O = K7Soft, CN = 192.168.0.178, emailAddress = admin@k7soft.pl
Getting Private key
W jenkins.sh
#use http
#jenkins_options="$jenkins_options --httpPort=$jp"
#use_https
jenkins_options="$jenkins_options --httpPort=-1 --httpsPort=8443 --httpsCertificate=$jh/ssh/cert.pem --httpsPrivateKey=$jh/ssh/key.pem"
A tu w logach
2020-10-30 11:55:00.779+0000 [id=1] WARNING winstone.Logger#logInternal: Using the --httpsPrivateKey/--httpsCertificate
options currently relies on unsupported APIs in the Oracle JRE.
Please use --httpsKeyStore and related options instead.
Create keystore
$ openssl pkcs12 -export -in cert.pem -inkey key.pem -out jenkins.p12 -name jenkins
Enter Export Password:
Verifying - Enter Export Password:
#Password: 12345678
$ keytool -importkeystore -deststorepass 12345678 -destkeypass 12345678 \
-destkeystore jenkins.keystore -srckeystore jenkins.p12 \
-srcstoretype PKCS12 -srcstorepass 12345678 -alias jenkins
Importing keystore jenkins.p12 to jenkins.keystore...
$ keytool -list -keystore jenkins.keystore
Enter keystore password:
Keystore type: PKCS12
Keystore provider: SUN
Your keystore contains 1 entry
jenkins, 30 paź 2020, PrivateKeyEntry,
Certificate fingerprint (SHA-256): D9:9C:7F:85:C5:1C:D8:32:82:0A:70:8E:8E:CF:59:15:B9:22:A2:9B:5D:20:35:6E:44:D8:FE:D1:EA:15:0D:0E
Dodatkowo w jenkins.sh
#jenkins_options="$jenkins_options --httpPort=-1 --httpsPort=8443 --httpsCertificate=$jh/ssh/cert.pem --httpsPrivateKey=$jh/ssh/key.pem"
jenkins_options="$jenkins_options --httpPort=-1 --httpsPort=8443 --httpsKeyStore=$jh/ssh/jenkins.keystore --httpsKeyStorePassword=12345678"
Firewall
## disabled
#
# systemctl status firewalld
# systemctl stop firewalld
## or open port
# firewall-cmd --get-active-zones
# firewall-cmd --permanent --zone=public --add-port=8443/tcp
# systemctl restart firewalld
Ćwiczenie 1.1
Ćwiczenie 1.2*
Uruchomienie ze skryptu po https
W każdym przypadku powinniśmy otrzymać stronę startową Hasło ze wskazanego pliku wklejamy w pole tekstowe
$ cat /opt/tools/jenkins/secrets/initialAdminPassword
Setup wizard
Jeżeli zamkniemy powyższe okno
Ważne!
- Pominięto utworzenie konta administratora. Aby się zalogować, użyj loginu ‘admin’ i hasła użytego podczas konfigurowania Jenkinsa.
- Pominięto konfiguracje bazowego adresu URL Jenkinsa. Aby go skonfigurować, przejdź do strony
Zarządzaj Jenkinsem
.
Instalujemy sugerowane wtyczki
Admin page
Login: admin
Hasło: 12345678
Pełna nazwa: Administrator
Adres email: admin@acme.com
2: Katalog domowy
Instalacja via dnf
JENKINS_HOME=/var/lib/jenkins
logfile=/var/log/jenkins/jenkins.log
war=/usr/lib/jenkins/jenkins.war
Instalacja via jar
JENKINS_HOME=/home/jenkins/.jenkins
Struktura
├── config.xml (jenkins root configuration)
├── *.xml (other site-wide configuration files)
├── jenkins.sh (start stop script)
├── jenkins.war (jenkins war :))
├── log (task log directory)
├── logs (logs)
├── nodes (nodes configuration)
├── plugins (stores plugins)
├── secrets (secretes needed when migrating credentials to other servers)
├── ssh (ssl configuration)
├── updates (updates config)
├── userContent (files in this directory will be served under your http://server/userContent/)
├── users (users configuration)
│ ├── admin_4702638789067880202
│ └── users.xml
├── war (unpacked jenkins.war)
├── workflow-libs (shared library)
├── jobs (jobs configuration and builds)
│ ├── Job A
│ │ ├── builds
│ │ │ ├── 1
│ │ │ │ ├── build.xml
│ │ │ │ ├── changelog.xml
│ │ │ │ └── log
│ │ │ ├── 2
│ │ │ │ ├── build.xml
│ │ │ │ ├── changelog.xml
│ │ │ │ └── log
│ │ │ ├── legacyIds
│ │ │ └── permalinks
│ │ ├── config.xml
│ │ └── nextBuildNumber
│ └── restCounter
│ ├── builds
│ │ ├── 1
│ │ │ ├── build.xml
│ │ │ └── log
│ │ ├── 2
│ │ │ ├── build.xml
│ │ │ ├── changelog.xml
│ │ │ ├── log
│ │ │ └── pl.klimas7$restCounter -> ../../modules/pl.klimas7$restCounter/builds/2
│ │ ├── 3
│ │ │ ├── build.xml
│ │ │ ├── changelog.xml
│ │ │ ├── log
│ │ │ └── pl.klimas7$restCounter -> ../../modules/pl.klimas7$restCounter/builds/3
│ │ ├── legacyIds
│ │ └── permalinks
│ ├── config.xml
│ ├── modules
│ │ └── pl.klimas7$restCounter
│ │ ├── builds
│ │ │ ├── 2
│ │ │ │ ├── archive
│ │ │ │ │ └── pl.klimas7
│ │ │ │ │ └── restCounter
│ │ │ │ │ └── 0.0.1-SNAPSHOT
│ │ │ │ │ ├── restCounter-0.0.1-SNAPSHOT.jar
│ │ │ │ │ └── restCounter-0.0.1-SNAPSHOT.pom
│ │ │ │ ├── build.xml
│ │ │ │ ├── junitResult.xml
│ │ │ │ └── log
│ │ │ ├── 3
│ │ │ │ ├── archive
│ │ │ │ │ └── pl.klimas7
│ │ │ │ │ └── restCounter
│ │ │ │ │ └── 0.0.1-SNAPSHOT
│ │ │ │ │ ├── restCounter-0.0.1-SNAPSHOT.jar
│ │ │ │ │ └── restCounter-0.0.1-SNAPSHOT.pom
│ │ │ │ ├── build.xml
│ │ │ │ ├── junitResult.xml
│ │ │ │ └── log
│ │ │ ├── legacyIds
│ │ │ └── permalinks
│ │ ├── config.xml
│ │ └── nextBuildNumber
│ └── nextBuildNumber
└── workspace (working directory for the version control system)
├── Job A
│ └── test_job_a.txt
└── restCounter
├── pom.xml
├── README.md
├── restCounter.sh
├── src
└── target
2.1 Ćwiczenie: Zmiana liczby egzekutorów
$ cd /opt/tools/jenkins/
$ vim config.xml
<?xml version='1.1' encoding='UTF-8'?>
<hudson>
<disabledAdministrativeMonitors/>
<version>2.235.5</version>
<installStateName>RUNNING</installStateName>
<numExecutors>4</numExecutors> <!-- z 2 na 4 -->
<mode>NORMAL</mode>
<useSecurity>true</useSecurity>
<!-- ... -->
</hudson>
Jenkins -> Zarządzaj Jenkinsem -> Odczytaj ponownie konfigurację z dysku
vim:
insert
ori
switch to insert modeesc
switch to command mode:wq
write and quit
2.2 Ćwiczenie: Odzyskanie dostępu
$ cd /opt/tools/jenkins/
$ ./jenkins.sh stop
$ vim config.xml
<hudson>
<disabledAdministrativeMonitors/>
<version>2.235.5</version>
<installStateName>RUNNING</installStateName>
<numExecutors>4</numExecutors>
<mode>NORMAL</mode>
<useSecurity>false</useSecurity> <!-- z true na false -->
<authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy">
<denyAnonymousReadAccess>true</denyAnonymousReadAccess>
</authorizationStrategy>
<!-- ... -->
</hubson>
$ ./jenkins.sh start
Jenkins -> Zarządzaj Jenkinsem -> Konfiguruj ustawienia bezpieczeństwa
Jenkins -> Użytkownicy -> admin -> Konfiguracja
2.3: Ćwiczenie* UserContent
Jenkins -> Nowy Projekt -> Ogólny projekt (nazwa UserContent) -> OK
Budowanie -> Dodaj krok budowania -> Uruchom powłokę
echo "Test" > ../../userContent/${BUILD_TAG}.txt
Wynik http://192.168.0.178:8080/userContent/
Udostępnianie ‘linków’
$ cd /opt/tools/jenkins/
$ ln -s /opt/tools/maven userContent/maven
$ ./jenkins.sh stop
$ vim jenkins.sh
java_options="-Dhudson.model.DirectoryBrowserSupport.allowSymlinkEscape=true"
java -Dcom.sun.akuma.Daemon=daemonized -Djava.awt.headless=true -DJENKINS_HOME=$jh $java_options -jar $jh/jenkins.war $jenkins_options &
$ ./jenkins.sh start
3: Aktualizacja i instalacja wtyczek
Aktualizacja Jenkinsa dostępna ze strony głównej
lub Jenkins -> Zarządzaj Jenkinsem
Powrót do poprzedniej wersji Jenkins -> Zarządzaj Jenkinsem
Aktualizacja wtyczek Menedżer wtyczek
Jenkins -> Zarządzaj Jenkinsem -> Zarządzaj wtyczkami
Instalacja wtyczek spoza repozytorium
Jenkins -> Zarządzaj Jenkinsem -> Zarządzaj wtyczkami
Menedżer wtyczek -> Zaawansowane
Ćwiczenie 3.1: Aktualizacja
Zainstaluj aktualizacje Jenkinsa, wtyczek oraz dwie wtyczki spoza repozytorium
logger-plugin.hpi
old-data-plugin.hpi
4: Status Information
4.1: Ćwiczenie
Tworzymy 4 projekty (joby)
Jenkins -> Nowy Projekt -> Ogólny projekt (JobShedule[1..4])
Opcje
- Wykonuj zadania współbieżnie, jeśli zajdzie potrzeba
- Buduj cyklicznie
* * * * *
(co minute) Budowanie -> Uruchom powłoke
random=$(( ( RANDOM % 70 ) + 1 )) echo "Job sleep $random" sleep $random
Kolejne 3 można stworzyć używając funkcji Kopiuj z
Jenkins -> Nowy Projekt -> Kopiuj z
4.2: Informacje o systemie
Dostępne informacje:
- jvm properties
- system properties
- plugin version
- Memory Usage (new!)
- Thread dump
4.3: Informacje o Jenkinsie
Dostępne informacje:
- Zewnętrzne biblioteki oraz ich licencje
- Statyczne zasoby
- Wtyczki i ich licencje
4.4: Statystyki obciążenia
4.5: Dziennik systemowy
Źródła wtyczki logger-plugin
4.5.1: Ćwiczenie
Tworzymy projekt ogólny Logger
w którym użyjemy kroku budowania Logger example
Jenkins -> Nowy Projekt -> Ogólny projekt (Logger)
Budowanie -> Logger example
Zapisz, Uruchom
Sprawdzamy co otrzymaliśmy w logach
Jenkins -> Zarządzaj Jenkinsem -> Dziennik systemwy -> Wszystkie zdarzenia Jenkinsa
lis 13, 2020 6:24:18 PM INFO io.jenkins.plugins.LoggerBuilder printLog
Log: INFO code: 800
lis 13, 2020 6:24:18 PM SEVERE io.jenkins.plugins.LoggerBuilder printLog
Log: SEVERE code: 1000
lis 13, 2020 6:24:18 PM WARNING io.jenkins.plugins.LoggerBuilder printLog
Log: WARNING code: 900
Dodanie dedykowanego loggera
Jenkins -> Zarządzaj Jenkinsem -> Dziennik systemwy -> Dodaj nowy rejestrator logów
Nazwa: Logger
Loggers: Logger: io.jenkins.plugins
Log level: ALL
Save, Uruchom ponownie projekt Logger
w nowo dodanym logerze
lis 13, 2020 6:42:06 PM ALL io.jenkins.plugins.LoggerBuilder
Log: ALL code: -2147483648
lis 13, 2020 6:42:06 PM CONFIG io.jenkins.plugins.LoggerBuilder
Log: CONFIG code: 700
lis 13, 2020 6:42:06 PM FINE io.jenkins.plugins.LoggerBuilder
Log: FINE code: 500
lis 13, 2020 6:42:06 PM FINER io.jenkins.plugins.LoggerBuilder
Log: FINER code: 400
lis 13, 2020 6:42:06 PM FINEST io.jenkins.plugins.LoggerBuilder
Log: FINEST code: 300
lis 13, 2020 6:42:06 PM INFO io.jenkins.plugins.LoggerBuilder printLog
Log: INFO code: 800
lis 13, 2020 6:42:06 PM SEVERE io.jenkins.plugins.LoggerBuilder printLog
Log: SEVERE code: 1000
lis 13, 2020 6:42:06 PM WARNING io.jenkins.plugins.LoggerBuilder printLog
Log: WARNING code: 900
5: Troubleshooting
Jenkins -> Zarządzaj Jenkinsem -> Troubleshooting -> Zarządzanie starymi danymi
Mechanizm zabezpieczający Jenkinsa przed zmianami w konfiguracji np. wtyczek. Administrator powinien świadomie przeglądnąć zmiany i ewentualnie podjąć odpowiednie działania.
- wyciszyć powiadomienie.
- przywrócić poprzednią wersję wtyczki (jenkinsa).
Przykład zmian w pluginie:
Źródła wtyczki OldDataPlugin
Tworzymy projekt ogólny OldData
w którym użyjemy kroku budowania Old Data
Jenkins -> Nowy Projekt -> Ogólny projekt (OldData)
Budowanie -> Old Data
First, Second
mogą być dowolne
Save, Uruchom
Uruchomiono przez użytkownika Administrator
Running as SYSTEM
Building in workspace /opt/tools/jenkins/workspace/OldData
First: One Second: Two
Finished: SUCCESS
Instalujemy kolejną wersję wtyczki oldData old-data-plugin_v2.hpi
Materiały
Jenkins -> Zarządzaj Jenkinsem -> Zarządzaj wtyczkami -> Zawansowane -> Prześlij wtyczkę (old-data-plugin_v2.hpi)
Restart.
5.1: Ćwiczenie Wykonać powyższy przykład.
6: Skonfiguruj system
Miejsce w Jenkinsie gdzie znajdziemy globalne ustawienia samego Jenkinsa oraz wtyczek, zmienne środowiskowe …
Jenkins -> Zarządzaj Jenkinsem -> Skonfiguruj system
- Globalne ustawienia Maven
- Konfiguracja węzła głównego (liczba wykonawców, etykiety, plan wykorzystania)
- Okres ponowienia projektu, okres spokoju Quiet period
- Liczba powtórzeń przy próbie z systemu kontroli wersji (SCM checkout retry count)
- Jenkins Location (jenkins URL, System Admin e-mail addres)
- Serve resource files from another domain - wskazujemy adres zasobów z które Jenkins traktuje jako zaufane
- Globalne zmienne (Global properties)
- Lokalizacja narzędzi, duplikat
Jenkins -> Zarządzaj Jenkinsem -> Globalne narzędzia do konfiguracji
- Zmienne środowiskowe, możemy zdefiniować pary klucz-wartość dostępne w każdym projekcie
- Lokalizacja narzędzi, duplikat
- Pipeline Speed/Durability Settings Scaling Pipelines
- None: use pipeline default (MAX_SURVIVABILITY)
- Performance-optimized: much faster (requires clean shutdown to save running pipelines)
- Less durability, a bit faster (specialty use only)
- Maximum durability but slowest (previously the only option)
- Usage Statistics
- Timestamper
- Global Build Discarders (news)
- Specific Build Discarder
- Global Pipeline Libraries Shared Library
- Shell
- E-mail Notification
6.1: Ćwiczenie, Jenkins Location
Zdefiniować System Admin e-mail addres jenkins.klimas7@gmail.com
6.2: Ćwiczenie, Global properties
Dodać kilka zmiennych środowiskowych
STAGE_1:false
TEST_BRANCH:master
TEST_SCRIPT:Test_script
6.3: Ćwiczenie, Global Build Discarders
Ustawić globalny log Rotation Maksymalna ilość zadań do przechowania: 5
6.4: Ćwiczenie, E-mail Notification
Powiadomienia e-mail z wykorzystaniem konta google
SMTP server: smtp.gmail.com
Use SMTP Authentication: true
User Name: jenkins.klimas7@gmail.com
Password: <?>
Use SSL: true
Use TLS: true
SMTP Port: 465
Konto Google
6.5: Quiet period
Okres spokoju, cichy okres, czas w sekundach gdzie określamy ile projekt czeka z uruchomieniem po wyzwoleniu przez
- inny projekt
- zmiany w repozytorium
Przykład 1: inny projekt
Jenkins -> Nowy Projekt -> Ogólny projekt (QP_Parent)
Budowanie -> Uruchom powłokę
echo "Test" ${JOB_NAME} sleep 4
Akcje po zadaniu -> Uruchom inne zadania
Projects to buildQP_Child
Jenkins -> Nowy Projekt -> Ogólny projekt (QP_Child)
Zaawansowane (Opcje) -> Cichy okres -> 60
Uruchamiamy projekt QP_Parent
Jeżeli w tym czasie uruchomimy QP_Parent
kilka razy to projekt podrzędny z cichym okresem nie będzie uruchomiony, w informacji o wystartowaniu otrzymamy
Started by upstream project "QP_Parent" build number 6
originally caused by:
Uruchomiono przez użytkownika Jenkins Administrator
Started by upstream project "QP_Parent" build number 7
originally caused by:
Uruchomiono przez użytkownika Jenkins Administrator
Started by upstream project "QP_Parent" build number 8
originally caused by:
Uruchomiono przez użytkownika Jenkins Administrator
Started by upstream project "QP_Parent" build number 9
originally caused by:
Uruchomiono przez użytkownika Jenkins Administrator
Przykład 2: zmiany w repozytorium
7: Zarządzanie węzłami (nodes)
Dobre praktyki:
- Węzeł będący masterem powinien myć jak najmniej obciążony
- Liczba wykonawców (executors) powinna być dostosowana do zasobów danego węzła
Jenkins -> Zarządzaj Jenkinsem -> Zarządzaj węzłami
Dodając nowy węzeł mamy możliwość dodania kolejnego lub utworzenie kopii już istniejącego.
Plan wykorzystania:
- Wykorzystuj ten węzeł tak bardzo, jak to tylko możliwe
- Uruchamiaj te projekty, które mają etykietę pasującą do tego węzła
Metoda uruchomienia:
- Launch agent by connecting it to the master (JNLP)
- Launch agents via SSH
- Launch agent via execution of command on the master
Availability:
- Keep this agent online as much as possible
- Bring this agent online according to a schedule
- Bring this agent online when in demand, and take offline when idle
7.1 Uruchomienie via JNLP
JNLP
Java Network Launch Protocol
W pierwszej kolejności należy w ustawieniach bezpieczeństwa odblokować port dla agentów
Jenkins -> Zarządzaj Jenkinsem -> Konfiguruj ustawienia bezpieczeństwa -> Agents
TCP port for inbound agents -> Fixed: 8082
Dodatkowo port należy odblokować w zaporze sieciowej
firewall-cmd --permanent --zone=public --add-port=8082/tcp
systemctl restart firewalld
Jenkins -> Węzły -> Linux_2
$ java -jar agent.jar -jnlpUrl http://192.168.0.178:8080/computer/Linux_2/slave-agent.jnlp -secret f9b49c94b7e9cae76ffb0b63be0f34a9e7ac52c14aec447a2d27a90ced8180d3 -workDir "/tmp/linux_2"
lis 15, 2020 11:52:30 PM org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
INFO: Using /tmp/linux_2/remoting as a remoting work directory
lis 15, 2020 11:52:30 PM org.jenkinsci.remoting.engine.WorkDirManager setupLogging
INFO: Both error and output logs will be printed to /tmp/linux_2/remoting
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main createEngine
INFO: Setting up agent: Linux_2
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener <init>
INFO: Jenkins agent is running in headless mode.
lis 15, 2020 11:52:30 PM hudson.remoting.Engine startEngine
INFO: Using Remoting version: 4.5
lis 15, 2020 11:52:30 PM org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
INFO: Using /tmp/linux_2/remoting as a remoting work directory
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Locating server among [http://192.168.0.178:8080/]
lis 15, 2020 11:52:30 PM org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver resolve
INFO: Remoting server accepts the following protocols: [JNLP4-connect, Ping]
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Agent discovery successful
Agent address: 192.168.0.178
Agent port: 8082
Identity: c9:b8:2c:81:18:d8:6a:ef:19:e8:d8:91:84:7a:ba:64
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Handshaking
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Connecting to 192.168.0.178:8082
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Trying protocol: JNLP4-connect
lis 15, 2020 11:52:30 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Remote identity confirmed: c9:b8:2c:81:18:d8:6a:ef:19:e8:d8:91:84:7a:ba:64
lis 15, 2020 11:52:31 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Connected
Odnośniki: java11-preview-availability, JENKINS-52282, Jenkins PR
7.3 Node Monitoring
7.4 Uruchomienie via SSH, Ćwiczenie
Utworzyć 2 dodatkowe węzły na naszej wirtualnej maszynie przy wykorzystaniu ssh
- Linux_1; etykiety: linux big_ram; katalog: /work/node1
- Windows, etykiety: windows; katalog: /work/node2
#Tworzymy katalogi dla naszych węzłów
$ mkdir -p /work/node{1,2}
Tworzymy pierwszy węzeł
Tworzymy drugi jako kopię pierwszego
Z odpowiednimi modyfikacjami
8: Globalne narzędzia do konfiguracji
8.1: Managed files
Managed files
to miejsce, w którym możemy definiować pliki konfiguracyjne dostępne dla wszystkich węzłów i projektów
8.2: Globalne narzędzia do konfiguracji
Jenkins pozwala zdefiniować narzędzia wykorzystywane w projektach, takie jak maven, java, docker.
Jenkins -> Zarządzaj Jenkinsem -> Globalne narzędzia do konfiguracji
Maven Configuration
JDK
Nazwa jdk8
JAVA_HOME /opt/java/jdk8
Nazwa jdk12
JAVA_HOME /opt/java/jdk12
Maven
Name mvn
MAVEN_HOME /opt/tools/maven
Docker
Name docker
Installation root /usr/bin
8.2: Globalne narzędzia do konfiguracji dla węzłów
8.3: Ćwiczenie
Zdefiniować jdk, maven oraz docker zgodnie z powyższym opisem
9: Konfiguruj ustawienia bezpieczeństwa
Jenkins -> Zarządzaj Jenkinsem -> (Security) -> Konfiguruj ustawienia bezpieczeństwa
9.1 Authentication
- Własna baza danych Jenkinsa
- LDAP
9.2 Authorization
- Każdy użytkownik może wszystko
- Logged-in users can do anything
- Matrix-based security
- Project-based Matrix Authorization Strategy
Uwaga W przypadku Matrix-based security
oraz Project-based Matrix Authorization Strategy
nie jest możliwe permanentnie
odebrać roli Administrator, jest ona przywracana ostatniej grupie lub użytkownikowi, który ją posiadał.
9.3 Agents
Definicja portu, z którego będą korzystać agenci do komunikacji z Jenkinsem
Uruchomienie via JNLP
9.4 SSH Server
Definicja portu dla serwera ssh wbudowanego w Jenkinsa wykorzystywany do Jenkins CLI
SSH
9.5 Ćwiczenie 1
Autentykacja za pomocą LDAP (Lightweight Directory Access Protocol). Nasz LDAP UI
Server: ovh.klimas7.pl
root DN: dc=acme,dc=com
User search filter: uid={0}
Group membership: Search for LDAP groups containing user
Manager DN: cn=admin,dc=acme,dc=com
Manager Password: <?>
Display Name LDAP attribute: displayname
Email Address LDAP attribute: mail
LDAP Hints!
- użytkownicy: jadmin, bklimas, jwajda, jzmarz, pkocot, rpaliga, mrawski, gwawryk, amarchut, pzadrozny, akalynovska, asemenkov, mkajdanska, mchernetskyi, upietrusa
- każdy ma hasło
<?>
- grupy: admin, dev, git, qa
- jadmin (Jenkins Administrator) należy tylko do grupy admin
- bklimas (Boguslaw Klimas) nie należy do grupy admin
- Pozostali użytkownicy należą do wszystkich grup
Test: Test LDAP settings
9.6 Ćwiczenie 2
Zalogowany użytkownik może wszystko, ananimowy użytkownik ma mieć możliwość odczytu
Authorization -> Logged-in users can do anything -> Allow anonymous read access
9.7 Ćwiczenie 3
Przykład z ćwiczenia 2 zrealizować za pomocą Matrix-based security
9.8 Ćwiczenie 4
Hint!
- Uprawnienia są sumą uprawnień danego użytkownika i grup, do których należy
Ćwiczenie:
- użytkownik bklimas powinien mieć dostęp tylko do projektów (uruchomienie i podgląd)
- użytkownik jadmin powinien mieć uprawnienia administratora
- wasz użytkownik (np. jzmarz) powinien mieć uprawnienia administratora
- Anonimowy użytkownik nie powinien mieć dostępu
- Inny zalogowany użytkownik powinien mieć tylko odczyt
9.9 Ćwiczenie 5
Wykujemy ćwiczenie 4 w kontekście Project-based Matrix Authorization Strategy
Dodajemy nowy projekty (nie musi robić nic)
Jenkins -> Nowy Projekt -> Ogólny projekt (Matrix_1)
Przy aktywowanym Project-based Matrix Authorization Strategy
w każdym nowym projekcie mamy dostępną opcję Enable project-based security
gdzie możemy określić konkretne uprawnienia w donośnie do konkretnego projektu
Hint!
- Mamy możliwość określenia strategi dziedziczenia uprawnień
Inheritance Strategy
Dla projektu matrix_1
zdefiniować następujące zasady bezpieczeństwa
- Anonimowy użytkownik powinien mieć odczyt
- Inny zalogowany użytkownik powinien mieć możliwość odczyt, jak i uruchomienia
- użytkownik bklimas powinien mieć pełen dostęp do tego projektu
10: Konsola skryptów
Kolejnym z przydatnych narzędzi dostępnych w Jenkinsie jest konsola skryptów.
Jenkins -> Zarządzaj Jenkinsem -> (Tools and Actions) -> Konsola skryptów
// Wyświetlenie dostępnych wtyczek
println(Jenkins.instance.pluginManager.plugins)
// Pobranie treści wybranej strony
println 'http://www.google.com'.toURL().text
// Wypisanie wszystkich zmiennych globalnych na masterze i agentach
import jenkins.*
import jenkins.model.*
import hudson.*
import hudson.model.*
jenkins = Jenkins.getInstance()
nodeProperties = jenkins.getGlobalNodeProperties()
props = nodeProperties.getAll(hudson.slaves.EnvironmentVariablesNodeProperty.class)
for (prop in props) {
prop.getEnvVars().each{ println "${it}"; }
}
//Agents
slaves = Jenkins.getInstance().slaves
for (slave in slaves) {
props = slave.nodeProperties.getAll(hudson.slaves.EnvironmentVariablesNodeProperty.class)
println slave.name
for (prop in props) {
prop.getEnvVars().each{ println "${it}"; }
}
}
println "OK";
//Wypisanie tych projektów które nie mają ustawionego logRotator
jenkins = Jenkins.getInstance()
def jobs = jenkins.getItems(hudson.model.FreeStyleProject.class) //hudson.maven.MavenModuleSet.class
jobs.findAll{ !it.logRotator}.each {
println it.name;
}
return "OK"
//Dodanie zmiennych globalnych
import jenkins.*
import jenkins.model.*
import hudson.*
import hudson.model.*
def addVariable(String key, String value, Map envVars){
String oldValue = envVars.get(key);
if (oldValue != null) {
println "Global variable exists: " + key + " -> " + oldValue + " Update this!";
}
envVars.put(key, value)
println "Add/Update global variable: " + key + " -> " + value;
}
jenkins = Jenkins.getInstance()
nodeProperties = jenkins.getGlobalNodeProperties()
props = nodeProperties.getAll(hudson.slaves.EnvironmentVariablesNodeProperty.class)
if ( props.size() != 1 ) {
println("error: unexpected number of environment variable containers: " + props.size() + " expected: 1")
} else {
envVars = props.get(0).getEnvVars();
addVariable("TEST_SCRIPT", "Test_script", envVars)
jenkins.save();
}
println "OK";
//Ustawienie logRotator w nie mają logRotator
import hudson.tasks.*
jenkins = Jenkins.getInstance()
def jobs = jenkins.getItems(hudson.model.FreeStyleProject.class) //hudson.maven.MavenModuleSet.class
jobs.findAll{ !it.buildDiscarder}.each {
println it.name;
println it.setBuildDiscarder(new LogRotator(-1, 20, -1, -1) )
it.save()
}
return "OK"
10.1: Ćwiczenie, wypróbować powyższe przykłady
11: CLI
CLI
Command Line Interface.
Dostęp do Jenkinsa z linii komend można zrealizować na 2 sposoby:
- ssh
- Klient dostarczony w postaci pliku jenkins-cli.jar
11.1: ssh
W pierwszej kolejności należy odblokować serwer ssh wbudowany w Jenkinsa (np. na porcie 8081).
Jenkins -> Zarządzaj Jenkinsem -> (Security) -> Konfiguruj ustawienia bezpieczeństwa -> SSH Server
Wybrany port należy odblokować w firewallu.
# firewall-cmd --permanent --zone=public --add-port=8081/tcp
# systemctl restart firewalld
Na naszym lokalnym komputerze możemy wykonać
$ ssh -l admin -p 8081 192.168.0.178 help
## Jendak w wyniku najprawdopodobniej otrzymamy
admin@192.168.0.178: Permission denied (publickey).
Nasz klucz publiczny należy dodać do listy kluczy wybranego użytkownika. W naszym przypadku może to być admin.
#Geracja kluczy rsa jeżeli wcześniej nie mieliśmy
$ ssh-keygen
$ ll ~/.ssh/
razem 12
-rw-------. 1 jenkins jenkins 1843 11-15 10:08 id_rsa
-rw-r--r--. 1 jenkins jenkins 411 11-15 10:08 id_rsa.pub
-rw-r--r--. 1 jenkins jenkins 571 11-15 09:55 known_hosts
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzsDiyJ+4WZAwaYDVz43Zrm6RCxPTzFWXnUfuBdAx6BmM98gxnveU0k+AbiiF739ZTDKxmWHyBmgGKVMzrSYoldKAPvTii4X/FtQfdsUrvl3gL9E+UM2MJ3k+yaYS88RmfDH7TTYBAwvFYqP3+F7F9tX+Te2Purf/ZwU+r5ekPMP4gtkO9Pi1XtzdOAW2vDHrXm2QJqOQPa8Ppx6SalrLOnlCjo5RtpEIBhjTN3WkwhRYHxDZedTZE0YFyn8fNna4UlZiEhPaLRu6utykDTyfOOhFiONYj9naNzv0/wIAYgYAFreD5CCNZEVMKIYwuKlS9Tb0gyzpDQYBjNGAvks2v jenkins@localhost.localdomain
Jenkins -> Użytkownicy -> admin -> Konfiguracja -> SSH Public Keys
Hint: Możemy dodać dowolną ilość kluczy
$ ssh -l admin -p 8081 192.168.0.178 help
add-job-to-view
Adds jobs to view.
build
Builds a job, and optionally waits until its completion.
11.2: jenkins-cli.jar
Pobieramy jenkins-cli.jar
ze strony naszego Jenkinsa
Jenkins -> Zarządzaj Jenkinsem -> (Tools and Actions) -> Wiersz poleceń Jenkinsa
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/
ERROR: You must authenticate to access this Jenkins.
Jenkins CLI
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:12345678
add-job-to-view
Adds jobs to view.
build
Builds a job, and optionally waits until its completion.
Jeżeli nie chcemy używać hasła możemy użyć tokena przypisanego do danego użytkownika
Jenkins -> Użytkownicy -> admin -> Konfiguracja -> API Token
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 help
add-job-to-view
Adds jobs to view.
build
Builds a job, and optionally waits until its completion.
# W tym przypadku musimy mieć zarejestrowany klucz publiczny
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -ssh -user admin
lis 15, 2020 6:41:35 PM io.jenkins.cli.shaded.org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar getOrCreateProvider
INFO: getOrCreateProvider(EdDSA) created instance of io.jenkins.cli.shaded.net.i2p.crypto.eddsa.EdDSASecurityProvider
add-job-to-view
Adds jobs to view.
build
Builds a job, and optionally waits until its completion.
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -ssh -user admin list-jobs
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -ssh -user admin get-job 'Job A'
tworzymy nowy job CLI_Invoke
na podstawie pustego szablonu (tworzymy plik template_job.xml)
<?xml version='1.1' encoding='UTF-8'?>
<!-- template_job.xml -->
<project>
<description></description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.plugins.jira.JiraProjectProperty plugin="jira@3.1.3"/>
</properties>
<scm class="hudson.scm.NullSCM"/>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers/>
<concurrentBuild>false</concurrentBuild>
<builders/>
<publishers/>
<buildWrappers/>
</project>
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 create-job CLI_Invoke < template_job.xml
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 build CLI_Invoke
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 console CLI_Invoke 1
<?xml version='1.1' encoding='UTF-8'?>
<!-- update_job.xml -->
<project>
<actions/>
<description></description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.plugins.jira.JiraProjectProperty plugin="jira@3.1.3"/>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.StringParameterDefinition>
<name>param</name>
<description></description>
<defaultValue>default value</defaultValue>
<trim>false</trim>
</hudson.model.StringParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
<scm class="hudson.scm.NullSCM"/>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers/>
<concurrentBuild>false</concurrentBuild>
<builders>
<hudson.tasks.Shell>
<command>echo "${param}"</command>
<configuredLocalRules/>
</hudson.tasks.Shell>
</builders>
<publishers/>
<buildWrappers/>
</project>
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 update-job CLI_Invoke < update_job.xml
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 build CLI_Invoke
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 console CLI_Invoke 2
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 build CLI_Invoke -p param='111 222'
$ java -jar jenkins-cli.jar -s http://192.168.0.178:8080/ -auth admin:116f410f45f00c7b533ddc889e978fec37 console CLI_Invoke 3
11.3: curl
Dodatkowo cześć rzeczy możemy wykonać wywołując odpowiedni url
curl -X POST 'http://admin:116f410f45f00c7b533ddc889e978fec37@192.168.0.178:8080/job/CLI_Invoke/buildWithParameters?param=abc'
11.4: Ćwiczenie.
Na podstawie powyższych informacji stworzyć nowy projekt CLI_Invoke
wykonać jego aktualizacje oraz uruchomić z parametrami, jak i bez.
12: Rest and xml api
Praktycznie każdą informację, którą mamy prezentowaną w Jenkinsie możemy również zaprezentować w formacie json
lub xml
Przykłady
13: Ogólny projekt (Freestyle project)
Najbardziej podstawowy typ projektu dostępny w Jenkinsie. Możemy w nim wykonać cokolwiek.
Oficjalny opis: To jest podstawowa funkcja Jenkinsa. Jenkins stworzy projekt łączący dowolny SCM z dowolnym systemem budującym, może to być również wykorzystane do czegoś innego niż budowanie oprogramowania.
najtrafniej oddaje istotę projektu.
Instalujemy wtyczki: Active Choices, Git Parameter, Conditional BuildStep, Parameterized Trigger
13.1 Istotne opcje
- Enable project-based security, omawiane w punkcie Project-based Matrix Authorization Strategy
- Porzuć stare zadania,
Strategi -> Log Rotation -> Maksymalna ilość zadań do przechowania -> 5
decydujemy, ile wykonań jest przechowywane wstecz. Jenkins pozostawi jedno prawidłowe wykonanie, jeżeli istnieje. Należy pamiętać, że wykonania usuwane są dopiero po skończonym wykonaniu projektu. - This build requires lockable resources
- Throttle builds, określenie jak często dany projekt może być wykonany np. nie częściej niż raz na godzinę Throttle builds
- To zadanie jest sparametryzowane Warto zwrócić uwagę
- Tekst
- Wartość logiczna
- Lista wyboru
- …
- Active Choice*
- Git parameter*
- Zablokuj zadania, oznacza, że zadanie nie będzie mogło być uruchomione przez użytkownika ani wyzwolone przez zmiany w repozytorium
- Wykonuj zadania współbieżnie, jeśli zajdzie potrzeba
- Restrict where this project can be run, określa etykiety agentów, na których dany projekt może być wykonany
- Cichy okres, możliwość nadpisania ustawień globalnych Skonfiguruj system
- Liczba ponowień, możliwość nadpisania ustawień globalnych Skonfiguruj system
13.2 Repozytorium kodu
- Git
- Additional Behaviours, Advanced checkout behaviours, Advanced clone behaviours
13.3 Wyzwalacze zadania
- Buduj cyklicznie
- Pobierz z repozytorium kodu (Pool SCM)
- Uruchamiaj, gdy inne zadania zostaną zakończone
13.4 Budowanie
- Invoke top-level Maven targets
- Uruchom powłokę (list of available environment variables)
- Conditional step
13.5 Akcje po zadaniu
- Zachowaj artefakty
- E-mail Notification
- Publish JUnit test result report
- Uruchom inne zadania
13.6 Ćwiczenie 1
- Tworzymy dwa ogólne projekty
Build, Deploy
- Projekt
Deploy
- powinien przetrzymywać 10 ostatnich wykonań
- powinien mieć parametr tekstowy o nazwie
info
(domyślnie pusty) - jako krok wykonania powinien wypisać wartość parametru
info
, nazwę projektu, numer kompilacji oraz build tag - w kolejnym kroku powinien poczekać
10 s
- Projekt
Build
- powinien przetrzymywać 10 ostatnich wykonań
- powinien zablokować dedykowany sobie zasób
- powinna być możliwość uruchomienia projektu współbieżnie
- powinien mieć parametr logiczny o nazwie
deploy
(domyślniefalse
) - powinien mieć parametr tekstowy o nazwie
info
(domyślniedeploy information
) - powinien być uruchomiony na agencie z etykietą
linux
- powinien być uruchomiany cyklicznie co godzinę
- jako krok wykonania powinien wypisać wartość parametru
deploy
, nazwę projektu, numer wykonania oraz build tag - w kolejnym kroku powinien poczekać
10 s
- jeżeli zaznaczono
deploy
to powinna być wypisana informacjaDeploy will be invoke
oraz wartość parametru info - jeżeli zaznaczono
deploy
powinien zostać wywołany projektDeploy
z przekazaniem wszystkich parametrów - Należy poczekać na zakończenie projektu
deploy
-
Deploy
Build
13.7 Ćwiczenie 2
- Tworzymy ogólne projekty
Build_Learn
- Projekt
Build_Learn
- powinien przetrzymywać 10 ostatnich wykonań
- powinna być możliwość wyboru gałęzi, z której będzie kompilowany projekt, nazwa parametru
branch
typBranch
wartość domyślnaorigin/master
- z cichym okresem
60 s
- liczba prób pobrania z repozytorium
3
- Repozytorium kodu: Git, Repository URL
ssh://git@ovh.klimas7.pl:7999/lab/learn.git
(waga musimy mieć wygenerowany klucz i dodany klucz publiczny do bitbucketa) - Branch Specifier odczytywany z parametru
branch
- Repository browser:
bitbucketweb
URLhttp://ovh.klimas7.pl/bitbucket/projects/LAB/repos/learn
- Projekt powinien sprawdzać zmiany w kodzie co 5 minut
*/5 * * * *
i wykonywać kompilację, jeśli zmiany zostały wprowadzone - Post-commit hooks powinny być ignorowane
- Krokiem budowania powinien być
Invoke top-level Maven targets
z wersją mavenamvn
oraz celamiclean install
- Wygenerowany artefakt (Learn.*.jar) powinien być archiwizowany
- Powinny być zachowywane wyniki testów
- Prawidłowy build powinien zostać zachowany i oznaczony jako
RC #1
Build_Learn
13.8 lockable resources*
Definicja Jenkins -> Zarządzaj Jenkinsem -> (System Configuration) -> Skonfiguruj system -> Lockable Resources Manager
Resource:
Name: Test_Resources
Description: Example Test Resources
Labels: test_resouce
Reserved by:
Monitoring Jenkins -> Lockable Resources
Dodajemy konfiguracje w projekcie
Uruchomiono przez użytkownika Jenkins Administrator
Running as SYSTEM
[lockable-resources] acquired lock on [Test_Resources]
Building on master in workspace /opt/tools/jenkins/workspace/Job_A
Jeżeli zasoby będą zablokowane to przy uruchomieniu projektu otrzymamy następujący komunikat
13.9 Throttle builds*
Definiujemy w projekcie
Przy próbie wcześniejszego uruchomienia projektu lub jego wyzwolenia otrzymamy
13.10 Dodatkowe parametry*
Active Choices
To zadanie jest sparametryzowane -> Active Choices Parameter
Name: param_a
Script: Groovy Script
Groovy Script: return ['a','b','c']
Choice Type: Single Select
To zadanie jest sparametryzowane -> Active Choices Reactive Parameter
Name: param_b
Script: Groovy Script
Groovy Script: def map = ['a': ['a1','a2','a3'],'b': ['b1','b2','b3'],'c': ['c1','c2','c3']]
return map[param_a]
Choice Type: Single Select
Referenced parameters: param_a
Git Parameter
Na podstawie zdefiniowanego repozytorium git wtyczka umożliwia wybranie tag, branch, branch lub tag, Revision, Pull Request
14: Maven project
W porównaniu z ogólnym projektem Maven project
ma odgórnie zdefiniowany główny krok budowania oraz kroki, które można wykonać przed (pre) jako i po (post).
14.1 Ćwiczenie
Dla projektu typu maven wykonać 13.7 Ćwiczenie 2
Hint!
- Wyniki testów są dostępne automatycznie
- W projekcie typu maven mamy dostępne moduły, w których przetrzymywane są artefakty
- Jeżeli projekt się kompiluje, ale w testy wykazują błędy wykonanie oznaczane jest jako niestabilne
15: Pipelines
Your Job is Your Code
Start:
- Local pipeline syntax: http://{jenkins_host}:{jenkins_port}/job/{job_name}/pipeline-syntax/
- Documentation: https://www.jenkins.io/doc/book/pipeline/
15.1: Ćwiczenie, Zainstalować wtyczki Blue Ocean
Blue Ocean (BlueOcean Aggregator)
15.2: Scripted vs Declarative Pipeline
- Groovy jako podstawa
- Brak funkcjonalnych różnic, w obu podejściach można zrealizować te same funkcjonalności.
- Próg wejścia w podejściu Declarative jest dużo mniejszy, główny powód, dla którego został wprowadzony.
- Scripted jest dużo bardziej elastyczny.
- Declarative jest bardziej formalny.
- Declarative walidowany jest na starcie*.
15.3: First pipeline!
Jenkins -> Nowy Projekt -> Pipeline (P_1)
properties([
parameters([
string(defaultValue: 'test_a', description: '', name: 'TEST', trim: false)
])
])
node {
stage('First stage') {
echo "Sii Power People"
}
stage("Environments") {
echo env.PATH
echo env.BUILD_ID
echo env.JOB_URL
}
stage('Parameters') {
echo params.TEST
}
stage('currentBuild') {
echo currentBuild.displayName
echo currentBuild.currentResult
//error 'Cos poszło nie tak'
}
}
Jenkins -> Nowy Projekt -> Pipeline (P_2)
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building..'
}
}
stage('Test') {
steps {
echo 'Testing..'
}
}
stage('Deploy') {
steps {
echo 'Deploying....'
}
}
}
}
W przypadku deklaratywnego stylu mamy do dyspozycji ponowne uruchomienie pipelinu z dowolnego etapu.
Jenkins -> Nowy Projekt -> Pipeline (P_(3,4))
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building..'
}
}
stage('Test') {
steps {
echo 'Testing..'
//echo 1
//message 'Test'
}
}
}
}
//vs
node {
stage('Build') {
echo "Build ..."
}
stage("Test") {
echo "Testing .."
//echo 1
//message 'Test'
}
}
Declarative owszem jest walidowany na starcie, ale nie każdy przypadek.
15.4 Agent
any
pipeline może wykonany na dowolnym dostępnym agencie zgodnie z ustalonymi regułaminone
pipeline wykonywany jest na żadnym agencie
Jenkins -> Nowy Projekt -> Pipeline (P_5)
pipeline {
agent none
stages {
stage('Build') {
steps {
echo 'Building..'
}
}
}
}
//next
pipeline {
agent none
stages {
stage('Build') {
steps {
echo 'Building..'
sh 'touch test.txt' //! próba utworzenia pliku spowoduje błąd
}
}
}
}
Jeżeli w pipeline nie jest wykonywana żadna interakcja z agentem (zapis, odczyt itp.) wtedy taki pipeline może być uruchomiony z sukcesem
docker
jako agent użyty jest dowolny obraz dockerowy
Jenkins -> Nowy Projekt -> Pipeline (P_6)
pipeline {
agent {
docker {
image 'maven:3-alpine'
}
}
stages {
stage('Build') {
steps {
sh 'mvn -v'
}
}
}
}
//next
pipeline {
agent {
docker {
image 'maven:3.6.0-jdk-12-alpine'
args '-v $HOME/.m2:/root/.m2'
}
}
stages {
stage('Git'){
steps {
git branch: 'master', url: 'https://github.com/klimas7/restCounter.git'
}
}
stage('Build') {
steps {
sh 'mvn clean install'
}
}
}
}
label
agent przydzielany zgodnie z etykietą
Jenkins -> Nowy Projekt -> Pipeline (P_7)
pipeline {
agent {
label 'Linux_1'
}
stages {
stage('Build') {
steps {
echo 'Building..'
}
}
}
}
15.5: Tools
Jenkins -> Nowy Projekt -> Pipeline (P_8)
pipeline {
agent any
tools {
maven 'mvn'
jdk 'jdk8'
}
stages {
stage('Stag1'){
steps {
sh 'java -version'
sh 'mvn -version'
}
}
}
}
//vs
pipeline {
agent any
tools {
maven 'mvn'
jdk 'jdk8'
}
stages {
stage('Stage 1'){
steps {
sh 'java -version'
}
}
stage('Stage 2'){
tools {
jdk 'jdk12'
}
steps {
sh 'java -version'
}
}
}
}
Jenkins -> Nowy Projekt -> Pipeline (P_8_a)
node {
stage("Stage 1") {
def javaHome = tool name: 'jdk12', type: 'jdk'
sh "${javaHome}/bin/java -version"
def mavenHome = tool name: 'mvn', type: 'maven'
sh "${mavenHome}/bin/mvn -version"
withEnv(["PATH+JAVA=${tool name: 'jdk12', type: 'jdk'}/bin",
"PATH+MAVEN=${tool name: 'mvn', type: 'maven'}/bin"]) {
sh 'java -version'
sh 'mvn -version'
}
}
}
15.6: Options
Jenkins -> Nowy Projekt -> Pipeline (P_9)
pipeline {
agent any
tools {
jdk 'jdk8'
}
options {
timestamps()
buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '2')
timeout(time: 15, unit: 'SECONDS')
}
stages {
stage('Stag1'){
options {
timeout(time: 10, unit: 'SECONDS')
}
steps {
sleep 9
}
}
stage('Stag2'){
steps {
sleep 15
}
}
}
}
Jenkins -> Nowy Projekt -> Pipeline (P_9_a)
properties([
buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '2'))
])
node {
timestamps {
timeout(time: 15, unit: 'SECONDS') {
stage("Stage 1") {
def javaHome = tool name: 'jdk12', type: 'jdk'
sh "${javaHome}/bin/java -version"
timeout(time: 10, unit: 'SECONDS') {
sleep 9
}
}
stage("Stage 2") {
echo "Stage 2"
sleep 3
}
}
}
}
15.7: Triggers
Jenkins -> Nowy Projekt -> Pipeline (P_10)
pipeline {
agent any
triggers {
cron '* * * * *'
upstream 'p_9'
}
stages {
stage('Stage 1') {
steps {
echo 'Stage 1'
sleep 10
}
}
}
}
Jenkins -> Nowy Projekt -> Pipeline (P_10_a)
properties([
pipelineTriggers([
cron('* * * * *'),
upstream('p_9')
])
])
node {
stage('Stage 1') {
echo "Stage 1"
sleep 10
}
}
15.8: Condition
Dodać Global properties
STAGE_1: false
Jenkins -> Nowy Projekt -> Pipeline (P_11)
pipeline {
agent any
stages {
stage('Stage 1') {
when {
environment name: 'STAGE_1', value: 'true'
}
steps {
echo 'Stage 1'
sleep 10
}
}
}
}
Jenkins -> Nowy Projekt -> Pipeline (P_11_a)
pipeline {
agent any
parameters {
booleanParam defaultValue: false, description: 'Czy coś ma się deployować', name: 'deploy'
}
stages {
stage('Build') {
steps {
echo 'Building ...'
}
}
stage('Test') {
steps {
echo 'Test ...'
unstable 'Cos poszło nie najlepiej'
}
}
stage('Deploy') {
when {
expression {params.deploy && currentBuild.currentResult == "SUCCESS" }
}
steps {
echo 'Deploying ...'
}
}
}
}
Jenkins -> Nowy Projekt -> Pipeline (P_11_b)
node {
stage('Stage 0') {
echo env.STAGE_1
}
if (env.STAGE_1 == "true") {
stage('Stage 1') {
echo "Stage 1"
sleep 10
}
}
}
Jenkins -> Nowy Projekt -> Pipeline (P_11_c)
properties([
parameters([
booleanParam(defaultValue: false, description: 'Czy coś ma się deployować', name: 'deploy')
])
])
node {
stage('Build') {
echo 'Building ...'
}
stage('Test') {
echo 'Test ...'
//unstable 'Cos poszło nie najlepiej'
}
if (params.deploy && currentBuild.currentResult == "SUCCESS") {
stage('Deploy') {
echo 'Deploying ...'
}
}
}
15.9: Parallel
Jenkins -> Nowy Projekt -> Pipeline (P_12)
pipeline {
agent none
stages {
stage('Run Tests') {
parallel {
stage('Test On Windows') {
agent {
label 'windows'
}
steps {
echo "Test on windows"
sleep 30
}
}
stage('Test On Linux') {
agent {
label "linux"
}
steps {
echo "Test on linux"
sleep 30
}
}
}
}
}
}
Jenkins -> Nowy Projekt -> Pipeline (P_12_a)
pipeline {
agent none
stages {
stage('Run Tests') {
parallel {
stage('Test On Windows') {
agent any
steps {
echo "Test on windows"
sleep 2
echo "currentResult: " + currentBuild.currentResult
echo "result: " + currentBuild.result
}
}
stage('Test On Linux') {
agent {
label "linux"
}
steps {
echo "Test on linux"
sleep 5
echo "currentResult: " + currentBuild.currentResult
echo "result: " + currentBuild.result
//error 'error'
//unstable 'unstable'
}
}
}
}
stage('Deploy') {
agent any
steps {
echo 'Deploy ...'
echo "currentResult: " + currentBuild.currentResult
echo "result: " + currentBuild.result
}
post {
always {
echo "currentResult: " + currentBuild.currentResult
echo "result: " + currentBuild.result
}
}
}
}
}
Jenkins -> Nowy Projekt -> Pipeline (P_12_b)
stage('Run Tests') {
parallel linux: {
node('linux') {
stage('Test On Linux') {
echo "Test on linux"
sleep 30
}
}
},
windows: {
node('windows') {
stage('Test On Windows') {
echo "Test on windows"
sleep 30
}
}
}
}
15.10: In-process Script Approval
Jenkins -> Nowy Projekt -> Pipeline (P_13)
pipeline {
agent {
label 'Linux_1'
}
stages {
stage('Build') {
steps {
script {
readFile()
}
}
}
}
}
def readFile() {
def fileContents = new File('/work/tag.txt').text
echo fileContents
}
[Pipeline] {
Scripts not permitted to use new java.io.File java.lang.String. Administrators can decide whether to approve or reject this signature.
[Pipeline] }
Jenkins -> Zarządzaj Jenkinsem -> In-process Script Approval
15.11: Walidacja
ssh -l admin -p 8081 192.168.0.178 declarative-linter < Jenkinsfile
15.12: try-catch
Jenkins -> Nowy Projekt -> Pipeline (P_14)
pipeline {
agent any
stages {
stage ('Build & Test') {
steps {
sh 'echo Building... Failure here will fail the build'
script {
try {
echo 'Running tests...'
sh 'exit 1'
}
catch (exc) {
echo "Message: " + exc.getMessage()
echo 'Testing failed!'
currentBuild.result = 'UNSTABLE'
}
}
}
}
}
}
15.13: RestCounter project
W pierwszej kolejności na naszej wirtualnej maszynie musimy otworzyć port 8088
w zaporze sieciowej
# as root
firewall-cmd --permanent --zone=public --add-port=8088/tcp
systemctl restart firewalld
firewall-cmd --list-all
Jenkins -> Nowy Projekt -> Pipeline (RestCounter_pipeline)
pipeline {
agent any
tools {
maven 'mvn'
jdk 'jdk8'
}
stages {
stage('Git'){
steps {
git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
}
}
stage ('Build') {
steps {
sh 'mvn clean install'
}
post {
success {
archive 'target/*.jar'
junit 'target/surefire-reports/*.xml'
}
}
}
}
}
Dodajemy opcje deploy
wraz z etapem Deploy
pipeline {
agent any
parameters {
booleanParam defaultValue: true, description: 'Deploy and run application', name: 'deploy'
}
tools {
maven 'mvn'
jdk 'jdk8'
}
stages {
stage('Git'){
steps {
git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
}
}
stage ('Build') {
steps {
sh 'mvn clean install'
}
post {
success {
archive 'target/*.jar'
junit 'target/surefire-reports/*.xml'
}
}
}
stage ('Deply') {
when {
expression { params.deploy }
}
steps {
echo "Deploy application"
}
}
}
}
Rozbudowujemy etap Deploy
def applicationDir = '/opt/tools/restCounter'
pipeline {
parameters {
booleanParam defaultValue: true, description: 'Deploy and run application', name: 'deploy'
}
agent any
tools {
maven 'maven'
jdk 'java8'
}
stages {
stage('Git'){
steps {
git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
}
}
stage ('Build') {
steps {
sh 'mvn clean install'
}
post {
success {
archive 'target/*.jar'
junit 'target/surefire-reports/*.xml'
}
}
}
stage ('Deply') {
when {
expression { params.deploy }
}
steps {
echo "Deploy application"
sh "mkdir -p ${applicationDir}"
sh "cp target/*.jar ${applicationDir}"
sh "cp restCounter.sh ${applicationDir}"
}
}
}
}
$ ll restCounter/
razem 17516
-rw-rw-r--. 1 jenkins jenkins 17928440 11-20 14:15 restCounter.jar
-rwxrwxr-x. 1 jenkins jenkins 556 11-20 14:15 restCounter.sh
Startujemy naszą aplikację
def applicationDir = '/opt/tools/restCounter'
pipeline {
agent any
parameters {
booleanParam defaultValue: true, description: 'Deploy and run application', name: 'deploy'
}
tools {
maven 'mvn'
jdk 'jdk8'
}
stages {
stage('Git'){
steps {
git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
}
}
stage ('Build') {
steps {
sh 'mvn clean install'
}
post {
success {
archive 'target/*.jar'
junit 'target/surefire-reports/*.xml'
}
}
}
stage ('Deply') {
when {
expression { params.deploy }
}
steps {
echo "Deploy application"
sh "mkdir -p ${applicationDir}"
sh "cp target/*.jar ${applicationDir}"
sh "cp restCounter.sh ${applicationDir}"
dir("${applicationDir}") {
sh "${applicationDir}/restCounter.sh stop"
sh "${applicationDir}/restCounter.sh start &"
sleep 120
}
}
}
}
}
Sprawdzamy wynik http://192.168.0.178:8088/api/count/xyz
Niestety nasza aplikacja będzie działała tylko przez 2 minuty! ProcessTreeKiller
Rozwiążmy ten problem i dodajmy sprawdzanie budowania
def applicationDir = '/opt/tools/restCounter'
pipeline {
agent any
parameters {
booleanParam defaultValue: true, description: 'Deploy and run application', name: 'deploy'
}
tools {
maven 'mvn'
jdk 'jdk8'
}
triggers {
pollSCM '*/5 * * * *', ignorePostCommitHooks: true
}
stages {
stage('Git'){
steps {
git branch: 'master', url: 'ssh://git@ovh.klimas7.pl:7999/lab/restcounter.git'
}
}
stage ('Build') {
steps {
sh 'mvn clean install'
}
post {
success {
archive 'target/*.jar'
junit 'target/surefire-reports/*.xml'
}
}
}
stage ('Deply') {
when {
expression { params.deploy }
}
steps {
echo "Deploy application"
sh "mkdir -p ${applicationDir}"
sh "cp target/*.jar ${applicationDir}"
sh "cp restCounter.sh ${applicationDir}"
dir("${applicationDir}") {
//sh "${applicationDir}/restCounter.sh stop"
//sh "${applicationDir}/restCounter.sh start &"
//sleep 120
sh "export JENKINS_NODE_COOKIE=REST_COUNTER; ${applicationDir}/restCounter.sh start &"
}
}
}
}
}
Sprawdzamy wynik http://192.168.0.178:8088/api/count/xyz
16: Multibranch Pipeline
Jenkins -> Nowy Projekt -> Multibranch Pipeline (LearnMB)
- Branch Sources
Bitbucket server
17: Shared Library
Pozwalają wyodrębnić oraz współdzielić wspólne części pomiędzy wieloma potokami. Elementy takie mogą być zamknięte w bibliotece przechowywanej w repozytorium kodu.
Struktura
├── README.md
├── src (Groovy source files)
│ └── pl
│ └── klimas7
│ └── jenkins
│ ├── Deployer.groovy
│ └── Rectangle.groovy
├── vars (Exposed variables)
│ ├── checkStatus.groovy
│ ├── deploy.groovy
│ ├── disableThrottling.groovy
│ ├── enableThrottling.groovy
│ └── printBuildinfo.groovy
└── resources (resource files)
└── pl
└── klimas7
└── jenkins
└── static_content.json
src
Standardowy katalog znany z projektów javy. Jest dodawany do classpath w trakcie wykonania potoku.
vars
Definicje zmiennych, które są dostępne w potoku.
Przykład jenkins-shared-lib.
Dodanie nowej biblioteki do Jenkinsa.
Jenkins -> Zarządzaj Jenkinsem -> Skonfiguruj system -> Global Pipeline Libraries
- Name: jenkins-shared-lib
- Default version: master
- Retrieval method: Modern SCM
- Select the Git type
- Project repository: ssh://git@ovh.klimas7.pl:7999/lab/jenkins-shared-lib.git
- Credentials: (leave blank)
Nowy projekt wykorzystujący przygotowaną bibliotekę.
Jenkins -> Nowy Projekt -> Pipeline (JenkinsSharedLib)
Proste wykorzystanie
@Library('jenkins-shared-lib')_
stage('Print Build Info') {
printBuildinfo {
name = "Sample Name"
}
} stage('Disable throttling') {
disableThrottling()
} stage('Deploy') {
deploy()
} stage('Enable throtling') {
enableThrottling()
} stage('Check Status') {
checkStatus()
}
Hint! @Library('jenkins-shared-lib')_
_
nie jest pomyłką.
Przykład z parametrami
@Library('jenkins-shared-lib')_
pipeline {
parameters {
string defaultValue: 'One default', description: '', name: 'param1', trim: false
string defaultValue: 'Two default', description: '', name: 'param2', trim: false
}
agent any
stages {
stage('Print Build Info') {
steps {
script {
printBuildinfo {
name = "Sample Name"
}
}
}
}
stage('Disable throttling') {
steps {
script {
disableThrottling()
}
}
}
stage('Deploy') {
steps {
script {
deploy()
}
}
}
stage('Enable throtling') {
steps {
script {
enableThrottling()
}
}
}
stage('Check Status') {
steps {
script {
checkStatus()
}
}
}
}
}
17.1: Ćwiczenie
Wykorzystać przygotowaną bibliotekę w przykładowym potoku, prosty skrypt bez parametrów.
17.2: Ćwiczenie*
Wykorzystać przygotowaną bibliotekę w przykładowym potoku, pełen skrypt z użyciem konwencji potoku i parametrów.
18: Bitbucket integration
Należy zainstalować wtyczki Bitbucket Server Integration
, Bitbucket Server Notifier
18.1 Hook oraz notyfikacja
Konfiguracja po stronie repozytorium
Konfiguracja po stronie Jenkinsa oraz projektu
Dashboard -> Zarządzaj Jenkinsem -> Skonfiguruj system -> Bitbucket Server Notifier
18.2 Bitbucket Server Integration
Konfiguracja po stronie Jenkinsa