Zum Inhalt

In diesem Artikel möchte ich euch meinen GIT-Workflow für OPSI zeigen. Im Rahmen eines Kundenprojekts habe ich mir überlegt, wie ich das Erstellen von OPSI-Packages ins GIT bringe - und auch gleich die CI von Gitlab nutze, um meine Packages auf den oder die OPSI-Server automatisch auszurollen. OPSI ist ein Linux basierender Server zum deployment von Windows- (aber auch Linux-) Rechnern. Mehr Informationen zu OPSI findet ihr hier, ich gehe hier einfach mal davon aus, dass ihr schon ein wenig mit OPSI vertraut seid.

Grundlage

In meiner Überlegung gehe ich davon aus, dass ich einen produktiven OPSI-Server habe, der bereits (oder in Zukunft) die Windows-Maschinen betankt. Für meine Entwicklung setze ich mir einen OPSI-Devel-Server auf. Hierzu lade ich mir das OPSI-VM Image von der Homepage und richte mir einen einfachen OPSI-Server ein. Zusätzlich kann ich mir in meiner Virtualbox noch einen Windows-PC einrichten - dieser kann natürlich mit OPSI eingerichtet werden. Neben den beiden OPSI-Servern benötige ich noch ein Gitlab, dieses kann bei GITLAB selbst oder ein selbst gehostetes Gitlab sein. Wichtig, GIT-LFS muss aktiviert sein.

Des Weiteren benötige ich auf den beiden OPSI-Servern "git" & "git-lfs". Auf dem produktiv OPSI installiere ich mir gitlab-runner.

Das “LFS” steht für “large file storage”, im Grunde ist es ein Speicher innerhalb eines Git-Servers für Binary-Dateien, denn Binary Dateien sollten selbst nicht in einem Git-Repository gespeichert werden. Allerdings wird es aus diversen Gründen dennoch manchmal nötig sein, daher wurde GIT-LFS als Erweiterung von GIT entwickelt.

Die Idee

Die Entwicklung meiner Packages findet auf dem DEVEL-Server statt, hier kann ich meine Packages bauen und auf einer Windows VM auch testen. Die für das OPSI-Package benötigten Dateien werden in ein Git-Repository geschrieben, die nötigen Binary-Dateien werden dank LFS ebenfalls im GIT-Server gespeichert.

In GIT arbeite ich mit Branches und Tags. Der Master-Branch beinhaltet die Relaeases meiner Packages (Dateien), andere Branches (Feature-Branch) beinhalten meine aktuelle Entwicklung. Merge ich nun meinen Feature-Branch in den Master-Branch, wird dies mit einem Tag (Version) versehen. In Gitlab richte ich mir eine CI-Pipeline ein, die den Gitlub-Runner auf dem produktiven OPSI verwendet. Wenn im Branch nun ein Commit (Merge) mit einem Tag commited wird, koperiert die CI-Pipeline das Package (Repo), ruft ein opsi-makeproduct auf und installiert das ganze dann im OPSI-Depot mittels opsi-package-manager.

Vorteil: Wenn ich das so wie hier beschrieben umsetze, ist alles reproduzierbar. Zum Zweiten kann ich sehr gut mit einem Team auch an einem Package arbeiten.

Die Umsetzung

Wie schon oben beschrieben, gehe ich davon aus, dass ihr mit OPSI vertraut seid. Ich erkläre hier nicht, wie man einen OPSI-Server einrichtet, das ist einen extra Artikel wert.

Auf meinem Devel-Server installiere ich mir zu nächst die nötigen Tools.

sudo apt install git git-lfs

Auf dem produktiven Server installiere ich mir noch zusätzlich meinen Gitlab-Runner.

sudo apt install git git-lfs gitlab-runner

Als Nächstes lege ich mir in Gitlab eine neue Gruppe an, in diese Gruppe kommen alle Packages, und ich kann hier auch meinen Gitlab-Runner registrieren.

Als erstes erstelle ich mir meine Gruppe "Packages" im Gitlab.

AddGroup

AddGroup

Jetzt muss ich den Registration-Token zur Registrierung eines Runners heraussuchen. Dazu gehe ich in der Gruppe (Links) in Einstellungen, CI/CD und scrolle auf der rechten Seite runter.

AddGroup

Neben den Token findet sich hier auch noch die URL zum Server und rechts sollte folgendes aktiviert sein.

AddGroup

Mit diesen Informationen können wir nun den Runner registrieren.

Dazu wechseln wir nun auf den produktiven Server in die Console und rufen Folgendes auf.

AddGroup

Im Bild oben kann man sehen, dass wir die URL zu unserem Server eintragen müssen, außerdem wir der Token hier verlangt. Dann wird noch eine Beschreibung erwartet, hier können wir eintragen, was wir wollen. Wichtig wird jetzt jedoch die "gitlab-ci tags". Diese werden dazu gebraucht, dass unsere PIPELINE weiß, welchen Runner sie benutzen soll. Ich habe mich hier für den Tag "opsi-packages" entschieden. Zuletzt müssen wir noch einen Executer auswählen. Gitlab-Runner unterstützt einge davon. Ich verwende hier die Shell, damit kann ich direkt auf der Maschine meine Skript (Pipeline) laufen lassen.

Jetzt sollte sich der Runner registriert haben.

AddGroup

Package erstellen

Jetzt erstellen wir unser erstes Package. Dazu wechseln wir in unseren Devel-Server und rufen Folgendes aus.

AddGroup

In dem von uns erstellen Package (Verzeichnis) initialisieren wir nun als Erstes git.

cd 7zip
git init

Als Nächstes müssen wir unserem Git sagen, welche Dateien wir per LFS in das Repo übernehmen wollen.

Note

Wir errinnern uns daran, dass Binar-Dateien eigentlich nicht ins git sollen!

git lfs track "*.msi"
git lfs track "*.exe"

"git lfs track" erzeugt jetzt eine Datei .gitattributes. Diese beinhaltet den LFS-Filter, der bei einen Commit alle Files (in unserem Fall alle msi & exe Files) per LFS speichert.

Note

Wichtig ist, dass wir die Gänsefüßchen richtig setzen, sonnst werden nur die aktuell in dem Verzeichnis liegenden Files übernommen. Wir wollen aber auch zukünftige Dateien übernehmen.

Jetzt kopieren wir unsere Binarys in das Verzeichnis CLIENT_DATA, wo normalerweise dann auch eine setup.opsiscript liegt, welches unsere Installationsanweisungen enthält. Informationen dazu findet Ihr auf der Homepage von OPSI.

cp /tmp/7z1900-x64.msi CLIENT_DATA/

Nun können wir alle unsere Dateien ins git übernehmen und committen.

git add *
git add .gitattributes
git commit -m "first init"
Nachdem wir nun unser lokales Git Repository erstellt haben, müssen wir dieses noch auf unseren GITLAB Server schaffen. Dazu erstellen wir zunächst in Gitlab ein neues Repository.

AddGroup

AddGroup

AddGroup

Nachdem in Gitlab das Repository erstellt wurde, zeigt GITLAB verschiedene Optionen, wie wir weiterverfahren können. Da wir bereits ein lokales Repo haben, müssen wir dieses nur noch mit dem GITLAB-Repo verbinden und es dann auf den Server pushen.

Auf unserem Devel-Server im Verzeichnis des Packages rufen wir nun folgende Kommandos auf:

git remote add origin ssh://git@<url gitlab>/packages/7zip.git
git push -u origin master

Hiermit sollte nun unser Package im Repository des Gitlabs liegen. Was uns nun noch fehlt, ist unsere CI-Pipeline.

Die CI-Pipeline

Eine CI-Pipeline wird in Gitlab über die Datei ".gitlab-ci.yml" konfiguriert bzw. gesteuert. Wir erstellen jetzt zunächst in unserem Package-Verzeichnis (Devel-Server) eine Datei .gitlab-ci.yml mit folgendem Inhalt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
---
#Zunächst werden die "stages", die in unserer CI existieren, definiert.
stages:
  - opsi-build
  - opsi-deploy

#Beginn des Stage: "opsi-build"
opsi-build:
  #hier wird definiert, zu welchem "stage" unser Block gehört.
  stage: opsi-build
  #hier wird definiert, dass der Stage nur dann ausgeführt wird:
  #  - wenn es sich um den Master Branch handelt,
  #  - und wenn dem Branch ein Tag zugeordnet ist.
  only: 
    - master
    - tags
  except:
    - branches
  #Der Runner mit dem Tag "opsi-packages" wird verwendet
  tags:
    - opsi-packages
  #Das eigentliche Skript
  script:
    - opsi-makepackage
  #Dateien die dem Path entsprechen, sollen zwei Tage im Artifacts-Storage gespeichert werden.
  artifacts:
    expire_in: 2 days
    paths:
      - ./*.opsi

#Beginn des Stage: "opsi-deploy"
opsi-deploy:
  stage: opsi-deploy
  only:
    - master
    - tags
  tags:
    - opsi-packages
  script:
    - cp *.opsi /var/lib/opsi/repository
    - opsi-package-manager -i -q *.opsi
  #Dieser Stage ist Abhängig von opsi-build Stage
  dependencies:
    - opsi-build

Nach em wir die Datei erstellt haben, müssen wir diese committen und an den Server pushen.

git add *
git commit -m "<commit message>"
git push -u origin master

Jetzt liegt die Datei auf dem Gitlab Server, allerdings passiert erst einmal nichts. Wir haben die gitlab-ci.yml so konfiguriert, dass diese nur die CI startet, wenn im Master-Branch ein Tag gesetzt wird. Also setzen wir einen Tag und pushen diesen in den Master.

git add *
git commit -m "<commit message>"
git tag -a <tag z.B. Versionsnummer> -m "<message>"
git push -u origin master <tag z.B. Versionsnummer>

Jetzt sollte die Pipeline starten.

Unsere Pipeline erstellt im ersten Stage das eigentliche OPSI-Package und speichert es im Artifacts-Storage für zwei Tage. Im zweiten Stage wird das Package dann in den OPSI-Depot Server eingefügt.

Aktuell arbeiten wir aber noch mit dem Master-Branch, das kann alleine auch gut funtkionieren. Sollte man aber mit mehreren Kollegen an einem Repo/Package arbeiten, bietet es sich an, mit Feature-Branches zu arbeiten.

Arbeiten mit Feature-Branch

Beim Arbeiten mit Feature-Branches ziehe ich mir von meinem Repo entweder einen CLONE oder mache, sofern das Repo schon geclont ist, einen PULL.

git clone <url git repo>
cd <package>

oder

cd <package>
git pull

Als Nächstes muss ich einen neuen Branch erstellen: Wir führen folgendes Kommando aus, welches einen neuen Branch erstellt und direkt in diesen wechselt.

git checkout -b <new feature branch name>

Jetzt kann ich an meinem Package entwickeln und die Änderungen dann in mein lokales Repo committen.

git add *
git commit -m "<commit message>"

Sobald ich mit meiner Entwicklung (Feature) fertig bin, kann ich dieses an den Gitlab Server schicken. Wichtig hier: ich gebe hinter nicht "master" an, sondern den Namen meines Branches (Feature).

git push -u origin <new feature branch name>

Nun liegt der Feature-Branch auch im GITLAB, lokal kann ich weiterhin Änderungen in dem Branch committen. Die Commits kann ich dann wieder mit dem Befehl oben an den Gitlab-Server schicken.

Nachdem nun mein Feature tatsächlich fertig ist, möchte ich den Branch in den Master-Branch mergen.

Note

Im Master-Branch liegt der fertige Source-Code, aus dem später dann via Gitlab-Runner unser Package gebaut werden soll.

Hierzu müssen wir im Gitlab zunächst einen Merge-Request erstellen. Der Merge-Request dient dazu, dass die Änderungen nochmal begutachtet werden, z.B vom Team oder vom Maintainer des Packages. Wird die Änderung genehmigt, wird der Merge ausgeführt.

Jetzt erstellen wir zunächst unseren Merge-Request im Gitlab.

AddGroup

Danach muss ich dem Merg-Request noch einen Titel und vielleicht eine Beschreibung mitgeben.

AddGroup

Jetzt ist der Merge-Request gestellt, und wir oder jemand anderes aus dem Team kann den Merge Request "Genehmigen und Ausführen" (merge.

Ist der Haken "Quellbranch löschen" aktiviert, wird der Feature-Branch gelöscht. Das ist der Default, der Branch wird aber nur im Gitlab gelöscht.

AddGroup

Nachdem nun unser Feature in den Master-Branch überführt ist, versehen wir das Ganze noch mit einer Versionsnummer, einem Tag. Da ich Leider noch keinen Weg gefunden habe, einem Merge-Request auch einen Tag mitzugeben, müssen wir dies manuell machen. Dazu wechseln wir wieder in "Project overview", dieses Mal klicke ich auf "+" neben "master 7zip/" und wähle "Neuer Tag" aus.

AddGroup

Jetzt vergebe ich noch eine Versionsnummer (Tagname) und eventuell eine Message.

AddGroup

Nun sollte auch schon die CI-Pipeline starten. Die CI/CD finde ich links im Menü.

AddGroup

Wenn ich nun auf "laufend" bzw "bestanden" klicke,

AddGroup

kann ich mir weitere Details ansehen.

AddGroup

Wie geht es weiter?

Als Nächstes kann ich lokal meinen Feature-Branch löschen - denn dieser befindet sich ja jetzt im Master (Gitlab). Damit mein Master-Branch wieder dem des Masters entspricht (wir haben ja im Feature-Branch gearbeitet), muss ich Remote und Lokal Repo abgleichen. Zuerst switchen wir in den Master, danach löschen wir den Feature-Branch (lokal) und gleichen unser Repo mit dem Remote-Repo ab.

git switch master
git branch -d <new feature branch name>
git pull

Für ein weiteres Feature lege ich mir weitere Branches an und pushe diese dann wieder ins GITLAB.

Fehlersuche

Sollte die Pipeline einmal nicht funktionieren, kann ich mir die Details unter CI/CD - Jobs nochmals genauer ansehen. Möchte ich den Runner debuggen, stoppe ich den Service und starte den Service wie folgt von Hand.

/usr/bin/gitlab-runner --debug run --working-directory /var/lib/gitlab-runner --config /etc/gitlab-runner/config.toml