KAPITEL 20

Team-Workflows

Der Einsatz von Code zum Bauen und Ändern von Infrastruktur ist ein Vorgehen, das sich massiv vom klassischen Vorgehen unterscheidet. Wir nehmen Änderungen an virtuellen Servern und an der Netzwerkkonfiguration indirekt vor, statt Aufrufe an der Befehlszeile einzutippen oder direkt die Live-Konfiguration zu bearbeiten. Das Schreiben von Code, um ihn dann zu transportieren und von automatisierten Systemen anwenden zu lassen, ist eine größere Veränderung als einfach nur ein neues Tool oder neue Fertigkeiten zu erlernen.

Infrastructure as Code ändert die Arbeitsweise von allen, die am Design, Bauen und Betreuen von Infrastruktur beteiligt sind – egal ob Einzelperson oder Gruppe. Dieses Kapitel will erläutern, wie verschiedene Personen an Infrastruktur-Code arbeiten. Prozesse zum Arbeiten an Infrastruktur beinhalten das Designen, Definieren und Anwenden von Code.

Manche Eigenschaften effektiver Prozesse für Teams, die Infrastructure as Code managen, sind:

Insgesamt ist ein gut automatisierter Workflow so schnell, dass ein Fix im Notfall durch das System gebracht werden kann, ohne dass man dazu verleitet wird, ihn mal eben per Hand vorzunehmen. Und er ist zuverlässig genug, dass die Entwicklerinnen und Entwickler ihm mehr vertrauen, als selbst an der Konfiguration eines Live-Systems Hand anlegen zu wollen.

Dieses und das nächste Kapitel behandeln Aspekte der Arbeitsweise eines Teams in Bezug auf den Infrastruktur-Code. Dieses Kapitel konzentriert sich darauf, was die Leute in ihren Workflows tun, das nächste stellt Möglichkeiten vor, Infrastruktur-Codebasen zu organisieren und zu verwalten.

image

Die Effektivität Ihres Workflows messen

Die in »Die Four Key Metrics« auf Seite 39 erwähnten Four Key Metrics aus der Accelerate-Forschung sind ein guter Ausgangspunkt für die Entscheidung, wie Sie die Effektivität Ihres Teams messen. Es zeigt sich, dass Organisationen, die bei diesen Metriken gut abschneiden, auch bei ihren zentralen Organisationszielen gut abschneiden, wie zum Beispiel bei der Profitabilität oder dem Aktienwert.

Ihr Team könnte diese Metriken nutzen, um SLIs (Service Level Indicators) zu erstellen (die zu messenden Elemente), SLOs (Service Level Objectives) zu definieren (die vom Team selbst festgelegten Ziele) und SLAs (Service Level Agreements) anzulegen, bei denen es sich um Zusagen für andere handelt.1 Welche Aspekte Sie messen wollen, hängt vom Umfeld Ihres Teams und der Art und Weise ab, wie Sie Ergebnisse verbessern wollen.

Die Menschen

Ein zuverlässiges, automatisiertes IT-System ist wie Soylent Grün – seine geheime Zutat sind Menschen.2 Man sollte zwar kein Eingreifen durch Personen brauchen, um eine Code-Änderung in ein Produktivsystem zu bringen (vielleicht abgesehen vom Begutachten von Testergebnissen oder dem Klicken auf ein paar Buttons), aber man braucht die Menschen, um das System kontinuierlich zu bauen, zu korrigieren, anzupassen und zu verbessern.

Es gibt eine Handvoll Rollen, die bei den meisten Infrastruktur-Systemen beteiligt sind – egal ob automatisiert oder nicht. Diese Rollen lassen sich oft nicht eins zu eins auf einzelne Personen abbilden – manche spielen mehr als eine Rolle, andere teilen sich Rollen mit anderen:

Anwendung

Wer nutzt die Infrastruktur direkt? In vielen Organisationen sind das die Anwendungsteams. Diese Teams entwickeln eventuell die Applikationen oder sie konfigurieren und verwalten Anwendungen von dritter Seite.

Governance-Spezialisierung

Viele Teams legen Richtlinien für die Umgebung entlang der verschiedenen Domänen fest, unter anderem in Bezug auf Sicherheit, rechtliche Vorgaben, Architektur, Performance, Kostenkontrolle und Korrektheit.

Design

Entwicklerinnen und Entwickler, die die Infrastruktur designen. In manchen Organisationen handelt es sich dabei um Personen aus dem Architekturbereich – vielleicht unterteilt in verschiedene Domänen wie Networking oder Storage.

Toolmaking

Personen, die Services, Tools und Komponenten bereitstellen, die von anderen Teams zum Bauen oder Betreiben von Umgebungen genutzt werden. Beispiele dafür sind ein Monitoring-Team oder Entwicklerinnen und Entwickler, die wiederverwendbare Infrastruktur-Code-Bibliotheken erstellen.

Building

Personen, die Infrastruktur bauen oder verändern. Das können sie manuell über Konsolen oder andere Schnittstellen erreichen, indem sie Skripte ausführen, oder durch das Nutzen von Tools, die Infrastruktur-Code anwenden.

Testen

Personen, die Infrastruktur validieren. Diese Rolle ist nicht auf QAs (Quality Analysts) beschränkt. Dazu gehören auch Leute, die Systeme für eine Governance-Domäne – zum Beispiel Sicherheit oder Performance – testen oder begutachten.

Support

Personen, die sicherstellen, dass das System immer korrekt läuft, und eingreifen, wenn etwas nicht funktioniert.

In Abbildung 20-1 sehen Sie eine klassische Struktur mit einem dedizierten Team für jeden Teil des Workflows zum Ändern eines Systems.

Viele Rollen lassen sich noch auf die verschiedenen Infrastruktur-Domänen aufteilen, wie zum Beispiel auf Networking, Storage oder Server. Es gibt auch eine mögliche Unterteilung nach Governance-Domänen wie Sicherheit, Compliance, Architektur oder Performance. Viele größere Organisationen schaffen verästelte Organisationsstrukturen mit vielen Mikro-Spezialisierungen.1

image

Abbildung 20-1: Eine klassische Zuteilung eines dedizierten Teams zu jedem Teil eines Workflows

Allerdings ist es auch üblich, dass eine Person oder ein Team mehr als eine dieser Rollen einnimmt. So kann beispielsweise ein Infosec-Team (Information Security) Standards setzen, Scanning-Tools bereitstellen und Sicherheits-Audits durchführen. Weiter unten in diesem Kapitel werden wir uns Möglichkeiten anschauen, die Zuständigkeiten anders zu ordnen (siehe »Zuständigkeiten neu ordnen« auf Seite 397).

Wer schreibt Infrastruktur-Code?

Es gibt eine Reihe von Möglichkeiten, wie Organisationen die Frage beantworten, wer Infrastruktur-Code schreibt und bearbeitet:

Builder schreiben Code

Manche Organisationen versuchen, klassische Prozesse und Team-Strukturen beizubehalten. Dann nutzt das Team, das die Infrastruktur baut (und vielleicht supportet), IaC-Tools zum Optimieren seiner Arbeit. Benutzerinnen und Benutzer fordern eine Umgebung an, und das Build-Team nutzt seine Tools und Skripte, um sie zu erstellen. In »Mit Value Stream Mapping die Workflows verbessern« auf Seite 387 finden Sie ein Beispiel dafür, wie das Optimieren des Prozesses eines Build-Teams nicht dazu tendiert, den End-to-End-Prozess zu verbessern – weder in Bezug auf Geschwindigkeit noch auf Qualität.

Anwenderinnen und Anwender schreiben Code

Viele Organisationen ermöglichen es Anwendungsteams, die Infrastruktur selbst zu definieren, die ihre Applikationen verwenden. Dadurch können die Anforderungen mit den Möglichkeiten abgeglichen werden. Aber es erfordert entweder in jedem Team Personen mit einer guten Infrastruktur-Expertise oder Tools, die das Definieren der Infrastruktur vereinfachen. Die Herausforderung bei den Tools ist, sicherzustellen, dass sie die Bedürfnisse der Anwendungsteams erfüllen, statt sie einzuschränken.

Toolmaker schreiben Code

Spezialisierte Teams können Plattformen, Bibliotheken und Tools erstellen, die es den Benutzerinnen und Benutzern ermöglichen, die erforderliche Infrastruktur zu definieren. In diesen Fällen schreiben Letztere eher Konfiguration als Code. Ein Builder-Team schreibt und nutzt Code als Reaktion auf Anforderungen von Anwendern und Anwenderinnen für das Erstellen oder Ändern einer Umgebung. Ein Toolmaker-Team schreibt Code, der genutzt werden kann, um die Umgebungen selbst zu erstellen oder anzupassen. In »Eine Abstraktionsschicht bauen« auf Seite 327 finden Sie ein Beispiel dafür, was Toolmaker bauen könnten.

Governance- und Test-Teams schreiben Code

Diejenigen, die Richtlinien und Standards definieren, und die Personen, die Änderungen absichern, können Tools erstellen, die anderen dabei helfen, ihren eigenen Code zu überprüfen. Diese Leute werden selbst zu Toolmakern oder sie arbeiten eng mit diesen zusammen.

Mit Value Stream Mapping die Workflows verbessern

Value Stream Mapping ist eine praktische Vorgehensweise, um Ihre Durchlaufzeit zu unterteilen, sodass Sie verstehen, wohin die Zeit verschwindet.1

Indem Sie messen, wie viel Zeit Sie mit den verschiedenen Aktivitäten verbringen – einschließlich des Wartens –, können Sie Ihre Verbesserungen auf Bereiche konzentrieren, die die größten Veränderungen ermöglichen. Allzu häufig optimieren wir Teile unseres Prozesses, die offensichtlich ineffizient zu sein scheinen, aber nur wenig Auswirkungen auf die gesamte Durchlaufzeit haben. So habe ich beispielsweise gesehen, wie Teams eine Automatisierung implementiert haben, um die Zeit zum Provisionieren eines Servers von acht Stunden auf zehn Minuten zu reduzieren. Das sind 98 Prozent weniger. Aber wenn die Benutzerinnen und Benutzer normalerweise zehn Tage warten, um ihren neuen Server zu erhalten, sind das nur 10 Prozent Zeitgewinn. Steckt Ihr Server-Request-Ticket für durchschnittlich acht Tage in einer Queue, sollten Sie sich eher darauf konzentrieren.

Value Stream Mapping macht die Zeit sichtbar, die zum Abschließen einer Aktion erforderlich ist, sodass Sie die besten Optimierungsmöglichkeiten finden können. Messen Sie aber auch während Ihrer Verbesserungen die End-to-End-Durchlaufzeit und andere Metriken. Das hilft dabei, Optimierungen an einem Teil Ihres Prozesses zu vermeiden, die den Gesamtablauf noch schlechter machen.

Code auf Infrastruktur anwenden

Ein typischer Workflow für das Vornehmen einer Änderung an der Infrastruktur beginnt mit Code in einem gemeinsamen Quellcode-Repository. Ein Mitglied des Teams holt die letzte Version des Codes in seine Arbeitsumgebung und bearbeitet sie dort. Ist der Code fertig, wird er in das Quellcode-Repository geschoben und die neueste Version auf die diversen Umgebungen angewendet.

Viele starten ihre Tools an der Befehlszeile in ihrer Arbeitsumgebung, wenn sie mit der Infrastruktur-Automatisierung beginnen. Aber dabei gilt es Fallstricke zu beachten.

Code von Ihrem lokalen Rechner aus anwenden

Es kann nützlich sein, Infrastruktur-Code direkt von der Befehlszeile aus anzuwenden – wenn Sie ihn auf eine Testinstanz der Infrastruktur loslassen, die niemand anderes sonst verwendet. Führen Sie das Tool von Ihrem lokalen Rechner aus aus, kann das aber zu Problemen in gemeinsam genutzten Infrastruktur-Instanzen führen – zum Beispiel bei einer Produktivumgebung oder einer Auslieferungsumgebung (siehe »Auslieferungsumgebungen« auf Seite 96).

Die Person nimmt vermutlich Änderungen an ihrer lokalen Version des Codes vor, bevor sie ihn anwendet. Geschieht das, bevor die Änderungen wieder in das gemeinsame Repository wandern, hat niemand sonst Zugriff auf diese Version des Codes. Das kann zu Problemen führen, wenn jemand anderes die Infrastruktur debuggen muss.

Überträgt die Person, die ihre lokale Version des Codes angewandt hat, ihre Änderungen nicht direkt in das Repository, holt sich vielleicht jemand anderes eine ältere Version des Codes und bearbeitet sie. Wird dann dieser Code angewendet, werden damit die Änderungen der ersten Person zurückgenommen. Diese Situation wird schnell verwirrend und nur schwer auflösbar (siehe Abbildung 20-2).

Beachten Sie, dass Locking-Lösungen, wie zum Beispiel Terraforms State Locking (https://oreil.ly/f_QGZ), diese Situation nicht verhindern. Durch das Sperren werden zwei Personen davon abgehalten, ihren Code gleichzeitig auf die gleiche Instanz anzuwenden. Aber aktuell halten diese Lösungen niemanden davon ab, auseinanderlaufende Versionen des Codes anzuwenden, sofern man nur abwartet.

image

Abbildung 20-2: Lokales Bearbeiten und Anwenden von Code führt zu Konflikten.

Die Lektion ist hier also, dass Code für jede Infrastruktur-Instanz immer vom selben Ort aus angewendet werden sollte. Sie könnten eine einzelne Person dafür auswählen, für eine bestimmte Instanz verantwortlich zu sein. Aber auch hier gibt es Fallstricke – unter anderem eine gefährliche Abhängigkeit von einer Person und ihrem Rechner. Besser ist es, ein zentrales System zu besitzen, dass sich um gemeinsam genutzte Infrastruktur-Instanzen kümmert.

Code von einem zentralisierten Service anwenden lassen

Sie können einen zentralisierten Service nutzen, um Infrastruktur-Code auf Instanzen anzuwenden – sei es eine Anwendung, die Sie selbst hosten, oder ein Service von dritter Seite. Der Service holt sich den Code aus einem Quellcode- oder einem Artefakt-Repository (siehe »Infrastruktur-Code als Artefakt verpacken« auf Seite 365) und wendet ihn auf die Infrastruktur an, wodurch man einen klaren, kontrollierten Prozess für das Anwenden definierter Versionen besitzt.

Holen sich zwei Personen den Code und bearbeiten ihn, müssen sie jegliche Unterschiede in ihrem Code auflösen, wenn sie ihn mit dem Branch integrieren, den das Tool nutzt. Gibt es ein Problem, lässt sich leicht erkennen, welche Version des Codes angewendet wurde, und die Situation einfach korrigieren (siehe Abbildung 20-3).

Ein zentraler Service stellt zudem sicher, dass das Infrastruktur-Tool konsistent eingesetzt wird, statt anzunehmen, dass eine Person keine Fehler macht oder den Workflow »verbessert« (also davon abweicht). Es werden jedes Mal die gleichen Versionen des Tools, der Skripte und der unterstützenden Programme genutzt.

image

Abbildung 20-3: Zentrales Integrieren und Anwenden von Code

Der Einsatz eines zentralen Service passt gut zum Pipeline-Modell für das Ausliefern von Infrastruktur-Code auf unterschiedlichen Umgebungen (siehe »Infrastruktur-Delivery-Pipelines« auf Seite 152). Egal welches Tool oder welchen Service Sie beim Orchestrieren von Änderungen in Ihrer Pipeline verwenden – das Tool beziehungsweise der Service übernimmt die Verantwortung dafür, Ihr Infrastruktur-Tool zum Anwenden der neuesten Version des Codes auf jede Umgebung zu starten.

Ein weiterer Vorteil eines zentralen Service zum Ausführen Ihres Infrastruktur-Codes ist, dass Sie und Ihr Team dazu gezwungen sind, den gesamten Prozess zu automatisieren. Lassen Sie das Tool auf Ihrem Arbeitsrechner laufen, bleiben gerne ein paar lose Enden zurück – Schritte, die vor oder nach dem Durchlaufen des Tools manuell durchgeführt werden müssen. Ein zentraler Service lässt Ihnen keine andere Wahl, als sicherzustellen, dass die gesamte Aufgabe automatisiert ist.

image

Tools und Services, die Ihr Infrastruktur-Tool für Sie laufen lassen

Es gibt eine Reihe von Möglichkeiten, den Infrastruktur-Code durch einen zentralisierten Service anwenden zu lassen. Nutzen Sie einen Build-Server wie Jenkins oder ein CD-Tool wie GoCD oder ConcourseCI, können Sie Jobs oder Stages implementieren, um Ihr Infrastruktur-Tool laufen zu lassen. Diese Tools unterstützen den Umgang mit verschiedenen Code-Versionen aus einem Quellcode-Repository und sie können den Code zwischen den Stages weiterbefördern. Diese universellen Tools erleichtern auch das Integrieren von Workflows über Anwendungen, Infrastruktur und andere Teile Ihres Systems hinweg. Sie können selbst gehostete Instanzen dieser Services einsetzen oder auf ein gehostetes Angebot zurückgreifen. In »Software und Services für die Delivery-Pipeline« auf Seite 156 finden Sie mehr zu Pipeline-Servern und -Software.

Es gibt eine Reihe von Anbietern von Produkten und Services, die sich auf das Ausführen von Infrastruktur-Tools spezialisiert haben. Beispiele dafür sind Terraform Cloud (https://oreil.ly/67w1M), Atlantis (https://oreil.ly/YJps9) und Pulumi for Teams (https://oreil.ly/-qhGe). Weaveworks bietet Weave Cloud an (https://oreil.ly/HrAI9), das Infrastruktur-Code auf Kubernetes-Cluster anwendet.

Private Infrastruktur-Instanzen

In den meisten in diesem Buch behandelten Workflows holen Sie sich Code, bearbeiten ihn und stecken ihn dann wieder in ein gemeinsam genutztes Code-Repository.1 Dann wird er von einem Pipeline-Delivery-Prozess angewendet.

Idealerweise können Sie Ihre Code-Änderungen testen, bevor Sie sie in das gemeinsam genutzte Repository übertragen. Damit haben Sie eine Möglichkeit, sicherzustellen, dass Ihre Änderung das tut, was Sie erwarten – und das Ganze schneller, als wenn Sie darauf warten müssen, dass eine Pipeline Ihren Code durch eine Online-Test-Stage schickt (siehe »Online-Test-Stages für Stacks« auf Seite 168). Auch hilft es, zu vermeiden, dass der Build nicht durchläuft, weil eine Ihre Änderungen eine Pipeline-Stage fehlschlagen lässt, wodurch alle anderen in ihrer Arbeit mit der Codebasis unterbrochen werden.

Ihr Team kann ein paar Dinge umsetzen, die es erleichtern, Code-Änderungen zu testen, bevor Sie sie in das Repository übernehmen.

Als Erstes stellen Sie sicher, dass jede Person, die am Infrastruktur-Code arbeitet, ihre eigene Instanz der Infrastruktur erstellen kann. Es gibt eine Grenze dafür, inwieweit die Leute lokal ohne Cloud-Plattform testen können (siehe »Offline-Test-Stages für Stacks« auf Seite 165). Sie mögen dazu verleitet sein, gemeinsam genutzte »Dev«-Instanzen auf der Infrastruktur laufen zu lassen. Aber wie weiter oben erklärt wird es unübersichtlich, wenn mehrere Personen lokal bearbeiteten Code auf eine gemeinsame Instanz anwenden. Sorgen Sie daher für eine Möglichkeit, eigene Infrastruktur-Instanzen starten zu können, die wieder heruntergefahren werden, wenn sie nicht mehr gebraucht werden.

Dann achten Sie darauf, die Elemente Ihrer Infrastruktur klein zu halten. Das ist (natürlich) eine der drei zentralen Praktiken in diesem Buch (siehe Kapitel 15). Sie sollten dazu in der Lage sein, eine Instanz jeder Komponente Ihres Systems für sich hochfahren zu können – eventuell mithilfe von Test-Fixtures für die Abhängigkeiten (siehe »Test-Fixtures für den Umgang mit Abhängigkeiten verwenden« auf Seite 172). Es ist schwierig, mit privaten Instanzen zu arbeiten, wenn dafür das gesamte System hochgefahren werden muss, sofern dieses nicht ausgesprochen klein ist.

Und drittens sollten die Leute die gleichen Tools und Skripte für das Anwenden und Testen ihrer Infrastruktur-Instanzen verwenden, die auch für die gemeinsam genutzten Instanzen (zum Beispiel in der Pipeline) zum Einsatz kommen. Es ist hilfreich, Pakete für das Tooling und die Skripte zu bauen, die Sie in verschiedenen Umgebungen einsetzen können.1

image

Zentral verwaltete private Instanzen

Es ist sicherer, wenn die Leute Code auf private Instanzen von ihren Arbeitsrechnern aus anwenden, statt ihn auf gemeinsam genutzte Instanzen loszulassen. Aber es kann Vorteile haben, einen zentralisierten Service für die privaten Instanzen zu verwenden. Ich habe ein Team kennengelernt, das Probleme damit hatte, eine private Instanz von jemandem zu beenden, der in den Urlaub gefahren war. Diese Instanz wurde mit einer lokalen Version des Infrastruktur-Codes erstellt, die nicht ins Repository geschoben worden war, wodurch sie nicht einfach heruntergefahren zu bauen, die Sie in verschiedenen Umgebungen einsetzen können.

In manchen Teams pusht daher jede Person Änderungen in einen eigenen Branch, den der zentrale Service auf deren persönliche Infrastruktur-Instanz anwendet, die sie dann zum Testen nutzen kann. Auf diesem Weg emuliert der eigene Branch lokalen Code, sodass die Leute eine Änderung erst dann als committet ansehen, wenn sie ihn in den gemeinsamen Branch oder Trunk gemergt haben. Aber der Code steht zentral für andere zur Verfügung, sodass sie ihn in Abwesenheit begutachten und verwenden können.

Quellcode-Branches in Workflows

Branches sind ein leistungsfähiges Feature gemeinsam genutzter Quellcode-Repositories, die es einfacher machen, Änderungen an unterschiedlichen Kopien einer Codebasis – den Branches – vorzunehmen und die Arbeit dann als Teil des Workflows eines Teams zu integrieren. Es gibt viele Strategien und Patterns für den Einsatz von Branches als Teil eines Team-Workflows. Statt hier weiter darauf einzugehen, verweise ich auf Martin Fowlers Artikel »Patterns for Managing Source Code Branches« (https://oreil.ly/Ozgav).

Es lohnt sich, auf ein paar Unterschiede bei den Branching-Strategien im Kontext von Infrastructure as Code hinzuweisen. Einer ist der zwischen Path-to-Production-Pattern und Integration-Pattern für das Branching. Der andere ist die Bedeutung der Integrationshäufigkeit.

Teams setzen Path-to-Production-Branching-Patterns ein, um im Griff zu behalten, welche Versionen des Codes auf die Umgebungen angewendet werden.1 Typische Path-to-Production-Patterns nutzen Release-Branches und Umgebungs-Branches (Umgebungs-Branches werden in »Code aus einem Quellcode-Repository ausliefern« auf Seite 367 behandelt).

Integrations-Patterns für das Branching beschreiben Möglichkeiten, wie und wann man bei der Arbeit an einer Codebasis seine Arbeit integriert.2 Die meisten Teams nutzen das Mainline-Integration-Pattern – entweder mit Feature Branching oder per Continuous Integration.

Es ist weniger wichtig, welche Patterns oder welche Strategie Sie genau wählen – entscheidender ist, wie Sie sie einsetzen. Der wichtigste Faktor für die Effektivität beim Einsatz von Branches ist die Integrationshäufigkeit – wie oft jeder all seinen Code in den gleichen (Main) Branch des zentralen Repositories mergt.3 Die Forschung von DORA Accelerate hat ermittelt, dass eine häufigere Integration des gesamten Codes in einem Team mit einer höheren kommerziellen Performance korreliert. Die Ergebnisse legen nahe, dass alle im Team ihren gesamten Code gemeinsam integrieren sollten – zum Beispiel nach main oder trunk –, und zwar mindestens einmal am Tag.

image

Mergen ist nicht Integrieren

Manchmal wird ein Build-Server, der automatisch Tests für Branches ausführt, mit Continuous Integration verwechselt. Die Praxis der Continuous Integration und deren Korrelation mit einer höheren Team-Performance basiert auf dem vollständigen Integrieren aller Änderungen aller Personen, die in der Codebasis arbeiten.

Auch wenn jemand, der das Feature-Branch-Pattern nutzt, den aktuellen Main-Branch häufig in seinen eigenen Branch mergen mag, integriert er normalerweise seine eigene Arbeit erst dann zurück in den Main-Branch, wenn er mit seinem Feature fertig ist. Und wenn andere Personen an ihren Feature-Branches genauso arbeiten, wird der Code erst dann vollständig integriert, wenn alle ihre Features abgeschlossen und ihre Änderungen in den Main-Branch integriert haben.

Zum Integrieren gehört das Mergen in beiden Richtungen – die Einzelpersonen mergen ihre eigenen Änderungen in den Main-Branch und den Main-Branch zurück in ihren eigenen Branch oder in ihre lokale Kopie des Codes. Continuous Integration bedeutet daher, dass das alle mindestens einmal täglich tun.

Konfigurationsdrift verhindern

In Kapitel 2 sind die Risiken des Konfigurationsdrift beschrieben (siehe »Konfigurationsdrift« auf Seite 48), bei der gleiche Infrastruktur-Elemente mit der Zeit inkonsistent werden. Konfigurationsdrift entsteht oft, wenn Teams Infrastruktur-Coding-Tools verwenden, um Teile der alten Arbeitsweise zu automatisieren, statt ihre Vorgehensweise vollständig anzupassen.

Es gibt eine Reihe von Maßnahmen, die Sie in Ihren Workflows ergreifen können, um Konfigurationsdrift zu vermeiden.

Automatisierungs-Verzögerung minimieren

Die Automatisierungs-Verzögerung ist die Zeit, die zwischen zwei Starts eines automatisierten Prozesses vergeht – zum Beispiel beim Anwenden von Infrastruktur-Code. Je länger die Zeitspanne seit dem letzten Ausführen ist, desto eher wird der Lauf fehlschlagen.1 Dinge ändern sich mit der Zeit – auch wenn niemand absichtlich eine Änderung vorgenommen hat.

Auch wenn sich kein Code geändert hat, kann er immer noch fehlschlagen, wenn man ihn nach einer langen Zeitspanne anwendet. Das hat verschiedene Gründe:

Die logische Folge der Automatisierungs-Verzögerung ist, dass ein Fehlschlagen der Anwendung von Infrastruktur-Code umso unwahrscheinlicher ist, je häufiger Sie ihn anwenden. Kommt es zu einem Fehler, können Sie die Ursache schneller ermitteln, weil sich seit dem letzten erfolgreichen Lauf weniger geändert hat.

Ad-hoc-Anwendung vermeiden

Eine Gewohnheit, die manche Teams aus der Eisenzeit mit übernehmen, ist, Code nur für eine spezifische Änderung anzuwenden. Sie nutzen ihren Infrastruktur-Code vielleicht nur, um neue Infrastruktur zu provisionieren, aber nicht für Änderungen an bestehenden Systemen. Oder sie schreiben und nutzen Infrastruktur-Code nur für Ad-hoc-Anwendungen auf bestimmte Teile ihres Systems. So codieren sie beispielsweise eine einmalige Konfigurationsänderung für einen ihrer Anwendungsserver. Auch wenn Teams Code für Änderungen nutzen und ihn auf alle Instanzen anwenden, verwenden sie ihn manchmal nur, wenn sie auch Änderungen am Code vornehmen.

Diese Angewohnheiten können zu Konfigurationsdrift oder Automatisierungs-Verzögerung führen.

Code kontinuierlich anwenden

Eine zentrale Strategie für das Ausmerzen von Konfigurationsdrift ist, Infrastruktur-Code kontinuierlich auf Instanzen anzuwenden, auch wenn sich der Code nicht geändert hat. Viele Serverkonfigurationstools – unter anderem Chef und Puppet – sind dazu gedacht, die Konfiguration nach einem Zeitplan immer wieder anzuwenden (meist stündlich).1

Die GitOps-Methodik (siehe »GitOps« auf Seite 396) beinhaltet das kontinuierliche Anwenden von Code aus einem Quellcode-Branch auf jede Umgebung. Sie sollten einen zentralen Service für das Anwenden des Codes nutzen (siehe »Code von einem zentralisierten Service anwenden lassen« auf Seite 389), um den Code kontinuierlich auf jede Instanz anzuwenden.

Immutable Infrastruktur

Immutable Infrastruktur löst das Problem des Konfigurationsdrift auf eine andere Art und Weise. Statt Konfigurationscode immer wieder auf eine Infrastruktur-Instanz anzuwenden, wenden Sie ihn nur einmal beim Erstellen der Instanz an. Ändert sich der Code, erstellen Sie eine neue Instanz und ersetzen die alte damit.

Änderungen durch Erstellen einer neuen Instanz vorzunehmen, erfordert ausgefeilte Techniken zum Vermeiden von Downtime (siehe »Zero-Downtime-Änderungen« auf Seite 422), und eventuell ist dies auch nicht in allen Anwendungsfällen umsetzbar. Eine Automatisierungs-Verzögerung kann trotzdem ein Problem sein, daher tendieren Teams, die immutable Server verwenden, dazu, Instanzen häufig neu zu bauen – zum Beispiel als Phönix-Server.2

GitOps

GitOps ist eine Abwandlung von Infrastructure as Code, bei der kontinuierlich Code aus Quellcode-Branches mit Umgebungen synchronisiert wird. GitOps hebt das Definieren von Systemen als Code hervor (siehe »Zentrale Praktik: Definieren Sie alles als Code« auf Seite 40).

Es schreibt keinen bestimmten Weg zum Testen und Ausliefern von Infrastruktur-Code vor, lässt sich aber mit einer Pipeline für das Ausliefern von Code verwenden (siehe »Infrastruktur-Delivery-Pipelines« auf Seite 152). Bei GitOps wird allerdings das Ausliefern von Artefakten abgelehnt (siehe »Infrastruktur-Code als Artefakt verpacken« auf Seite 365) – stattdessen wird bevorzugt, Code-Änderungen durch Mergen mit Quellcode-Branches zu transportieren (siehe »Infrastruktur-Code mit einem Repository ausliefern« auf Seite 365).

Ein weiteres zentrales Element von GitOps ist das kontinuierliche Synchronisieren von Code mit Systemen (siehe »Code kontinuierlich anwenden« auf Seite 395). Statt durch einen Build-Server-Job oder eine Pipeline-Stage den Code anzuwenden, wenn er sich ändert (siehe »Ad-hoc-Anwendung vermeiden« auf Seite 395), verwendet GitOps einen Service, der den Code fortlaufend mit dem System vergleicht und damit Konfigurationsdrift verringert (siehe »Konfigurationsdrift« auf Seite 48).

Manche Teams bezeichnen ihren Prozess als GitOps, implementieren aber nur die Branches für die Umgebungen, ohne den Code kontinuierlich mit den Umgebungen zu synchronisieren. Damit wird es zu leicht, in einen Ad-hoc-Änderungsprozess zu verfallen und auf schlechte Angewohnheiten zurückzugreifen, indem Code-Änderungen für jede Umgebung über das Copy-Paste-Antipattern kopiert werden (siehe »Antipattern: Copy-Paste Environments« auf Seite 100).

Governance in einem Pipeline-basierten Workflow

Governance ist für viele Organisationen – insbesondere für größere und solche, die in regulierten Branchen arbeiten (wie zum Beispiel im Finanz- oder Gesundheitsbereich) – ein wichtiger Aspekt. Manche betrachten Governance als böses Wort, das unnötige Reibereien für eigentlich nützliche Arbeit erzeugt. Aber Governance bedeutet nur, sicherzustellen, dass die Dinge ordentlich unter Befolgung der Richtlinien der Organisation gemacht werden.

In Kapitel 1 wurde erklärt, dass Qualität – und Governance kann als ein Aspekt der Qualität betrachtet werden – die Auslieferungsgeschwindigkeit erhöhen kann, und die Fähigkeit zum schnellen Ausliefern von Änderungen wiederum die Qualität verbessert (siehe »Infrastructure as Code nutzen, um für Änderungen zu optimieren« auf Seite 35). Compliance as Code1 verbessert die Automation und kollaborativere Arbeitsweisen, um diese positive Schleife mit Leben zu füllen.

Zuständigkeiten neu ordnen

Durch das Definieren von Systemen als Code schaffen Sie Möglichkeiten, die Zuständigkeiten der an der Infrastruktur beteiligten Personen (also den in »Die Menschen« auf Seite 384 beschriebenen) und die Art und Weise ihrer Arbeit neu zu ordnen. Manche dieser Faktoren sind:

Wiederverwendbarkeit

Infrastruktur-Code kann designt, begutachtet und für mehrere Umgebungen und Systeme wiederverwendet werden. Sie brauchen keine längliche Design-, Review- und Zustimmungs-Aktivitäten für jeden neuen Server oder jede neue Umgebung, wenn Sie Code einsetzen, der diesen Prozess schon durchlaufen hat.

Funktionierender Code

Weil sich Code schnell schreiben lässt, können Reviews durchgeführt und Entscheidungen auf Grundlage von funktionierendem Code und Beispiel-Infrastruktur getroffen werden. Das sorgt für schnellere und genauere Feedback-Schleifen, als wenn man nur mit Diagrammen und Spezifikationen arbeiten kann.

Konsistenz

Ihr Code erzeugt Umgebungen viel konsistenter als Menschen, die Checklisten abarbeiten. Daher sorgen Testen und Reviewen der Infrastruktur in früheren Umgebungen für schnelleres und besseres Feedback, als wenn das erst später im Prozess geschieht.

Automatisiertes Testen

Automatisierte Tests, auch für Governance-Aspekte wie Sicherheit und Compliance, liefern Personen, die an Infrastruktur-Code arbeiten, ein schnelles Feedback. Sie können viele Probleme schon korrigieren, während sie noch an ihrem Code arbeiten, ohne Spezialistinnen und Spezialisten hinzuziehen zu müssen.

Qualität demokratisieren

Personen, die kein Spezialwissen haben, können Änderungen am Code für potenziell kritische Infrastrukturbereiche vornehmen, wie zum Beispiel für das Networking oder bei Sicherheitsrichtlinien. Sie können Tools und Tests einsetzen, die von Spezialisten und Spezialistinnen erstellt wurden, um ihre Änderungen zu überprüfen. Und diese Spezialistinnen und Spezialisten können immer noch Änderungen begutachten und genehmigen, bevor sie auf Produktivsysteme angewendet werden. Ein Review ist auf diese Art und Weise effizienter, weil Code, Testberichte und funktionierende Testinstanzen direkt gereviewt werden können.

Governance-Kanäle

Die Infrastruktur-Codebasis und Pipelines, die zum Ausliefern von Änderungen auf Produktiv-Instanzen genutzt werden, können anhand ihrer Governance-Anforderungen organisiert werden. So kann eine Änderung an Sicherheitsrichtlinien einen Review- und Sign-off-Schritt durchlaufen, der für weniger kritische Bereiche nicht erforderlich ist.

Zu vielen der Aspekte, wie wir das Managen von Systemen durch die Beteiligten ändern können, gehört ein Anpassen der Zuständigkeiten, die im Prozess übrig bleiben.

Shift Left

In Kapitel 8 werden Prinzipien und Praktiken für das Implementieren automatisierter Tests und Pipelines zum Ausliefern von Code-Änderungen auf Umgebungen vorgestellt. Der Begriff Shift Left beschreibt, wie sich das auf Workflows und Auslieferungspraktiken auswirkt.

Code wird beim Implementieren umfassend getestet – in den meisten Ablaufdiagrammen am »linken« Ende dargestellt. Daher müssen Organisationen nicht mehr so viel Zeit mit den schwergewichtigen Prozessen auf der »rechten« Seite verbringen – kurz vor dem Anwenden von Code auf die Produktivumgebungen.

Die Personen, die mit Governance und Testen zu tun haben, konzentrieren sich darauf, was während der Implementierung geschieht, sie arbeiten mit Teams zusammen, stellen Tools bereit und unterstützen Praktiken zum frühen und häufigen Testen.

Ein Beispielprozess für Infrastructure as Code mit Governance

ShopSpinner nutzt einen Reusable Stack (siehe »Pattern: Reusable Stack« auf Seite 102), mit dem es die Infrastruktur für einen Anwendungsserver erstellen kann, auf dem eine Instanz ihres Service für einen Kunden läuft. Ändert jemand den Code für diesen Stack, kann sich das auf alle Kunden auswirken.

Deren Technical Leadership Group, die für Architekturentscheidungen zuständig ist, definiert CFRs (wie in »Was sollten wir bei der Infrastruktur testen?« auf Seite 140 beschrieben), die die Anwendungsserver-Infrastruktur unterstützen muss. Diese CFRs enthalten die Anzahl und Häufigkeit von Aufträgen, die Anwenderinnen und Anwender auf einer Kunden-Instanz ausführen können, die Reaktionszeiten für die Schnittstelle und Wiederherstellunsgzeiten bei Server-Ausfällen.

Das Infrastruktur-Team und das Anwendungsteam tun sich mit zwei Site Reliability Engineers (SREs, https://oreil.ly/eC5yw) und einer QA zusammen, um ein paar automatisierte Tests zu implementieren, die die Performance des Anwendungsserver-Stacks gegen die CFRs abgleicht. Sie bauen diese Tests in die diversen Stages der Pipeline ein und testen progressiv unterschiedliche Komponenten des Stacks (siehe »Progressives Testen« auf Seite 148).

Seit die Gruppe die Tests eingerichtet hat, müssen die Leute ihre Infrastruktur-Änderungen nicht mehr zum Review bei der Technical Leadership Group, bei SREs oder jemand anderem einreichen. Ändert jemand in der Entwicklung beispielsweise die Networking-Konfiguration, prüft die Pipeline automatisch, ob die sich daraus ergebende Infrastruktur immer noch die CFRs erfüllt, bevor sie sie auf die produktiven Kunden-Instanzen anwenden kann. Gab es einen Fehler, der eine CFR missachten würde, wird das innerhalb von Minuten entdeckt, weil eine Pipeline-Stage rot wird, und man kann sofort korrigierend eingreifen.

In manchen Fällen führt eine Änderung zu einem Problem an einer Kunden-Instanz, die nicht durch die automatisierten Tests erkannt wurde. Die Gruppe kann dann ein Blameless Postmortem durchführen (https://oreil.ly/fqPKj), um herauszufinden, was passiert ist. Vielleicht lag das Problem darin, dass keine der CFRs die Situation erfasst hat – dann muss eine CFR geändert oder zusätzlich auf die Liste genommen werden. Oder beim Testen gibt es eine Lücke, die das Problem übersieht – dann wird die Testsuite erweitert.

image

Normalisieren Sie Ihren Emergency-Fix-Prozess

Viele Teams haben einen eigenen Prozess für Notfalländerungen, sodass sie Fixes schnell ausliefern können. Die Notwendigkeit eines separaten Prozesses für schnellere Fixes ist ein Zeichen dafür, dass der normale Änderungsprozess verbessert werden könnte.

Ein Emergency-Änderungsprozess beschleunigt die Dinge auf einem von zwei Wegen. Entweder werden unnötige Schritte ausgelassen. Oder es werden notwendige Schritte übersprungen. Können Sie einen Schritt in einem Notfall zuverlässig auslassen, gilt das vermutlich auch für Ihren normalen Prozess. Führt das Überspringen eines Schritts zu einem nicht akzeptablen Risiko, müssen Sie einen Weg finden, diesen effizienter zu durchlaufen - und das jedes Mal.1

Zusammenfassung

Definiert eine Organisation ihre Infrastruktur als Code, sollten die Beteiligten weniger Zeit mit Routineaufgaben und als Gatekeeper verbringen. Sie sollten stattdessen mehr Zeit für das kontinuierliche Verbessern ihrer Fähigkeiten aufwenden, das System selbst zu verbessern. Diese Aufwände werden sich in den Four Metrics für Softwareauslieferung und operative Performance widerspiegeln.