Blog

Von Jenkins zu GitLab CI: Wie gelingt der Einstieg?

DEC 6, 2024

Liegen wir richtig mit der Vermutung, dass ihr auf diese Seite gekommen seid, weil ihr nach Orientierung sucht, wie ihr eine Migration von Jenkins zu GitLab CI startet? Wenn ja, seid ihr hier genau richtig! Bevor wir jedoch einsteigen, geben wir euch zunächst eine kurze Einführung in das Thema Software-Pipelines.

Joonas Jauhiainen

DevOps Lead

Joonas is a DevOps lead with experience in telecom, banking, insurance, and manufacturing, among other industries. His hobbies include investigation of IT devices, developing games and other SW projects not to mention underwater rugby!

Laut Wikipedia:

Eine Software-Pipeline ist ein automatisierter Prozess, der definierte Phasen der Softwareentwicklung – Build, Test, Deployment und Release – systematisch und wiederholbar ausführt.

Wenn ihr versteht, was eine Pipeline ist, erkennt ihr, wie sich die Wahl eines CI/CD-Tools auf die Effizienz und Zuverlässigkeit eures Workflows auswirkt. Da GitLab CI euer neues Tool der Wahl ist, beginnt ein neues Kapitel im Lebenszyklus eurer Software, das auch die Arbeitsweise eures Teams verändern wird.

Bevor ihr eine 1:1-Migration versucht (was vermutlich nicht möglich sein wird), empfehle ich euch, kurz innezuhalten und mit eurem Team zu besprechen, ob ihr damit zufrieden seid, wie eure Pipeline bisher funktioniert hat. Für dieses Gespräch könnt ihr verschiedene Hilfsmittel nutzen, etwa unser Pipeline-Spiel!

Start eurer Migration

Jetzt, da ihr eure Pläne definiert und erstellt habt: Was ist das Ziel der Pipeline, und welchen Mehrwert bringt sie euch und dem Entwicklungsteam, das die Pipeline nutzt? (Das beste Ergebnis wäre, wenn ihr selbst direkt zum Entwicklungsteam gehört.) Schauen wir uns an, was wir brauchen.

Für den Start einer Migration von Jenkins zu GitLab CI empfehle ich folgende Kenntnisse:

  • YAML

  • YAML

  • YAML

  • Fachliches Verständnis dessen, woran euer Team arbeitet

Warum liegt der Fokus so stark auf YAML? Die Antwort ist einfach: Alles in GitLab CI verwendet die YAML-Syntax. Im nächsten Teil des Kurses geht es darum zu verstehen, was aus Jenkins in GitLab CI benötigt wird.

Die Terminologie von CI

Da GitLab CI ein völlig anderes Tool als Jenkins ist (technisch gesehen sind beide glorifizierte Cronjob-Runner), ist es wichtig, sich auf eine Terminologie zu einigen.

Jenkins

GitLab

Difference

Agent/Node

GitLab runner

In Jenkins, an "agent" or "node" is a machine where jobs run. In GitLab, the equivalent is a "runner," which executes jobs defined in .gitlab-ci.yml. GitLab offers shared runners (hosted by GitLab) or self-managed runners.

Jenkinsfile

.gitlab-ci.yml

The Jenkinsfile defines the pipeline in Jenkins, typically using Groovy-based syntax. In GitLab CI, the pipeline is defined using the .gitlab-ci.yml file, which uses YAML syntax.

Stages and steps

Stages and jobs

A pipeline is divided into stages in both Jenkins and GitLab CI. In Jenkins, each stage contains multiple steps, while in GitLab CI, a stage contains jobs (which can run sequentially or in parallel).

Trigger / Build trigger

Pipeline triggers

Jenkins triggers jobs via SCM polling, webhook, or manual triggers. GitLab has similar options, such as webhooks, schedules, and manual pipeline runs, but GitLab also integrates with GitLab-specific events (e.g., push events).

Workspace

CI/CD Runner directory

In Jenkins, the workspace is a directory on the agent machine where jobs run. The runner's working directory functions similarly in GitLab, but the GitLab Runner manages it, and artifacts are stored differently (using GitLab's artifact storage).

Jenkins plugin

GitLab CI templates /components

GitLab CI minimizes dependency on external extensions, simplifying management. Components provide modularity, while templates enable reuse. Explore more at GitLab CI Catalog.

Artifacts

Artifacts

Both Jenkins and GitLab CI allow the storage of build artifacts (e.g., logs and reports). GitLab CI has a built-in feature for managing and downloading artifacts stored for a specific duration or pipeline job.

Jobs

Jobs

In Jenkins, a job represents a single unit of work (e.g., a build). In GitLab, a job is a single task within a pipeline stage. The key difference is how jobs are defined and executed in .gitlab-ci.yml vs. in Jenkins' job configuration.

Jenkins

GitLab

Unterschied

Agent/Node

GitLab Runner

In Jenkins ist ein „Agent“ oder „Node“ eine Maschine, auf der Jobs ausgeführt werden. In GitLab entspricht dies einem „Runner“, der die in .gitlab-ci.yml definierten Jobs ausführt. GitLab bietet Shared Runner (von GitLab gehostet) oder selbstverwaltete Runner.

Jenkinsfile

.gitlab-ci.yml

Das Jenkinsfile definiert die Pipeline in Jenkins, typischerweise mit einer auf Groovy basierenden Syntax. In GitLab CI wird die Pipeline über die Datei .gitlab-ci.yml definiert, die YAML-Syntax verwendet.

Stages und Steps

Stages und Jobs

Sowohl in Jenkins als auch in GitLab CI wird eine Pipeline in Stages unterteilt. In Jenkins enthält jede Stage mehrere Schritte, während eine Stage in GitLab CI Jobs enthält, die sequenziell oder parallel ausgeführt werden können.

Trigger / Build-Trigger

Pipeline-Trigger

Jenkins startet Jobs über SCM-Polling, Webhooks oder manuelle Trigger. GitLab bietet ähnliche Optionen wie Webhooks, Zeitpläne und manuelle Pipeline-Ausführungen, integriert aber auch GitLab-spezifische Ereignisse wie Push-Events.

Workspace

CI/CD-Runner-Verzeichnis

In Jenkins ist der Workspace ein Verzeichnis auf der Agent-Maschine, in dem Jobs ausgeführt werden. Das Arbeitsverzeichnis des Runners erfüllt in GitLab eine ähnliche Funktion, wird jedoch von GitLab Runner verwaltet, und Artifacts werden anders gespeichert – über den Artifact-Speicher von GitLab.

Jenkins-Plugin

GitLab-CI-Templates / Komponenten

GitLab CI reduziert die Abhängigkeit von externen Erweiterungen und vereinfacht so die Verwaltung. Komponenten sorgen für Modularität, während Templates die Wiederverwendung ermöglichen. Mehr dazu im GitLab CI Catalog.

Artifacts

Artifacts

Sowohl Jenkins als auch GitLab CI ermöglichen das Speichern von Build-Artefakten, etwa Logs und Berichten. GitLab CI bietet eine integrierte Funktion zum Verwalten und Herunterladen von Artifacts, die für eine bestimmte Dauer oder für einen bestimmten Pipeline-Job gespeichert werden.

Jobs

Jobs

In Jenkins steht ein Job für eine einzelne Arbeitseinheit, etwa einen Build. In GitLab ist ein Job eine einzelne Aufgabe innerhalb einer Pipeline-Stage. Der wesentliche Unterschied liegt darin, wie Jobs in .gitlab-ci.yml und in der Job-Konfiguration von Jenkins definiert und ausgeführt werden.

Die benötigten Umgebungen

Wie wir anhand der Terminologie sehen können, verfügt GitLab CI nicht über „Built-ins“, „Nodes“ oder „Agents“ wie Jenkins. Stattdessen verwendet es Runner. Hier ist eure Fachkenntnis gefragt, denn ihr müsst die spezifischen Anforderungen eures Teams berücksichtigen.

Welche Umgebungen benötigt ihr? Wenn ihr GitLab On-Premises betreibt, müsst ihr eigene Runner einrichten. Diese können direkt unter Linux, Windows oder macOS ausgeführt werden. Ihr könnt sie auf diesen Betriebssystemen auch über Docker ausführen oder Kubernetes nutzen, um Docker-Runner bei Bedarf im Hintergrund zu orchestrieren. Wenn ihr GitLab.com gewählt habt, also den SaaS-Ansatz, könnt ihr je nach Richtlinien eures Unternehmens öffentliche Runner verwenden.

Nachdem ihr eure Anforderungen definiert habt, ist es Zeit, eure bestehenden Jenkins-Nodes und -Agents zu bewerten. Prüft, ob ihr sie durch die Installation von GitLab Runner weiterverwenden könnt oder ob es Zeit ist, sie außer Betrieb zu nehmen und nach Server Valhalla zu schicken, wo jede ausgemusterte Hardware ihren Frieden findet.

Tipps für die Umstellung vom Jenkinsfile auf eine GitLab-CI-YAML-Datei

Bevor ihr mit der .gitlab-ci.yaml-Datei beginnt, werft einen Blick auf eure bestehenden Jenkinsfiles oder die Konfigurationen eurer Freestyle-Jobs.

Versteht ihr wirklich, was diese tun? Falls nicht, empfehle ich euch dringend, zuerst die technischen Schulden anzugehen und das Tasker-Tool zu verwenden.

sh '''

if ! command -v node &> /dev/null; then

curl -fsSL https://deb.nodesource.com/setup_${LTS}.x -o nodesource_setup.sh

            bash nodesource_setup.sh

            rm nodesource_setup.sh

            apt-get install -y nodejs

else

node -v

fi

npm install -g grunt-cli

rm -rf node_modules && ||

npm cache clean --force && npm install -g npm@latest

npm -v

npm outdated --long

npm install

npm audit fix

npm ls --depth=0

npm cache verify

node -v

npm install --no-save eslint prettier jest

which node

which npm

npm ls -g --depth=0

'''

Das könnte also z. B. in sh 'npm run install-global-dependencies' umgewandelt werden.

Nun zu den eigentlichen Tipps und Tricks. Es kann gute Gründe für mehrzeilige Shell-/Bash-Skripte geben, die es nicht rechtfertigen, sie in einem Tasker oder einer separaten Skriptdatei unterzubringen.

Mehrzeiliges Beispiel aus Jenkins

 sh '''

# Create a Python virtual environment

python3 -m venv venv

# Activate the virtual environment

source venv/bin/activate

# Install dependencies from requirements.txt

pip install -r requirements.txt

# Run the Python script

python my_script.py

# Deaktiviert die virtuelle Umgebung

deactivate

'''

So verwendet ihr Artefakte aus verschiedenen Phasen

Im Jenkinsfile gibt es die Schritte stash und unstash, um Build-Binärdateien und andere Artefakte für den Pipeline-Durchlauf zu speichern. In GitLab gibt es kein vergleichbares Konzept, aber Artefakte als Konzept.

Jenkinsfile-Beispiel:

stage('Build Python Project') {

steps {

script {

// Build the Python project using pdm

sh '''

pdm build

'''

// Stash the build artifacts (e.g., .whl or .tar.gz files)

stash name: 'build-artifacts', includes: 'dist/*'

}

}

}

stage('Deploy Python Project') {

steps {

script {

// Unstash the build artifacts from the previous stage

unstash 'build-artifacts'

// Install the built package from the dist folder

sh '''

pip install dist/*.whl

python -m my_module

'''

}

}

}

Beispiel für GitLab-CI-YAML:

build_python_project:

  stage: build

  script:

- pdm build  # Python-Projekt bauen

  artifacts:

Pfade:

- dist/*  # Die erstellten Artefakte speichern (z. B. .whl, .tar.gz)

expire_in: 1 hour  # Ablaufzeit für die Artefakte festlegen

deploy_python_project:

  stage: deploy

  dependencies:

- build_python_project  # Weist GitLab CI an, Artefakte aus der Build-Phase zu verwenden

  script:

- pip install dist/*.whl  # Das erstellte Wheel-Paket installieren

- python -m my_module  # Das Python-Modul ausführen

Aktionen nach dem Build

In Jenkins sind Aktionen nach dem Build sehr leistungsstark. Ihr könnt sie mit zusätzlichen Schritten definieren, z. B. always, failure, cleanup usw. In GitLab habt ihr nur after_script, das natürlich Bedingungen enthalten kann. Eine der wichtigsten Beobachtungen zu GitLab CI ist, dass ihr die Pipeline nicht mit dem Block after_script fehlschlagen lassen könnt. Wenn eure Pipeline also fehlschlagen soll, etwa weil eure Unit-Tests keine ausreichend hohe Erfolgsquote erreicht haben, müsst ihr das in einem script-Block statt in after_script umsetzen.

Jenkinsfile-Beispiel:

stage('Run JUnit Tests') {

  steps {

script {

sh 'mvn test'

}

  }

  post {

always {

junit '**/target/test-classes/*.xml'

}

  }

}

GitLab-CI-YAML-Beispiel:

test:

  stage: test

  script:

- mvn test

  after_script:

- |

if [ -f "target/test-classes/*.xml" ]; then

echo "JUnit test results found. Archiving..."

# Use GitLab's artifact feature to store the test results

mv target/test-classes/*.xml /tmp/junit-test-results/

else

echo "No JUnit test results found."

            exit 1 # doesn’t affect the pipeline status

fi

  artifacts:

paths:

- /tmp/junit-test-results/*.xml

expire_in: 1 hour

Es gab einige konkrete Beispiele dafür, was euch bei einer 1:1-Migration von Jenkinsfiles zu GitLab CI YAML begegnen kann. Ich empfehle dringend, die Datei .gitlab-ci.yml vor dem Push ins Remote-Repository lokal mit YAMLLint zu prüfen. Außerdem können AI-Tools wie GitHub Copilot die aufwendige Arbeit übernehmen, sobald ein grundlegendes Verständnis vorhanden ist.

Jenkins-Plugins

„Was ist mit Jenkins-Plugins?“, fragt ihr euch vielleicht. In den vorherigen Beispielen wurden sie nicht erwähnt oder verwendet, da GitLab CI nicht auf Plugins angewiesen ist. Kein Plugin-Management und keine Probleme mehr mit Maven-/Gradle-Versionen oder Java-Abhängigkeiten!

Statt Plugins und Shared Libraries verwendet GitLab CI Templates! GitLab stellt viele vordefinierte Templates bereit, die ihr in eurem Unternehmen erstellen und teilen könnt.

Eine weitere Alternative zu Plugins ist die Suche nach CLI-Tools für verschiedene Anwendungsfälle. JFrog bietet beispielsweise sowohl ein Artifactory-Jenkins-Plugin als auch ein CLI-Tool.

Statt ein eigenes maßgeschneidertes Tool zu entwickeln, empfehle ich daher, zunächst zu prüfen, ob bereits ein passendes Standardtool verfügbar ist.

Zusammenfassung

Bevor ihr mit der eigentlichen Arbeit beginnt, empfehle ich, euch Zeit für die Planung zu nehmen. Stellt euch und dem Team, das die Pipeline nutzt, die folgenden Fragen:

  • Erzeugt die aktuelle Pipeline die Ergebnisse, die wir benötigen?

  • Schafft die aktuelle Pipeline für uns Mehrwert?

  • Was ist das Nervigste an der aktuellen Pipeline?

Ein Toolwechsel ist wie ein Start mit einer leeren Leinwand – und eine solche Situation sollte sorgfältig angegangen werden.

Sobald die Ziele und Kontrollinstanzen der Pipeline definiert sind, vergleicht, was aus dem alten System benötigt wird. Beginnt dann mit der eigentlichen Arbeit. Nehmt euch außerdem Zeit, euch mit GitLab CI/CD vertraut zu machen – versteht, was ihr wo findet. Die Web-UI unterscheidet sich deutlich von Jenkins. Daher empfehle ich, eine Schulung zu besuchen oder zumindest einige Beispielvideos anzusehen.

Und wenn ihr bei dieser Aufgabe Hilfe braucht, sprecht uns gerne an!

  • GitLab
  • CI/CD
  • Software development

Subscribe to our newsletter