
Ansatz IaC (Infrastructure as Code) umfasst nicht nur den Code, der im Repository gespeichert ist, sondern auch die Menschen und Prozesse, die diesen Code umgeben. Können Ansätze aus der Softwareentwicklung im Management und der Beschreibung der Infrastruktur wiederverwendet werden? Es ist sinnvoll, diese Idee im Hinterkopf zu behalten, während Sie den Artikel lesen.
Dies ist die Transkription meines findet man .
Präsentationen und Videos
- Dry Run 2019-04-24
Infrastruktur als Bash-Historie

Stellen Sie sich vor, Sie kommen zu einem neuen Projekt, und man sagt Ihnen: "Wir haben Infrastructure as Code". In Wirklichkeit stellt sich heraus, Infrastruktur als Bash-Historie oder beispielsweise Dokumentation als Bash-Historie. Dies ist eine durchaus realistische Situation; ein ähnlicher Fall wurde von Denis Lysenko in seinem Vortrag beschrieben , er erzählte, wie sie aus der Bash-Historie eine strukturierte Infrastruktur im Projekt gewannen.
Mit ein wenig Engagement könnte man sagen, dass Infrastruktur als Bash-Historie es wie Code ist:
- die Konfiguration: Sie können die Bash-Historie nehmen, die Befehle dort ausführen, und wenn alles gut geht, erhalten Sie möglicherweise eine funktionierende Konfiguration.
- Versionierung: Sie wissen, wer Zugriff hatte und was getan wurde, aber es ist nicht sicher, dass dies Sie zu einer funktionierenden Konfiguration führt.
- Geschichte: die Geschichte, wer was getan hat. Aber Sie können sie nicht nutzen, wenn Sie den Server verlieren.
Was ist zu tun?
Infrastructure as Code

Selbst so seltsame Fälle wie Infrastruktur als Bash-Historie kann man hierher ziehen Infrastructure as Code, aber wenn wir etwas Komplexeres als den guten alten LAMP-Server machen wollen, werden wir feststellen, dass dieser Code irgendwie angepasst, verändert und weiterentwickelt werden muss. In der Folge möchten wir Parallelen zwischen Infrastructure as Code und der Softwareentwicklung betrachten.
D.R.Y.

Bei einem Projekt zur Entwicklung von Speichersystemen gab es eine Unteraufgabe : Wir bringen eine neue Version heraus – diese muss für weitere Tests verteilt werden. Die Aufgabe ist äußerst einfach:
- Loggen Sie sich hier per SSH ein und führen Sie den Befehl aus.
- Kopieren Sie die Datei dorthin.
- Ändern Sie die Konfiguration hier.
- Starten Sie den Dienst dort
- …
- PROFIT!
Für die beschriebene Logik reicht bash vollkommen aus, insbesondere in den frühen Phasen des Projekts, wenn es gerade gestartet wird. Es ist , aber im Laufe der Zeit entstehen die Anforderungen, etwas Ähnliches, aber mit kleinen Abweichungen zu implementieren. Das Erste, was einem in den Sinn kommt: Copy-Paste. Und jetzt haben wir bereits zwei sehr ähnliche Skripte, die fast dasselbe tun. Im Laufe der Zeit hat sich die Anzahl der Skripte erhöht, und wir sind auf das Problem gestoßen, dass es eine bestimmte Geschäftslogik für das Deployment der Installationen gibt, die zwischen den verschiedenen Skripten synchronisiert werden muss, was ziemlich kompliziert ist.

Es existiert eine Praxis namens D.R.Y. (Do not Repeat Yourself). Die Idee besteht darin, bestehenden Code wiederzuverwenden. Es klingt einfach, aber wir sind nicht sofort dazu gekommen. In unserem Fall war es die triviale Idee, Konfigurationen von den Skripten zu trennen. Das heißt, die Geschäftslogik zum Deployment der Installation ist separat, die Konfigurationen sind separat.
S.O.L.I.D. für CFM

Im Laufe der Zeit wuchs das Projekt und dass Ansible aufkam. Der Hauptgrund für sein Entstehen war die Expertise im Team und dass Bash nicht für komplexe Logik gedacht ist. Auch Ansible enthält komplexe Logik. Um zu verhindern, dass komplexe Logik im Chaos endet, gibt es in der Softwareentwicklung Prinzipien zur Organisation von Code. S.O.L.I.D. Ein Beispiel ist, dass Grigori Petrow in seinem Vortrag "Warum IT-Spezialisten eine persönliche Marke brauchen" das Thema angesprochen hat, dass Menschen so strukturiert sind, dass sie leichter mit sozialen Entitäten umgehen können, und in der Softwareentwicklung sind das Objekte. Wenn man diese beiden Ideen zusammenbringt und weiterentwickelt, kann man feststellen, dass auch in der Beschreibung von Infrastruktur ähnliche Konzepte verwendet werden können. S.O.L.I.D. so dass es später einfacher wird, diese Logik zu unterstützen und zu modifizieren.
Das Prinzip der Einzelverantwortung

Jede Klasse erfüllt lediglich eine Aufgabe.
Es ist nicht nötig, den Code zu vermischen und monolithische, göttliche Spaghetti-Monster zu schaffen. Die Infrastruktur sollte aus einfachen Bausteinen bestehen. Es stellt sich heraus, dass wenn man ein Ansible-Playbook in kleine Teile zerlegt, sprich Ansible-Rollen, sie leichter zu warten sind.
Das Open-Closed-Prinzip

Das Prinzip der Offenheit und Geschlossenheit.
- Offen für Erweiterungen: Das bedeutet, dass das Verhalten einer Entität durch die Erstellung neuer Entitätstypen erweitert werden kann.
- Geschlossen für Änderungen: Das bedeutet, dass bei der Erweiterung des Verhaltens einer Entität keine Änderungen am Code vorgenommen werden sollten, der diese Entitäten verwendet.
Ursprünglich haben wir unsere Testinfrastruktur auf virtuellen Maschinen aufgesetzt, aber da die Geschäftslogik für die Bereitstellung von der Implementierung getrennt war, konnten wir problemlos Bare-Metal-Deployments hinzufügen.
Das Liskov'sche Substitutionsprinzip

Das Prinzip der Substitution von Barbara Liskov. Objekte in einem Programm sollten durch Instanzen ihrer Subtypen ersetzt werden können, ohne die Korrektheit der Ausführung des Programms zu verändern.
Wenn man es umfassender betrachtet, ist es keine Besonderheit eines spezifischen Projekts, dass man es anwenden kann. S.O.L.I.D., es betrifft insgesamt CFM, zum Beispiel in einem anderen Projekt, wo man eine fertige Java-Anwendung auf verschiedenen Java-Servern, Anwendungsservern, Datenbanken, Betriebssystemen usw. bereitstellen muss. Dieses Beispiel wird als Grundlage für die weiteren Prinzipien dienen. S.O.L.I.D.
In unserem Fall gibt es im Rahmen des Infrastrukturteams die Vereinbarung, dass wenn wir die Rolle imbjava oder oraclejava installiert haben, wir eine ausführbare Java-Binärdatei haben. Das ist notwendig, da übergeordnete Rollen von diesem Verhalten abhängen; sie erwarten das Vorhandensein von Java. Gleichzeitig ermöglicht es uns, eine Implementierung bzw. Version von Java durch eine andere zu ersetzen, ohne die Bereitstellungslogik der Anwendung zu ändern.
Das Problem liegt darin, dass Ansible so etwas nicht umsetzen kann, was dazu führt, dass in der Befehlszeile bestimmte Vereinbarungen getroffen werden.
Das Prinzip der Schnittstellentrennung

Das Schnittstellentrennungsprinzip besagt: "Viele Schnittstellen, die speziell für Kunden entwickelt wurden, sind besser als eine universelle Schnittstelle."
Ursprünglich haben wir versucht, sämtliche Variabilität der Anwendungsbereitstellung in einem einzigen Ansible-Playbook zu bündeln, doch das war schwer zu warten. Mit einem spezifizierten externen Interface (der Kunde erwartet Port 443) kann die Infrastruktur für die spezifische Implementierung aus einzelnen Bausteinen zusammengesetzt werden.
Das Prinzip der Abhängigkeitsinversion

Das Prinzip der Abhängigkeitsinversion besagt: Hochstufige Module sollten nicht von niederen Modulen abhängen. Beide Modularten sollten von Abstraktionen abhängen. Abstraktionen dürfen nicht von Details abhängen. Details sollten von Abstraktionen abhängen.
Hier wird ein Beispiel basierend auf einem Antipattern vorgestellt.
- Einer unserer Kunden hatte eine private Cloud.
- Innerhalb der Cloud bestellten wir virtuelle Maschinen.
- Aufgrund der Eigenheiten der Cloud war die Bereitstellung der Anwendung an den Hypervisor gebunden, auf dem die VM landete.
Das bedeutet, dass die hochgradige Logik für die Bereitstellung von Anwendungen und deren Abhängigkeiten auf die darunterliegenden Hypervisor-Ebenen abgeleitet wurde, was zu Problemen bei der Wiederverwendung dieser Logik führte. Das müssen Sie nicht tun.
Interaktion

Infrastructure as Code bedeutet nicht nur Code, sondern auch die Beziehungen zwischen Code und Mensch sowie die Interaktionen zwischen Infrastrukturentwicklern.
Bus-Faktor

Nehmen wir an, dass Sie in Ihrem Projekt einen Vasja haben. Vasja weiß alles über Ihre Infrastruktur. Was passiert, wenn Vasja plötzlich ausfällt? Das ist eine sehr reale Situation, denn er könnte von einem Bus erfasst werden. Manchmal passiert so etwas. Wenn dies eintritt und das Wissen über den Code, seine Struktur, wie er funktioniert, sowie Zugangsdaten nicht im Team verteilt sind, kann es zu unangenehmen Situationen kommen. Um diese Risiken zu minimieren und das Wissen im Team zu verteilen, können verschiedene Ansätze verwendet werden.
Pair DevOpsing

Das ist nicht so wie , was die Administratoren Bier tranken, Passwörter änderten, und das Äquivalent des Pair-Programming. D.h. zwei Ingenieure setzen sich an einen Computer, eine Tastatur und beginnen gemeinsam, Ihre Infrastruktur einzurichten: den Server zu konfigurieren, eine Ansible-Rolle zu schreiben usw. Klingt schön, hat aber bei uns nicht funktioniert. Es gab jedoch spezielle Fälle dieser Praxis, die funktioniert haben. Ein neuer Mitarbeiter kommt, sein Mentor nimmt gemeinsam mit ihm eine reale Aufgabe und arbeitet — Wissen wird weitergegeben.
Ein anderer spezieller Fall ist der Incident Call. Während eines Problems versammelt sich eine Gruppe von Bereitschaftspersonen und Betroffenen, ein Moderator wird bestimmt, der seinen Bildschirm teilt und seinen Denkprozess erläutert. Die anderen Teilnehmer folgen den Gedanken des Moderators, schauen sich Tricks aus der Konsole an, überprüfen, ob eine Zeile im Log übersehen wurde, und erfahren Neues über das System. Dieser Ansatz funktionierte eher als nicht.
Code Review

Subjektiv wurde das Wissen über die Infrastruktur und deren Aufbau effektiver durch Code Reviews vermittelt:
- Die Infrastruktur ist im Code im Repository beschrieben.
- Änderungen erfolgen in einem separaten Branch.
- Beim Merge-Request kann man die Delta-Änderungen der Infrastruktur sehen.
Das Besondere hier war, dass die Reviewer nach einem festgelegten Zeitplan ausgewählt wurden, was bedeutete, dass man mit einer gewissen Wahrscheinlichkeit in einen neuen Bereich der Infrastruktur vordringen konnte.
Code-Stil

Im Laufe der Zeit traten Konflikte während der Reviews auf, da jeder Reviewer seinen eigenen Stil hatte und die Rotation der Reviewer dazu führte, dass sie unterschiedliche Stile hatten: 2 Leerzeichen oder 4, camelCase oder snake_case. Die Implementierung verlief nicht sofort reibungslos.
- Die erste Idee war, zu empfehlen, einen Linter zu verwenden, denn schließlich sind wir Ingenieure und alle sind klug. Aber unterschiedliche Editoren und Betriebssysteme machten dies unpraktisch.
- Das entwickelte sich zu einem Bot, der bei problematischen Commits in Slack eine Nachricht schrieb und die Ausgabe des Linters anhängte. In den meisten Fällen gab es jedoch wichtigere Aufgaben, sodass der Code unberührt blieb.
Green Build Master

Die Zeit vergeht, und wir kamen zu dem Schluss, dass Commits, die bestimmte Tests nicht bestehen, nicht in den Master gelangen dürfen. Voilà! Wir haben den Green Build Master erfunden, der schon seit langem in der Softwareentwicklung praktiziert wird:
- Die Entwicklung erfolgt in einem separaten Branch.
- In diesem Branch werden die Tests durchgeführt.
- Wenn die Tests fehlschlagen, gelangt der Code nicht in den Master.
Die Entscheidung fiel uns durchaus schwer, da sie viele Diskussionen auslöste. Doch es hat sich gelohnt: Die Anfragen für Merge-Requests kamen fortan ohne Stilkonflikte und mit der Zeit nahm die Anzahl problematischer Stellen ab.
IaC-Tests

Neben der Stilüberprüfung können auch andere Aspekte berücksichtigt werden, wie etwa die tatsächliche Bereitstellung Ihrer Infrastruktur zu testen. Oder zu überprüfen, ob Änderungen in der Infrastruktur nicht zu finanziellen Verlusten führen. Warum könnte das nötig sein? Diese Frage ist komplex und philosophisch. Eine Anekdote dazu: Ein Auto-Scaler in PowerShell prüfte keine Grenzbedingungen, was dazu führte, dass mehr VM erstellt wurden als nötig, sodass der Kunde mehr ausgab, als geplant. Das ist alles andere als angenehm, aber solcher Fehler hätte viel früher erkannt werden können.
Man könnte fragen, warum man eine komplexe Infrastruktur noch komplexer machen sollte? Tests für die Infrastruktur sind, ähnlich wie für Code, nicht dazu da, es einfacher zu machen, sondern um zu wissen, wie Ihre Infrastruktur funktionieren sollte.
IaC-Testpyramide

IaC-Tests: Statische Analyse
Wenn man sofort die gesamte Infrastruktur aufbaut und überprüft, ob sie funktioniert, kann das viel Zeit in Anspruch nehmen und viel Aufwand erfordern. Daher sollte etwas Schnelles und Umfangreiches die Grundlage bilden, das viele primitive Stellen abdeckt.
Bash ist knifflig
Schauen wir uns ein einfaches Beispiel an: Alle Dateien im aktuellen Verzeichnis auszuwählen und an einen anderen Ort zu kopieren. Das Erste, was einem in den Sinn kommt:
for i in * ; do
cp $i /some/path/$i.bak
doneWas ist, wenn im Dateinamen ein Leerzeichen vorhanden ist? Nun gut, wir sind ja clever und wissen, wie man Anführungszeichen verwendet:
for i in * ; do cp "$i" "/some/path/$i.bak" ; doneGut gemacht? Nein! Was ist, wenn im Verzeichnis nichts vorhanden ist, d.h. das Globing nicht funktioniert.
find . -type f -exec mv -v {} dst/{}.bak ;Jetzt gut gemacht? Nichts dergleichen… Wir haben vergessen, dass im Dateinamen n.
touch x
mv x "$(printf "foonbar")"
find . -type f -print0 | xargs -0 mv -t /path/to/target-dirStatische Analysetools
Das Problem aus dem vorherigen Schritt hätte erkannt werden können, als wir die Anführungszeichen vergessen haben. Dafür gibt es in der Natur viele Mittel. , es gibt sie wirklich viele, und wahrscheinlich finden Sie einen Linter für Ihren Stack, der zu Ihrer IDE passt.
Sprache
Werkzeug
bash
Ruby
Python
ansible
IaC-Testen: Unit-Tests

Wie wir aus dem vorherigen Beispiel sehen konnten, sind Linter nicht allmächtig und können nicht auf alle problematischen Stellen hinweisen. In Anlehnung an die Softwareentwicklung können wir hier an Unit-Tests denken. Sofort kommen einem die Begriffe , , , in den Sinn. Aber was ist mit Ansible, Chef, SaltStack und ähnlichen Tools?
Zu Beginn haben wir über die S.O.L.I.D. gesprochen und darüber, dass unsere Infrastruktur aus kleinen Bausteinen bestehen sollte. Ihre Zeit ist jetzt gekommen.
- Die Infrastruktur wird in kleine Bausteine zerlegt, wie z.B. Ansible-Rollen.
- Es wird eine Umgebung eingerichtet, sei es Docker oder eine VM.
- Auf diese Testumgebung wenden wir unsere Ansible-Rolle an.
- Wir überprüfen, ob alles wie erwartet funktioniert (wir führen Tests durch).
- Wir entscheiden, ob alles in Ordnung ist oder nicht.
IaC Testing: Unit Testing-Tools
Die Frage ist, was sind Tests für CFM? Man kann simpel ein Skript ausführen oder bereits vorhandene Lösungen dafür nutzen:
CFM
Werkzeug
Ansible
Chef
Chef
SaltStack
Ein Beispiel für Testinfra: Wir überprüfen, ob die Benutzer test1, test2 existieren und in der Gruppe sshusers:
sind:
def test_default_users(host):
users = ['test1', 'test2']
for login in users:
assert host.user(login).exists
assert 'sshusers' in host.user(login).groupsWas soll man wählen? Eine schwierige und nicht eindeutige Frage – hier ist ein Beispiel für Änderungen in Projekten auf GitHub in den Jahren 2018-2019:

IaC-Testframeworks
Wie bekommt man das alles zusammen und zum Laufen? Man kann sofern genügend Ingenieure vorhanden sind. Alternativ kann man fertige Lösungen nutzen, wobei es davon nicht allzu viele gibt:
CFM
Werkzeug
Ansible
Chef
Terraform
Ein Beispiel für Änderungen in Projekten auf GitHub zwischen 2018 und 2019:

Molecule vs. Testkitchen

Ursprünglich haben wir :
- Eine VM parallel erstellen.
- Ansible-Rollen anwenden.
- Inspec durchführen.
Für 25-35 Rollen dauerte das 40-70 Minuten, was lang war.

Der nächste Schritt war der Umstieg auf Jenkins / Docker / Ansible / Molecule. Ideologisch ist alles dasselbe.
- Playbooks lintern.
- Rollen lintern.
- Container starten.
- Ansible-Rollen anwenden.
- Testinfra durchführen.
- Idempotenz prüfen.

Das Lintern von 40 Rollen und die Tests für ein Dutzend benötigten etwa 15 Minuten.

Die Wahl hängt von vielen Faktoren ab, wie dem verwendeten Stack, der Expertise im Team usw. Hier entscheidet jeder selbst, wie er das Thema Unit-Tests angeht.
IaC-Testing: Integrationstests

Auf der nächsten Stufe der Testpyramide erscheinen Integrationstests. Sie ähneln den Unit-Tests:
- Die Infrastruktur wird in kleine Bausteine zerlegt, beispielsweise Ansible-Rollen.
- Es wird eine Umgebung eingerichtet, sei es Docker oder eine VM.
- Auf dieser Testumgebung werden eine Vielzahl Ansible-Rollen.
- Wir überprüfen, ob alles wie erwartet funktioniert (wir führen Tests durch).
- Wir entscheiden, ob alles in Ordnung ist oder nicht.
Groß gesagt überprüfen wir nicht die Funktionsfähigkeit eines einzelnen Elements des Systems wie in Unit-Tests, sondern wir schauen uns an, wie der Server insgesamt konfiguriert ist.
IaC-Testing: End-to-End-Tests

An der Spitze der Pyramide haben wir die End-to-End-Tests. Das bedeutet, dass wir nicht die Funktionsfähigkeit eines einzelnen Servers, eines individuellen Skripts oder eines Teils unserer Infrastruktur überprüfen. Wir stellen sicher, dass viele Server, die miteinander verbunden sind, unsere Infrastruktur so funktionieren, wie wir es erwarten. Leider habe ich keine fertigen Lösungen gesehen, wahrscheinlich weil die Infrastruktur oft einzigartig ist und es schwierig ist, sie zu standardisieren und ein Framework für deren Tests zu erstellen. Letztendlich schaffen alle ihre eigenen Lösungen. Es gibt eine Nachfrage, aber keine Antworten. Daher werde ich mitteilen, was es gibt, um andere zu inspirieren oder um mich darauf hinzuweisen, dass alles schon längst erfunden wurde.

Ein Projekt mit reicher Geschichte. Es wird in großen Organisationen eingesetzt, und wahrscheinlich ist jeder von Ihnen indirekt damit in Berührung gekommen. Die Anwendung unterstützt eine Vielzahl von Datenbanken, Integrationen usw. Zu wissen, wie die Infrastruktur aussehen kann, umfasst viele Docker-Compose-Dateien, und zu wissen, welche Tests in welcher Umgebung ausgeführt werden müssen, ist Jenkins.

Dieses Schema hat ziemlich lange funktioniert, bis wir im Rahmen dies auf Openshift umsetzen wollten. Die Container blieben gleich, aber die Ausführungsumgebung änderte sich (Hallo DRY noch einmal).

Der Gedanke an die Forschung ging weiter, und in Openshift fand sich etwas namens APB (Ansible Playbook Bundle), das es ermöglicht, Wissen darüber, wie man Infrastruktur bereitstellt, in einen Container zu packen. Das bedeutet, es gibt einen reproduzierbaren, testbaren Wissenspunkt, wie man Infrastruktur aufbaut.

Das alles klang gut, bis wir auf eine heterogene Infrastruktur stießen: Für unsere Tests benötigten wir Windows. Letztendlich sitzt das Wissen darüber, wo und wie etwas umgesetzt und getestet werden kann, in Jenkins.
Fazit

Infrastructure as Code bedeutet
- Code im Repository.
- Interaktion zwischen Menschen.
- Testen der Infrastruktur.
links
- Dry Run 2019-04-24
- &
Quelle: habr.com
