Close

5 Tipps für CI-freundliche Git-Repositorys (und GIT-freundliche CI)

Gehe auf Erfolgskurs – der erste Schritt ist dein Repository.

Porträt Sarah Goff-Dupont
Sarah Goff-Dupont

Principal Writer


Git und Continuous Delivery sind eine der seltenen Softwarekombinationen, bei denen zwei für sich schon hervorragende Komponenten zusammen eine ideale Lösung ergeben. Daher möchte ich dir in diesem Artikel Tipps dazu geben, wie du erreichen kannst, dass deine Builds in Bamboo optimal mit deinen Bitbucket-Repositorys ineinandergreifen. Da der überwiegende Teil der Interaktion zwischen den zwei Lösungen in der Build- und Testphase von Continuous Delivery erfolgt, schreibe ich hier eher über CI als über CD.

1: Speichere große Dateien nicht im Repository


Oft wird im Zusammenhang mit Git empfohlen, große Dateien wie Binär- und Mediendateien oder archivierte Artefakte nicht im Repository zu speichern. Der Grund dafür ist, dass eine einmal hinzugefügte Datei dauerhaft im Verlauf des Repositorys verbleibt. Bei jedem Klonen des Repositorys wird also die große Datei mitgeklont.

Eine Datei aus dem Verlauf des Repositorys zu entfernen ist extrem heikel und entspricht einer Lobotomie an deiner Codebasis. Dieser chirurgische Eingriff zur Extraktion einer Datei verändert den gesamten Verlauf des Repositorys, sodass du nicht mehr klar erkennen kannst, wann welche Änderungen vorgenommen wurden. Es gibt also gute Gründe, im Allgemeinen große Dateien zu meiden.

Große Dateien nicht in Git-Repositorys aufzunehmen, ist besonders für CI wichtig.

Jedes Mal, wenn ein Build läuft, muss dein CI-Server dein Repo in das Build-Arbeitsverzeichnis klonen. Bei einem aufgeblähten Repository mit einigen Riesenartefakten verlangsamt sich dieser Prozess und es entstehen für deine Entwickler längere Wartezeiten auf die Build-Ergebnisse.

So weit, so gut. Was aber, wenn deine Builds von Binärdateien anderer Projekte oder großer Artefakte abhängig sind? Dies kommt sehr häufig vor und daran wird sich wohl auch so schnell nichts ändern. Wie gehen wir also effizient damit um?

See solution

Build and operate software with Open DevOps

Zugehöriges Material

Weitere Informationen über Trunk-basierte Entwicklung

Ein externes Storage-System wie Artifactory (das über ein Add-on für Bamboo verfügt), Nexus oder Archiva kann für Artefakte, die von deinem Team oder den Teams um dich herum generiert wurden, hilfreich sein. Die benötigten Dateien können zu Beginn deines Builds in das Build-Verzeichnis gepullt werden – genauso wie die Bibliotheken von Drittanbietern, die du über Maven oder Gradle pullst.

Profitipp: Wenn sich die Artefakte häufig ändern, bist du vielleicht versucht, deine großen Dateien jeweils nur abends mit dem Build-Server zu synchronisieren, damit du sie nur beim Build übertragen musst. Dies ist nicht empfehlenswert. Zwischen diesen abendlichen Synchronisationen arbeitest du dann nämlich mit statischen Versionen der Artefakte. Außerdem benötigen die Entwickler diese Dateien für Builds ohnehin auf ihren lokalen Rechnern. Die sauberste Lösung ist also, den Artefakt-Download zum Bestandteil deines Builds zu machen.

Wenn in deinem Netzwerk noch kein externes Storage-System vorhanden ist, bietet sich Git Large File Storage (LFS) als unkomplizierteste Lösung an.

Git LSF ist eine Erweiterung, die Verweise auf große Dateien statt der Dateien an sich im Repository speichert. Die Dateien selbst werden auf einem Remote-Server gespeichert. Wie du dir vorstellen kannst, reduziert dies die zum Klonen benötigte Zeit erheblich.

Git LSF

Wahrscheinlich hast du bereits Zugriff auf Git LFS, da es von Bitbucket und GitHub unterstützt wird.

2: Verwende für CI oberflächliche Klone

Bei jeder Erstellung eines Builds klont der Build-Server das Repository in das aktuelle Arbeitsverzeichnis. Wie bereits erwähnt, wird beim Klonen eines Repositorys durch Git standardmäßig immer auch der gesamte Verlauf des Repositorys geklont. Im Verlauf der Zeit dauert dieser Prozess daher immer länger. Als Gegenmaßnahme kannst du in Bamboo flaches Klonen aktivieren.

Mit oberflächlichen Klonen wird nur der aktuelle Snapshot deines Repos heruntergepullt. Dies kann sehr hilfreich zur Verkürzung von Build-Zeiten sein, insbesondere bei großen und/oder älteren Repositorys.


Each time a build runs, your build server clones your repo into the current working directory. As I mentioned before, when Git clones a repo, it clones the repo’s entire history by default. So over time, this operation will naturally take longer and longer. Unless you enable shallow cloning in Bamboo.

With shallow clones, only the current snapshot of your repo will be pulled down. So it can be quite useful for reducing build times, especially when working with large and/or older repositories.

Git-Repositorys (Screenshot)

Angenommen, für deinen Build wird tatsächlich der vollständige Repository-Verlauf benötigt – beispielsweise, weil mit einem der Schritte in deinem Build die Versionsnummer im POM (o. ä.) aktualisiert wird oder weil du mit jedem Build zwei Branches mergst. In beiden Fällen muss Bamboo Änderungen an dein Repository zurück übermitteln.

Seit Git 1.9 können einfache Änderungen an Dateien (zum Beispiel die Aktualisierung einer Versionsnummer) ohne den gesamten Verlauf gepusht werden. Für einen Merge ist jedoch weiterhin der komplette Verlauf des Repositorys erforderlich, da Git zurückblicken muss, um den gemeinsamen Vorfahren der beiden Branches zu finden – dies wird zum Problem, wenn dein Build oberflächliche Klone nutzt. Und hiermit kommen wir zu Tipp Nr. 3.

3: Cache das Repo in Build-Agenten


Dadurch wird auch der Klonvorgang erheblich beschleunigt, weshalb dies bei Bamboo Standard ist.

Ein Hinweis: Repository-Caching ist nur von Vorteil, wenn deine Agenten von Build zu Build erhalten bleiben. Wenn du Agenten auf EC2 oder bei einem anderen Cloud-Anbieter bei jedem Build neu erstellst und später wieder entfernst, hat das Repository-Caching keinen Nutzen, weil du dann mit einem leeren Build-Verzeichnis arbeitest und ohnehin jedes Mal eine vollständige Kopie des Repositorys abrufen musst.

Flache Klone plus Repository-Caching, geteilt durch bleibende Agenten gegenüber elastischen Agenten ergibt ein spannendes Netz aus Faktoren. Hier eine kleine Matrix, die dir beim Entwickeln einer Strategie hilft.

Screenshot: Vergleich zwischen persistenten Agenten und elastischen Agenten

4: Wähle deine Trigger mit Bedacht


Es ist (nahezu) selbstredend, dass die Ausführung von CI in allen aktiven Branches eine gute Idee ist. Aber ist es sinnvoll, bei jedem Commit die Builds für alle Branches laufen zu lassen? Eher nicht. Und zwar aus folgendem Grund.

Nehmen wir zum Beispiel Atlassian. Bei uns arbeiten über 800 Entwickler, die alle mehrmals täglich ihre Änderungen in das Repository pushen – meistens in ihre Feature-Branches. Das sind eine Menge Builds. Wenn die Zahl an Build-Agenten nicht umgehend ins Unermessliche erhöht wird, sind lange Wartezeiten vorprogrammiert.

Einer unserer internen Bamboo-Server beherbergt 935 verschiedene Build-Pläne. Wir hatten 141 Build-Agenten an diesen Server angeschlossen und nutzten Best Practices wie das Weiterleiten von Artefakten und Paralleltests, um jeden Build so effizient wie möglich zu gestalten. Trotzdem führten die Builds nach jedem Push zu Engpässen.

Anstatt einfach eine weitere Bamboo-Instanz mit mindestens 100 zusätzlichen Agenten einzurichten, sind wir in uns gegangen und haben uns gefragt, ob dies wirklich nötig ist. Die Antwort darauf lautete schließlich: Nein, ist es nicht.

Also haben wir Entwicklern die Möglichkeit gegeben, ihre Branch-Builds selbst zu kontrollieren, statt sie immer automatisch triggern zu lassen. Dies ist ein guter Weg, um ein ausgewogenes Verhältnis zwischen strikten Tests und Ressourcenschonung zu erzielen. Da die meisten Änderungsaktivitäten in Branches stattfinden, ist das Einsparpotenzial dort am größten.

Viele Entwickler wissen die stärkere Kontrolle bei nicht-automatischen Builds zu schätzen und finden, dass sich diese Builds gut in ihren Workflow einfügen. Andere Entwickler möchten lieber nicht selbst über den optimalen Zeitpunkt zum Erstellen eines Builds entscheiden und behalten die automatischen Trigger bei. Beide Ansätze können funktionieren. Wichtig ist nur, die Branches zunächst zu testen und so sicherzustellen, dass dein Build sauber ist, bevor du ihn upstream mergst.

Miniaturansicht: kontinuierliche Trigger

Wichtige Branches wie der Haupt-Branch und stabile Release-Branches sind allerdings ein ganz anderes Thema. Dort werden Builds automatisch ausgelöst – entweder durch das Abrufen von Änderungen aus dem Repository oder durch das Senden einer Push-Benachrichtigung von Bitbucket an Bamboo. Da wir Entwicklungs-Branches für alle laufenden Arbeiten nutzen, sollten (theoretisch) auch nur Entwicklungs-Branches und keine anderen Commits in den Haupt-Branch gemergt werden. Dies sind außerdem die Codezeilen, auf denen unsere Releases und Entwicklungs-Branches basieren. Es ist daher sehr wichtig, frühzeitig Testergebnisse zu jedem Merge zu erhalten.

5: Vergiss Abfragen, Hooks sind angesagt


Für Bamboo ist es kein Problem, das Repository alle paar Minuten auf Änderungen abzufragen. Wenn jedoch mehrere Hundert Builds und mehrere Tausend Branches mit Dutzenden Repositorys vorhanden sind, summieren sich die Prozesse schnell. Statt Bamboo mit all diesen Abfragen zu belasten, kannst du Bitbucket so einrichten, dass signalisiert wird, wenn eine Änderung gepusht wurde und ein Build erforderlich ist.

In der Regel musst du dein Repository dafür mit einem Hook versehen, doch die Integration von Bitbucket und Bamboo nimmt dir praktischerweise die gesamte Einrichtung ab. Sobald Bamboo und Bitbucket Server am Back-End verbunden sind, lassen sich Repository-orientierte Builds ohne weiteres Eingreifen auslösen. Es sind keine Hooks oder spezielle Konfigurationen erforderlich.

Screenshot: Konfigurieren von Bitbucket

Unabhängig von den Tools haben Repository-orientierte Trigger den Vorteil, dass sie automatisch wieder verschwinden, sobald der Ziel-Branch nicht mehr aktiv ist. Anders gesagt wirst du so nie die CPU-Zyklen deines CI-Systems mit der Abfrage Hunderter stillgelegter Branches vergeuden. Oder deine eigene Zeit beim manuellen Stilllegen von Branch-Builds. (Es sei jedoch angemerkt, dass in Bamboo das Ignorieren von Branches nach einer bestimmten Anzahl von Tagen problemlos konfiguriert werden kann, falls du weiterhin Abfragen bevorzugst.)

Der Schlüssel zur Verwendung von Git mit CI

Du musst viele Aspekte berücksichtigen. Nicht alles, was bei CI mit einem zentralisierten VCS reibungslos funktioniert, lässt sich einfach auf Git übertragen. Im ersten Schritt solltest du deine Annahmen überprüfen. Atlassian-Kunden können im zweiten Schritt Bamboo mit Bitbucket integrieren. Nähere Informationen findest du in unserer Dokumentation. Viel Erfolg!


...simply being thoughtful. All the things that worked great when you were doing CI with a centralized VCS? Some of them will work less great with Git. So check your assumptions – that's the first step. For Atlassian customers, the second step is integrating Bamboo with Bitbucket. Check out our documentation for details, and happy building!

Sarah Goff-Dupont
Sarah Goff-Dupont

Software steht nun seit zehn Jahren im Mittelpunkt meiner Arbeit: Ich habe sie getestet und automatisiert und nun schreibe ich über sie. Wenn ich gerade nicht arbeite, lese ich aktuelle Bestseller, tobe mich beim CrossFit aus oder rolle mit meinen Kindern auf dem Boden umher. Folge mir auf Twitter! @DevToolSuperfan


Diesen Artikel teilen

Lesenswert

Füge diese Ressourcen deinen Lesezeichen hinzu, um mehr über DevOps-Teams und fortlaufende Updates zu DevOps bei Atlassian zu erfahren.

Abbildung: DevOps

DevOps-Community

Abbildung: DevOps

Simulations-Workshop

Abbildung: Karte

Kostenlos loslegen

Melde dich für unseren DevOps-Newsletter an

Thank you for signing up