Pulp¶
Siehe auch
- Verwandte Artikel
- Offizielle Dokumentation
- Linuxfabrik
Kickstart (Provisioning, das Pulp selbst nicht leistet)
Pulp ist ein Repository-Server für die zentrale, versionierte Spiegelung und Bereitstellung von Software-Paketen: RPMs, Debs, Container-Images, Ansible-Collections, Python-Wheels und beliebige Dateien.
Zum Einsatz kommt Pulp typischerweise dort, wo eine Flotte RHEL-/Debian-Hosts Pakete aus kontrollierten, firmen-eigenen Mirror-URLs zieht.
Was Pulp kann und was nicht¶
Pulp verwaltet Content, nicht Maschinen: Es spiegelt, versioniert, signiert und published Paket-Repositories und liefert sie an Clients aus (im Detail im nächsten Abschnitt). Was es dagegen nicht macht, und wofür es andere Werkzeuge braucht:
Betriebssysteme installieren oder Maschinen provisionieren: Kickstart/Anaconda, Preseed, Autoinstall, cobbler oder MAAS (siehe Anaconda und Kickstart)
Hosts konfigurieren: Ansible (LFOps), Puppet
Subscriptions und Entitlements verwalten
Hosts inventarisieren und Lifecycle-Umgebungen verwalten (das setzt Katello/Satellite obendrauf)
Vorteil gegenüber einem einfachen Mirror¶
Ein Mirror lässt sich auch ohne Pulp bauen: reposync holt die Pakete, createrepo erzeugt die Metadaten, ein Webserver liefert das Verzeichnis aus, ein Timer hält es aktuell. Für einen einzelnen, immer aktuellen RPM-Spiegel reicht das. Pulp lohnt sich, sobald mehr als das gebraucht wird:
Versionierte Snapshots statt Überschreiben:
reposyncführt das Spiegelverzeichnis immer auf den neuesten Stand, der vorherige Stand ist weg. Pulp hält jede Repository-Version vor und kann zurückrollen.Beliebige Versions-Aufbewahrung je Paket:
reposynckennt nur „nur die neueste“ (--newest-only) oder „alle“ Versionen. Pulp hält wahlweise die N neuesten je Paket vor (retain_package_versions), zum Beispiel die letzten drei.Kontrolliertes Weiterschalten statt sofort sichtbar: Bei
reposyncsehen Clients den neuen Stand, sobald der Sync durch ist. Pulp trennt Sync, Publication und Distribution; derselbe gespiegelte Stand lässt sich gezielt erst in Test, dann in Staging, dann in Produktion sichtbar machen.Automatisch signierte Metadaten:
createreposigniert dierepomd.xmlnicht, das müsste man manuell nachrüsten. Pulp signiert die Metadaten bei jeder Publication über den Signing Service.Mehr als RPM:
reposynckann nur RPM. Pulp spiegelt mit denselben Konzepten auch Deb, Container-Images, Python-Wheels, Ansible-Collections und beliebige Dateien.Sparsamer Speicher: Pulp dedupliziert identische Pakete per Checksum über Repositories hinweg, unabhängig von der Download-Policy. Mit
on_demandlädt es Pakete zusätzlich erst beim ersten Client-Zugriff, statt sie vorab zu spiegeln.reposynclädt immer alles und hält pro Repo eine eigene Kopie.Konsistente Auslieferung: Eine Publication wird atomar sichtbar, Clients sehen nie einen halb synchronisierten Zwischenstand.
Pulp im Red Hat Satellite¶
In genau dieser Rolle steckt Pulp auch in Red Hat Satellite (bzw. im Upstream Foreman/Katello). Die Satellite-Architektur verteilt die Aufgaben auf mehrere Dienste:
Foreman: Provisioning und Lifecycle-Management der Hosts, mit automatischer Konfiguration über Kickstart, Ansible-Playbooks und Puppet-Module.
Katello: Foreman-Plug-in, das Pulp und Candlepin in Foreman integriert und die Satellite-Abstraktionen wie Content Views, Lifecycle Environments, Sync Plans und Activation Keys beisteuert.
Candlepin: Subscription- und Entitlement-Engine (Manifest, Subscriptions, Entitlement-Zertifikate).
Pulp: spiegelt Repositories und liefert sie als Publications über Distributions aus, auch an die Capsule Server. Die Katello-Konstrukte (Content Views, Sync Plans) übersetzt Katello in diese Pulp-Operationen.
Pulp ist dort also die Content-Schicht; das Provisioning der Hosts übernimmt Foreman. Standalone-Pulp, wie in diesem Artikel beschrieben, ist dieselbe Content-Engine ohne den Satellite-Überbau.
Begriffe¶
- Content App
Der HTTP-Server-Anteil von Pulp. Liefert die Dateien über
https://<pulp>/pulp/content/<base-path>/an Clients aus, nach dem, was die jeweilige Distribution freigibt.- Distribution
Öffentlicher Pfad, unter dem eine Publication erreichbar ist. Die Distribution verweist entweder auf eine konkrete Publication (statisch) oder auf ein Repository (dann zeigt sie immer die neueste Publication davon). Erst durch eine Distribution wird Inhalt für Clients sichtbar.
- Orphan
Content-Unit (RPM, Deb, File), die in keinem Repository-Version mehr referenziert wird. Bleibt nach
repository destroyim Store liegen, bispulp orphans cleanupaufräumt.- Publication
Versionsgebundener Snapshot eines Repositories, inklusive signierter Metadaten. Ohne Publication gibt es keine Auslieferung - selbst wenn das Repository aktuell ist, sieht der Client bis zur nächsten Publication nichts.
- Pulp CLI
Offizielles Kommandozeilen-Werkzeug (
pulp-cli). Redet mit der REST-API und ist der empfohlene Weg für den Admin-Alltag. Alles, was die CLI kann, geht auch direkt percurloderpulpcore-manager.- pulpcore
Kern-Komponente, stellt API, Tasking, Auth und Content App bereit. Plugins wie
pulp_rpm,pulp_deb,pulp_file,pulp_container,pulp_ansibleundpulp_pythonbinden sich in pulpcore ein und liefern den Content-Typ-spezifischen Code.- Remote
Definiert die Upstream-Quelle eines Repositories: URL, TLS-Validierung, Credentials, Download-Policy (
immediatelädt alle Dateien,on_demandlädt erst bei Client-Zugriff,streamedwirft sie nach dem Ausliefern weg).- Repository
Container für Content. Sammelt die Inhalte, die
syncvom Remote holt oder die percontent uploadhochgeladen werden. Ein Repository hat mehrere Versionen (Version 0 = leer, Version N nach dem N-ten Sync).- Signing Service
Konfigurations-Objekt in Pulp, das auf ein Signier-Skript plus einen GPG-Key zeigt. Wird ein Repository mit einem Signing Service verknüpft, signiert Pulp die Metadaten bei jeder neuen Publication automatisch.
Architektur¶
Pulp besteht aus drei Diensten, die je nach Installations-Methode als eigene Systemd-Units oder Container laufen:
API-Server (Django/Gunicorn): REST-API, nimmt
pulp-cli-Aufrufe und Browser-Anfragen entgegen. Hält seinen gesamten Zustand in PostgreSQL, das zugleich die Task-Queue bereitstellt. Einen separaten Message-Broker (RabbitMQ, Redis) gibt es nicht.Worker-Pool: führt asynchrone Tasks aus - Syncs, Publications, Orphan-Cleanup, Signing. Die Worker ziehen ihre Aufträge aus der PostgreSQL-Task-Queue. Mehrere Worker parallel lassen sich anhand der Sync-Last skalieren.
Content App: liefert die Dateien aus. Üblicherweise vor der Content App ein Apache oder nginx als Reverse Proxy (TLS, Auth, Rewrites).
PostgreSQL ist die einzige zwingende Datenbank und das einzige unterstützte Backend; MySQL/MariaDB oder SQLite gehen nicht. Die Instanz darf lokal oder auf einem externen Host laufen, die Verbindung wird in /etc/pulp/settings.py über das DATABASES-Dict konfiguriert. Redis ist optional und dient ausschliesslich als Content-Cache (CACHE_ENABLED); ohne Redis arbeitet Pulp vollständig, nur ohne diesen Cache.
Pakete und andere Content-Units landen im Storage (Dateisystem unter /var/lib/pulp/ oder ein S3-Bucket).
Installation¶
Offiziell unterstützt werden zwei Wege: die Ansible-Collection pulp_installer (pulp.pulp_installer), die PostgreSQL, pulpcore, die gewünschten Plugins, Systemd-Units, optional Redis als Content-Cache und den Content-App-Reverse-Proxy in einem Rutsch einrichtet, und die Pulp-OCI-Images als Container in Podman oder Docker - bequem für Teststände und kurzlebige Setups.
Voraussetzung für die nachstehenden Beispiele: ein laufender Pulp-Server mit den Plugins pulp_rpm und pulp_file.
Pulp CLI einrichten¶
Der Admin-Alltag läuft über pulp-cli (gegen die REST-API). Es gibt zwar auch eine community-getriebene Web-UI (pulp-ui, in den OCI-Images enthalten) sowie die browsable REST-API, beide sind aber funktional begrenzt; eine vollwertige Management-Oberfläche liefert erst Katello/Satellite.
pip install 'pulp-cli[pygments]'
pulp config create \
--username=admin \
--base-url=https://mirror.example.com \
--password=<secret>
Die Konfiguration landet in ~/.config/pulp/cli.toml. Für automatisierte Aufrufe empfiehlt sich ein Service-Account mit eigenem Passwort statt des admin-Users und eine TOML-Datei, die nur diesem Account gehört und chmod 0600 trägt.
Verbindung testen:
pulp status
Signing Service einrichten¶
Ein Signing Service ist die Voraussetzung, damit Clients den Pulp-Metadaten trauen. Er wird einmal eingerichtet und danach per Name in Repositories referenziert.
GPG-Key auf dem Pulp-Host anlegen oder importieren, dann ein Wrapper-Skript, das Pulp zum Signieren aufruft:
#!/usr/bin/env bash
set -euo pipefail
FILE_PATH=$1
SIGNATURE_PATH="${FILE_PATH}.asc"
KEY_ID="${PULP_SIGNING_KEY_FINGERPRINT}"
gpg --quiet --batch --pinentry-mode loopback \
--passphrase "${PULP_SIGNING_KEY_PASSPHRASE}" \
--armor --detach-sign --local-user "${KEY_ID}" \
--output "${SIGNATURE_PATH}" "${FILE_PATH}"
echo "{\"file\": \"${FILE_PATH}\", \"signature\": \"${SIGNATURE_PATH}\", \"key_id\": \"${KEY_ID}\"}"
Skript-Permissions und Besitzer auf den pulp-User setzen. Dann den Service in Pulp registrieren:
pulpcore-manager add-signing-service sign-metadata \
/var/lib/pulp/scripts/sign-metadata.sh \
<fingerprint>
pulp signing-service show --name=sign-metadata
RPM-Repository spiegeln und veröffentlichen¶
Am Beispiel des offiziellen MariaDB-11.8-Repos für Rocky 9, gespiegelt für x86_64 und aarch64. Der Ablauf je Architektur: Remote anlegen, Repository mit Signing Service anlegen, syncen, publishen, distributen.
MariaDB benennt die Upstream-Pfade nach Debian-Art (amd64, aarch64), DNF nutzt dagegen $basearch (x86_64, aarch64). Die Schleife löst dieses Mapping auf und benennt die Pulp-Distribution nach dem DNF-Namen, damit auf dem Client eine einzige .repo mit $basearch reicht.
sign=$(pulp signing-service show --name=sign-metadata | jq --raw-output .pulp_href)
# pair = <mariadb upstream arch>:<dnf $basearch>
for pair in amd64:x86_64 aarch64:aarch64; do
up="${pair%%:*}"
arch="${pair##*:}"
pulp rpm remote create \
--name="mariadb-11.8-rocky-9-${arch}-remote" \
--url="https://mirror.mariadb.org/yum/11.8/rocky9-${up}/" \
--policy=immediate \
--tls-validation=true
pulp rpm repository create \
--name="mariadb-11.8-rocky-9-${arch}" \
--remote="mariadb-11.8-rocky-9-${arch}-remote" \
--metadata-signing-service="${sign}"
pulp rpm repository sync \
--name="mariadb-11.8-rocky-9-${arch}" \
--sync-policy=mirror_content_only
pulp rpm publication create --repository="mariadb-11.8-rocky-9-${arch}"
pulp rpm distribution create \
--name="mariadb-11.8-rocky-9-${arch}-dist" \
--base-path="mariadb-11.8-rocky-9/${arch}" \
--repository="mariadb-11.8-rocky-9-${arch}"
done
Die --sync-policy steuert, was mit vorhandenem Inhalt und mit den Metadaten passiert:
additive(Default): fügt den Upstream-Inhalt hinzu und entfernt nichts. Pulp erzeugt die Metadaten selbst. Nur diese Policy erlaubtretain_package_versions.mirror_content_only: gleicht den Paketbestand exakt an den Upstream an (entfernt, was dort weg ist), erzeugt die Metadaten aber selbst.mirror_complete: spiegelt Inhalt und die Upstream-Metadaten 1:1, also eine exakte Kopie.
Fürs Signieren wichtig: Pulp signiert nur Metadaten, die es selbst erzeugt, also bei additive und mirror_content_only. Bei mirror_complete bleiben die Upstream-Metadaten unverändert, der Signing Service greift dort nicht. Für einen signierten Spiegel ist daher mirror_content_only die richtige Wahl.
Statt aller Versionen lassen sich auch nur die N neuesten je Paket vorhalten: retain_package_versions am Repository setzen (0 = alle behalten), ältere fliegen beim nächsten Sync automatisch raus. Das geht nur mit additive, nicht mit den beiden Mirror-Policies; mit --policy=on_demand am Remote werden die alten Versionen gar nicht erst heruntergeladen.
for arch in x86_64 aarch64; do
pulp rpm repository update \
--name="mariadb-11.8-rocky-9-${arch}" \
--retain-package-versions=3
done
Wird die Distribution mit --repository statt --publication angelegt, zeigt sie automatisch immer auf die jeweils neueste Publication des Repositories - nach einem sync plus neuer Publication ist der neue Stand sofort sichtbar, ohne dass die Distribution umgehängt werden muss. Das ist der Default für Mirror-Setups.
Beide Architektur-Repositories sind danach unter https://mirror.example.com/pulp/content/mariadb-11.8-rocky-9/x86_64/ bzw. .../aarch64/ erreichbar, jeweils mit einer von Pulp signierten repomd.xml samt repomd.xml.asc.
Auf dem Client genügt dank $basearch eine einzige .repo-Datei für beide Architekturen:
[mariadb]
name=MariaDB 11.8 (Rocky 9)
baseurl=https://mirror.example.com/pulp/content/mariadb-11.8-rocky-9/$basearch/
gpgcheck=1
gpgkey=https://mirror.example.com/pulp/content/keys/GPG-KEY-mariadb-signing-key.asc
repo_gpgcheck=1
enabled=1
File-Repository für GPG-Keys und statische Dateien¶
GPG-Keys, Shell-Skripte, Ansible-Inventories oder Konfigurationsdateien, die per HTTP bereitgestellt werden sollen, leben in File-Repositories.
Repository anlegen, Datei hochladen, veröffentlichen:
pulp file repository create --name=keys
pulp file content upload \
--repository=keys \
--file=/home/linuxfabrik/GPG-KEY-galeracluster.com \
--relative-path=GPG-KEY-galeracluster.com
pulp file publication create --repository=keys
pulp file distribution create \
--name=keys-dist \
--base-path=keys \
--repository=keys
Updates einspielen¶
Ein Routine-Update besteht aus zwei Kommandos je Architektur-Repository: erneut syncen, neue Publication anlegen. Die Distribution zeigt automatisch auf den neuen Stand (sofern sie mit --repository angelegt wurde):
for arch in x86_64 aarch64; do
pulp rpm repository sync --name="mariadb-11.8-rocky-9-${arch}"
pulp rpm publication create --repository="mariadb-11.8-rocky-9-${arch}"
done
Für regelmässige Mirror-Läufe eignet sich ein Systemd-Timer oder ein Cron-Job, der diese Kommandos für jede relevante Repo-Definition absetzt.
Aufräumen¶
Pulp löscht Content nicht automatisch. Nach dem Entfernen von Repositories oder Repository-Versionen bleiben verwaiste Content-Units im Storage; der Aufräum-Lauf ist explizit:
pulp orphans cleanup
Ein Auto-Cleanup-Intervall lässt sich über ORPHAN_PROTECTION_TIME (in Minuten) in der Pulp-Konfiguration setzen; per Default wartet Pulp 24 Stunden, bevor ein Orphan entfernt werden darf.
Repository und Distribution löschen¶
In umgekehrter Reihenfolge zum Anlegen, je Architektur: Distribution, Repository, Remote. Die Publications hängen an der Repository-Version und verschwinden bereits mit dem repository destroy. Danach die Orphans aufräumen, das entfernt die jetzt verwaisten Content-Units (RPMs, Debs, Files):
for arch in x86_64 aarch64; do
pulp rpm distribution destroy --name="mariadb-11.8-rocky-9-${arch}-dist"
pulp rpm repository destroy --name="mariadb-11.8-rocky-9-${arch}"
pulp rpm remote destroy --name="mariadb-11.8-rocky-9-${arch}-remote"
done
pulp orphans cleanup
Backup und Restore¶
Ein vollständiges Backup besteht zwingend aus drei Teilen, die zueinander konsistent sein müssen:
PostgreSQL-Datenbank: hält ausschliesslich Metadaten - welche Content-Unit zu welcher Repository-Version gehört, dazu Remotes, Publications, Distributions, Signing-Services und Task-Historie. Die Pakete selbst stecken hier nicht drin.
Storage (
/var/lib/pulp/bzw. der S3-Bucket): die eigentlichen Content-Units (RPMs, Debs, Files). Die Datenbank referenziert sie nur über ihre Prüfsumme.DB-Encryption-Key (
/etc/pulp/certs/database_fields.symmetric.key): verschlüsselt sensible Felder in der Datenbank, vor allem Remote-Credentials, Storage-Konfigurationen und Task-Argumente.
Warum der Content zwingend mitgesichert werden muss: Der Nutzen von Pulp liegt in den versionierten Repository-Ständen, auf die sich bei Bedarf zurückrollen lässt. Diese Versionen verweisen über die Prüfsumme auf Content-Units im Storage. Wird nur die Datenbank gesichert und der Storage nicht, kennt Pulp nach dem Restore zwar alle Versionen, Publications und Distributions, die zugehörigen Paketdateien fehlen aber. Ein Rückrollen auf einen alten Stand ist dann unmöglich, und auch der aktuelle Stand liefert keine Dateien mehr aus.
Erneutes Herunterladen ist kein Ersatz: Bei --policy=on_demand liegen noch nicht abgerufene Pakete zwar nicht im Storage und liessen sich theoretisch neu vom Upstream holen. Aber genau die alten Versionen, deretwegen man die Historie überhaupt vorhält, sind upstream oft längst entfernt. Darauf kann man sich nicht verlassen.
Konsistenz: Datenbank und Storage müssen denselben Zeitpunkt abbilden. Wird der Storage gesichert, während ein Sync läuft, referenziert die Datenbank hinterher Content-Units, die im Backup fehlen (dangling references), oder das Backup enthält Units, die keine Version mehr kennt (Orphans). Für ein konsistentes Backup entweder die Pulp-Dienste kurz stoppen oder einen Storage-Snapshot (LVM, ZFS, S3-Versioning) nutzen, der den Stand atomar einfriert.
Den DB-Encryption-Key getrennt und sicher aufbewahren: Wird die Datenbank ohne den passenden Key restauriert, sind die verschlüsselten Felder unlesbar. Remotes mit hinterlegten Credentials funktionieren dann nicht mehr, im schlimmsten Fall droht Datenverlust. Bei den Pulp-OCI-Images und dem pulp_installer wird der Key automatisch erzeugt; er gehört von Anfang an ins Backup.
Zusätzlich /etc/pulp/settings.py sichern (Konfiguration inklusive DB- und Storage-Anbindung), damit sich der Server ohne manuelle Rekonstruktion wieder aufsetzen lässt.
Troubleshooting¶
pulp syncschlägt mit TLS-Fehler fehlDas Upstream-Zertifikat ist Pulp nicht bekannt. Bei öffentlich gültigem Zertifikat das CA-Root des Pulp-Hosts aktualisieren (
update-ca-trustunter RHEL). Bei selbstsignierten Zertifikaten das Zertifikat am Remote hinterlegen:pulp rpm remote update --name=<remote> --ca-cert @/path/to/ca.pemoder, nur für kurzlebige Tests,--tls-validation=false.- Clients sehen ein Paket nicht, obwohl
syncgelaufen ist Der
syncbringt Content nur ins Repository; sichtbar wird er erst mit einer neuenpublication createund - falls die Distribution statisch auf eine Publication zeigt - einer neuendistribution update --publication=.... Distributions mit--repository(Default für Mirrors) folgen automatisch; einsystemctl reload httpdoder Ähnliches ist nie nötig, weil die Content App keine Zwischenpuffer hat.- Metadaten-Signatur fehlt in
repomd.xml.asc Repository nicht mit
--metadata-signing-serviceverknüpft. Nachziehen überpulp rpm repository update --name=<repo> --metadata-signing-service=<href>und danach eine neue Publication erstellen.pulpcore-manager add-signing-servicefindet den Key nichtDer GPG-Key muss im Keyring des
pulp-Systemusers liegen, nicht im Keyring des Admins, der das Kommando ausführt. Key alspulp-User importieren oder per--gnupghomeexplizit den richtigen Pfad angeben.pulp orphans cleanupentfernt nichts, obwohl Repos gelöscht sindORPHAN_PROTECTION_TIMEgibt dem Content eine Schonfrist. Entweder warten oder den Parameter in/etc/pulp/settings.pyauf0setzen und den API-Server neu starten (nur auf Teststellen sinnvoll).- Uploads oder Syncs bleiben im Status
waitingstehen Kein freier Worker.
systemctl status pulpcore-worker@*prüfen; bei Bedarf weitere Worker-Units über die pulp_installer-Variablepulpcore_workershochskalieren.