676 lines
76 KiB
TeX
676 lines
76 KiB
TeX
\documentclass{article}
|
|
|
|
\usepackage[utf8]{inputenc}
|
|
\usepackage{helvet}
|
|
\usepackage[T1]{fontenc}
|
|
\usepackage[ngerman]{babel}
|
|
\usepackage{multicol}[]
|
|
\usepackage{blindtext}
|
|
\usepackage{float}
|
|
\usepackage{booktabs}
|
|
\usepackage{makecell}
|
|
|
|
\usepackage{fancyhdr}
|
|
\usepackage{geometry}
|
|
\usepackage{abstract}
|
|
\usepackage{graphicx}
|
|
\usepackage{acronym}
|
|
\usepackage{hyperref}
|
|
\usepackage{xurl}
|
|
|
|
\usepackage{amsmath}
|
|
\usepackage{xcolor}
|
|
\usepackage{listings}
|
|
|
|
\usepackage{tikz}
|
|
\usetikzlibrary{positioning,arrows.meta,fit,backgrounds}
|
|
|
|
\usepackage{biblatex}
|
|
|
|
% Lstlisting-Stil für YAML-Beispiele
|
|
\definecolor{lstbg}{rgb}{0.97,0.97,0.97}
|
|
\definecolor{lstkw}{rgb}{0.10,0.30,0.65}
|
|
\definecolor{lstcm}{rgb}{0.40,0.40,0.40}
|
|
\lstdefinelanguage{yaml}{
|
|
keywords={true,false,null},
|
|
keywordstyle=\color{lstkw}\bfseries,
|
|
sensitive=false,
|
|
comment=[l]{\#},
|
|
commentstyle=\color{lstcm}\itshape,
|
|
morestring=[b]',
|
|
morestring=[b]",
|
|
}
|
|
\lstset{
|
|
backgroundcolor=\color{lstbg},
|
|
basicstyle=\ttfamily\footnotesize,
|
|
breaklines=true,
|
|
captionpos=b,
|
|
frame=single,
|
|
framesep=4pt,
|
|
rulecolor=\color{black!20},
|
|
xleftmargin=4pt,
|
|
xrightmargin=4pt,
|
|
showstringspaces=false,
|
|
}
|
|
|
|
\newcommand{\authornote}[1]{\textit{\small Verfasst von: #1}\par\medskip}
|
|
|
|
\graphicspath{ {./bilder/} }
|
|
|
|
\geometry{
|
|
a4paper,margin=25mm
|
|
}
|
|
|
|
\title{\huge{Dev Ops mit Microservices - GitLab}}
|
|
\date{\today}
|
|
\author{
|
|
\begin{tabular}{ccc}
|
|
\textbf{Roman Schöne} & \textbf{Christopher Schmitt}\\
|
|
2211275 & 2023467\\
|
|
roman.schoene@stud.th-mannheim.de & christopher.schmitt@stud.th-mannheim.de
|
|
\end{tabular}\\\\
|
|
Technische Hochschule Mannheim
|
|
}
|
|
\addbibresource{literatur/dms.bib}
|
|
|
|
% Abstract-Titel auf "Abstract" setzen (nach babel, sonst überschreibt ngerman es)
|
|
\AtBeginDocument{\renewcommand{\abstractname}{Abstract}}
|
|
% Lange URLs in der Bibliografie umbrechen, auch im howpublished-Feld
|
|
\setcounter{biburlnumpenalty}{100}
|
|
\setcounter{biburlucpenalty}{100}
|
|
\setcounter{biburllcpenalty}{100}
|
|
\DeclareFieldFormat{howpublished}{\begingroup\urlstyle{same}\url{#1}\endgroup}
|
|
|
|
\renewcommand\familydefault{\sfdefault} % Helvetica
|
|
|
|
\begin{document}
|
|
\pagestyle{fancy}
|
|
%... then configure it.
|
|
\fancyhead{} % clear all header fields
|
|
\fancyhead[L]{GitLab}
|
|
\fancyhead[R]{DMS - DevOps mit Micro Services}
|
|
\fancyfoot{} % clear all footer fields
|
|
\fancyfoot[C]{\thepage}
|
|
|
|
\maketitle
|
|
|
|
\begin{abstract}
|
|
\authornote{Christopher Schmitt}
|
|
GitLab vereint Versionsverwaltung, \ac{CI}/\ac{CD} und Projektorganisation in einer Plattform. Betreiben lässt es sich als gehosteter Dienst oder auf eigener Infrastruktur. Diese Arbeit untersucht GitLab als DevOps-Plattform mit Schwerpunkt auf der \ac{CI}/\ac{CD}-Pipeline und der selbstgehosteten Variante. Erklärt werden die zentralen Bausteine \emph{Runner}, \emph{Pipeline} und \emph{Job}. Danach folgt ein Anwendungsbeispiel mit einer Demo-Pipeline, die im Container \texttt{node:20-alpine} die Stages \texttt{lint}, \texttt{test} und \texttt{deploy} durchläuft. Für die selbstgehostete Lösung wurde GitLab \ac{CE} in Version 18.11 über das Omnibus-Paket auf einem Linux-Server installiert, über \texttt{gitlab.rb} konfiguriert und mit \texttt{gitlab-ctl} betrieben. Den größeren Teil der Einrichtung erledigte die Weboberfläche, ohne dass viel an der Kommandozeile nötig war. In der Evaluierung wurde die Demo-Pipeline auf der eigenen Instanz vermessen. Die eigentliche Prüfung liegt je Job unter einer Sekunde. Den Rest verbrauchen Containerstart, Klonen und das Installieren der Abhängigkeiten. Ein unwirksamer Cache ließ sich entfernen und sparte rund drei Sekunden je Job.
|
|
\end{abstract}
|
|
|
|
\clearpage
|
|
|
|
\begin{multicols}{2}
|
|
\tableofcontents
|
|
|
|
\section{Einleitung}
|
|
\authornote{Roman Schöne}
|
|
\subsection{Motivation}
|
|
|
|
\textbf{\ac{CI}} ist eine Methodik zur Softwareentwicklung. Ziele der Vorgehensweise sind die Erstellung von qualitativen Quellcode, durch stetiges Überprüfen der Funktionalität des verfassten Code. Der Entwicklungsstand einer Software liegt innerhalb des sogenannten Hauptzweiges (auch \textit{Main-Branch} genannt). Entwickler nehmen fortlaufend kleinere Änderungen vor und bauen diese innerhalb kurzen Entwicklungszyklen in den Hauptzweig ein. Der Entwickler stellt eine Anfrage, ob sein Code in den Hauptzweig aufgenommen werden kann (Pull Request). Damit eine korrekte Funktionsweise des Hauptzweiges sichergestellt werden kann, wird jeweils eine Reihe an Tests durchgeführt. Schlagen diese Tests fehl, so muss der Entwickler seine Änderungen ausbessern und den Prozess erneut anstossen und wiederholt diesen Schritt solange bis die Tests erfolgreich sind. Die Änderungen können somit in den Hauptzweig übernommen werden (Merge).
|
|
Häufig findet \ac{CI} in Kombination mit agiler Projektmethodik statt. \cite{arefeen_continuous_2019}.\\
|
|
\textbf{\ac{CD}} beschreibt den durch \ac{CI} notwendigen Aspekt der Automatisierung. Eine manuelle Durchführung der Tests benötigt einen höheren zeitlichen Aufwand und kann Fehler enthalten. Zu den Vorteilen von \ac{CI}/\ac{CD} zählen eine frühe Fehlererkennung und schnellere Entwicklungszyklen.
|
|
GitLab ist eine Plattform, die es ermöglicht \ac{CI}/\ac{CD} in Verbindung mit agiler Entwicklung umzusetzen.
|
|
Nach der jährlichen Entwicklerumfrage von Stack Overflow, ist GitLab, mit 35.6 \%, neben GitHub, mit 81.8 \%, und Jira, mit 46.4 \% eine der meistgenutzten Plattformen zur Dokumentation und zur kollaborativen Zusammenarbeit an Code \cite{2025StackOverflow}. Siehe \ref{fig:stackoverflow-devsurvey-tools}
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\linewidth]{bilder/stack_overflow_developer_survey_collab_tools_documentation.png}
|
|
\caption{Auswahl der 10 meist verwendeten Dokumentations- und Kollaborations Werkzeuge nach Stack Overflow Survey 2025 \cite{2025StackOverflow}}
|
|
\label{fig:stackoverflow-devsurvey-tools}
|
|
\end{figure}
|
|
Diese wissenschaftliche Ausarbeitung beschäftigt sich damit, inwiefern sich die von GitLab bereitgestellten Werkzeuge bzw. Möglichkeiten eignen um eine beispielhafte Anwendung zu entwickeln, zu testen und einzusetzen. Ebenso wird betrachtet in welchen Punkten sich der Aufbau von \ac{CI}/\ac{CD} in GitLab zu seinem Mitstreiter GitHub unterscheidet.
|
|
\subsection{Softwarelösung}
|
|
GitLab ist eine mit Git kompatible Plattform für Code-Hosting. Der Code von GitLab ist in Ruby verfasst \cite{degeler_gitlab_2014}. Unter der Haube verwendet Git die relationale Datenbank PostgreSQL. Auf GitLab kann mithilfe einer Weboberfläche zugegriffen werden \cite{gitlab_about}. Es wird differenziert in die kostenfreie \ac{CE} und kostenpflichtige \ac{EE}.
|
|
GitLab kann in folgenden unterschiedlichen Formen genutzt werden:
|
|
|
|
\begin{itemize}
|
|
\item \textbf{\ac{SaaS}} Die Plattform wird innerhalb der Cloud von GitLab gehostet. Die Arbeit mit GitLab kann sofort gestartet werden.
|
|
\item \textbf{Selbst gehostet} GitLab kann auf linux-basierten Servern selbst betrieben werden. Installation, Konfiguration und Administration der Infrastruktur muss selbst übernommen werden.
|
|
\item \textbf{Dediziertes \ac{SaaS}} Für Unternehmen und Regierungen kann GitLab in eigenen isolierten Instanzen verwendet werden, um hohe Sicherheitsstandards und gesetzliche Regulierungen zu gewährleisten.
|
|
\end{itemize}
|
|
|
|
Für die \ac{SaaS}- und die selbst gehostete Variante von GitLab gibt es drei unterschiedliche Preispläne: Free, Premium und Ultimate. Tabelle \ref{tab:saas_plans} zeigt eine Auswahl an Funktionalitäten und deren Verfügbarkeit nach Preisplan. In der selbst gehosteten Variante von GitLab sind die technischen Limitierungen Nutzeranzahl, Rechnungszeit und Speicher von der Bereitstellung eigener Serverkapazitäten abhängig. Die Verfügbarkeit der restlichen Funktionalitäten ist analog.
|
|
\begin{table}[H]
|
|
\begin{tabular}{@{}llll@{}}
|
|
\toprule
|
|
& \multicolumn{3}{c}{Verfügbarkkeit} \\ \midrule
|
|
Preisplan & Free & Premium & Ultimate \\ \midrule
|
|
Nutzerzahl & 5 & $\infty$ & $\infty$ \\
|
|
\makecell[cl]{Rechnungs-\\zeit} & 400 min & 10000 min & 50000 min \\
|
|
Speicher & 10 GiB & 500 GiB & 500 GiB\\\midrule
|
|
\ac{CI}/\ac{CD} & X & X & X\\
|
|
\makecell[cl]{Container-\\Scan} & X & X & X\\
|
|
Web \acs{IDE} & - & X & X\\
|
|
Pushregeln & - & X & X\\
|
|
\makecell[cl]{Integrierte\\Testfälle} & - & - & X\\ \midrule
|
|
\makecell[cl]{Zeitracking} & X & X & X\\
|
|
Wikis & X & X & X\\
|
|
\makecell[cl]{Issue-\\Gewichte} & - & X & X\\
|
|
\makecell[cl]{Projekt-\\analyse} & - & X & X\\
|
|
\makecell[cl]{Statusseite} & - & - & X\\
|
|
\midrule
|
|
Preis & 0\texteuro & \makecell[cl]{29\texteuro je$\frac{\text{Nutzer}}{\text{Monat}}$} & \makecell[cl]{kunden-\\spezifisch}\\ \bottomrule
|
|
\end{tabular}
|
|
\caption{Auswahl an Funktionalitäten und deren Verfügbarkeit, nach Preisplan \cite{gitlab_about}}
|
|
\label{tab:saas_plans}
|
|
\end{table}
|
|
|
|
\subsection{Geschichte}
|
|
|
|
GitLab wurde von Sytse Sijbrandij und von Dmitriy Zaporozhets gegründet. Zaporozhets entwickelte GitLab 2011 als Hilfsmittel für seine eigenen Projekte. GitLab war zu dem Zeitpunkt eine private und freie Plattform zum eigenen Code-Hosting, entwickelt von Zaporozhets. Zaporozhets und Sijbrandij lernten sich auf der \href{https://thenextweb.com/conference}{The Next Web} Konferenz kennen. Nachdem Zaporozhets sich entschied den Quellcode von GitLab frei zugänglich zu machen, entstand die Partnerschaft mit Sijbrandij. Um den Einstieg in die Nutzung von GitLab zu erleichtern entschieden sich Sijbrandij GitLab als \ac{SaaS} unter der Domain \url{https://gitlab.com/} anzubieten. Der Quellcode von GitLab ist frei unter \url{https://gitlab.com/gitlab-org/gitlab} verfügbar \cite{degeler_gitlab_2014}. Der Quellcode des Konkurrenten GitHub ist nicht frei zugänglich. 2014 wurde GitLab erstmalig als Unternehmen eingetragen und erfuhr seitdem einen grossen Zuwachs an Mitarbeitern. Aktuell zählt GitLab mehr als 2600 Mitarbeiter, die über 65 Länder verteilt leben \cite{gitlab_about}. GitLab selbst besitzt keinen grossen ausgebauten Firmenhauptsitz, da Mitarbeiter hauptsächlich remote arbeiten und freie Verfügung über ihre Arbeitszeit besitzen. Der organisatorische Aufbau und der Ablauf interner Prozesse von GitLab, können in dem von GitLab öffentlichem Handbuch \url{https://handbook.gitlab.com/} nachgelesen werden \cite{choudhuryGitLabWorkWhere2020}.
|
|
GitLab finanziert sich durch Spenden um neue Funktionalitäten zu realisieren. Ein weiterer Teil der Einnahmen kommt durch den Abschluss von kostenpflichtiger Abonnements. \cite{degeler_gitlab_2014}.
|
|
Abbildung \ref{fig:gitlablogo} zeigt das Logo von GitLab. Es besteht aus einem Tanuki, einem in Japan heimischen Waschbärhund, und dem GitLab Schriftzug.
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=0.8\linewidth]{./bilder/gitlab-logo-100-rgb.png}
|
|
\caption{Logo GitLab}
|
|
\label{fig:gitlablogo}
|
|
\end{figure}
|
|
GitLab erregte in mehreren Fällen mediale Aufmerksamkeit auf sich. 2017 verlor die \ac{SaaS}-Lösung von GitLab sechs Stunden an Nutzerdaten, aufgrund von einer menschlichen Fehlreaktion, ausgelöst durch Spamanfragen die in Problemen mit der Datenbank resultierten \cite{GitLabcomDatabaseIncident}. GitLab wagte im Oktober 2021 den Gang an die Börse und ist aktuell als Aktie im \ac{NASDAQ} unter dem Kürzel: GTLB erhältlich \cite{teamGitLabIncGTLB}.
|
|
Ebenso bestanden 2022 Pläne inaktive Repositories löschen. Nach starker Kritik wurde sich dazu entschieden, anstatt zu löschen, zu archivieren \cite{onlineVersionsverwaltungGitLabRudert2022}.
|
|
|
|
\subsection{Aufbau}
|
|
|
|
Zusammenarbeit in GitLab ist in Form von Gruppen organisiert. Diese können in Subgruppen unterteilt werden. Gruppen und Subgruppen können Projekte und Mitarbeiter zugeordnet werden.
|
|
Jedes Projekt besteht aus einer Issue-Seite und einem Code-Repository. Für ein Projekt kann optional ein Wiki angelegt werden, um Informationen in Form von \ac{GLFM} zu dokumentieren \cite{gitlab_gitlab_nodate}. \ac{GLFM} ist eine spezielle Markdown-Spezifikation, welche die grundlegende Definition erweitert. Projekte besitzen folgende Sichtbarkeitsstufen:
|
|
\begin{itemize}
|
|
\item \textbf{Public} Als öffentlich zugängliche Ressourcen kann jedermann zugreifen. Ein Zugriff ist auch ohne einen GitLab Account möglich.
|
|
\item \textbf{Internal} Nur GitLab-Nutzer können auf die als intern markierte Ressource zugreifen.
|
|
\item \textbf{Private} Nur Nutzer die explizit als Mitarbeiter/Teilnehmer zu einem Projekt hinzugefügt wurden, können auf private Ressourcen zugreifen.
|
|
\end{itemize}
|
|
Ein Projekt wird immer unter einem Namensraum angelegt. In GitLab wird in 2 Typen von Namensräumen unterschieden:
|
|
\begin{itemize}
|
|
\item \textbf{Nutzer} Der Namensraum von einem Nutzer kann keine Subgruppen enthalten. Wird sich entschieden den Nutzernamen zu ändern, so ändert sich auch die URL des Namensraum
|
|
\item \textbf{Gruppe} Innerhalb von Gruppen können Subgruppen erstellt werden, welche ihre Einstellungen vorerst von ihrer Elterngruppe erben.
|
|
\end{itemize}
|
|
Tabelle \ref{tab:namespaces} zeigt eine Auflistung beispielhafter Namensräume mit entsprechender URL für eine Gitlab-Instanz unter der Domain \texttt{domain.com}.
|
|
\begin{table}[H]
|
|
\centering
|
|
\resizebox{\columnwidth}{!}{%
|
|
\begin{tabular}{@{}lll@{}}
|
|
\toprule
|
|
Namensraum & URL & Beschreibung \\ \midrule
|
|
\texttt{max} & https://domain.com/max & \makecell[cl]{Nutzer mit Namen \\ \texttt{max}} \\
|
|
\texttt{team} & https://domain.com/team & \makecell[cl]{Gruppe mit Namen \\ \texttt{team}} \\
|
|
\texttt{team/design} & https://domain.com.com/team/design & \makecell[cl]{Subgruppe \texttt{design}\\ der Gruppe \texttt{team}} \\ \bottomrule
|
|
\end{tabular}%
|
|
}
|
|
\caption{Beispielhafte Namensräume von Projekten in GitLab}
|
|
\label{tab:namespaces}
|
|
\end{table}
|
|
|
|
\subsection{GitLab vs. GitHub}
|
|
|
|
Bei der Wahl von Code-Hosting ist oft GitHub die erste Wahl. GitHub wurde 2008 von Chris Wanstrath, Tom Preston-Werner und Phillip Jeffrey Hyett gegründet. GitHub wurde 2018 für ungefähr 7,5 Milliarden USD an den Software-Giganten Microsoft verkauft \cite{jrHowThis33yearold2018}. Der Code von GitHub selber ist nicht öffentlich zugänglich. Für Github besteht keine Option die Plattform selbst zu hosten. Nutzer sind daran gebunden, unter der Haube, auf Services zuzugreifen, die in Microsoft Azure laufen. \url{github.com} besitzt im Vergleich zu \url{gitlab.com} eine höhere Anzahl an Nutzern für Q4 2025. GitHub hat verdächtige Nutzer und Bots in ihrer Angabe herausgefiltert und kommt auf eine Gesamtanzahl an ca. 179 Millionen Nutzer \cite{GitHubInnovationGraph}. GitLab gibt an, dass von mindestens 50 Millionen Nutzern ausgegangen werden kann \cite{Q4FY2026GitLab}.
|
|
Aufgrund der grösseren Nutzerbasis eignet sich GitHub mehr für eine kollaborative Zusammenarbeit an \ac{OSS}. Ein Projekt erreicht mehr Nutzer, die potenziell beitragen können.
|
|
GitLab und GitHub teilen sich neben demselben Namenspräfix, ähnliche Mechanismen \cite{gitlab_gitlab_nodate}.\\\\
|
|
\ac{CI}/\ac{CD} ist für jeweils beide Plattformen erhältlich. Unter GitHub bieten \textit{GitHub Actions} die Möglichkeit Build-, Test- und Deploymentprozesse zu automatisieren. In GitHub sind diese Abläufe in Form von \textit{Workflows} organisiert. Ein \textit{Workflow} wird durch ein Ereignis ausgelöst. Ein \textit{Workflow} ist in \textit{Jobs} aufgegliedert. \textit{Jobs} beinhalten Anweisungen bspw. in Form von Kommandozeilenskripts, die sequentiell ausgeführt werden. Eine \textit{Action} beschreibt eine Menge an wiederverwendbaren \textit{Jobs}. \textit{Workflows} werden auf einem Server bzw. einem \textit{Runner} ausgeführt. Für GitHub ist auch das eigene Hosten eines \textit{Runners} möglich. Die Verwendung von eigens gehosteten Runnern, sowie der von GitHub bereitgestellten Runnern ist für öffentliche Repositories kostenlos. Für private Repositories existiert ein Zeitbudget. \cite{GitHubcomHelpDocumentation} \cite{gitlab_gitlab_nodate}\\\\
|
|
Beide Plattformen bieten KI-Assistenten an. Für GitHub existiert \textit{GitHub Copilot} und für GitLab \textit{GitLab Duo Agent}. Aktuell kann \textit{GitHub Copilot} mit einem eingeschränkten Zugriff auf Funktionalitäten, kostenlos verwendet werden. Im Gegensatz ist aktuell eine kostenfreie Nutzung von \textit{GitLab Duo Agent} nicht möglich. Copilot beschränkt sich auf alle Artefakte, die in einem Code-Repository liegen. \textit{GitLab Duo Agent} besitzt einen größeren Kontext, zu dem zusätzlich Dokumentation, Planung und Sicherheit gehören. Aufgrund der Aneignung von \textit{Visual Studio Code} durch Microsoft ist \textit{GitHub Copilot} nativ enthalten. Eine Unterstützung für \textit{GitLab Duo Agent} muss über die offizielle \href{https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow}{Erweiterung} ergänzt werden. Interaktionen mit \textit{GitHub Copilot} werden standardmässig von GitHub zum trainieren und verbessern von KI-Modellen verwendet. UM eine Weiterverarbeitung der Daten zu verhindern muss manuell widersprochen werden. Für beide KI-Assistenten kann das unterliegende Modell variiert werden. \cite{GitHubcomHelpDocumentation} \cite{gitlab_gitlab_nodate}\\\\
|
|
Das Hosten von Code-Schnipseln ist in GitHub als \href{https://gist.github.com/}{Gist} und in GitLab unter \href{https://gitlab.com/dashboard/snippets}{Snippets} möglich. Organisationen können in GitHub ebenfalls abgebildet werden.
|
|
|
|
\section{CI/CD}
|
|
\authornote{Christopher Schmitt}
|
|
|
|
\textbf{\ac{CI}}, \textbf{\ac{CD}} und Continuous Deployment bauen aufeinander auf. In der Literatur werden sie aber oft vermischt. \ac{CI} bezeichnet das automatische Übersetzen und Testen jeder Codeänderung gegen den gemeinsamen Hauptzweig. \ac{CD} steht in dieser Arbeit für Continuous Delivery. Jeder geprüfte Stand lässt sich damit jederzeit in eine produktionsnahe Umgebung ausliefern, der eigentliche Sprung in die Produktion bleibt aber eine bewusste, manuelle Freigabe. Continuous Deployment geht einen Schritt weiter und automatisiert auch diese Freigabe. Die Abkürzung CD wird dabei für beide letztgenannten Stufen verwendet, was regelmäßig zu Verwechslungen führt \cite{shahin_continuous_2017}. Alle drei Stufen bildet GitLab mit denselben Bausteinen ab: Runner, Pipelines und Jobs. Gesteuert werden sie über eine einzige Konfigurationsdatei im Repository \cite{cowell_automating_2023}.
|
|
|
|
\subsection{GitLab Runner}
|
|
|
|
Die GitLab-Instanz führt die \ac{CI}/\ac{CD}-Jobs nicht selbst aus. Das Ausführen übernimmt ein separater Prozess, der \emph{Runner} genannt wird \cite{gitlab_gitlab_nodate}. Runner werden einmal registriert, dies geschieht mittels eines Tokens, den die GitLab-Instanz selbst ausstellt. Danach holt der Runner sich selbständig offene Aufgaben zum Abarbeiten. Durch diese Trennung können mehrere Runner parallel an verschiedenen Aufgaben arbeiten. Plattformabhängige Aufgaben lassen sich so gezielt auf Runner mit passender Hardware verteilen und dort ausführen \cite{painter_practical_2024}. Runner können auf drei verschiedenen Ebenen registriert werden:
|
|
\begin{itemize}
|
|
\item \textbf{Instanz} Runner, die auf der Instanzebene registriert werden, sind für alle Projekte des GitLab-Servers verfügbar. Diese werden meistens von der Administration erstellt und gewartet.
|
|
\item \textbf{Gruppe} Runner, die auf Gruppenebene registriert sind, nehmen nur Aufgaben aus Projekten innerhalb dieser Gruppe und ihrer Subgruppen an. Sie werden meistens dann eingesetzt, wenn die Gruppe besondere Anforderungen an Hardware oder Software stellt.
|
|
\item \textbf{Projekt} Runner, die auf Projektebene registriert werden, sind an ein einzelnes Projekt gebunden. Für sensitive Projekte eignet sich diese Variante besonders, wenn gezielt kein gemeinsamer Runner verwendet werden soll.
|
|
\end{itemize}
|
|
Der konfigurierte \emph{Executor} gibt an, wie ein Runner eine konkrete Aufgabe ausführen soll. Dieser entscheidet, in welcher Umgebung das Skript letztendlich läuft. Tabelle~\ref{tab:executors} listet die in der Praxis am häufigsten verwendeten Executors auf.
|
|
\begin{table}[H]
|
|
\centering
|
|
\caption{Häufig verwendete Executors des GitLab Runners \cite{gitlab_gitlab_nodate}}
|
|
\label{tab:executors}
|
|
\resizebox{\columnwidth}{!}{%
|
|
\begin{tabular}{@{}lll@{}}
|
|
\toprule
|
|
Executor & Ausführungsumgebung & Typischer Einsatz \\ \midrule
|
|
\texttt{shell} & direkt auf dem Hostsystem & einfache Setups, Eigenbau-Server \\
|
|
\texttt{docker} & je Aufgabe ein neuer Container & in der Praxis der Standardfall \\
|
|
\texttt{docker-machine} & kurzlebige Cloud-VMs & elastische Skalierung \\
|
|
\texttt{kubernetes} & ein Pod je Aufgabe & große Cluster, Autoskalierung \\
|
|
\texttt{ssh} & entfernter Host über SSH & Legacy-Systeme \\ \bottomrule
|
|
\end{tabular}%
|
|
}
|
|
\end{table}
|
|
Die Wahl des Executors ist auch eine Sicherheitsfrage, weil sie die Ausführungsumgebung festlegt. Der \texttt{shell}-Executor ist am schnellsten eingerichtet, führt die Jobs aber direkt auf dem Hostsystem aus. Dadurch sind die Jobs nicht voneinander getrennt, sie teilen sich dasselbe Dateisystem und dieselben installierten Programme. In der Produktion wird der \texttt{shell}-Executor deshalb meist durch den \texttt{docker}- oder \texttt{kubernetes}-Executor ersetzt \cite{rostami_usage_2023}. Diese geben jedem Job eine eigene, abgekapselte Umgebung ohne vollen Zugriff auf das Hostsystem. Problematisch wird es, wenn mehrere Projekte denselben \texttt{shell}-Runner verwenden und ein Job das Hostsystem dauerhaft verändert, etwa durch das Ändern einer Softwareversion. Diese Änderung wirkt sich dann auf alle nachfolgenden Jobs aus.
|
|
|
|
\subsection{Pipelines}
|
|
|
|
Eine \emph{Pipeline} ist in GitLab die höchste Ebene der Automatisierung. In einer Pipeline werden vordefinierte Aufgaben abgearbeitet, die bei einem Ereignis automatisch starten \cite{gitlab_gitlab_nodate}. Sie wird durch verschiedene definierte Ereignisse gestartet. Auslöser kann ein Push sein, das Öffnen eines \emph{Merge Requests}, ein vordefinierter Zeitplan oder ein Klick in der Weboberfläche \cite{cowell_automating_2023}. Auch das Ende einer anderen Pipeline kann als Auslöser dienen.
|
|
|
|
In einer Pipeline werden einzelne Arbeitsschritte zu sogenannten \emph{Stages} zusammengefasst. Innerhalb von Stages laufen Aufgaben parallel zueinander und müssen erfolgreich abgeschlossen sein, damit die nächste Stage beginnen kann \cite{gitlab_gitlab_nodate}. Eine typische Reihenfolge der Stages ist etwa \texttt{lint}, \texttt{test}, \texttt{build} und \texttt{deploy}. In \texttt{lint} prüfen statische Werkzeuge den Quellcode auf Stil, Formatierung und offensichtliche Fehler. Nach der \texttt{lint}-Stage führt die \texttt{test}-Stage die automatischen Tests aus. Anschließend baut \texttt{build} die Anwendung, etwa zu einem Container-Image. \texttt{deploy} bringt die fertige Anwendung dann auf das Zielsystem und startet sie. Abbildung~\ref{fig:pipeline-stages} zeigt das Verhältnis von Pipeline, Stages und Jobs.
|
|
|
|
\input{diagrams/pipeline_stages}
|
|
|
|
Die gesamte Definition einer Pipeline liegt in einer einzigen Datei. Diese Datei muss \texttt{.gitlab-ci.yml} heißen und befindet sich im Wurzelverzeichnis des Repositories \cite{gitlab_gitlab_nodate}. Dadurch folgt GitLab dem \emph{Pipeline-as-Code}-Prinzip, das von Humble und Farley eingeführt wurde \cite{humble_continuous_2010}. Die Beschreibung des Build- und Auslieferungsprozesses wird so wie Anwendungscode versioniert und im Review geprüft. Listing~\ref{lst:gitlabciyml} zeigt einen minimalen Aufbau mit drei Stages.
|
|
|
|
\begin{lstlisting}[language=yaml,caption={Minimaler Aufbau einer .gitlab-ci.yml},label={lst:gitlabciyml}]
|
|
stages:
|
|
- test
|
|
- build
|
|
- deploy
|
|
|
|
unit-test:
|
|
stage: test
|
|
script:
|
|
- npm ci
|
|
- npm test
|
|
|
|
build-image:
|
|
stage: build
|
|
script:
|
|
- docker build -t app:$CI_COMMIT_SHA .
|
|
|
|
deploy-staging:
|
|
stage: deploy
|
|
script:
|
|
- ssh user@host 'docker pull app'
|
|
only:
|
|
- main
|
|
\end{lstlisting}
|
|
|
|
Die starre Stage-Reihenfolge lässt sich mit dem Schlüsselwort \texttt{needs} aufbrechen. Durch \texttt{needs} entsteht ein gerichteter Graph ohne Zyklen, ein sogenannter \ac{DAG}. Ohne Zyklen heißt, dass kein Job direkt oder über Umwege wieder von sich selbst abhängt. Wenn die definierten Vorgänger eines Jobs fertig sind, startet dieser automatisch und wartet nicht auf die gesamte vorherige Stage \cite{cowell_automating_2023}. Dadurch entfallen unnötige Wartezeiten, und unabhängige Jobs können bei mehreren verfügbaren Runnern parallel laufen. Wiederkehrende Pipeline-Bausteine, die an mehreren Stellen gebraucht werden, lassen sich über \texttt{include} aus anderen Repositories nachladen \cite{gitlab_gitlab_nodate}.
|
|
|
|
\subsection{Jobs}
|
|
|
|
Eine Pipeline besteht aus \emph{Jobs}, die abgearbeitet werden. Jobs werden vom Anwender über ein Skript festgelegt, das in der Regel in einer isolierten Umgebung ausgeführt wird \cite{gitlab_gitlab_nodate}. Jeder Job ist genau einer Stage zugeordnet. Jobs derselben Stage laufen unabhängig voneinander und bei mehreren verfügbaren Runnern sogar parallel. Ob die Pipeline weiterläuft, hängt vom Ergebnis aller Jobs einer Stage ab.
|
|
|
|
Über \texttt{rules} wird festgelegt, unter welchen Bedingungen ein Job ausgeführt wird. Beispiele sind die Beschränkung eines Jobs auf einen bestimmten Branch, die Ausführung nur bei Änderungen an bestimmten Dateien oder die Abhängigkeit vom Ergebnis vorheriger Jobs \cite{cowell_automating_2023}. Ein als \texttt{manual} markierter Job wird erst gestartet, wenn ihn ein Nutzer über die Weboberfläche anstößt. Dieses Vorgehen wird für die Auslieferung von Anwendungen in Produktivumgebungen genutzt.
|
|
|
|
Manche Jobs erzeugen während ihrer Ausführung Dateien, die spätere Jobs weiterverarbeiten. Beispiele sind ein gebautes Bundle, ein Container-Image oder ein Testbericht. GitLab unterscheidet dafür zwei Mechanismen mit unterschiedlicher Lebensdauer:
|
|
\begin{itemize}
|
|
\item \textbf{Artefakte} sind die benannten Dateien, die ein Job als Ergebnis festhält \cite{gitlab_gitlab_nodate}. Nach einem erfolgreichen Durchlauf werden sie an die GitLab-Instanz hochgeladen, stehen dort zum Download bereit und lassen sich an spätere Jobs der Pipeline weitergeben.
|
|
\item \textbf{Caches} dienen ausschließlich dazu, wiederholte Ausführungen zu beschleunigen. Häufig werden heruntergeladene Abhängigkeiten zwischengespeichert. Das spart Bandbreite und Zeit und umgeht die Rate Limits der Paketquellen. Ein Cache wird nicht automatisch angelegt, sondern im Job über \texttt{cache} mit Pfaden und einem Schlüssel definiert. Er liegt zudem meist lokal auf dem Runner, der ihn erzeugt hat. Holt sich ein späterer Job einen anderen Runner, ist der Cache dort nicht zwangsläufig vorhanden. GitLab behandelt ihn deshalb als Beschleunigung auf Best-Effort-Basis, nicht als verlässliche Datenquelle.
|
|
\end{itemize}
|
|
Ein \emph{Environment} benennt das Ziel einer Auslieferung \cite{cowell_automating_2023}. Ein Environment fasst alle Auslieferungen in eine bestimmte Zielumgebung zusammen. In der Weboberfläche wird zusätzlich protokolliert, welche Version dort gerade läuft. Einen Sonderfall bilden \emph{Review-Apps}. Für jeden offenen Merge Request erzeugt GitLab automatisch eine isolierte, kurzlebige Auslieferung, in der die geänderte Anwendung vor dem Merge begutachtet werden kann \cite{gitlab_gitlab_nodate}. Nach Rostami Mazrae et al.~\cite{rostami_usage_2023} ist diese direkte Kopplung zwischen Review und laufender Vorschau eines der Merkmale, mit denen sich GitLab von alternativen Plattformen abgrenzt.
|
|
|
|
Auch eine Pipeline kann schlecht aufgebaut sein. Zampetti et al.~\cite{zampetti_empirical_2020} haben typische Fehler aus realen CI-Konfigurationen analysiert. Zwei treten besonders oft auf. Einmal laufen Stages nacheinander, obwohl sie parallel laufen könnten. Zum anderen werden Cache und Artefakte vermischt, sodass ein späterer Job auf eine Datei zugreifen will, die aber nicht zuverlässig vorhanden ist. Die Pipeline schlägt dann unregelmäßig fehl, wenn die Datei gerade nicht im Cache liegt.
|
|
|
|
\subsection{CI/CD-Komponenten}\label{subsec:komponenten}
|
|
|
|
Über \texttt{include} lassen sich ganze Pipeline-Fragmente aus anderen Repositories oder von einer externen \acs{URL} einbinden \cite{gitlab_gitlab_nodate}. Dabei treten zwei Probleme häufig auf. Eingebundene Fragmente werden meist nicht versioniert und haben keine eigene Schnittstelle. Eine Änderung am Fragment schlägt deshalb sofort auf alle Pipelines durch, die es einbinden \cite{cowell_automating_2023}.
|
|
|
|
Mit CI/CD-Komponenten lassen sich diese Probleme lösen. Eingeführt wurden sie experimentell in GitLab 16, allgemein verfügbar sind sie seit Version 17.0 \cite{gitlab_gitlab_nodate}. Eine Komponente ist ein versionierter und parametrisierbarer Baustein einer Pipeline. Komponenten werden in einem eigenen Projekt entwickelt, gespeichert und bereitgestellt. Zu ihnen gehören:
|
|
\begin{itemize}
|
|
\item \textbf{Manifest} Jede Komponente besitzt eine Manifest-Datei, üblicherweise \texttt{template.yml}. Darin wird festgelegt, welche Eingabeparameter die Komponente annimmt, jeweils mit Datentyp und meist einem Defaultwert. Ebenso stehen dort die Jobs, die die Komponente zur Pipeline beisteuert. Das Manifest bildet damit die Schnittstelle der Komponente. Es beschreibt, was sich von außen einstellen lässt und was die Komponente an die Pipeline liefert.
|
|
\item \textbf{Version} Komponenten werden über Git-Tags veröffentlicht. Im einbindenden Projekt wird eine Komponente mit einer festen Version ausgewählt, wahlweise auch mit einem Branch oder einem Commit. Mit einer festen Version ist sichergestellt, dass sich der Stand der Komponente nachträglich nicht ändert und die Pipeline dadurch nicht unerwartet bricht.
|
|
\item \textbf{Katalog} Wird eine Komponente als Release veröffentlicht, erscheint sie automatisch im \emph{CI/CD Catalog} der GitLab-Instanz. Dort lassen sich Komponenten suchen und einem Projekt zuordnen, ohne dass der genaue Repository-Pfad bekannt sein muss.
|
|
\end{itemize}
|
|
Listing~\ref{lst:component} zeigt die Einbindung einer beispielhaften Lint-Komponente in eine bestehende Pipeline.
|
|
|
|
\begin{lstlisting}[language=yaml,caption={Einbindung einer CI/CD-Komponente mit gesetzten \texttt{inputs}},label={lst:component}]
|
|
include:
|
|
- component: $CI_SERVER_FQDN/team/lint@1.2.0
|
|
inputs:
|
|
stage: lint
|
|
node-version: '20'
|
|
|
|
stages:
|
|
- lint
|
|
- test
|
|
\end{lstlisting}
|
|
|
|
Anders als beim klassischen \texttt{include} hat eine Komponente eine klar definierte Schnittstelle mit benannten Eingabeparametern. Die Werte setzt das einbindende Projekt im Block \texttt{inputs}. Innerhalb der Komponente stehen sie über die Schreibweise \texttt{\$[[ inputs.<name> ]]} zur Verfügung \cite{gitlab_gitlab_nodate}. Der Aufruf in Listing~\ref{lst:component} greift mit \texttt{\$CI\_SERVER\_FQDN} auf eine von GitLab vorgegebene Variable zu. Sie enthält den vollständigen Hostnamen der Instanz, den \ac{FQDN}, sodass der Pfad nicht fest in der Datei stehen muss. Nach Cowell et al.~\cite{cowell_automating_2023} bleibt durch diese Trennung von Schnittstelle und Implementierung die Pipeline-Logik wartbar. Tritt in einer Komponente ein Fehler auf, wird er an einer Stelle behoben und nicht in jeder einbindenden Pipeline.
|
|
|
|
\subsection{Anwendungsbeispiel}
|
|
|
|
Für die praktische Demonstration der zuvor behandelten Konzepte wurde eine Webanwendung auf der in Abschnitt~\ref{sec:selfhosted} beschriebenen selbstgehosteten Instanz aufgesetzt. Die Anwendung ist bewusst klein gehalten. So liegt der Fokus auf der \ac{CI}/\ac{CD}-Konfiguration und nicht auf dem Anwendungscode.
|
|
|
|
Die Demo ist ein \texttt{HTTP}-Dienst, der in \emph{Node.js} mit dem Web-Framework \emph{Express} geschrieben wurde und einen einzelnen Endpunkt \texttt{GET /} bereitstellt. Dieser Endpunkt liefert eine kleine HTML-Seite mit der Bezeichnung der aktuellen Umgebung und der im Build verbauten Versionskennung. Beide Werte werden zur Laufzeit über Umgebungsvariablen aus dem Container gelesen und im jeweiligen Deploy-Job gesetzt. Eine kleine Testsuite mit dem Framework \texttt{vitest} prüft, ob der Endpunkt eine gültige HTML-Antwort mit Statuscode~200 liefert. Der Test prüft zudem, ob die Seite die ausgelieferte Versionskennung enthält.
|
|
|
|
In der \texttt{.gitlab-ci.yml} sind drei Stages definiert: \texttt{lint}, \texttt{test} und \texttt{deploy}. Die Stage \texttt{deploy} enthält zwei Jobs, von denen pro Pipeline immer nur einer läuft. Welcher der beiden greift, hängt vom Branch ab. Tabelle~\ref{tab:demo-pipeline} fasst zusammen, welcher Schritt unter welchen Bedingungen ausgeführt wird und in welche Zielumgebung er ausliefert.
|
|
\begin{table}[H]
|
|
\centering
|
|
\caption{Stages der Demo-Pipeline und ihre Auslöser}
|
|
\label{tab:demo-pipeline}
|
|
\resizebox{\columnwidth}{!}{%
|
|
\begin{tabular}{@{}llll@{}}
|
|
\toprule
|
|
Stage / Job & Auslöser & Ergebnis & Environment \\ \midrule
|
|
\texttt{lint} & jeder Push & Stilbericht & -- \\
|
|
\texttt{unit-test} & jeder Push & Testbericht & -- \\
|
|
\texttt{deploy:staging} & alle Branches außer \texttt{main}, manuell & Container in Staging & \texttt{staging} \\
|
|
\texttt{deploy:prod} & Push auf \texttt{main}, automatisch & Container in Produktion & \texttt{production} \\ \bottomrule
|
|
\end{tabular}%
|
|
}
|
|
\end{table}
|
|
Die ersten beiden Stages werden bei jedem Push auf jeden Branch automatisch gestartet. Sie entsprechen den klassischen \ac{CI}-Schritten nach Duvall et al.~\cite{duvall_continuous_2007} und Fowler~\cite{fowler_continuous_2006}. Ihr Zweck ist es, für jeden Codestand ein schnelles, automatisches Feedback zu liefern. Die Stage \texttt{deploy} deckt den Continuous-Delivery-Anteil ab. Hier trennen sich zwei Pfade. Auf einem Feature-Branch steht der Job \texttt{deploy:staging} als manueller Schritt bereit, der in der Pipeline-Übersicht sichtbar ist. So lässt sich eine Änderung bei Bedarf in einer laufenden Vorschau begutachten und testen, bevor sie in den Hauptzweig übernommen wird. Ein Push auf \texttt{main} baut die Anwendung automatisch und liefert sie nach \texttt{production} aus. Nach der Begriffsabgrenzung von Shahin et al.~\cite{shahin_continuous_2017} entspricht der automatische Pfad nach \texttt{main} damit Continuous Deployment, während der manuelle Pfad nach Staging dem klassischen Continuous Delivery entspricht.
|
|
|
|
Diese Jobs laufen auf zwei verschiedenen Runnern. \texttt{lint} und \texttt{unit-test} nutzen einen gemeinsamen Runner mit Docker-Executor und führen ihre Schritte in einer Container-Umgebung mit \texttt{node:20-alpine} aus. Über \texttt{tags} sind die Deploy-Jobs an einen zweiten Runner mit Shell-Executor gebunden. Sie bauen das Image mit \texttt{docker build} und starten es anschließend mit \texttt{docker run}. Für diese Befehle braucht der Runner direkten Zugriff auf den Docker-Daemon des Hostsystems. Die Prüfung bleibt so in einem isolierten Container, während die Veröffentlichung selbst auf dem Hostsystem läuft.
|
|
|
|
Der Lebenszyklus einer Quellcodeänderung lässt sich mit dem Demo-Quellcode in drei Schritten nachvollziehen. Auf einem Feature-Branch entsteht eine sichtbare Änderung am Quellcode. Ein Commit überträgt sie in das Repository. Durch dieses Ereignis werden die Stages \texttt{lint} und \texttt{unit-test} automatisch ausgeführt und laufen im Hintergrund durch. Anschließend lässt sich der Job \texttt{deploy:staging} über die Weboberfläche starten. Bei erfolgreichem Ablauf ist die Version unter der Staging-Adresse aufrufbar, ohne dass die Änderung bereits im Hauptzweig liegt. Als letzter Schritt folgt der Merge Request gegen \texttt{main}, der von einem Nutzer manuell akzeptiert werden muss. Durch das Akzeptieren startet eine neue Pipeline auf \texttt{main} und beginnt erneut mit \texttt{lint} und \texttt{unit-test}. Danach löst sie automatisch \texttt{deploy:prod} aus, sodass die neueste Version ohne weiteren Eingriff auf dem Produktivsystem läuft.
|
|
|
|
\section{Selbstgehostete Lösung}\label{sec:selfhosted}
|
|
\authornote{Christopher Schmitt}
|
|
|
|
GitLab kann als \ac{SaaS} genutzt oder auf eigener Hardware selbst betrieben werden. Letzteres bezeichnet GitLab als \emph{Self-Managed} \cite{gitlab_gitlab_nodate}. Self-Managed-Instanzen sind dann interessant, wenn Datenschutz- oder Compliance-Vorgaben es verlangen, etwa wenn der Quellcode nicht auf fremden Servern liegen darf. Der zweite Fall ist der Kostenpunkt, denn bei steigender Nutzerzahl steigen auch die Kosten der \ac{SaaS}-Variante. Installation, Konfiguration, Aktualisierungen und Sicherungen müssen dafür allerdings selbst übernommen werden \cite{painter_practical_2024}. Im Folgenden wird beschrieben, wie eine solche Instanz im Rahmen dieses Papers auf einem privaten Linux-Server aufgesetzt wurde. Ein besonderes Augenmerk liegt dabei auf dem Aufwand für Einrichtung und Wartung.
|
|
|
|
\subsection{Installation}
|
|
|
|
Für die selbstgehostete Community Edition bietet GitLab das \emph{Omnibus-Paket} an \cite{painter_practical_2024}. Es bündelt alle Komponenten, die GitLab im Betrieb braucht, und liefert sie gleich mit. Laut der GitLab-Dokumentation \cite{gitlab_gitlab_nodate} zählen dazu der Webserver \texttt{nginx}, der Anwendungsserver \texttt{Puma}, die relationale Datenbank \texttt{PostgreSQL} und der Schlüssel-Wert-Speicher \texttt{Redis}. Weitere Hintergrundprozesse kommen hinzu. Abbildung~\ref{fig:omnibus-arch} zeigt das Zusammenspiel der einzelnen Bausteine.
|
|
|
|
\input{diagrams/omnibus_arch}
|
|
|
|
Beim Omnibus-Paket entfällt das einzelne Installieren und Abstimmen der Komponenten. Ein einzelner Aufruf des Paketmanagers genügt. Laut der GitLab-Dokumentation \cite{gitlab_gitlab_nodate} wird auf einem Debian-basierten System einmalig das GitLab-Paketrepository hinzugefügt, danach installiert \texttt{apt install gitlab-ce} den Rest.
|
|
|
|
In dieser Arbeit wurde die Community Edition von GitLab in Version 18.11 auf einem privaten Linux-Server unter Ubuntu 24.04 LTS installiert. GitLab empfiehlt für eine produktive Instanz mit bis zu 20 aktiven Nutzern mindestens vier Kerne und 8\,GiB Arbeitsspeicher \cite{gitlab_gitlab_nodate}. Für die Installation wurde ein Server mit einer 12-Kern-CPU, 64\,GiB Arbeitsspeicher und einer NVMe-SSD genutzt, der damit über der Empfehlung liegt. Die Installation lief ohne manuelle Eingriffe durch. Nach dem Absenden des Befehls wurden alle benötigten Pakete heruntergeladen und entpackt, woraufhin das mitgelieferte Skript \texttt{gitlab-ctl reconfigure} die gesamte GitLab-Instanz innerhalb von Minuten in einen lauffähigen Zustand brachte. Direkt nach Abschluss der Installation ließ sich die Weboberfläche öffnen, und eine erste Anmeldung war sofort möglich.
|
|
|
|
Um die Instanz nicht nur lokal aufrufen zu können, wurde sie über ein privates Overlay-Netzwerk auf VPN-Basis erreichbar gemacht. Theoretisch ließe sich die Instanz über eine öffentliche IP-Adresse ans Internet anbinden, mit einer Domain versehen und per HTTPS absichern. Das würde aber unnötig eine Angriffsfläche nach außen schaffen. Für die Demo reicht der gewählte Aufbau aus. Der Webserver \texttt{nginx} bindet sich daher nur an die lokale Schnittstelle und ist von außen nicht direkt erreichbar. Ein Proxy des Overlay-Dienstes nimmt die Anfragen auf demselben Host entgegen und reicht sie an den lokalen Port weiter. Das zugehörige Zertifikat stellt der Proxy selbst aus, sodass die Verbindung über HTTPS verschlüsselt ist. Zugriff haben nur Geräte, die im Overlay-Netzwerk freigegeben sind. Die Ports 80 und 443 sind zu keinem Zeitpunkt zum öffentlichen Internet geöffnet.
|
|
|
|
\subsection{Konfiguration}
|
|
|
|
Eine Self-Managed-Instanz kann über die zentrale Datei \texttt{/etc/gitlab/gitlab.rb} konfiguriert werden \cite{painter_practical_2024}. Die einzelnen Komponenten des Omnibus-Pakets lesen ihre Einstellungen aus dieser Datei. Durch den Befehl \texttt{gitlab-ctl reconfigure} werden die Komponenten mit dem aktuellen Stand neu initialisiert. Tabelle~\ref{tab:gitlabrb} zeigt eine Auswahl der für den vorliegenden Aufbau relevanten Optionen.
|
|
\begin{table}[H]
|
|
\centering
|
|
\caption{Auswahl an Konfigurationsoptionen aus \texttt{gitlab.rb} \cite{gitlab_gitlab_nodate, painter_practical_2024}}
|
|
\label{tab:gitlabrb}
|
|
\resizebox{\columnwidth}{!}{%
|
|
\begin{tabular}{@{}lll@{}}
|
|
\toprule
|
|
Option & Wert & Zweck \\ \midrule
|
|
\texttt{external\_url} & \texttt{https://<host>} & von außen sichtbare \acs{URL} \\
|
|
\texttt{nginx['listen\_addresses']} & \texttt{['127.0.0.1']} & nur lokales Binding \\
|
|
\texttt{letsencrypt['enable']} & \texttt{false} & kein eigenes Zertifikat \\
|
|
\texttt{registry['enable']} & \texttt{true/false} & Container Registry \\
|
|
\texttt{gitlab\_rails['smtp\_*']} & Provider & Versand von Systemmails \\
|
|
\texttt{puma['worker\_processes']} & \texttt{0--n} & Anzahl Anwendungsprozesse \\
|
|
\texttt{sidekiq['max\_concurrency']} & \texttt{8--25} & Hintergrundjobs parallel \\
|
|
\texttt{backup\_keep\_time} & in Sekunden & Aufbewahrungsdauer Backups \\ \bottomrule
|
|
\end{tabular}%
|
|
}
|
|
\end{table}
|
|
|
|
Die \ac{SaaS}-Variante bietet eine geführte Erstkonfiguration, die Self-Managed-Variante nicht \cite{painter_practical_2024}. Nach der Installation läuft die Instanz in einem Standardzustand. Der Aufwand der Installation war geringer als erwartet. Die reine Paketinstallation war insgesamt in unter zehn Minuten abgeschlossen. Danach mussten an der Datei \texttt{gitlab.rb} nur wenige Optionen angepasst werden, die die Erreichbarkeit über das Overlay-Netzwerk sowie das lokale Binding von \texttt{nginx} steuern. Die meisten Einstellungen ließen sich über die Weboberfläche erledigen, darunter Gruppen und Projekte, Nutzer sowie die Registrierung der Runner.
|
|
|
|
\subsection{Betrieb}
|
|
|
|
Die GitLab-Instanz kann über das Kommandozeilenwerkzeug \texttt{gitlab-ctl} gesteuert werden. Mit diesem lassen sich die Hintergrundprozesse starten, stoppen und überwachen \cite{painter_practical_2024}. Um GitLab zu aktualisieren, dienen der Paketmanager und das von GitLab vorgegebene Versionsschema. Falls mehrere Hauptversionen übersprungen werden sollen, muss trotzdem eine feste Reihenfolge eingehalten werden, um die Instanz nicht zu beschädigen. Die enthaltenen Datenbankmigrationen bauen aufeinander auf, und ein direkter Sprung von einer alten auf eine deutlich neuere Version kann den Datenbestand inkonsistent zurücklassen \cite{gitlab_gitlab_nodate}. Eine Sicherung kann mit dem Befehl \texttt{gitlab-backup create} erzeugt werden und umfasst die \texttt{PostgreSQL}-Datenbank sowie alle Repositories und Artefakte.
|
|
|
|
Der bereits in Abschnitt~1.3 angesprochene Datenbankvorfall bei GitLab.com aus dem Jahr 2017 zeigt, was ein nicht funktionierendes Backup anrichtet. Durch eine fehlerhafte manuelle Aktion gingen sechs Stunden an Nutzerdaten verloren \cite{GitLabcomDatabaseIncident}. Betroffen war zwar die \ac{SaaS}-Instanz, das Beispiel ist aber auch für eine eigene Instanz von Bedeutung. Ein Backup, das nicht regelmäßig in einem Restore-Test überprüft wird, ist im Ernstfall kein Backup. Die eigentlichen Aufwände einer Self-Managed-Instanz fallen nicht bei der Erstinstallation an, sondern über deren Lebensdauer \cite{painter_practical_2024}. Sicherheitsaktualisierungen und regelmäßige Restore-Tests kommen laufend hinzu, ebenso die Überwachung der Datenbankkonsistenz.
|
|
|
|
\section{Evaluierung}
|
|
|
|
Die vorigen Kapitel haben GitLabs CI/CD-Konzepte und die Einrichtung einer eigenen Instanz behandelt. Dieses Kapitel bewertet die Plattform im Eigenbetrieb anhand von sechs Kriterien. Wie dabei vorgegangen wird, legt der folgende Abschnitt offen.
|
|
|
|
\subsection{Vorgehensweise}
|
|
\authornote{Christopher Schmitt}
|
|
|
|
Bewertet wird GitLab als selbstbetriebene Plattform. Die Bewertung stützt sich, soweit möglich, auf die in Abschnitt~\ref{sec:selfhosted} beschriebene selbstgehostete \ac{CE}-Instanz und die \ac{CI}/\ac{CD}-Pipeline aus dem Anwendungsbeispiel. Kompatibilität, Skalierbarkeit und Dokumentation lassen sich an einer einzelnen Instanz nicht erproben und werden auf Ebene der Plattform beurteilt.
|
|
|
|
Die Bewertung folgt einem Katalog aus sechs Kriterien. Drei davon, Performanz, Sicherheit und Kompatibilität, orientieren sich an etablierten Softwarequalitätsmerkmalen. Nachhaltigkeit, Skalierbarkeit und Dokumentation kommen hinzu, weil sie im Eigenbetrieb stärker ins Gewicht fallen als bei einer fertig betriebenen \ac{SaaS}-Lösung.
|
|
|
|
Innerhalb jedes Kriteriums wird qualitativ argumentiert. Herangezogen werden die offizielle GitLab-Dokumentation, die im jeweiligen Abschnitt zitierte Literatur und die eigenen Beobachtungen aus Installation und Betrieb. Die Performanz bildet die Ausnahme, denn dort werden die Laufzeiten der Demo-Pipeline gemessen und gegenübergestellt.
|
|
|
|
Zwei Grenzen sind vorab zu nennen. Die Instanz läuft auf einem einzelnen Server. Aussagen zur Skalierbarkeit stützen sich daher auf die Architektur und die Herstellerangaben, nicht auf einen eigenen Lasttest. Die gemessenen Laufzeiten stammen aus einer bewusst kleinen Anwendung und zeigen Größenordnungen, keine repräsentativen Benchmarks.
|
|
|
|
\subsection{Performanz}
|
|
\authornote{Christopher Schmitt}
|
|
|
|
Die reine Nutzlast der \ac{CI}-Jobs im Anwendungsbeispiel ist winzig. Auf einem Entwicklungsrechner mit 16 Kernen braucht \texttt{npm ci} für die 221 Pakete des Projekts rund eine Sekunde, bei warmem npm-Cache und zuvor leerem \texttt{node\_modules}. \texttt{npm run lint} braucht etwa 0,3 Sekunden, \texttt{npm test} etwa 0,8 Sekunden. Tabelle~\ref{tab:perf-local} listet diese lokalen Referenzwerte. Sie sind keine Runner-Zeiten und sollen nur die Größenordnung der eigentlichen Arbeit zeigen.
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\caption{Lokale Referenzmessungen der Pipeline-Schritte auf einem Entwicklungsrechner, nicht auf dem GitLab Runner}
|
|
\label{tab:perf-local}
|
|
\begin{tabular}{@{}lll@{}}
|
|
\toprule
|
|
Schritt & Befehl & Gemessene Zeit \\ \midrule
|
|
Abhängigkeiten & \texttt{npm ci} & rund 1\,s \\
|
|
Lint & \texttt{npm run lint} & rund 0,3\,s \\
|
|
Test & \texttt{npm test} & rund 0,8\,s \\ \bottomrule
|
|
\end{tabular}
|
|
\end{table}
|
|
|
|
Auf dem Runner, gemessen an der Demo-Pipeline auf der selbstgehosteten Instanz, liegen die Zeiten deutlich höher. Erfasst wurde sie einmal in der ursprünglichen Konfiguration und einmal mit zwei Korrekturen am Cache. Tabelle~\ref{tab:perf-runner} stellt die drei Varianten gegenüber. In jeder Variante liegt die eigentliche Prüfung unter einer Sekunde. Den Rest eines Jobs verbrauchen der Start des \texttt{node:20-alpine}-Containers, das Klonen des Repositorys, die Installation der Abhängigkeiten und je nach Konfiguration, das Sichern und Wiederherstellen des Caches.
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\caption{Runner-Laufzeiten der Demo-Pipeline nach Cache-Konfiguration in Sekunden, Steady-State mit Cache-Treffer, reine Ausführungszeit ohne die Wartezeit in der Warteschlange}
|
|
\label{tab:perf-runner}
|
|
\begin{tabular}{@{}lrr@{}}
|
|
\toprule
|
|
Cache-Konfiguration & \texttt{lint} & \texttt{unit-test} \\ \midrule
|
|
\texttt{node\_modules} (Original) & 10,5 & 11,2 \\
|
|
ohne Cache & 7,6 & 7,8 \\
|
|
npm-Paketspeicher \texttt{.npm} & 7,7 & 8,3 \\ \bottomrule
|
|
\end{tabular}
|
|
\end{table}
|
|
|
|
Unsere erste Pipeline legte das falsche Verzeichnis im Cache ab. Sie speicherte \texttt{node\_modules} unter einem aus \texttt{package.json} berechneten Schlüssel. Dass das nichts bringt, fiel uns erst im Messlauf auf. Der erste Schritt jedes Jobs ist nämlich \texttt{npm ci}, und dieser Befehl löscht \texttt{node\_modules} vor der Installation und baut es neu auf. Ob der Cache traf oder nicht, änderte an der Laufzeit darum nichts. Im Treffer-Lauf meldete der Runner \texttt{Successfully extracted cache}, im Lauf ohne Treffer \texttt{Cache file does not exist}. \texttt{npm ci} installierte die 221 Pakete in beiden Fällen in rund zwei Sekunden neu. Der Cache kostete hier sogar Zeit, statt sie zu sparen. Das große \texttt{node\_modules}-Verzeichnis wurde bei jedem Job gesichert und wiederhergestellt, ohne je genutzt zu werden. Nach Zampetti et al.~\cite{zampetti_empirical_2020} ist eine solche Fehlkonfiguration von Cache und Artefakten ein häufiger Schwachpunkt von CI-Pipelines, und genau das ist uns im Anwendungsbeispiel passiert.
|
|
|
|
Daraufhin wurden zwei Korrekturen gemessen. Ohne Cache entfällt das Sichern und Wiederherstellen. Der \texttt{lint}-Job sank von 10,5 auf 7,6 Sekunden, der \texttt{unit-test}-Job von 11,2 auf 7,8. Allein das Entfernen des unwirksamen Caches sparte also rund drei Sekunden je Job. Die zweite Korrektur legt statt \texttt{node\_modules} den npm-Paketspeicher unter \texttt{.npm} im Cache ab und ruft \texttt{npm ci} mit den Optionen \texttt{-{}-cache .npm} und \texttt{-{}-prefer-offline} auf. Bei einem Treffer installierte \texttt{npm ci} dann aus dem lokalen Speicher, die Installationszeit sank von rund zwei Sekunden auf knapp eine (981\,ms im Messlauf). Am Job änderte das hier kaum etwas, weil der eingesparte Download durch den Aufwand für den \texttt{.npm}-Cache wieder aufgewogen wurde. Der Paket-Cache lohnt sich erst, wenn der Download die Installation bestimmt. Auf dieser Instanz mit schnellem Zugriff auf die Registry ist das nicht der Fall. Caches beschleunigen wiederholte Läufe also nicht von selbst. Anders als Artefakte sind sie über Jobgrenzen hinweg nicht garantiert verfügbar \cite{cowell_automating_2023}.
|
|
|
|
Der \ac{DAG} über \texttt{needs}, also die Verknüpfung der Jobs nach ihren Abhängigkeiten, wirkt sich hier kaum aus. Die Demo-Pipeline läuft nahezu linear, erst \texttt{lint}, dann \texttt{test}, dann \texttt{deploy}, und bietet kaum Parallelität, die sich lohnt. Bei größeren Pipelines sieht das anders aus. Dort ist die Parallelisierung serieller Stages der wichtigste Ansatzpunkt. Nach Zampetti et al.~\cite{zampetti_empirical_2020} sind serielle Stages, die parallel laufen könnten, ein typischer Konfigurationsfehler. Der \texttt{deploy:staging}-Job baut das Docker-Image neu. Das Image würde die Abhängigkeiten ein weiteres Mal installieren, doch der Docker-Layer-Cache hält den Abhängigkeits-Layer vor und überspringt diesen Schritt. So lief der Job im Messlauf in 2,3 Sekunden durch. Erst eine Änderung an \texttt{package.json} macht den Cache-Layer ungültig und löst die Installation erneut aus.
|
|
|
|
\subsection{Nachhaltigkeit}
|
|
|
|
\authornote{Roman Schöne}
|
|
|
|
Unter Betrachtung der in \ref{sec:scalability} erwähnten Referenzarchitektur wird betrachtet inwiefern die von GitLab vorgeschlagenen Architekturen sich bezüglich ihres Stromverbrauchs bemessen. GitLab liefert Empfehlungen für virtuelle Server für die Anbieter \ac{GCP}, \ac{AWS} und Azure. Ausgenommen von GPUs sind CPU und Arbeitsspeicher die Komponenten mit dem höchsten Stromverbrauch \cite{davyEstimatingAWSEC22022}. im folgenden wird eine Schätzung abgeben, die auf Daten von \ac{AWS} Instanzen basieren und sich auf die Leistung von CPU und Arbeitsspeicher beschränkt. Im konkreten werden Instanzen der Typen \texttt{c5, c5n} und \texttt{m5} in der GitLab Empfehlung erwähnt. \\
|
|
Für unterschiedliche \ac{AWS}-Instanzen liefert \cite{davyEstimatingAWSEC22022} einen Datensatz der Auskunft über die Leistung in Watt nach Anzahl der vCPUS ,unter einer bestimmten Auslastung, gibt. Eine \ac{vCPU} bezeichnet eine virtualisierte Variante einer physischen CPU \cite{VCPUWasIst2023}. Es ist zu beachten, dass die Testdaten aus \ref{tab:power_consumption} untertakteten Instanzen stammen und bieten eine untere Schätzung. Ebenso wird angenommen, dass der Arbeitsspeicher immer in Relation zur Anzahl von \ac{vCPU}s liegt und ist in \ref{tab:power_consumption} miteinbezogen.
|
|
\begin{table}[H]
|
|
\centering
|
|
\resizebox{\columnwidth}{!}{%
|
|
\begin{tabular}{@{}llllllllllll@{}}
|
|
\toprule
|
|
Typ & 0\% & 10\% & 20\% & 30\% & 40\% & 50.00\% & 60\% & 70\% & 80\% & 90\% & 100\% \\ \midrule
|
|
c5 & 2 & 2.3 & 2.7 & 3.1 & 3.5 & 3.8 & 4.2 & 4.6 & 5 & 5.4 & 5.7 \\
|
|
m5 & 2.1 & 2.4 & 2.8 & 3.1 & 3.4 & 3.7 & 4.1 & 4.4 & 4.7 & 5 & 5.4 \\ \bottomrule
|
|
\end{tabular}%
|
|
}
|
|
\caption{Leistung in Watt nach vCPUs abgestuft nach Auslastung \cite{davyEstimatingAWSEC22022}}
|
|
\label{tab:power_consumption}
|
|
\end{table}
|
|
Da in \ref{tab:power_consumption} keine Daten zu \texttt{c5n} enthalten sind, werden die Werte mit denen von \texttt{c5} geschätzt. Abbildung \ref{fig:estimation_power} zeigt die Schätzung der Leistung für die empfohlenen Referenzarchitekturen nach Benutzern für eine Abstufung nach Auslastung.
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\columnwidth]{./bilder/power_consumption.png}
|
|
\caption{Schätzung der Leistung nach Anzahl der Nutzer}
|
|
\label{fig:estimation_power}
|
|
\end{figure}
|
|
Die grösstmögliche Architektur kann dabei eine Leistung von ca. 1548 Watt bis zur Höchstlast von 4412 Watt in Anspruch nehmen. Die kleinste Architektur liegt bei ungefähr maximal 46 Watt. Ein durchschnittlicher Office-PC benötigt 135 bis 200 Watt die Stunde. Ein Gaming-PC üblicherweise bis zu 350 Watt \cite{WievielStromVerbraucht}.
|
|
|
|
\subsection{Sicherheit}
|
|
|
|
\authornote{Roman Schöne}
|
|
|
|
Eine eigene GitLab-Instanz kann so konfiguriert werden, dass diese dem US-amerikanischen Sicherheitsstandard \acs{NIST} \acs{SP} 800-53 für Sicherheitskontrolle in Informationssystemen des \acl{NIST} entsprechen \cite{gitlab_gitlab_nodate}. Die Angebote von GitLab, damit die \ac{SaaS}-Version bzw. GitLab.com und GitLab Dedicated sind nach \acs{ISO}/\acs{IEC} 27001:2022 zertifiziert \cite{GitLab16522166}. Diese Norm spezifiziert Anforderungen an Informationssicherheitsmanagement im Kontext von Organisationen. Für dieselben Angebote besitzt GitLab eine Zertifizierung nach Norm \acs{ISO}/\acs{IEC} 27017:2015, welche Richtlinien zur Kontrolle der Informationssicherheit von Cloud-Services festlegt \cite{gitlab_gitlab_nodate}. Es ist zu erwarten, dass diese Norm bald erneuert wird \cite{ISOIEC27017}.
|
|
Im folgenden Abschnitt wird darauf eingegangen mit welchen Möglichkeiten wie GitLab vor Angriffen von ausserhalb geschützt werden kann. Die GitLab-Dokumentation unterteilt Schutzmassnahmen in die Kategorien \cite{gitlab_gitlab_nodate}:
|
|
\begin{itemize}
|
|
\item Anwendungseinstellungen
|
|
\item \acs{CI}/\acs{CD} Einstellungen
|
|
\item Konfigurationseinstellungen
|
|
\item Betriebssystemeinstellungen
|
|
\end{itemize}
|
|
Innerhalb der Anwendungseinstellungen können Push-Regeln festgelegt werden, sodass unverifizierte Nutzer keinen Code hochladen können. Es kann sichergestellt werden, dass geheime Dateien nicht versehentlich gepusht werden. Ebenso sollen projektspezifische Keys/Schlüssel für Deployment-Prozesse eingerichtet werden. Die Sichtbarkeit von Gruppen, Snippets und Projekten soll als \textit{privat} gesetzt werden. Benutzer können gezwungen werden komplexere Passwörter für ihren GitLab Account zu wählen. Eine 2-Faktor Authentifizierung kann eingerichtet werden. Die Kommunikation mit aussenstehenden Systemen kann konkret festgelegt werden, indem externe Systeme zuerst anhand ihrer IP-Adresse für ausgehende anfragen in GitLab eingetragen werden müssen. Für die GitLab-Instanz kann ein Rate-Limit, mit Ausnahmen für bestimmte Nutzer, eingestellt werden \cite{gitlab_gitlab_nodate}.\\
|
|
Für \acs{CI}/\acs{CD}-Pipelines besteht die Möglichkeit sensitive Daten wie bspw. Passwörter oder Tokens in Form von \textit{Secrets} zu speichern. \textit{Secrets} werden durch externe Dienstleister gespeichert. Neben \textit{Secrets} besteht die weniger empfohlene Variante Daten in Form von \textit{CI/CD Variablen} zu speichern. Eine solche Variable kann maskiert werden und wird damit in Logs durch einen definierten Platzhalter ersetzt. Zusätzlich zu einer Maskierung erlaubt GitLab \textit{CI/CD Variablen} verstecken. Die Variable wird nicht mehr auf der Einstellungsseite für \acs{CI}/\acs{CD} angezeigt. Ebenso kann eine Variable geschützt werden, sodass nur auf geschützten Branches Zugriff besteht \cite{gitlab_gitlab_nodate}.\\
|
|
Eine GitLab-Instanz kann konfiguriert werden, sodass Nutzer per \ac{SSH} oder über \ac{HTTPS} zugreifen können \cite{gitlab_gitlab_nodate}. Ein Zugang kann auf eine der beiden Protokolle beschränkt werden. \ac{HTTPS} ist meist zugänglicher für Nutzer, da wenig Konfiguration vorgenommen werden muss. Allerdings wird die Authentifizierung per Nutzername und \textit{Access Token} oder Passwort vorgenommen, welche häufig erneut abgefragt werden. \ac{SSH} bietet eine höhere Sicherheit. Es ist sichergestellt, dass keine Daten während der Übertragung unbemerkt verändert werden. Die Konfiguration ist komplexer. \ac{SSH} basiert auf einer \textit{Public-Key}-Infrastruktur, wobei der öffentliche Schlüssel auf GitLab zuvor hinterlegt werden muss \cite{marijanSSHVsHTTPS2025}. GitLab kann ebenso E-Mails versenden und empfangen. Versendete E-mails können mit \ac{S/MIME} versendet signiert werden, um deren Legitimität zu sichern \cite{gitlab_gitlab_nodate}. \ac{S/MIME} ist ein Verschlüsselungsstandard für E-Mails.\\
|
|
Die GitLab-Dokumentation bietet zusätzlich Konfigurationen für das unterliegende Betriebssystem, wie \ac{SSH}-Konfiguration, Firewall Einstellungen und Änderungen am Kernel \cite{gitlab_gitlab_nodate}.
|
|
|
|
\subsection{Kompatibilität}
|
|
|
|
\authornote{Roman Schöne}
|
|
|
|
GitLab als Code-Hosting Plattform ist ausschließlich für Linux-Distributionen erhältlich. Es wird differenziert in die kostenpflichtige Variante \ac{EE} und die kostenlose \ac{CE} erhältlich.
|
|
GitLab plant Pakete hauptsächlich für Betriebssysteme, mit \ac{LTS} Versionen zu veröffentlichen. Releases werden nicht mehr publiziert, wenn der Anbieter des Betriebssystems \ac{EOL} des Systems bekannt gibt. GitLab nimmt sich die Freiheit unter anderen Gründen den Support für ein Betriebssystem einzustellen \cite{gitlab_gitlab_nodate}:
|
|
\begin{itemize}
|
|
\item \emph{keine Wirtschaftlichkeit}, da zu hohe Wartungskosten oder zu wenig Kunden auf die Technologien setzen
|
|
\item \emph{technische Einschränkungen} wie bspw. zusätzliche Abhängigkeiten, Sicherheitsanforderungen oder technologische Veränderungen die eine Erstellung von Paketen erschwert oder unmöglich gestaltet.
|
|
\end{itemize}
|
|
Die folgende Tabelle \ref{tab:supported_os} zeigt eine Auflistung aller aktuell (zum 15.05.2026) von GitLab unterstützten Betriebssysteme und ihrer Architekturen (Siehe \url{https://docs.gitlab.com/install/package/#supported-platforms}). Neben den offiziellen GitLab-Paketen existieren ebenso inoffizielle Pakete der GitLab-Community.
|
|
\begin{table}[H]
|
|
\centering
|
|
\resizebox{\columnwidth}{!}{%
|
|
\begin{tabular}{@{}llll@{}}
|
|
\toprule
|
|
Betriebsystem & OS-Version & CE & EE \\ \midrule
|
|
AlmaLinux & 8-10 & X & X \\
|
|
Amazon Linux & 2, 2023 & X & X \\
|
|
Debian & 11-13 & X & X \\
|
|
openSUSE Leap & 15.6 & X & X \\
|
|
\makecell[cl]{SUSE Linux Enterprise Server} & 12 & & X \\
|
|
Oracle Linux & 8-9 & X & X \\
|
|
\makecell[cl]{Red Hat Enterprise Linux } & 8-10 & X & X\\
|
|
Ubuntu & \makecell[cl]{22.04, 24.04} & X & X\\ \bottomrule
|
|
\end{tabular}%
|
|
}
|
|
\caption{unterstützte Betriebssysteme für GitLab}
|
|
\label{tab:supported_os}
|
|
\end{table}
|
|
Folgende Installationsmöglichkeiten existieren für \ac{CE} und \ac{EE} sowie für GitLab Runner:
|
|
\begin{itemize}
|
|
\item Installation als Linux-Paket für das jeweilige unterstützte Betriebssystem
|
|
\item Betrieb mithilfe des offiziellen Docker-Images von GitLab (Siehe Dockerhub: \url{https://hub.docker.com/u/gitlab})
|
|
\item In Kubernetes als Helm-Chart, einem speziellen Paketformat für Kubernetes
|
|
\item Installation per GitLab Operator unter Kubernetes. Ein Kubernetes Operator erlaubt Erweiterung bzgl. des Verhaltens von Clustern, ohne dabei den ursprünglichen Quellcode von Kubernetes zu modifizieren.
|
|
\end{itemize}
|
|
GitLab Runner kann für eine grössere Auswahl an Betriebssystemen installiert werden. Tabelle \ref{tab:runner_os} zeigt eine Auflistung unterstützter Versionen von Betriebssystemen für GitLab Runner. Wird - verwendet, so ist keine Version von GitLab für das jeweilige Betriebssystem spezifiziert.
|
|
\begin{table}[H]
|
|
\centering
|
|
\resizebox{\columnwidth}{!}{%
|
|
\begin{tabular}{@{}ll@{}}
|
|
\toprule
|
|
Betriebsystem & OS-Version \\ \midrule
|
|
Debian & 11-15 \\
|
|
Mint & 21-22.1 \\
|
|
Raspbian & 11-15 \\
|
|
Ubuntu & 25.10, 24.04, 22.04, 20.4, 18.04 \\
|
|
Amazon Linux & 2, 2023, 2025 \\
|
|
\makecell[cl]{Red Hat\\Enterprise Linux} & 7-10 \\
|
|
Fedora & 42, 43 \\
|
|
Oracle Linux & 10, 9, 8, 7 \\
|
|
openSUSE & 16.0, 15.6 \\
|
|
\makecell[cl]{SUSE Linux\\Enterprise Server} & 15.7, 15.6, 15.5, 15.4, 12.5 \\
|
|
FreeBSD & - \\
|
|
Windows & - \\
|
|
macOS & - \\ \bottomrule
|
|
\end{tabular}%
|
|
}
|
|
\caption{Unterstützte Betriebssysteme für GitLab Runner}
|
|
\label{tab:runner_os}
|
|
\end{table}
|
|
Zur Kompatibilität zählt ebenso die Möglichkeit von anderen Code-Hosting Plattformen zu GitLab zu migrieren. GitLab bietet nur die Möglichkeit an automatische Migrationen von Git-Repositories durchzuführen. Auf \ac{SVN} basierende Repositories müssen externe Werkzeuge (wie bspw. \href{https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git}{git svn} oder \href{http://www.catb.org/~esr/reposurgeon/repository-editing.html}{Reposurgeon})verwendet werden um diese in Git-Repositories umzuwandeln. Für eine Auswahl an Plattformen bietet GitLab ein Migrationswerkzeug. Bei der Migration werden, bspw. für Autoren von Commits, Platzhalter für Nutzer eingeführt. Es besteht die Möglichkeit Platzhalter-Nutzer wieder realen Nutzern der GitLab-Instanz zuzuordnen. Dieser Vorgang wird als \textit{Post-migration Mapping} bezeichnet. Wird das Projekt in den Namensraum eines Nutzers exportiert, ist eine solche Zuordnung nicht möglich. Stattdessen wird dem Besitzer des Namensraum jeder Beitrag zugeordnet. \cite{gitlab_gitlab_nodate}. Tabelle \ref{tab:migration_platforms} zeigt eine Auflistung an Plattformen bzw. Repositories, für die Gruppen oder Projekte, unter der Verwendung des Migrationswerkzeug und \textit{Post-migration Mapping} importiert werden können.
|
|
\begin{table}[H]
|
|
\centering
|
|
\resizebox{\columnwidth}{!}{%
|
|
\begin{tabular}{@{}lllll@{}}
|
|
\toprule
|
|
Ursprung & Gruppen & Projekte & Werkzeug & Post-Mapping \\ \midrule
|
|
GitLab (direkter Transfer) & X & X & X & X \\
|
|
GitLab (Datei-Export) & X & X & X & - \\
|
|
Bitbucket Server & - & X & X & X \\
|
|
GitHub & - & X & X & X \\
|
|
Gitea & - & X & X & X \\
|
|
Bitbucket Cloud & - & X & X & - \\
|
|
FogBugz & - & X & X & - \\
|
|
Git Repo (Manifest) & - & X & X & - \\
|
|
Git Repo (URL) & - & X & X & - \\
|
|
IBM Devops ClearCase & - & X & - & - \\
|
|
\acs{CVS} & - & X & - & - \\
|
|
Perforce P4 & - & X & - & - \\
|
|
Subversion & - & X & - & - \\
|
|
\acs{TFVC} & - & X & - & - \\
|
|
Jira (Issues) & - & - & X & - \\ \bottomrule
|
|
\end{tabular}}
|
|
\caption{Migrations-Unterstützung von GitLab}
|
|
\label{tab:migration_platforms}
|
|
\end{table}
|
|
\subsection{Skalierbarkeit}
|
|
|
|
\authornote{Roman Schöne}
|
|
|
|
\label{sec:scalability}
|
|
|
|
GitLab bietet Empfehlungen für Referenz-Architekturen, welche versuchen einen Kompromiss zwischen den Kriterien Performanz, Resilienz und finanzieller Kosten herzustellen. Um eine passende Architektur zu wählen orientiert sich GitLab an der Metrik: \ac{RPS}. Die RPS lassen sich nach Anfrage in folgende Typen aufgliedern \cite{gitlab_gitlab_nodate}:
|
|
\begin{itemize}
|
|
\item API-Anfragen, zu welchen Integrationen, Automatisierungsmechanismen, Webhooks und sonstige Dienste, die direkt auf die API zugreifen, gehören. Üblicherweise machen API-Anfragen ca. 80 \% der gesamten \ac{RPS}-Anfragen aus
|
|
\item Web-Anfragen, dazu zählen beispielsweise Interaktionen mit der Weboberfläche. Dies sind meist 10 \% aller Anfragen.
|
|
\item Git-Operationen, hier aufgeschlüsselt in Pull und Push-Operationen. Git-Operationen entsprechen ca. den restlichen 10 \%.
|
|
\end{itemize}
|
|
Mithilfe von Abbildung \ref{fig:reference_architecture_users} lassen sich ungefähre Anzahlen an \ac{RPS} nach Benutzern einer GitLab-Instanz schätzen. In einigen Umgebungen können die vorgegebenen Schätzungen von der Realität stark abweichen. Es ist notwendig selbst zu evaluieren wie das System aufgebaut werden soll bzw. welche Anpassungen gemacht werden müssen.
|
|
\begin{figure}[H]
|
|
\includegraphics[width=\columnwidth]{bilder/gitlab_reference_architecture_users_rps.png}
|
|
\caption{RPS-Schätzung nach Benutzeranzahl für das Linux-Paket \cite{gitlab_gitlab_nodate}}
|
|
\label{fig:reference_architecture_users}
|
|
\end{figure} Im Grunde gibt es zwei Möglichkeiten zu skalieren. Dazu zählt die Verwendung einer höher liegenden Referenzarchitektur oder die horizontale oder Vertikale Skalierung einzelner Komponenten. Bei der horizontalen Skalierung müssen Lastverteiler hinzugefügt und konfiguriert werden.
|
|
GitLab bietet für einige Intervalle an zu erwartenden Nutzern (bzw. \ac{RPS}) Empfehlungen für \href{https://docs.gitlab.com/administration/reference_architectures/}{Architekturen}. Alle Architekturen sind auf eine Skalierung ausgelegt. Weitere Funktionalitäten ergeben erst ab einer gewissen Anzahl an Benutzern Sinn. Der Betrieb von GitLab im High Availability Modus stellt sicher, dass einzelne Komponenten bei Ausfällen kompensiert werden können. Durch Redundanz ergeben sich höhere finanzielle Kosten. GitLab gibt an, dass die Schwelle für den Betrieb im High Availability Modus bei ungefähr 3000 Nutzern liegt \cite{gitlab_gitlab_nodate}. Um die Ausfallsicherheit weiter zu erhöhen kann GitLab in mindestens zwei Umgebungen innerhalb unterschiedlicher Regionen betrieben werden. Dies führt zu höheren Kosten und einem komplexeren Systemaufbau. Einzelne Komponenten von GitLab können in die Cloud ausgelagert werden. Grössere Repositories, sogenannte \textit{Monorepos} mit einer Grösse von mehreren Gigabytes, können zu hohen Auslastungen führen. Die hohe Last ist softwareseitig begründet, weshalb die Skalierung durch zusätzliche Hardware kaum Nutzen erweist. Die Auslastung kann durch Optimierungsmassnahmen am Repository verringert werden \cite{gitlab_gitlab_nodate}. Eine Gefahr beim Skalieren einer einzelnen Komponente kann zu ungewollten Konsequenzen führen. Die empfohlenen Architekturen sind so ausgelegt, dass die Skalierung der einzelnen Komponenten in einem passenden Verhältnis zueinander stehen. Besitzt eine skalierte Komponente einen höheren Durchlauf kommt die Last, bei abhängigen Komponenten an. Diese müssen zwangsweise ebenso skaliert werden \cite{gitlab_gitlab_nodate}.
|
|
|
|
\subsection{Dokumentation}
|
|
|
|
\authornote{Roman Schöne}
|
|
|
|
Alle GitLab-Lösungen sind umfassend auf der offiziellen Seite \url{https://docs.gitlab.com/} dokumentiert. Jeder Release von GitLab besitzt bezüglich Haupt- und Nebenversionsnummer eine eigene Version der Dokumentation. Online kann auf eine eingeschränkte Auswahl zugegriffen werden. Nach den Ausgaben der Dokumentation kann unter \url{https://archives.docs.gitlab.com/} gesucht werden. Diese Suche reicht zurück bis zur Version 16.0. Nach Bedarf können ältere Versionen im Offline-Archiv \url{https://docs.gitlab.com/archives/#offline-archives} als Docker-Container heruntergeladen und gestartet werden. Das Offline Archiv reicht bis zu der Version 10.3 zurück. Die Dokumentation wird aus den Quellcode-Repositories der GitLab Projekte gebaut und in regelmässigen Abständen aktualisiert \cite{gitlab_gitlab_nodate}.
|
|
|
|
\section{Diskussion}
|
|
|
|
\authornote{Roman Schöne}
|
|
|
|
Einige Aspekte über GitLab wurden aufgrund der Breite der Thematik nur leicht angeschnitten. Der Vergleich von GitLab mit anderen Code-Hosting Plattformen wie GitHub könnte um zusätzliche Plattformen, wie in \ref{tab:migration_platforms} erweitert werden. Das Anwendungsbeispiel oder auch das Einrichten einer eigenen Instanz hätte für weitere Plattformen übertragen werden können um einen direkten Vergleich zu schaffen. Ebenso wurde aufgrund von Demonstrationszwecken und einer höheren Nachvollziehbarkeit eine minimalistische Webanwendung als Anwendungsbeispiel verwendet. In der Realität sind die Anwendungsfälle von komplexerer Natur. Ein Grossteil der Evaluationen basieren auf der Dokumentation von GitLab. Die Dokumentation unterliegt selbst einer kontinuierlichen Änderung mit den neuen Releases von GitLab und bietet selbst nur das Abbild eines Zeitausschnitts. Für die erstellten Schätzung im Kapitel Nachhaltigkeit ist zu beachten, dass viele Annahmen getroffen werden müssen. Da die Messdaten aus untertakteten Instanzen stammen kann es zu Abweichungen mit realen Daten führen. Dieser Vergleich beschränkt sich nur auf \ac{AWS} und könnte erweitert werden. Die von GitLab empfohlenen Maschinentypen sind eher als Richtlinie zu sehen. In den meisten Anwendungsfällen muss selbst abgewägt werden, wie das eigene System aufgebaut sein muss. Ebenso hätten selbst praktische Tests durchgeführt werden können um echte Daten für die Kapitel Nachhaltigkeit sowie Skalierbarkeit zu erhalten. Diese Ergebnisse hätten mit weiteren Plattformen verglichen werden können. Für den Aspekt der Sicherheit hätte beispielsweise ein Schwachstellen-Scan der von GitLab bereitgestellten Docker-Container unter dem Dockerhub \url{https://hub.docker.com/r/gitlab/gitlab-ce} durchgeführt werden können. Aufgrund zusätzlichem zeitlichen und finanziellen Aufwand wurde sich gegen diese Punkte entschieden. Die hier erwähnten Probleme bieten jedoch einen Ausgangspunkt für auf diesem Dokument aufbauenden Arbeiten.
|
|
|
|
\section{Ausblick}
|
|
|
|
\authornote{Roman Schöne}
|
|
|
|
Aufgrund der Open Source Philosophie von GitLab wird versucht die Differenz bezahlter und frei nutzbare Funktionalitäten gering zu halten \cite{degeler_gitlab_2014}. \textit{GitLab Inc.} ist demnach weniger stark gewinnorientiert wie andere Softwareunternehmen. GitLab als Code-Hosting Plattform und Open Source Projekt wird mit grosser Wahrscheinlichkeit für die Zukunft erhalten bleiben und an zusätzlichen Funktionalitäten gewinnen.
|
|
|
|
\section{Zusammenfassung}
|
|
\authornote{Christopher Schmitt}
|
|
|
|
Diese Arbeit ist der Frage nachgegangen, wie gut sich GitLab in Version 18.11 als selbstbetriebene \ac{CI}/\ac{CD}-Plattform eignet. Betrachtet wurden die zentralen Bausteine \emph{Runner}, \emph{Pipeline} und \emph{Job}, der Aufbau einer eigenen Instanz und eine Bewertung entlang von sechs Kriterien. Den praktischen Kern bildete eine kleine \emph{Node.js}-Anwendung mit einer Pipeline aus den Stages \texttt{lint}, \texttt{test} und \texttt{deploy}.
|
|
|
|
Im Vergleich zu GitHub liegt der große Unterschied beim Hosting. GitLab gibt es als SaaS-Angebot unter \url{gitlab.com} und als selbstbetreibbare Variante. Untersucht haben wir die selbstgehostete Community Edition, die sich komplett auf eigener Hardware betreiben lässt. GitHub bindet seine Nutzer dagegen an die Microsoft-Cloud. Für unsere Instanz war das Omnibus-Paket der einfache Weg. Es bringt \texttt{nginx}, \texttt{Puma}, \texttt{PostgreSQL} und \texttt{Redis} mit, ein \texttt{apt install gitlab-ce} und ein \texttt{gitlab-ctl reconfigure} genügten. Die Installation auf einem Server unter Ubuntu 24.04 LTS lief unauffällig und war in unter zehn Minuten erledigt. An der \texttt{gitlab.rb} mussten wir nur wenige Optionen anpassen, vor allem die Erreichbarkeit über das Overlay-Netzwerk und das lokale Binding von \texttt{nginx}.
|
|
|
|
Nach Painter~\cite{painter_practical_2024} fallen die eigentlichen Kosten einer Self-Managed-Instanz nicht bei der Erstinstallation an, sondern verschieben sich über die Lebensdauer, durch Sicherheitsaktualisierungen, Restore-Tests und die Überwachung der Datenbankkonsistenz. Der GitLab.com-Vorfall von 2017 mit sechs Stunden verlorener Nutzerdaten unterstreicht, wie wichtig regelmäßige Restore-Tests der Sicherungen sind.
|
|
|
|
Die Performanzmessung an der Demo-Pipeline zeigte ein klares Bild. Die eigentliche Prüfarbeit ist winzig, \texttt{npm run lint} und \texttt{npm test} laufen lokal in Sekundenbruchteilen. Auf dem Runner dominierten dagegen Containerstart, Klonen und Installation. Die ursprüngliche Konfiguration cachte ausgerechnet das \texttt{node\_modules}-Verzeichnis, das \texttt{npm ci} ohnehin bei jedem Lauf neu aufbaut. Der unwirksame Cache kostete Zeit, statt sie zu sparen. Sein Entfernen senkte den \texttt{lint}-Job von 10,5 auf 7,6 Sekunden und den \texttt{unit-test}-Job von 11,2 auf 7,8. Ein Cache des npm-Paketspeichers unter \texttt{.npm} brachte hier kaum etwas, weil der Zugriff auf die Registry schon schnell genug war. Solche Fehlkonfigurationen von Cache und Artefakten zählen nach Zampetti et al.~\cite{zampetti_empirical_2020} zu den häufigen Schwachpunkten von CI-Pipelines, und genau das hat sich in der kleinen Pipeline bestätigt.
|
|
|
|
Als selbstbetriebene Plattform ist GitLab brauchbar, wenn Datenschutz oder Compliance den Eigenbetrieb erzwingen oder die \ac{SaaS}-Variante zu teuer wird. Die Einstiegshürde ist niedriger als erwartet, der laufende Betrieb verlangt aber Disziplin bei Updates und Sicherungen. Die Aussagen zur Skalierbarkeit stützen sich auf die Architektur und die Herstellerangaben und nicht auf einen eigenen Lasttest, und die gemessenen Laufzeiten stammen aus einer bewusst kleinen Anwendung. Für ein Unternehmen, das Quellcode im eigenen Haus halten will und die Wartung einplant, ist die selbstgehostete \ac{CE} eine tragfähige Wahl. Solange GitLab quelloffen bleibt, dürfte dieser Weg auch über kommende Versionen Bestand haben.
|
|
|
|
\section*{Anhang}
|
|
|
|
Ein Teil der Daten und Grafiken welche innerhalb dieses Artikels verwendet wurden können unter dem Gitea Repository \url{https://gitty.informatik.hs-mannheim.de/2211275/dms} eingesehen werden.
|
|
|
|
\section*{Abkürzungsverzeichnis}
|
|
\begin{acronym}[Abkürzungsverzeichnis]
|
|
\acro{IDE}{Integrated Development Environment}
|
|
\acro{CD}{Continuous Delivery, kontinuierliche Auslieferung}
|
|
\acro{SaaS}{Software as a Service}
|
|
\acro{CI}{Continuous Integration, kontinuierliche Integration}
|
|
\acro{NASDAQ}{National Association of Securities Dealers Automated Quotations}
|
|
\acro{GLFM}{Gitlab Flavored Markdown}
|
|
\acro{EOL}{End of Life}
|
|
\acro{LTS}{Long Term Support}
|
|
\acro{CE}{Community Edition}
|
|
\acro{EE}{Enterprise Edition}
|
|
\acro{OS}{Operating System}
|
|
\acro{SVN}{Subversion}
|
|
\acro{TFVC}{Team Foundation Version Control (TFVC)}
|
|
\acro{CVS}{Concurrent Versions System}
|
|
\acro{OSS}{Open Source Software}
|
|
\acro{TLS}{Transport Layer Security}
|
|
\acro{DAG}{Directed Acyclic Graph}
|
|
\acro{URL}{Uniform Resource Locator}
|
|
\acro{FQDN}{Fully Qualified Domain Name}
|
|
\acro{SP}{Special Publication}
|
|
\acro{NIST}{National Institute of Standards and Technology}
|
|
\acro{ISO}{Internationale Organisation für Normung}
|
|
\acro{IEC}{International Electrotechnical Commission}
|
|
\acro{HTTPS}{Hypertext Transfer Protocol Secure}
|
|
\acro{SSH}{Secure Shell}
|
|
\acro{S/MIME}{Secure/Multipurpose Internet Mail Extensions}
|
|
\acro{RPS}{Requests per Second}
|
|
\acro{GCP}{Google Cloud Platform}
|
|
\acro{AWS}{Amazon Web Services}
|
|
\acro{vCPU}{virtual Central Processing Unit}
|
|
\end{acronym}
|
|
|
|
\printbibliography
|
|
\end{multicols}
|
|
|
|
\end{document}
|