Tech Blog.

Thoughts, stories, ideas.

Automatisierter Debian-Paketbau mit GitLab CI

20. March 2017

Um Debian-Pakete automatisiert zu bauen, haben wir uns entschieden, GitLab CI einzusetzen. GitLab CI bietet die Möglichkeit Tasks aufgrund von definierbaren Ereignissen, wie zum Beispiel Git-Tags, auszuführen.

Wir haben einen generischen Docker-Container erstellt, der die Basis-Tools für den Paketbau enthält und von GitLab verwendet wird, um das Paket zu bauen. Der Docker-Container erlaubt es, Updates in der Build-Umgebung einfach einzuspielen, da der Container einfach durch einen neuen ersetzt werden kann.

Nachfolgend wird die automatisierte Paketierung des Log Analyse Tools GoAccess gezeigt. Viele Tools sind jeweils nicht in der neusten Version paketiert, womit selbst Hand angelegt werden muss.

Debian-Paket vorbereiten

Als erstes werden die Dateien erstellt, welche den Bau des Debian-Pakets steuern. Im Fall von GoAccess sind das die folgenden:

debian/changelog # Änderungen am Paket und der Software
debian/compat    # Kompatibilitätslevel für debhelper
debian/control   # Paketspezifische Angaben wie Abhängigkeiten und Beschreibung
debian/rules     # Anweisungen für debhelper

Debian selbst bietet bereits eine ausführliche Dokumentation an, welche den Einstieg in die Paketierung vereinfacht.

Docker-Container vorbereiten

Auf einem Hostsystem muss ein Container vorbereitet werden, in dem das Paket danach gebaut werden kann. Dazu wird zuerst das Dockerfile erstellt:

FROM debian:wheezy
ADD  setup.sh /opt/
RUN  /bin/bash /opt/setup.sh

Im Dockerfile (offizielle Dokumentation) wird angegeben, welches Basis-Image verwendet werden soll. In diesem Fall ist das Debian Wheezy. Danach wird das Script setup.sh in das Verzeichnis /opt/ des Containers kopiert.
In setup.sh wird der zu verwendende Mirror konfiguriert sowie die grundlegendsten Abhängigkeiten installiert, die bei jedem Build verwendet werden können:

#!/bin/sh

# change to our own mirror
echo "deb http://pkg.adfinis.com/debian/ wheezy main non-free contrib" > /etc/apt/sources.list
echo "deb http://security.debian.org/ wheezy/updates main" >> /etc/apt/sources.list
echo "deb http://pkg.adfinis.com/debian/ wheezy-updates main contrib non-free" >> /etc/apt/sources.list

# requirements
apt-get update
apt-get -y install git dh-make build-essential autoconf autotools-dev

Sobald diese Dateien vorbereitet sind, kann man den Docker-Container bauen:

$ docker build -t generic-package-build-runner:v1 .

Der Docker-Container ist nun erstellt und kann verwendet werden.

GitLab CI konfigurieren

Jetzt muss der vorbereitete Docker-Container für das aktuelle Projekt registriert werden, in dem ein Paket gebaut werden soll:

$ gitlab-ci-multi-runner register \
--non-interactive \
--url "$(GITLAB_URL)" \
--registration-token "$(CI_TOKEN)" \
--description "Generic debian wheezy package build runner" \
--executor "docker" \
--docker-image "generic-package-build-runner:v1"

Die GitLab URL und das CI Token können im GitLab-Projekt auf der Seite “Settings” > “Runners” eingesehen werden. Jedes Projekt hat sein eigenes CI-Token.

Damit GitLab CI weiss, welche Befehle im Container ausgeführt werden müssen, wird die Datei .gitlab-ci.yml innerhalb des Repositories erstellt.

# Wird vor den Scripts in den Stages ausgeführt
before_script:
  - source /etc/profile

# Definiert Stages, die ausgeführt werden sollen
stages:
  - build

# Stage "build"
run-build:
  stage: build
  script:
    - apt-get install -y libncurses5-dev libglib2.0-dev libgeoip-dev libtokyocabinet-dev zlib1g-dev libncursesw5-dev libbz2-dev
    - autoreconf -fvi
    - cp COPYING debian/copyright
    - dpkg-buildpackage -us -uc
    - mkdir build
    - mv ../goaccess*.deb build/

  # Diese Stage wird nur bei neuen Tags ausgeführt
  only:
    - tags

  # Die Dateien, welche in GitLab zur Verfügung gestellt werden sollen
  artifacts:
    paths:
      - build/*

Der wichtigste Teil dieser Datei ist die Stage run-build. In diesem Teil wird definiert, welche Aktionen durchgeführt werden, wann diese durchgeführt werden und wo die Dateien liegen, welche durch den Build erstellt werden.

Da ein generischer Docker-Container erstellt wurde, müssen die benötigten Abhängigkeiten im ersten Schritt zuerst noch installiert werden.

Anschliessend wird der Bauvorgang mit autoreconf vorbereitet. Dadurch wird unter anderem das Makefile erstellt, welches für den Build unerlässlich ist. Da wir das Copyright aus dem Paket übernehmen, kopieren wir dieses nach debian/.

Mit dem Befehl dpkg-buildpackage wird der Bauvorgang anschliessend gestartet. Dadurch wird das Paket kompiliert und das Debian-Paket erstellt. Diese Pakete werden danach in den erstellten Ordner build verschoben und auf GitLab hochgeladen.

Workflow

Sobald wir einen neuen Release haben, wird ein Git-Tag erstellt. Durch den erstellten Git-Tag wird in GitLab ein neuer Build gestartet, welcher das Paket in der neuen Version baut.
Das erstellte Paket steht dann im Webinterface von GitLab zur Verfügung, wo es heruntergeladen werden kann.

Ausblick

Die gebauten Pakete bzw. Artifakte sollten idealerweise entsprechend automatisiert weiterverarbeitet werden, indem man diese beispielsweise auf einen Mirror hochlädt. In unserem Fall verwenden wir einen Bot, der auf Anweisung eines GitLab-Webhooks die Artifakte auf den Zielserver herunterlädt, dort einem Aptly Repository hinzufügt und das Repository entsprechend publiziert, so dass der Prozess vom Bau des Pakets bis zur Publizierung vollständig automatisiert werden kann. Das Resultat kann schlussendlich auf unserem öffentlichen Aptly Mirror eingesehen werden.