Immer frische Tools geliefert - Pakete bauen auf U7

Posted by noa on Tuesday, November 21, 2023

Wie ihr an der ein oder anderen Stelle, und zuletzt leider bei unserem kürzlichen Security Vorfall, vielleicht festgestellt habt, bauen wir zunehmend viele Pakete für U7 selbst. Inzwischen sind das schon über 100 einzelne Projekt Repos in unserer Gitlab Instanz. Das hat vor allem mit dem unterliegenden Betriebssystem CentOS7 zu tun, das eine eher “konservative” Strategie fährt und von Haus aus nur gut abgehangene und lange profilierte Software anbietet. Das ist zwar ein Plus für Stabilität und Sicherheit, bedeutet aber auch im Endeffekt eine ziemliche Einschränkung der Bewegungsfreiheit auf der Konsole und im Ausbau des eigenen Asteroids.

Viele moderne Tools sind aus dem Standard Yum Repository für CentOS7 gar nicht verfügbar oder nur in alten Versionen mit fehlender Funktionalität. Ganz “normale” praktische Kommandos wie htop und wp-cli oder auch die diversen Programmiersprachen, die wir in mehreren aktuellen Versionen anbieten würden die offiziellen Quellen des Betriebssystems schlicht nicht hergeben.

Das ist natürlich nicht so schön für unsere User:innen oder uns selbst, denn auch wir sind ja immer mal wieder auf den Hosts unterwegs und nutzen Kommandos wie bat, htop, lsd oder den aktuellsten node_exporter um Metriken für unser Monitoring zu liefern. Auch das Haraka, dass wir inzwischen als Mailserver auf den Hosts einsetzen, bauen wir selbst für unser Uberspace CentOS7. Ganz abgesehen davon betreiben einige von uns natürlich auch private Accounts und sind quasi selbst User:innen mit gewissen Vorlieben 🙂

Darum haben wir schon auf U6 mit Tools wie brew und toast angeboten, dass ihr in eurem Uberspace selbst Pakete zusammenbauen könnt. Für U7 haben wir uns dann entschieden, dass einfach generell global und damit immer für alle zu machen. Sofern das mit vertretbarem Aufwand geht, oder auch mal etwas darüber hinaus.

Wenn wir im Support also eine Anfrage bekommen in die Richtung “Gibt es eigentlich …?” “Könnte ich nicht …?” oder direkt auch ein “Könnt ihr mal foo installieren?” schauen wir erstmal nach ob das Tool nicht doch im Yum Repo für Centos7 bereitstünde. Dann können wir den Wunsch ganz kurz und schmerzlos mit einem yum install foo auf dem einen Host der fragenden User:in realisieren. Beim nächsten Release rollen wir es dann auf alle Hosts aus.

Falls das nicht so einfach ist müssen wir selbst ran und dann gibts erstmal ein Issue für unser Dev Team um zu schauen ob wir hier ein eigenes RPM Paket bauen können.

Was heißt eigentlich “ein Paket bauen”?

Erst mal ein kleiner Zwischeneinschub über was wir hier eigentlich reden, also was überhaupt ein “Paket” auf einem Linux System ist und was wir da “bauen”.

Das Ziel ist es eine Archivdatei (wie ein Zip) zu generieren, in unserem Fall eine RPM Datei mit der Endung .rpm, das ist unser Paket. Von anderen Systemen kennt ihr wahrscheinlich die Endung .deb. Warum diese Formate praktisch sind und wie wir sie verwenden können, dazu mehr am Ende, jetzt erst nochmal zum Anfang.

Wenn wir vom bauen sprechen, meinen wir natürlich nicht, dass wir die Software in irgendeiner Weise selber entwickeln. Stattdessen bedienen wir uns an dem, was die Open Source Welt für uns und euch bereit stellt. Und das von ganz unterschiedlichen Quellen und in unterschiedlichen Formaten.

Manchmal wird die Software einfach über das Repository der entsprechenden Programmiersprachen bereitgestellt, die dann über Paketmanager wie pip, cargo oder npm genutzt werden können. Andere Entwickler:innen nutzen ihre eigenen Download Websites, viele bieten ihren Code und fertige Releases auf Portalen wie Gitlab und Github an. Im Idealfall werden dort dann auch schon fertig ausführbare Binary Dateien für verschiedene Systeme bereitgestellt. Gibt es diese nicht, müssen wir diese noch selbst kompilieren. Das heißt (ganz grob eingedampft): ausführen von configure; make; make install auf einem passenden System um die ausführbaren Dateien zu erstellen.

Dazu kommen dann oft noch mehrere Konfigurationsdateien oder Service Dateien, die benötigt werden, um ein dauerhaft laufendes Programm wie z.B. den node_exporter von einem Daemon kontrollieren zu lassen. Alle diese Dateien müssen am Ende auf allen Uberspace Hosts in der richtigen Konfiguration in den richtigen Pfaden landen. Und anstatt das für jeden Host neu durchzuspielen, packen wir alles einmalig in ein RPM Paket und laden es in unser eigenes Yum Repository hoch. Von dort kann sich der Host das dann selbst herunterladen, auspacken und installieren.

Und zwar wieder genau so einfach wie ganz am Anfang erwähnt, nämlich mit einem yum install foo.

Jetzt klingt das zwar schön und abschließend gelöst, irgendwer will ein neues Tool, wir frickeln ein Paket zusammen, laden das in unser Yum Repo hoch und dann steht es für alle Uberspace Hosts bereit. Und im Grunde ist das auch erstmal so, nur ist es mit dem einmaligen Ein- und Auspacken leider nicht getan. Denn Software will immer auf dem neuesten Stand gehalten werden. Und bei über 100 selbstgebauten Paketen machen wir das nicht manuell sondern haben das automatisiert, zumindest zum allergrößten Teil. Willkommen bei Firehose - unserem Package Update Automatisierer 🚀

Updates mit 🔥 Firehose 🔥

Ein Hund der mit einer Wasserbrause nass gespritzt wird und von dem Wasser trinkt
Like drinking from the firehose - lasst uns die Updates einfach automatisieren
Wir haben also unseren klaren Ablauf für den Paketbau festgelegt: Wir laden den Quellcode oder die Binaries von URL xyz herunter, schieben Dateien hierher und dorthin, Kompilieren und Konfigurieren, erstellen vielleicht ein paar Service Dateien und suchen uns dann alles wieder zusammen um es mit ein paar Metadaten in eine Paketdatei zu packen und dann für Yum bereitzustellen. Diesen ganzen Ablauf konfigurieren wir mit der aktuellen Versionsnummer in der .gitlab-ci des Tool Repos und richten damit ein CI System ein, das bei jedem neuen Commit eine eigene Umgebung generiert in der unsere konfigurierten Kommandos ausgeführt werden und dann am Ende ein Paket in unser Repository hochlädt.

So haben wir also den Paketbau an sich schön automatisiert, aber trotzdem ist das ganze noch recht statisch. Wer schaut nach Updates, setzt die Versionsnummer hoch und bringt die CI in Gang? Und das jede Woche für über 100 Pakete. Hier kommt nun Firehose ins Spiel.

Jedes Tool Repo enthält noch eine zusätzliche firehose.yml in der wir festlegen, von welcher Quelle und unter welcher ID wir am einfachsten eine Angabe über die aktuelle Version erhalten können. In den meisten Fällen ist das Anitya bzw. release-monitoring.org, die diese Informationen für zig Software Quellen gesammelt aufbereiten und dann eine schöne API zur Abfrage bereitstellen (Danke!). Unsere config Datei sieht dann dementsprechend simpel aus:

---
source: anitya
id_at_source: 1332

Wenn wir dann Anfang jeder Woche Firehose anwerfen, läuft es über alle unsere Packages Repos und lädt diese Config Dateien ein. Es überprüft die Quellen und vergleicht die aktuellste Version mit der TOOL_VERSION in der .gitlab-ci des Repos. Bei einem verfügbaren Update erstellt Firehose einen neuen GIT Branch, ändert die Versionsnummer, Commited und Pusht die Änderung und erstellt einen Merge Request in unserem Gitlab Repo. Dort laufen dann die CI Pipelines an, die das Paket in der neuen Version bauen, Tests durchführen und bei Erfolg das Paket in unser Yum Testing Repository hochladen.

In diesem Zwischenschritt können wir auf unseren Testhosts die neuen Versionen erst einmal ausprobieren. Außerdem können wir eingreifen wenn Fehler in den Pipelines auftreten weil sich etwas im Build Prozess geändert hat oder die automatischen Tests ein Problem mit der bisherigen Konfiguration feststellen. Dann können wir in dem Update Branch nachbessern und weiter testen bevor wir alles in den Main Branch mergen und damit eine weitere Pipeline anstoßen die das RPM von dem Testing Repo in das Production Repo kopiert und damit für alle Hosts verfügbar macht.

Dieser Ablauf garantiert uns eine direkte Nachvollziehbarkeit des automatischen Paketbaus und gibt uns die Möglichkeit immer auch wieder manuell einzugreifen.

Nachdem wir die Pakete in unserem eigenen Yum Repo upgedatet haben starten wir in den folgenden Tagen ein yum update auf allen Hosts und installieren damit alle verfügbaren Updates, auch diese aus den offiziellen Quellen. Wir verteilen das über Batches von Hosts über mehrere Tage, so können wir erst einmal feststellen, ob ein Update größere Probleme bringt, bevor wir es komplett ausrollen. Daher kann es passieren, dass ihr zwischen diesen Tagen auf unterschiedlichen Hosts leicht abweichende Versionsnummern in manchen Tools bemerkt.

Ausblick auf U8

Nun sind wir hier ja damit gestartet, dass der Grund für den ganzen Paketbau vor allem unser in die Jahre gekommenes Betriebssystem CentOS7 ist, dessen Ende im nächsten Jahr auch schon bevorsteht. Für unsere neue Version U8 schlagen wir eine andere Richtung ein, weg vom konservativen gut abgehangenen Ansatz zu einem Rolling Release, das uns dauerhaft aktuelle Versionen von Haus aus bescheren soll. Das wird uns den eigenen Paketbau aber aller Voraussicht nach auch nicht ersparen und ein System wie Firehose werden wir weiterhin betreiben. Allerdings haben wir das allgemeine Update Prozedere auf den Hosts noch einmal umgedacht um uns den neuen Gegebenheiten anzupassen.

Aber dazu erzählen wir mehr zu gegebener Zeit, soweit erstmal weiterhin viel Freude mit unseren U7 Paketen 👷‍♀️

Bike Photo by the author Dog Photo by Pierre Bamin on Unsplash