Nachdem wir uns das Jahr 2020 Zeit genommen haben, um die Perl-Academy etwas umzubauen (mit neuem Design, der Einführung dieses Blogs, mit Gregor als zusätzlicher Trainer, ...), wollen wir heute unseren Plan für 2021 vorstellen.
Wir werden wenige feste Termine für offene Schulungen haben. Wir wollen mehr Firmenschulungen anbieten und offene Schulungen zusätzlich planen, wenn sich Interessenten melden.
Unsere Schulungsthemen werden wir am Lebenszyklus einer Anwendung ausrichten. Auch in agilen Umgebungen hat man immer wieder den Lebenszyklus in klein. Momentan haben wir vier Themen geplant:
Einsteigen werden wir mit User Story Mapping
. Kein originäres Perl-Thema, aber eine ganz praktische Vorgehensweise, um eine Anwendung aus Sicht des Benutzers zu planen.
Die mit User Story Mapping
geplante Software werden wir mit Mojolicious umsetzen. Dabei werden wir mit einer kleinen Version anfangen um die Grundlagen von Mojolicious kennenzulernen. Anschließend werden wir die Anwendung wachsen lassen, um die weitergehenden Fähigkeiten von Mojolicious nutzen zu können.
Natürlich muss man nicht erst die User-Story-Mapping Schulung besucht haben, um an der Mojolicious-Schulung teilzunehmen.
Das Thema Sicherheit in Perl-Anwendungen
spielt natürlich auch bei Mojolicious-Anwendungen ein Thema. Wir haben eine eigene Schulung daraus gemacht, um dem Ganzen genügend Raum zu geben und uns nicht auf Mojolicious zu beschränken.
Als viertes Thema bieten wir Gitlab und Perl
an. Die Versionsverwaltung begleitet die Anwendung ein Leben lang. Wir zeigen, was man abseits der reinen Versionsverwaltung mit Gitlab machen kann – von Continuous Integration
bis zum Ausliefern der Anwendung.
Interesse an einer der Schulungen? Dann melden Sie sich bei uns!
Permalink: /2020-12-07-schulungen-2021
In verschiedenen Projekten setzen wir ein jeweils selbst gehostetes Gitlab ein. Gitlab bietet viel mehr als nur die Versionskontrolle. Gregor hat vor einiger Zeit schon einmal etwas dazu geschrieben, wie wir bei Perl-Services.de Gitlab benutzen.
Bei einem Projekt stand der Umzug in die Cloud
zu AWS an und damit auch das Gitlab, das in dem Projekt genutzt wurde. Für das Projekt gibt es aber nicht alle Möglichkeiten der AWS-Cloud, so dass das Gitlab nicht in gewohnter Umgebung (ein Ubuntu-Linux) fortgeführt werden konnte, sondern auf ein RHEL8-System wechseln musste.
Bevor der Umzug losgeht, muss eine Serverinstanz mit RHEL8 erstellt werden. Nach der Erstellung per SSH mit der Maschine verbinden.
Da RHEL8 kein Docker mehr unterstützt, kommt Podman zum Einsatz. Podman ist eine Container-Engine ähnlich zu Docker. Es können die Befehle von Docker genutzt werden, da Podman einen docker-Alias anlegt. Man bekommt Podman auch, wenn man
yum install docker
nutzt.
Wenn man von einem anderen Server migriert, muss man dort erst einmal schauen, welche Version zum Einsatz kommt. Die gleiche Version muss dann auch (erst einmal) auf dem neuen Server laufen. Wenn es neuere Versionen von Gitlab gibt, sollte nach dem Umzug dann noch das Update gemacht werden.
Die entsprechend getaggten Gitlab-Images sind unter https://hub.docker.com/r/gitlab/gitlab-ce/tags zu finden.
Auf dem neuen Server führt man einfach
docker pull gitlab/gitlab-ce:<version>-ce.0 aus.
Damit die Daten auch persistent auf der EC2-Instanz bleiben, werden beim Starten des Containers Verzeichnisse der Instanz in den Container gemappt. Die müssen vor dem Start des Containers existieren:
$ mkdir -p /srv/gitlab/config
$ mkdir -p /srv/gitlab/data/backups
$ mkdir -p /srv/gitlab/logs
Als systemd-Service einrichten
Um anschließend Gitlab als Service starten und stoppen zu können, muss es entsprechend eingerichtet sein. Dazu in der Datei /etc/sysconfig/gitlab folgendes eintragen:
IMAGE=docker.io/gitlab/gitlab-ce:<version>-ce.0
HOSTNAME=git.domain.tld
NAME=gitlab-ce
Damit sind dann Umgebungsvariablen gesetzt, die in der Service-Datei genutzt werden. Die ist unter /etc/systemd/system/gitlab.service zu finden. In dieser Service-Datei ist beschrieben, wie der Gitlab-Service aussieht:
[Unit]
Description=GitLab Podman container
After=network.target
[Service]
Type=simple
TimeoutStartSec=5m
Restart=always
RestartSec=30s
EnvironmentFile=/etc/sysconfig/gitlab
ExecStartPre=-/usr/bin/podman rm ${NAME}
ExecStart=/usr/bin/podman run \
--name ${NAME} \
--hostname ${HOSTNAME} \
--publish 443:443 \
--publish 80:80 \
--publish 8014:22 \
--volume /srv/gitlab/config:/etc/gitlab:Z \
--volume /srv/gitlab/logs:/var/log/gitlab:Z \
--volume /srv/gitlab/data:/var/opt/gitlab:Z \
${IMAGE}
ExecReload=-/usr/bin/podman stop ${NAME}
ExecReload=-/usr/bin/podman rm ${NAME}
ExecStop=-/usr/bin/podman stop ${NAME}
[Install]
WantedBy=multi-user.target
Ich gehe hier nicht auf alle Einzelheiten ein. Nur so viel: Wo die Umgebungsvariablen zu finden sind, wird in EnvironmentFile definiert. Dort wir die Datei eingetragen, die im Schritt vorher definiert wurde. Bei den Befehlen zum Starten, Neustarten und Stoppen des Dienstes taucht hier podman auf. Wer Docker einsetzt, muss bei diesen Befehlen dann das podman durch docker ersetzen.
Die Port-Weiterleitungen können auch abweichen. In dem Projekt wurde der Port 22 von Gitlab über den Port 8014 des Servers bekanntgemacht, damit der Server selbst per SSH über den Port 22 ansprechbar bleibt.
Abschließend muss der Service noch aktiviert werden:
systemctl enable gitlab.service
machen, damit das Gitlab bei jedem Systemstart ebenfalls gestartet wird
Bevor man das Backup einspielen kann, muss es erstellt werden. Dazu auf der alten Instanz folgendes machen:
$ docker exec -it gitlab /bin/bash/
gitlab-rake gitlab:backup:create
Das erstellt eine .tar-Datei in */srv/gitlab/data/backups *. Diese muss an die gleiche Stelle auf dem neuen Server geschoben werden. Der Dateiname ist nach diesem Muster aufgebaut: <Timestamp>*<YYYY>*<mm>*<dd>*<version>.tar
.
In dem Backup sind nicht die Dateien /srv/gitlab/config/gitlab.rb und /srv/gitlab/config/gitlab-secrets.json enthalten. Diese müssen separat auf den neuen Server kopiert werden.
Anschließend müssen auf dem neuen Server ein paar Services von Gitlab gestoppt werden:
$ docker exec -it gitlab /bin/bash
/ gitlab-ctl reconfigure
/ gitlab-ctl start
/ gitlab-ctl stop unicorn
/ gitlab-ctl stop sidekiq
Sind die Services gestoppt, kann das Backup eingespielt werden:
/ gitlab-rake gitlab:backup:restore --trace
Abschließend gitlab neu starten:
/ gitlab-ctl restart
Permalink: /2020-12-04-gitlab-umziehen
In meinem Artikel über die Optimierung von Docker-Images habe ich erwähnt, dass wir die »Gitlab-CI« einsetzen. In diesem Artikel beschreibe ich nun näher, was das eigentlich ist und welche Erfahrungen wir gemacht haben.
Mit »CI« wird in der Software-Entwicklung »continuous integration« bezeichnet, also die fortlaufende Integration von Software-Komponenten. Dies ist ein Vorgehen im Team und weniger ein Werkzeug, bei dem Änderungen an der gemeinsamen Code-Basis fortlaufend an einer definierten Stelle zusammengeführt und auf ihre Qualität hin untersucht werden.
Diese Untersuchung besteht in der Regel aus einer Reihe von automatisierten Tests, die Abweichungen von gewünschten Eigenschaften aufzeigen sollen. Hierzu kann auch das Einhalten von Codier-Richtlinien zählen.
Durch die häufige Integration und einem hohen Grad an Automatisierung möchte das Team sicherstellen, dass Fehler und Abweichungen schneller gefunden und behoben werden können.
Die fortlaufende Integration ist also ein Baustein, um qualitätssichernde Maßnahmen im Softwareentwicklungsprozess umzusetzen.
Das Produkt Gitlab unterstützt fortlaufende Integration durch eine textuelle Beschreibung von »Pipelines«, in denen konfigurierbare Befehle ausgeführt werden.
Nach einem Push von Änderungen in einem Repository wird ein Docker-Container gestartet, in dem dann die beschriebenen Befehle ausgeführt werden.
Das folgende Beispiel zeigt eine hypothetische Distribution Foo::Bar::Baz, die mit Dist::Zilla in Ubuntu gebaut und getestet wird.
image: ubuntu
stages:
- build
build:
stage: build
script:
- dzil build
- dzil cover
- dzil test
coverage: '/^Total.+\s+(\d+.\d)$/'
artifacts:
when: always
name: "${CI_BUILD_STAGE}_${CI_BUILD_REF_NAME}"
paths:
- foo-bar-baz-*.tar.gz
Diese Befehle werden typischerweise das Compilieren und Linken von Software sein, an die sich Testautomatisierung und weitere Prüfungen anschließen.
Wenn alle Befehle erfolgreich ausgeführt wurden, ist der Build erfolgreich. Erfolg oder Misserfolg eines Builds werden dem Entwickler, dessen Änderungen die Pipeline ausgelöst haben, per E-Mail mitgeteilt. Der Zustand eines Builds ist zusätzlich im Gitlab-Projekt sichtbar.
Welche Arten von Pipelines es gibt und wie dies in Gitlabs Oberfläche dargestellt wird, ist in Gitlabs Dokumentation nachzulesen.
Eine Pipeline wird wie oben beschrieben nach einem Push der Änderungen gestartet. Bei der Konfiguration dieser Pipelines legen wir auf zwei Aspekte großen Wert: Geschwindigkeit und Reproduzierbarkeit des Builds.
Geschwindigkeit ist für uns wichtig, da wir binnen weniger Minuten Rückmeldung über die Qualität der Änderungen erhalten wollen. Wenn hier etwas bricht, wollen wir den Fehler sofort korrigieren, da diese Behebungen meist einfacher sind als bei großen Änderungen.
Geschwindigkeit erreichen wir durch konfektionierte Docker-Images. Außerdem stimmen wir die Reihenfolge der Tests aufeinander ab und verfolgen beim Schreiben der Tests einen »fail fast«-Ansatz. Wenn ein Build fehlschlägt, bekommen wir so früh Rückmeldung.
Die Reproduzierbarkeit eines Builds ist für uns wichtig, damit wir Builds nachvollziehen können, indem wir die Docker-Images auf beliebigen Systemen bauen. In der Vergangenheit hatte uns Gitlab öfter Fehler in Builds gemeldet, die wir dann lokal auf unseren Entwicklungsrechnern nicht nachvollziehen konnten. Grund hierfür war die Installation von Abhängigkeiten und dadurch entstandene Versionsunterschiede.
Die Reproduzierbarkeit erhalten wir, indem wir so gut es geht die Versionen der Abhängigkeiten angeben und während des Builds keine Komponenten nachinstallieren. Da Installationen entfallen, erhöht sich als Nebenwirkung auch die Build-Geschwindigkeit.
Für uns hat sich Dist::Zilla als sehr hilfreich erwiesen. Einerseits ermöglichen uns die Kommandos dieses Werkzeugs, die Konfiguration einer Pipeline übersichtlich zu halten. Andererseits bietet die Funktionalität von dzil
vieles, was wir sinnvoll einsetzen können.
Kommandos, die wir nutzen, sind zum Beispiel »test« für das Ausführen der Tests, »build« zum Erstellen der Distribution und »cover« zum Messen der Testabdeckung.
Wir setzen Dist::Zilla ein, um am Ende eines Builds die Distribution als Artefakt »an den Build zu hängen«. Das von dzil erstellte Archiv wird einem Build zugeordnet; wir können es dann im Browser, in anderen Projekten oder über die API übertragen.
Auch bei der Versionsverwaltung ist Dist::Zilla nützlich: Sein git-Bundle hilft dabei, Branches nach dem Veröffentlichen einer Distribution zu taggen. Somit ist automatisiert und nachvollziehbar im Git-Repository eine Versionshistorie abrufbar.
Das Produkt Gitlab hilft einem Team beim fortlaufenden Integrieren von Software-Komponenten. Entwickler können »Pipelines« über Textdateien beschreiben, in denen Befehle nach Änderungen einer gemeinsamen Code-Basis ausgeführt werden. Wir können damit schnelle Rückmeldungen über Code-Änderungen und reproduzierbare Builds erhalten. Dist::Zilla hilft uns, einen Build übersichtlich zu beschreiben und effizient durchzuführen.
Permalink: /2020-11-26-wie-setzen-wir-gitlab-ci-ein
Auch während der aktuell hohen Infektionszahlen in der Corona-Pandemie schauen wir nach vorne. Nach aktuellem Stand findet der Deutsche Perl-/Raku-Workshop 2021 Ende März in Leipzig statt (sollte die Corona-Situation das nicht hergeben, wird da mit Sicherheit reagiert).
Wir wollen den Workshop zum Anlass nehmen, am Tag vorher (23. März 2021) zwei Halbtagesschulungen anzubieten:
Den Tag beginnen wird Gregor mit Gitlab und Perl
. In dem Kurs, der von 08:00 Uhr bis 12:00 Uhr stattfinden wird, geht es um die Entwicklung von Perl-Distributionen mit Hilfe der Plattform Gitlab. Gregor wird anhand einer CPAN-Distribution kurz die hierfür relevanten integrierten Komponenten der Plattform vorstellen und insbesondere auf die continuous integration eingehen. Ein Schwerpunkt liegt hier auf der schnellen Rückmeldung an Entwickler nach einem Commit.
Am Nachmittag (13:30 Uhr bis 17:30 Uhr) geht es dann um REST-APIs mit Mojolicious. Ich werde hierbei kurz auf REST an sich eingehen, bevor eine Schnittstelle definiert und dann als Mojolicious-Anwendung mit Leben gefüllt wird. Dabei werden dann auch Themen wie Sicherheit etc. angesprochen. Zum Abschluss wird die Schnittstelle noch getestet.
Als Veranstaltungsort ist das Hotel Michaelis geplant und die Teilnehmerzahl ist auf 10 Personen pro Schulung beschränkt.
Wer Interesse an einer der Schulungen hat, kann sich gerne bei uns melden. Die Teilnahme an einer Schulung kostet 200,00 € netto (238,00 € inkl. MwSt), ein Kombiticket für beide Schulungen kostet 350,00 € netto (416,50 € inkl. MwSt).
Wir werden natürlich die Entwicklungen bzgl. Corona weiter im Auge behalten. Sollte eine Präsenzveranstaltung nicht möglich sein, werden wir die Schulungen als Online-Veranstaltung anbieten.
Permalink: /2020-11-10-gpw-schulungen
Wenn man als Entwickler mit der Erstellung von Docker-Images anfängt, dann ist das Image zunächst auf Funktionalität optimiert. Mit fortschreitender Entwicklung wird eine schnelle Rückmeldung über das Build-Ergebnis an die Entwickler wichtiger. Zudem soll ein Build reproduzierbar sein, damit von der CI-Umgebung gemeldete Fehler nachvollzogen und Änderungen für den Betrieb einplant werden können. Dieser Artikel zeigt, worauf man bei achten sollte, damit das klappt.
Wir nutzen Gitlab und haben uns zu Beginn der Nutzung dieses Dienstes darauf konzentriert, überhaupt einen Build nach einem Commit durchlaufen zu lassen. Das ist manchmal gar nicht so einfach, da diverse Abhängigkeiten benötigt werden, die vor Ausführung von Tests in einem laufenden Container installiert werden müssen.
Wir bei Perl-Services haben uns zu Beginn auf die CI-Stufe von Gitlab verlassen. Da es hier aber mitunter etwas dauern kann, bis ein Build nach einem Commit startet, haben wir schnell gemerkt, dass lokales Bauen von Images sinnvoller ist. Dafür haben wir dann ein Makefile erstellt:
#!/bin/make -f
#
# Makefile pointers...
# Self-documenting:
# https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
# Argument passing:
# https://stackoverflow.com/questions/2826029/passing-additional-variables-from-command-line-to-make
#
# To add a new option, follow the format below, e.g.:
# make command: ## Documentation of command
# ./the-command-to-run.sh
# Default to displaying the help
.DEFAULT_GOAL := help
.PHONY: help
REGISTRY=registry.gitlab.com
IMAGE_NAME=perlservices/groupname/projectname-docker
IMAGE_TAG=latest
FULL_IMAGE_NAME=${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
# Displays all the make options and their descriptions
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
build: ## Build Docker image
docker build -t ${FULL_IMAGE_NAME} .
shell: ## Start a container from the image built and run a shell in it
docker run --rm -it --entrypoint /bin/bash \
-v $$PWD/src:/src \
-p 8080:8080 \
${FULL_IMAGE_NAME}
Dieses Makefile erstellt beim Aufruf von make build
ein Docker-Image anhand des im gleichen Verzeichnisses liegenden Dockerfile
s. Der Aufruf von make shell
startet einen Container mit diesem Image und öffnet eine Shell darin, wobei hier das lokale Verzeichnis src
im Container unter /src
zur Verfügung steht.
Mit Hilfe dieses Makefiles können wir recht einfach ein Docker-Image erstellen und dieses lokal weiterentwickeln.
Damit eine Perl-Distribution im Rahmen eines Docker-Images erstellt werden kann, werden viele Bausteine benötigt.
Einerseits benötigen wir auf Systemebene einige Werkzeuge. Dazu gehört zum Beispiel curl
, um andere Builds auf Gitlab anzustoßen. Diese installieren wir über den Paketmanager des Betriebssystems.
Anderseits brauchen wir auf Anwendungsebene Module, die wir installieren müssen. Wenn sie in der geeigneten Version als Paket des Betriebssystems vorliegen, installieren wir sie ebenfalls mit dem Paketmanager des Betriebssystems. Wenn dies nicht der Fall ist, installieren wir sie von CPAN.
Diese Installationen dauern entsprechend lange und greifen auf externe Quellen zu. Dadurch dauert der Build lange und kann schlimmstenfalls fehlschlagen.
Daher haben wir hier in zwei Schritten optimiert: Wir erstellen für eine zu entwickelnde Perl-Distribution (oder Anwendung) ein spezifisches Image, das alle benötigten Abhängigkeiten enthält.
Im Dockerfile installieren wir zunächst mit apt-get install
die benötigten Pakete des Betriebssystems:
FROM registry.gitlab.com/perlservices/groupname/projectname-docker:latest
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
lsb-core \
wget
# Add PostgreSQL repo for the matching release of the OS
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
postgresql \
postgresql-client \
git \
apache2 \
curl \
phppgadmin \
php-pgsql \
cpanminus \
libdbd-pg-perl \
libnet-ssleay-perl \
libxml-parser-perl \
gosu \
...
Die Perl-Abhängigkeiten legen wir in einem cpanfile
fest:
...
requires 'Mojolicious' => 8;
requires 'Mojolicious::Plugin::Bcrypt' => 0;
requires 'Mojolicious::Plugin::Status' => 0;
requires 'Moo' => 0;
requires 'MySQL::Workbench::DBIC', '>= 1.13';
requires 'MySQL::Workbench::Parser', '>= 1.06';
...
Das so erstellte Image verwenden wir dann in der .gitlab-ci.yaml
unseres Projektes. So können dann sofort nach einem Commit in der CI-Stufe die konfigurierten Befehle ausgeführt werden, denn alle Abhängigkeiten sind bereits im Image vorhanden.
Gerade zu Beginn der Arbeit mit den oben beschriebenen projektspezifischen Docker-Images haben wir öfter Probleme mit der Nachvollziehbarkeit von Fehlern im Build auf Gitlab gehabt. Ursache dafür war, dass sich Abhängigkeiten in jedem Build geändert haben.
Beispiele für diese Änderungen:
Es wird dann unter Umständen auf Gitlab ein Fehler im Build angezeigt, der lokal beim Entwickler nicht mehr oder anders auftritt.
Durch das Erstellen von projektspezifischen Docker-Images werden schon viele Abhängigkeiten festgezurrt, so dass hier weitestgehend reproduzierbare Builds umgesetzt werden konnten.
Eine wesentliche Abhängigkeit ist jedoch noch nicht festgezurrt: Die des grundlegenden Images für alle Builds – Das verwendete Betriebssystem.
Vielfach wird in Beispielen für ein Dockerfile
entweder kein Tag bei einem verwendeten Image angegeben oder eines mit dem Namen »latest«. Beide führen dazu, dass jeweils die letzte Fassung eines Images verwendet wird:
FROM debian:latest
Im schlimmsten Fall kann es dadurch dazu kommen, dass zwischen zwei Builds ein Versionssprung des Betriebssystems stattfindet und alle abhängigen Images neu gebaut werden. In der Regel läuft dies nicht ohne Probleme ab und zieht weitere Arbeiten nach sich.
Die dann auftretenden Probleme im Build können im günstigsten Fall eine kurze Verzögerung bedeuten, im schlimmsten Fall ungeplante Wartungsarbeiten nach sich ziehen.
Außerdem bedeuten gerade bei Betriebssystemen neue Versionen größere Änderungen im Betrieb, die sorgfältig vorbereitet und eingeplant werden müssen.
Diese Art von Überraschung kann umgangen werden, indem in Dockerfile
s bei jedem verwendeten Image stets ein Tag mit angegeben wird, das eine feste Version spezifiziert.
Es sollte also auf Angaben wie »rolling« und »latest« verzichtet werden.
FROM ubuntu:20.04
Während Entwickler zunächst auf Funktionalität bedacht sind, wenn sie in CI-Umgebungen mit dem Erstellen von Docker-Images beginnen, zeigt sich sehr schnell in der täglichen Arbeit, dass weitere Faktoren wichtig sind. Schnelle Rückmeldung durch kurze Durchlaufzeiten und stabiler Betrieb durch reproduzierbare Builds werden mit zunehmender Zeit wichtiger.
Dieser Artikel hat beschrieben, wie durch projektspezifische Images die Durchlaufzeit verringert werden kann. Außerdem wurde gezeigt, wie durch das ausdrückliche Konfigurieren von Softwareversionen planvolle Updates ohne große Überraschungen im Betrieb möglich werden.
Permalink: /2020-10-14-optimierung-von-docker-images
Gitlab ist eine webbasierte Plattform für die kontinuierliche Auslieferung von Software. Diese Plattform bietet viele Funktionen, die über die reine Softwareentwicklung und das Deployment hinausgehen. Wir erstellen damit Perl-Distributionen, Docker-Images sowie Dokumentation.
Gitlab ist eine webbasierte Plattform für die kontinuierliche Auslieferung von Software. Unter anderem integriert es Komponenten wie ein Wiki, ein Ticket-System und die Unterstützung des Versionskontrollsystems Git. Außerdem bietet es eine automatisierte Ausführung von beliebigem Code in Docker-Images an, die von Commits ausgelöst werden können.
Welche Befehle ausgeführt werden, kann man in einer YAML-Datei beschreiben. Dort wird auch beschrieben, welches Docker-Image dafür verwendet werden soll und unter welchen Bedingungen die Befehle ausgeführt werden sollen.
Wie wir bei Perl-Services diese Möglichkeiten nutzen, beschreibe ich in diesem Artikel.
Als wir begonnen haben, Gitlab zu evaluieren, haben wir zunächst kleinere Perl-Distributionen als Projekt angelegt und die Git-Repositorys eingebunden. In den YAML-Dateien dieser Projekte haben wir dann konfiguriert, dass nach jedem Commit die Unit-Tests ausgeführt werden und zum Abschluss die Perl-Distribution paketiert werden sollen. Hier ein Auszug aus einem mittlerweile so nicht mehr existenten Projekt:
image: ubuntu:rolling
build:
script:
- apt-get update -qq
- cpanm --installdeps .
- dzil authordeps | cpanm
- dzil listdeps | cpanm
- dzil build
artifacts:
when: always
name: "${CI_BUILD_STAGE}_${CI_BUILD_REF_NAME}"
paths:
- *.tar.gz
Unser Ansatz war zunächst, Fehler durch Tests zu finden und bei erfolgreichem Testdurchlauf die Perl-Distribution zu erstellen. Die benötigten Abhängigkeiten haben wir mit cpanm
installiert. Dabei haben wir festgestellt, dass wir gerne schneller Rückmeldung erhalten würden. Dies führte dann dazu, dass wir die Build-Pipeline optimiert haben.
Verkürzte Durchlaufzeiten erreichen wir einerseits durch eine Anpassung der Befehle, andererseits aber auch dadurch, dass wir ein Docker-Image verwenden, das für diese Distribution optimiert ist und bereits alle Abhängigkeiten enthält.
Was sind die nächsten Schritte? Wir prüfen, ob wir die erfolgreich gebauten Distributionen automatisiert in einen Pinto-Server laden oder auf CPAN veröffentlichen.
Im vorherigen Abschnitt habe ich erwähnt, dass wir Docker-Images für die Entwicklung von Perl-Distributionen optimieren. Wir wollen dafür alle Abhängigkeiten einer Perl-Distribution in einem Docker-Image zur Verfügung stellen, damit wir die Durchlaufzeit der Perl-Distribution klein halten und dann schnell Rückmeldung nach einem Commit bekommen.
Wie haben wir dies mit Gitlab erreicht? Wir setzen Dist::Zilla
ein und beschreiben Abhängigkeiten über dessen Konfiguration dist.ini
und das cpanfile
. Im verwendeten Docker-Image werden diese darüber beschriebenen Abhängigkeiten mit cpanm
installiert.
Dafür haben wir Gitlab so konfiguriert, dass bei einer Änderung dieser Dateien im Master-Branch der Perl-Distribution das verwendete Docker-Image neu erstellt wird. Hier ein beispielhafter Auszug aus einer Gitlab-Konfiguration:
trigger_image_build:
stage: trigger
script:
- curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=master https://gitlab.com/api/v4/projects/9398220/trigger/pipeline
only:
refs:
- /^master$/
changes:
- cpanfile
- dist.ini
Die Beschreibungen der Abhängigkeiten werden beim Erstellen des Docker-Images aus dem abhängigen Projekt gelesen. Hier ein beispielhafter Auszug aus einem Dockerfile:
RUN cd /home/mydocker/src && \
git clone https://gitlab-ci-token:${CI_TOKEN}@gitlab.com/perlservices/project.git && \
[...]
RUN cd /home/mydocker/src/project && \
cpanm --skip-satisfied --installdeps . && \
dzil authordeps --missing | cpanm --skip-satisfied && \
dzil listdeps | cpanm --skip-satisfied
Die Gitlab-Dokumentation hat uns hier teilweise eher verwirrt als geholfen, aber letzten Endes haben wir ein perfekt abgestimmtes Image erhalten, in dem alle Abhängigkeiten enthalten sind. Dadurch können in dem Projekt, das auf diesem Image basiert, nach einem Commit sofort die Unit-Tests ausgeführt werden können.
Die Durchlaufzeiten sind daher sehr gering und liegen bei einem gehosteten Gitlab-Repository bei etwa drei Minuten. Der Löwenanteil dieser drei Minuten liegt darin begründet, dass wir auf einen freien Gitlab-Runner warten müssen.
Um die Durchlaufzeiten weiter zu verringern, migrieren wir die Repositorys derzeit auf einen eigenen Gitlab-Server.
Hinweis: Das müssen wir wegen dem Fall des Privacy Shields ohnehin machen 🙂
Die in Projekten zu erstellende Dokumentation reicht je nach Wunsch des Kunden von kurzen Beschreibungen über HTML-Dateien bis zu sauber mit LaTeX gesetzten Dokumenten. Das Ausgangsformat der Dokumente ist für uns stets Markdown, da dies einfach zu schreiben ist, wir uns auf das Wesentliche konzentrieren können und alle gewünschten Zielformate erzeugt werden können.
In Gitlab erzeugen wir mit pandoc
aus den Quelldateien das Zielformat. Gegebenenfalls verarbeiten wir das Resultat automatisiert weiter. Wenn als Ergebnis beispielsweise ein PDF erzeugt werden soll, dann bedeutet dies, dass wir aus Markdown LaTeX-Dokumente erzeugen und diese im Anschluss mit lualatex
in ein PDF wandeln. Damit dies möglich ist, haben wir uns ein Docker-Image erstellt, in dem TeX Live installiert ist.
Da wir lualatex aus TeX Live einsetzen, ist unser Docker-Image mehrere Gigabyte groß. Diese Kröte müssen wir wohl schlucken
. Pandoc
leistet sehr gute Dienste und die Entscheidung für Markdown als Ausgangsformat hat sich bewährt.
Wir werden diese Kombination beibehalten, jedoch gelegentlich prüfen, ob wir das Docker-Image etwas verkleinern können 🙂
Wir erstellen mit Gitlab automatisiert Perl-Distributionen, Docker-Images und Dokumente in unterschiedlichen Formaten. Gitlab als Plattform hat sich hierfür bewährt. Durch den Einsatz einer selbst betriebenen Gitlab-Instanz erhoffen wir uns geringere Durchlaufzeiten.
Permalink: /2020-09-01-wie-nutzen-wir-gitlab