Build-Systeme

TL;DR

  • Jede component deklariert build_system: <key> in component.yml. Der Schlüssel wählt ein kuratiertes Paar von Images: ein Build-Image, das den Quellcode kompiliert, und ein runtime-Image, das ihn in der Produktion ausführt. Keine handgeschriebenen Dockerfiles für die häufigen Fälle.
  • Das runtime-Image enthält bereits pipelogic plus seine Kern-Abhängigkeiten (numpy, opencv, pyyaml, protobuf, pika, das C++-ML-SDK). Der component-Autor schreibt nur, was component-spezifisch ist — zusätzliche Python-Pakete in requirements.txt, zusätzliche C++-Pakete über xmake_packages.
  • ppl component publish führt den Build auf einem entfernten Build-Cluster aus, nicht auf der Maschine des Autors. Kein lokales Docker, keine lokalen CUDA-Treiber, keine lokale Toolchain. Der Build gelingt entweder remote (und erzeugt ein veröffentlichbares Artefakt) oder schlägt remote fehl (mit demselben Fehler, den jeder andere Autor sehen würde).
  • Pinne alles component-spezifische genau mit ==. Ungepinnte Abhängigkeiten lösen zu dem auf, was die Registry am Build-Tag zurückgibt, sodass ein Build, der gestern funktionierte, morgen einen anderen Baum auflösen kann.
  • Die kuratierten Schlüssel sind ein Katalog, den die Plattform pflegt. Neue Stacks werden hinzugefügt, wenn sie weithin nützlich sind; einmalige Bedürfnisse werden durch xmake_packages oder Dockerfile-base bedient, mit einem vollständig benutzerdefinierten Dockerfile (gebaut auf einer kuratierten build_system_base), das in Plänen verfügbar ist, die das Custom-Build-Tier berechtigen.

Mentales Modell — der build_system-Schlüssel treibt den gesamten Build

   component.yml                   ppl component publish
   ┌───────────────────────────┐   ┌───────────────────────────────────┐
   │ language: py              │   │ Build stage   (build_system img)  │
   │ build_system:             │──▶│   pip wheel <requirements.txt>    │
   │   2-cuda12.8-torch2.8-    │   │   xmake against xmake_packages    │
   │   onnxrtgpu1.22           │   ├───────────────────────────────────┤
   │ requirements.txt:         │   │ Runtime stage (build_system img)  │
   │   transformers==4.44.2    │   │   install wheels / copy binary    │
   │   huggingface-hub==0.24.6 │   │   image already has pipelogic +   │
                                   │   numpy + opencv + pyyaml + …     │
   └───────────────────────────┘   └───────────────────────────────────┘
                                                     │
                                                     ▼
                                               worker image

Die Plattform besitzt die kuratierten Images, hält sie aktuell und garantiert einen funktionierenden Stack aus Systembibliotheken, Sprach-runtimes und ML-Frameworks hinter jedem Schlüssel. Der component-Autor besitzt den Quellcode und die component-spezifische requirements.txt (oder xmake_packages).

Warum kuratierte Build-Images statt beliebiger Dockerfiles

Der build_system-Schlüssel dient zwei Zwecken: er strafft die component-Erstellung — der Autor schreibt nie ein Dockerfile für den häufigen Fall — und er vermeidet Image-Aufblähung über den gesamten Katalog. Eine kuratierte Basis wird einmal gebaut und wiederverwendet; wenn jede component eine abweichende Basis verwendete, würde jede Gigabytes duplizierter Layer ausliefern.

Die Größendimension ist konkret. Ein GPU-Stack — CUDA, cuDNN, PyTorch, ONNX Runtime — ist mehrere Gigabytes groß, bevor irgendein component-Code hinzugefügt wird. Wenn components eine Basis teilen, wird dieser Stack einmal gebaut: die Registry speichert die geteilten Layer ein einziges Mal, und ein Node, der bereits eine component gezogen hat, hat sie für die nächste im Cache. Wenn jede component eine andere Basis wählt, wird nichts geteilt. Jede component liefert ihre eigene Multi-Gigabyte-Kopie nahezu identischer Systembibliotheken, die Registry speichert alles, und jeder Node zieht es erneut. Ein Katalog aus einigen Dutzend components wird zu Hunderten von Gigabytes meist-duplizierter Layer.

Abweichende Basen verschärfen das Problem auf andere Weise: sie pinnen verschiedene CUDA-Versionen, und ein einziger Sicherheitspatch bedeutet, jedes Dockerfile von Hand zu bearbeiten.

Ein kuratierter Katalog von build_system-Schlüsseln adressiert beides. Jeder Schlüssel fixiert eine getestete Kombination aus CUDA-Version, Framework-Version und unterstützenden Bibliotheken, gepatcht nach dem Zeitplan der Plattform statt pro component, und — weil der Schlüssel zu einem geteilten Basis-Image auflöst — verwendet jede component, die ihn wählt, dieselben Layer wieder, statt sie zu duplizieren. Der Autor wählt einen Schlüssel; requirements.txt trägt dann nur, was component-spezifisch ist: der Modell-Loader, die Transformer-Version, die Bildverarbeitungsbibliothek. Der geteilte Layer wird einmal gebaut, hinter dem Schlüssel.

Im Austausch dafür, aus dem Katalog zu wählen statt beliebige Basis-Images zu schreiben, bekommen Autoren reproduzierbare Builds über Teammitglieder hinweg, Sicherheitsupdates, die hinter einem Schlüssel landen statt über Dutzende geforkte Dockerfiles, vorhersagbare Image-Größen und eine Plattform, die die CUDA- / Framework-Kompatibilitätsmatrix im Namen des Autors pflegt.

Durchgang — Schlüssel wählen, deps pinnen, publish

# component.yml — Python Component auf einem GPU-Stacklanguage: pybuild_system: 2-cuda12.8-torch2.8-onnxrtgpu1.22worker:  input_type: Image  output_type: "[BoundingBox]"
# requirements.txt — nur genaue Versionen; NIEMALS pipelogic / numpy / opencv / pyyaml / protobuf / pika erneut pinnen
transformers==4.44.2
huggingface-hub==0.24.6
pillow==11.3.0
# den Build remote validieren, ohne eine Versionszeile zu erstellenppl component publish --dry-run# einen prerelease veröffentlichen (Build läuft auf dem entfernten Build-Cluster — kein lokales Docker)ppl component publish -m "add foo support"# den prerelease in eine veröffentlichte Version umlegen (veröffentlicht das worker-Image, wendet Tags an)ppl component promote

ppl component publish lädt den Quellbaum + component.yml + benachbarte Dockerfiles auf den Build-Cluster hoch; der Build läuft dort. Kein lokales Docker erforderlich.

Referenz-Snapshot

Häufige Python-Schlüssel (language: py)

build_systemIn runtime vorinstalliertWähle wenn
2Python 3.10, numpy, pipelogicReine Python-Component, keine ML-deps.
2-opencv4.11Oben + opencv-python-headless 4.11Bildverarbeitung, kein ML.
2-torch2.8Oben + torch 2.8 (CPU)Torch-CPU-Inferenz.
2-torch2.8-visionOben + torch 2.8 + torchvision 0.23 (CPU)Torch + torchvision, CPU.
2-cuda12.6CUDA 12.6 + cuDNN 9 + opencvCUDA 12.6 ohne ein Framework.
2-cuda12.6-torch2.8-onnxrtgpu1.22CUDA 12.6 + torch (cu126) + onnxruntime-gpu + opencvTorch + ONNX GPU auf CUDA 12.6.
2-cuda12.8CUDA 12.8 + cuDNN 9 + opencvCUDA ohne ein Framework.
2-cuda12.8-onnxrtgpu1.22CUDA + cuDNN 9 + onnxruntime-gpuONNX-Inferenz auf GPU.
2-cuda12.8-torch2.8CUDA + torch + torchvision + opencvTorch-GPU-Inferenz.
2-cuda12.8-torch2.8-onnxrtgpu1.22CUDA + torch + onnxruntime-gpuGemischt Torch + ONNX GPU.
2-cuda12.8-torch2.8-onnxrtgpu1.22-roboflowOben + rfdetr, inference (Roboflow-Stack)Roboflow-Stack-Components.
2-cuda12.8-torch2.8-ultralyticsOben + ultralytics + torchvisionYOLO / Ultralytics.

Häufige C++-Schlüssel (language: cpp)

build_systemWasWähle wenn
2C++-Toolchain, keine ML-LibsReine C++-Utility-Component.
2-mlOben + C++-ML-SDK + OpenCV + FFmpegC++-Component, die Bild- / Signal- / Video-Arbeit macht.
2-opencv4.11C++-Toolchain + OpenCV + C++-ML-SDK-HeaderC++-Component, die OpenCV braucht.

Den leichtesten Schlüssel wählen

Größere Schlüssel liefern größere Images und längere Pulls. Wenn eine component nur OpenCV braucht, wähle 2-opencv4.11, nicht den vollen GPU-ML-Stack. Wenn unsicher, beginne mit 2-opencv4.11 (CPU-Bildverarbeitung) oder 2-cuda12.8-torch2.8-onnxrtgpu1.22 (GPU-ML).

Wie das Pinnen von Abhängigkeiten mit dem kuratierten Layer interagiert

Dieser Abschnitt behandelt, wie ein pip install-Schritt mit dem koexistiert, was das runtime-Image bereits bereitstellt.

Das runtime-Image liefert pipelogic und seine Kern-Abhängigkeiten vorinstalliert. Diese Menge umfasst numpy, opencv-python / opencv-python-headless, pyyaml, protobuf, pika und eine Handvoll weiterer je nach Schlüssel. Diese gehören nicht dir zum erneuten Pinnen. Wenn requirements.txt numpy==1.26.4 auflistet, installiert der Build ein paralleles numpy über das, das die runtime bereits hat; beim component-Start löst Pythons Import-Maschinerie auf, was zuerst auf dem Pfad ist, und das Ergebnis ist entweder ein ABI-Konflikt (sofortiger Absturz) oder eine stille Versions-Nichtübereinstimmung (subtile Bugs).

Die Regel ist: alles, was die runtime liefert, lässt die component in Ruhe. Alles, was die component wirklich braucht und die runtime nicht liefert, pinnt die component mit == auf eine bestimmte Version. Der pip install-Schritt installiert dann genau die zusätzlichen Pakete, über dem vorhandenen Layer, ohne Überschneidung.

Die Pinning-Disziplin ist nicht optional. Ein ungepinntes transformers in requirements.txt löst zu dem auf, was PyPI zur Build-Zeit zurückgibt — 4.44.0 heute, 4.45.0 nächste Woche, 5.0.0-rc1, wenn der Major-Bump landet — sodass ein Build von einem Tag zum nächsten einen anderen Abhängigkeitsbaum auflösen kann. Genaues ==-Pinnen bedeutet, dass der Build entweder reproduziert, was funktionierte, oder sauber mit einem „version not found"-Fehler fehlschlägt.

Wenn die kuratierten Schlüssel nicht ausreichen

Dieser Abschnitt behandelt die Optionen, wenn die Bedürfnisse eines Teams zu keinem einzelnen Schlüssel passen.

In grober Reihenfolge der Präferenz:

xmake_packages (C++). Für C++-components, die zusätzliche Bibliotheken brauchen, deklariere sie in component.yml unter xmake_packages, statt einen neuen Builder zu erzwingen. Die xmake-Registry deckt die meisten Fälle ab; einfache Pakete sind String-Einträge, Pakete, die Konfiguration brauchen, sind Objekt-Einträge mit einer require-Zeile. Der Build holt sie ab und verlinkt sie hinein.

xmake_packages:  - fast_float  - require: arrow 7.0.0    package: arrow    configs:      parquet: true      snappy: true      zstd: true

Dockerfile-base für Systempakete. Für components, die zusätzliche apt-Pakete brauchen (eine Systembibliothek, eine Schriftart, einen Codec, ein Tool), lege ein benachbartes Dockerfile-base mit dem apt-install-Hook ab. Der Build führt zuerst das kuratierte Basis-Image aus, legt dann Dockerfile-base obendrauf, führt dann pip install aus. Nur Systempakete — Python-Abhängigkeiten bleiben in requirements.txt, wo die Pinning-Disziplin gilt.

# Dockerfile-baseRUN apt-get update && apt-get install -y --no-install-recommends \      libsndfile1 \ && rm -rf /var/lib/apt/lists/*

build_system: custom (gepaart mit build_system_base). Für die component, die ihr Dockerfile direkt besitzen muss — eine zusätzliche Build-Stufe, eine Compiler-Toolchain, einen Build-Schritt auf Systemebene, den die kuratierten Schlüssel nicht abdecken. Es ist kein Build von nichts: build_system_base muss einen kuratierten Katalog-Schlüssel benennen, das benutzerdefinierte Dockerfile baut FROM dieser Basis, und die Basis ist das, was pipelogic und die Kern-runtime liefert. Weil die Basis ein Katalog-Schlüssel ist, kann ein Dockerfile, das sie korrekt referenziert, trotzdem von der Plattform migriert werden, wenn diese Basis gepatcht wird, und die Layer der Basis bleiben geteilt. Die Kosten des Autors sind das Besitzen der Build-Schritte obendrauf und das manuelle Verdrahten des component-Shims der Plattform. Verfügbar in Plänen, die das Custom-Build-Tier berechtigen; eine Handvoll First-Party-components (FFmpeg- und GStreamer-Ingest, FAISS, RNNoise, Gaussian-Splatting) bauen auf diese Weise.

Die Eskalationsleiter existiert, weil jeder Schritt operative Schuld hinzufügt. xmake_packages ist billig. Dockerfile-base fügt einen apt-Layer hinzu. custom übergibt dem Team sein eigenes Dockerfile — immer noch auf einer Katalog-build_system_base verankert, aber die Build-Schritte obendrauf sind ihre zu pflegen. Die richtige Antwort ist fast immer die am weitesten links liegende Option, die das Bedürfnis erfüllt.

Ausführen

ppl component builders                 # Live-Katalog der build_system-Schlüsselppl component publish --dry-run        # entfernte Build-Validierung, keine Versionszeileppl component publish -m "<msg>"       # einen prerelease veröffentlichenppl component promote                  # prerelease → released umlegen

Wo das hineinpasst

Build-Systeme sind der kuratierte Layer der Plattform zwischen dem component-Quellcode, den der Autor schreibt, und dem Image, das in einem deployten Container läuft. Die Plattform besitzt diesen Layer — die kuratierten Images, die unterstützten Framework-Matrizen, die Sicherheitsupdates, die Layer-Caches. Der Autor schreibt die component-spezifischen Teile; der kuratierte Layer deckt alles darunter ab.

Verwandt

  • Components — die Einheit, die gebaut wird.
  • Install-Modiinstall: node verschiebt die Paketinstallation auf die Deploy-Zeit.
  • Models — Build-Stacks mit Serving-Services und Modell-Artefakten paaren.
  • Publish-Semantik — die publish + promote-Schleife, die der Build speist.

War diese Seite hilfreich?