c't 13/2021
S. 158
Praxis
Linux: Snap-Pakete bauen

Privates Schnäppchen

Snap-Pakete für eigene Anwendungen erstellen

Unter Ubuntu kommt man nicht mehr um das Paketformat Snap herum. Auch eigene Programme und Skripte schnüren Sie mit den passenden Werkzeugen schnell zu kleinen Snap-Paketen.

Von Tim Schürmann

Neben der Software aus den hauseigenen Repositories liefert Ubuntu mittlerweile so manche Software als Snap-­Paket aus. Während in der restlichen Linux-­Welt das alternative Paketformat Flatpak boomt, hält Canonical für Ubuntu am im eigenen Haus entwickelten Snap-­Format fest. So verpackt liefert der Distributor derzeit vor allem Serverdienste und Software, die häufig Updates erhalten. Das leichtgewichtige Ubuntu Core nutzt sogar ausschließlich Snap-Pakete.

Wie beim Konkurrenten Flatpak [1] lässt sich Software leichter als Snap-Paket bereitstellen, statt für jede Distribution und Version ein eigenes Paket zu bauen. Das Paket enthält dann alles in der richtigen Version, was die Software zum Betrieb braucht. Das hat noch einen weiteren Vorteil: So lassen sich leicht mehrere Ver­sionen einer Software parallel installieren und nutzen. Während Flatpak vor allem bei Desktop-Anwendungen beliebt ist, eignen sich Snaps explizit auch für Serverdienste. Für das Schnüren eigener Snap-­Pakete bietet Canonical eigens das Build-­Tool Snapcraft an.

Bezugsfertig

In einem Snap-Paket landen neben der eigentlichen Anwendung auch alle von ihr benötigten Bibliotheken, Grafiken und weiteren Komponenten – ausgenommen ist der Linux-Kernel. Auf diese Weise läuft das Programm auf allen Distributionen, die das Snap-Format unterstützen. Damit die Snap-Pakete durch all die zusätzlichen Inhalte nicht übermäßig groß ausfallen, wird auf jedem System ein sogenanntes Base Snap (früher auch als OS Snap bezeichnet) installiert, das ein minimales Ubuntu-System bereitstellt. Es enthält unter anderem Glibc, OpenSSL und Python. Sie dürfen zwischen mehreren Base Snaps wählen.

Einen Überblick über die im Snap Store angebotene Software gibt es auf snapcraft.io.

Als zentrale Anlaufstelle für Snap-­Pakete hat Canonical den Snap Store eingerichtet. Die dort angebotene Software steht wie gewohnt über Ubuntus Softwareverwaltung bereit und lässt sich ebenso einfach installieren wie andere Programme. Im Snap Store lagern zudem die Base Snaps: Setzt ein Snap-Paket ein bestimmtes Base-Paket voraus, kümmert sich der Paketmanager automatisch um dessen Installation.

Sandkasten

Sicherheitshalber sperrt das Linux-System jede Anwendung aus einem Snap-Paket in eine eigene Sandbox. Diese funktioniert ähnlich wie ein Docker-Container: Die Anwendung glaubt, allein auf einem minimalen Linux-System zu laufen, und sieht nur das vom Base Snap bereitgestellte Minimalsystem. Standardmäßig darf das per Snap-Paket installierte Programm weder auf das Netzwerk noch auf viele weitere Ressourcen des Computers zugreifen. Wie das im Einzelnen funktioniert, haben wir bereits beschrieben [2, 3].

Mit dem Werkzeug namens Snapcraft können Sie selbst für Anwendungen halbautomatisch ein Snap-Paket bauen. Das Tool erzeugt zunächst eine neue Sandbox, in die es den Quellcode Ihres Programms kopiert. Im zweiten Schritt übersetzt Snapcraft Ihre Anwendung in der Sandbox. Das Ergebnis verpackt das Werkzeug abschließend in ein Snap-Paket. Das Kompilieren der Software in der Sandbox ist notwendig, damit die Anwendung später reibungslos in ihrem abgeschotteten Bereich läuft. Allerdings erschwert das auch die Fehlersuche. Sie sollten daher sicherstellen, dass Ihr Quellcode in einem eigenen Projektverzeichnis untergebracht ist und sich dort problemlos kompilieren lässt.

Ein kleines Beispiel soll die Verwendung von Snapcraft demonstrieren. Das Tool soll einen kleinen Newsfeed-Reader in ein Snap-Paket verpacken. Der Reader besteht aus dem kleinen Python-Skript newsfeed.py, das nur wenige Zeilen Code enthält und selbst ohne umfangreiche ­Python-Kenntnisse leicht verständlich sein sollte:

import feedparser
newsfeed = feedparser.parse(
"https://heise.de/rss/heise-atom.xml")
for i in range(0 ,5):
  entry = newsfeed.entries[i]
  print(entry.title.encode('ascii',
    'replace'))

Das Skript greift auf die Hilfe der Bibliothek feedparser zurück, die deshalb installiert sein muss. Fehlt sie auf dem System, erhalten Sie beim Aufruf des Skripts mit python3 newsfeed.py eine entsprechende Fehlermeldung. Rüsten Sie dann die ­Bibliothek beispielsweise mit dem Paket ­python3-feedparser aus der Paketverwaltung nach:

sudo apt install python3-feedparser

Das Skript importiert die Bibliothek in der ersten Zeile, daher muss sie zusammen mit einer Python-Umgebung ebenfalls in das Snap-Paket. Die zweite Zeile ruft den heise-online-Newsfeed ab. Erst die nachfolgende Schleife schreibt die Überschriften der ersten fünf Einträge auf den Bildschirm.

Eine kleine Stolperfalle gibt es noch: Der Feedreader startet mit dem Aufruf python3 newsfeed.py. Auch das muss das Snap-Paket entsprechend berücksichtigen.

Eingeschnappt

Holen Sie zunächst Snapcraft auf Ihr ­System, indem Sie unter Ubuntu das Paket snapcraft über die Softwareverwaltung oder mit sudo apt install snapcraft ­installieren. Erstellen Sie ein neues Projektverzeichnis und speichern Sie darin den Quellcode des Feedreaders als Datei newsfeed.py.

Außerdem brauchen Sie die Datei snapcraft.yaml, die Snapcraft die nötigen Instruktionen liefert. Neben ein paar grundlegenden Informationen listet sie auch sämtliche Abhängigkeiten der zu verpackenden Anwendung auf. Eine einfache Vorlage generiert der Befehl snapcraft init, der im aktuellen Ordner das Unterverzeichnis snap anlegt und darin die Datei snapcraft.yaml. Alternativ erstellen Sie die Datei manuell in Ihrem Projekt­verzeichnis. Der Aufbau der Datei folgt dem YAML-Format. Achten Sie bei Ihren Änderungen darauf, mit Leerzeichen einzurücken. Für den Newsfeed-Reader in Python sieht die Datei snapcraft.yaml wie im Listing unten aus.

Zunächst erhält das Snap hinter name eine Bezeichnung. Sie darf nur aus kleingeschriebenen, alphanumerischen Zeichen sowie Bindestrichen bestehen und muss unter den lokal verfügbaren Snaps eindeutig sein. Hinter version folgt die Versionsnummer der Anwendung, die Sie frei wählen können. summary fasst in maximal 78 Zeichen zusammen, was die Anwendung leistet, hinter description steht eine ausführlichere Beschreibung.

Wie stark das Snap-System die Anwendung abschirmt, verrät confinement. Im Fall von classic erhält die Anwendung normalen Zugriff auf das System, bei strict läuft sie in einer äußerst restriktiven Sandbox, die wir in diesem Fall empfehlen. Letzteres gilt auch für den devmode, der jedoch die Zugriffsbeschränkungen ein wenig aufweicht und zusätzliche Debug-­Meldungen generiert.

Das Snap-System errichtet die Sandbox aus einem der verfügbaren Base Snaps, die derzeit ausschließlich Ubuntu-Systeme mit Long Term Support enthalten. Welches der Ubuntu-Systeme Ihre Software nutzen soll, legt in der Datei snapcraft.yaml die Einstellung base fest. core20 steht für Ubuntu 20.04, core18 für Ubuntu 18.04 und core für Ubuntu 16.04. Die Snapcraft-Dokumentation empfahl bei Redaktionsschluss den Griff zu core18, da core20 unter anderem noch nicht korrekt mit einigen Snapcraft-Plug-ins zusammenarbeitet (siehe ct.de/yzqh). Um diese Plug-ins soll es im nächsten Schritt gehen.

Das Base Snap mit Ubuntu 20.04 LTS ­unterstützt das Python-Plug-in noch nicht, ­deshalb kann Snapcraft in der Sandbox keine Python-Umgebung ­einrichten.

Teilbar

Die Bibliotheken, Grafiken und anderen Bestandteile einer Anwendung bezeichnet Snapcraft als „Parts“. Für unser Beispiel-Python-Skript newsfeed.py genügt ein Part, dem Sie in der YAML-Datei etwa die Bezeichnung ct-newsfeed geben.

Wie Snapcraft einen Part behandeln beziehungsweise kompilieren muss, erfährt das Werkzeug durch ein Plug-in. So kümmert sich beispielsweise das Python-Plug-in um die korrekte Erstellung eines Python-Programms. Welche Plug-ins zur Verfügung stehen, verrät folgendes Kommando:

snapcraft list-plugins

Das für einen Part notwendige Plug-in nennt die Datei snapcraft.yaml hinter plugin. Unter Umständen benötigt ein Plug-in weitere spezielle Einstellungen. In unserem Beispiel verwendet Snapcraft die hinter python-version angegebene Python-­Version 3. Welche Einstellungen das Plug-in verlangt, finden Sie mit snapcraft help python heraus. Bei anderen Plug-ins ersetzen Sie python im Befehl durch den jeweiligen Plug-in-Namen.

Die Bezeichnungen der Snapcraft-Plug-ins sind weitgehend ­selbsterklärend

Für alle Parts existiert die Einstellung source. Sie verrät Snapcraft, wo es den Quellcode beziehungsweise die Dateien des Parts findet. Im Beispiel ist es das aktuelle Verzeichnis (das ein Punkt kennzeichnet). Anstelle eines Verzeichnisses können Sie auch ein Archiv oder den Link zu einem Versionskontrollsystem angeben. stage-packages listet alle Ubuntu-­Pakete auf, die später zum Betrieb der Anwendung notwendig sind. Im Beispiel ist das die Bibliothek feedparser aus dem Ubuntu-Paket python3-feedparser.

Die aufgelisteten Pakete installiert Snapcraft mit dem normalen Paketmanager. Sie passen zudem immer zum gewählten Base Snap. Haben Sie sich etwa für core18 und somit Ubuntu 18.04 als Grundlage entschieden, holt Snapcraft auch den in Ubuntu 18.04 verfügbaren feedparser in die Sandbox. Benötigen Sie Pakete zum Kompilieren Ihrer Anwendung in der Sandbox, listen Sie diese unter build-packages auf. Beachten Sie, dass die build-packages später nicht bei der Ausführung der Anwendung zur Verfügung stehen. Ergänzend gibt es noch build-snaps und stage-snaps, die Snap-­Pakete aus dem Snap Store in der Sandbox einrichten.

Ein Snap-Paket darf auch mehrere Anwendungen enthalten. So könnte eine Datenbank neben dem Server auch noch eine Client-Anwendung mitbringen. Welche Programme ein Snap offeriert, verrät der Abschnitt apps der YAML-Datei. Im Beispiel gibt es nur ein Programm namens ct-newsfeed. Wenn der Name wie hier mit dem Namen des Snaps übereinstimmt, lässt sich die Anwendung später direkt über diesen Namen starten. Würden Sie hingegen showfeed verwenden, müsste man den Newsreader später per ct-newsfeed.showfeed aufrufen. Sinnvoll ist das nur, wenn das Snap mehrere Programme enthält. Bei einer Datenbank könnte so foodb.server den Server starten und foodb.client das Client-Programm. Wenn Sie den Newsfeed-Reader später mit ct-newsfeed aufrufen, startet automatisch der hinter command angegebene Befehl in der Sandbox.

Snapcraft in Aktion

Mit der Außenwelt kommunizieren die eingesperrten Snap-Anwendungen über sogenannte Interfaces. So ermöglicht beispielsweise das Interface network den Zugriff auf das Netzwerk. Um ein Interface nutzen zu können, steckt die Anwendung einfach einen virtuellen Stecker (Plug) in das passende Interface. Das geschieht in der Datei snapcraft.yaml unter plugs. Der Newsfeed-Reader erhält auf diesem Weg Zugriff auf das Netzwerk (network) und das Home-Verzeichnis (home).

Die Snapcraft-Dokumentation listet alle verfügbaren Interfaces auf und liefert zu jedem eine kurze Beschreibung.

Um nun ein Snap-Paket zu bauen, rufen Sie im Projektverzeichnis das Kommando snapcraft auf. Das Build-Werkzeug Snapcraft sucht dann automatisch nach der Datei snapcraft.yaml und zwar sowohl im aktuellen Verzeichnis als auch im Unter­ordner snap. Beim ersten Aufruf dauert die Zusammenstellung der Sandbox eine Weile. Wenn Sie Snapcraft später erneut anstoßen, geht das Bauen des Pakets etwas schneller.

Sollte das Werkzeug den Fehler Error while refreshing snap ... melden, starten Sie den für die Paketverwaltung zuständigen Daemon snapd einmal neu mit dem Konsolenbefehl sudo systemctl restart snapd. Stoßen Sie dann Snapcraft erneut an. Hilft auch das nicht, führen Sie einen Refresh mit snap refresh snapd durch.

Bei einem erfolgreichen Durchlauf entsteht ein Paket mit der Dateiendung „.snap“, das Sie mit folgendem Befehl ­installieren:

sudo snap install ct-newsfeed_*.snap --dangerous

Das Snap-Paket enthält alle notwendigen Daten in einem Squashfs-Dateisystem, das der Snap-Paketmanager beim Start der Anwendung mountet. Im Gegensatz zum Konkurrenten Flatpak lässt sich derzeit nicht einfach ein eigenes Repository aufsetzen. Stattdessen können Entwickler ihr Snap nur direkt anbieten oder es über den Snap Store von Canonical einer breiten Anwenderschaft zur Verfügung stellen (siehe ct.de/yzqh).

Wenn Sie Snapcraft in einer virtuellen Maschine starten, müssen Sie noch den ­Parameter --use-lxd ergänzen. Snapcraft startet dann die Sandbox nicht mit der ­virtuellen Maschine KVM, sondern mithilfe der Container-Technik LXD.
Die von feedparser zurückgelieferten Texte sind UTF-8-kodiert. Snaps geben im strict-Modus jedoch nur ASCII-Zeichen aus. Im Python-Skript ­wandelt encode() die Ausgabe um, wodurch Sonderzeichen und ­Umlaute verloren gehen.

Schöne neue Welt

Ein Snap-Paket ist zwar mit Snapcraft schnell geschnürt, das Format bereitet allerdings ein paar neue Probleme. Derzeit ist das Paketformat hauptsächlich in der Ubuntu-Welt verbreitet, daher kommt man nicht umhin, weitere Pakete bereitzustellen. Die Sandbox sorgt zwar für zusätzliche Sicherheit, erschwert aber das Testen der Anwendung. Werfen Sie Snapcraft deshalb erst dann an, wenn Ihre Software fehlerfrei läuft. Bedenken Sie auch, dass Sie beim Anbieten von Paketen auch dafür verantwortlich sind, die mitpaketierten Bibliotheken von Dritten auf aktuellem Stand zu halten. Und nicht zuletzt sind Sie natürlich auch an Canonicals Snap Store und dessen Regeln gebunden. (lmd@ct.de)

Snap Store & Infos: ct.de/yzqh

Kommentare lesen (1 Beitrag)