Continuous Delivery für embedded C/C++-Entwickler
Dependency Management ist eine unabdingbare Voraussetzung für Continuous Delivery. In der traditionellen eingebetteten Programmierung werden dafür geeignete Tools noch höchst zögerlich eingesetzt. Dieser Artikel stellt einen Ansatz basierend auf dem Open Source-Werkzeug conan.io vor, der speziell für die cross-platform Umgebungen von C und C++-Programmierern entwickelt wurde
Anbieter zum Thema

Schnelle, reproduzierbare Builds sind unabdingbar für eine Continuous Delivery Pipeline. Erneutes Kompilieren aller Abhängigkeiten für jeden Build und Einchecken von Binärdateien im Versionskontrollsystem sind in der DevOps-Welt als Anti-Pattern identifiziert worden. Das dafür notwendige deklarative Abhängigkeitsmanagement mittels pom files, Nuget, NPM, etc. hat sich seit Jahren in der Java, JavaScript, Python und .NET-Welt durchgesetzt.
Dependency Management für „Non-Hipsters“?
Die angeführten Gründe für den Nichteinsatz von deklarativen Paketmanagern in der eingebetteten Programmierung zielen häufig auf die Spezifika von C und C++ ab. Die größte Besonderheit ist der für kompilierte Sprachen einzigartige Fakt, dass es für eine Versionsnummer einer Bibliothek nicht nur ein Binärartefakt gibt, sondern je nach Betriebssystem, Compiler-Version, Release- und Debug-Flags, Standardbibliotheks-version, statischer und dynamischer Verlinkung und beliebig vielen weiteren Dimensionen eine Vielzahl von unterschiedlichen, zueinander nicht-kompatiblen Binärartefakten existiert. Da die meisten existierenden Paketmanager für Sprachen entworfen worden, die auf einer virtuellen Maschine abgewickelt werden können, existiert diese Unterscheidung nicht; es gibt daher typischerweise genau ein Binärartefakt pro Versionsnummer. Aus diesem Grunde hat der Autor von mehreren Praktikern im C/C++-Umfeld die Aussage bekommen, dass deklaratives Abhängigkeitsmanagement ja schön und gut, dies jedoch nur für „Hipster“-Sprachen praktikabel sei.
Dieser Artikel stellt kurz vor
- warum deklaratives Paketmanagement eine Grundvoraussetzung für Continuous Delivery und DevOps darstellt und welche zusätzlichen Vorteile daraus resultieren
- wie conan.io, ein Open Source Paketmanager, genutzt werden kann um plattformunabhängig C und C++-Bibliotheken zu verwalten
- dass es keine Ausrede mehr geben sollte, als embedded Entwickler ebenfalls Continuous Delivery zu betreiben und dieselben Vorteile wie in den „Hipster”-Programmiersprachen zu genießen
Deklaratives Paketmanagement als Voraussetzung für Continuous Delivery
Continuous Delivery ermöglicht es Unternehmen, Software-Releases kontinuierlich, somit wesentlich häufiger als im traditionellem Wasserfall-Modell an externe und interne Kunden auszuliefern. Dadurch kann sowohl das akkumulierte Risiko durch Veränderungen zwischen Releases drastisch reduziert werden, als auch die Software an Kundenbedürfnisse angepasst werden, die zum Projektstart noch nicht feststanden und sich erst beim Benutzen des Produktes ergeben.
Continuous Delivery hat einige Grundvoraussetzungen, die von den begriffsbildenden Pionieren Jez Humble und David Farley wie folgt definiert wurden:
- 1. Keep everything in version control
- 2. Done means released
- 3. Don’t Check In on Broken Build
- 4. Never Go Home on a Broken Build
- 5. Fail the Build for Slow Tests
- 6. Only Build Your Binaries Once
- 7. Deploy the Same Way for Every Environment
Insbesondere Prinzip 6 – „Only Build Your Binaries Once“ – wird in der cross-platform C und C++-Welt selten befolgt. Oft werden alle Abhängigkeiten (Submodule, Bibliotheken, Komponenten) in jeder Entwicklungsstufe – Entwicklung, Modul-Integration, System-Integration, Last-Test-Umgebung, Pre-Produktion, Produktion erneut gebaut, manchmal sogar mit voneinander abweichenden Build-Parametern, Basisbibliotheken und Compilern. Da das Kompilieren von C und C++-Artefakten eine lange Zeit in Anspruch nehmen kann, dauert der Durchlauf der gesamten Pipeline häufig mehrere Stunden - wenn nicht sogar Tage. Dadurch ist es praktisch unmöglich, eine Feature-/ oder Performance-Regression auf einen einzelnen Commit zurückzuführen bzw. iterativ zu testen und das Risiko zwischen Releases zu minimieren – die für Continuous Delivery nötige kurze Feedbackschleife ist schlicht zu lang. Erschwerend kommt hinzu, dass bei voneinander abweichenden Build-Einstellungen pro Umgebung noch nicht einmal eine Garantie gegeben werden kann, dass Test-Fälle, welche in einer vorherigen Stufe korrekt durchliefen, auch mit den (durch andere beim Build benutzte Parameter) abweichenden Binärartefakten aus späteren Stufen kompatibel sind. „Immutable“, d.h. unveränderliche Build-Artifacts sind daher eine Grundvoraussetzung für Continous Delivery und Continuous Integration, die bisher oft nicht gegeben ist.
Warum das Einchecken von vorgebauten Binärartefakten oft keine Option ist
Um lange Durchlaufzeiten durch erneutes Kompilieren von Projektabhängigkeiten zu vermeiden, ist ein häufig praktizierter Ansatz, die resultierenden Binärartefakte direkt ins Versionskontrollsystem einzuchecken und in späteren Integrationsstufen erneut zu nutzen. Dieser Ansatz ist aus mehreren Gründen ebenfalls problematisch. Zunächst einmal ist das heutzutage dominierende Versionskontrollsystem Git nicht darauf optimiert mit Binärdateien umzugehen. Git’s snapshot-basiertes Design kombiniert mit der Tatsache, dass bei einem Clone-Vorgang sämtliche Versionen eines Binärartefaktes übertragen werden, sorgt schon bei einer kleineren Anzahl von Entwicklern und CI-Systemen zu einer großen Auslastung des Netzwerkes als auch des Arbeitsspeichers des Git-Servers. Der Autor hat bei mehreren Unternehmen, die von anderen Versionskontrollsystemen auf Git umgestiegen sind und bei der Migration Binärdateien nicht gesondert behandelten, große Stabilitätsprobleme bis zum Produktionsausfall erlebt. Diese Produktionsausfälle können durch das Verschieben der Binärdateien in ein deklaratives Dependency Management-System wie conan.io behoben werden.
Weitere Gründe, die für eine direkte Ablage der Kompilate im Versionskontrollsystem sprechen, sind die entgangenen Vorteile, welche moderne Paketmanager wie conan.io mit sich bringen, auf welche nun kurz eingegangen wird.
Vorteile von modernen Dependency Management Systemen
Moderne Paketmanager bieten eine Reihe von Vorteilen, darunter unter anderem:
- License/Security-Scanning: Moderne Paketmanager können für alle direkt und indirekt eingebundenen Abhängigkeiten die damit assoziierten Software-Lizenzen als auch bekannte Sicherheitsprobleme auflisten und bei entsprechender Konfiguration auch das Einbinden problematischer Lizenzen und Pakete mit schwerwiegenden Sicherheitslücken automatisch unterbinden.
- Impact Analyse: Falls ein Defekt in einer spezifischen Version einer Komponente gefunden wurde, können alle davon abhängigen Komponenten sofort identifiziert werden – es ist kein zeitintensives, manuelles Suchen in Build-Skripten um alle transitiven Abhängigkeiten aufzulösen notwendig.
- Semantische Versionierung: Im Gegensatz zu alternativen Ansätzen wie Git Submodules und Git LFS können Paketmanager nicht nur auf eine spezifische Version einer Abhängigkeit verweisen, sondern auch einen Versionsbereich angeben, der für sie akzeptabel ist, beispielsweise Versionen 1.4.1 bis 1.4.3 oder immer auf die neuste Version verweisen, welche freigegeben wurde (und kompatibel ist).
- Geo-Verteilung: Moderne Paketmanager können aus der IP-Adresse des Konsumenten einer Bibliothek den geografisch nächsten Server ermitteln und die Binärartefakte mittels eines Content Delivery Networks so schnell wie möglich ausliefern, ohne die Stabilität des Primärservers bei tausenden parallelen Downloads zu gefährden. Das Einschränken auf Konsumenten in bestimmten Ländern aus exportrechtlichen Gründen ist ebenfalls möglich.
- Speicherung zusätzlicher Meta-Daten mit den erzeugten Artefakten: Das Debugging von Fehlern in einer Komponente, die nur noch als Binärartefakt vorliegt, kann sich als sehr schwierig gestalten. Durch die Möglichkeit, assoziierte Meta-Daten wie Umgebungsvariablen im CI-System während des Build-Vorgangs von Version zu Version zu vergleichen, ergeben sich ganz andere Möglichkeiten, Fehler aufzuspüren.
- Artefakt-Promotion: Während Entwickler in der Explorationsphase die Möglichkeit bekommen sollten, auch mit neuen, eventuell zuvor unbenutzten Komponenten zu experimentieren, muss vor der Freigabe an den Endkunden sichergestellt werden können, dass nur abgenommene Komponenten benutzt werden dürfen. Moderne Paketmanager erlauben Build-Artefakte, welche sich frühen Entwicklungsstufen bewährt haben, in die Produktionsumgebung zu „promoten“, sie daher auch für nachgelagerte Stufen freizugeben. Der Promotions-Prozess kann nur von dazu berechtigten Release-Managern erfolgen und wird lückenlos protokolliert.
(ID:45662058)