Lehren aus dem Testen von über 200.000 Zeilen Infrastrukturcode


IaC (Infrastruktur als Code) ist ein moderner Ansatz und ich glaube, dass Infrastruktur Code ist. Dies bedeutet, dass wir für die Infrastruktur dieselbe Philosophie verwenden sollten wie für die Softwareentwicklung. Wenn wir davon sprechen, dass Infrastruktur Code ist, sollten wir die Entwicklungspraktiken für die Infrastruktur wiederverwenden, dh Unit-Tests, Paarprogrammierung und Codeüberprüfung. Bitte denken Sie beim Lesen des Artikels an diese Idee.


Russische Version


Es ist die Übersetzung meiner Rede ( Video RU ) auf der DevopsConf 2019-05-28 .



Infrastruktur als Bash-Geschichte



Stellen wir uns vor, Sie nehmen an einem Projekt teil und hören etwas wie: "Wir verwenden Infrastruktur als Code- Ansatz". Leider bedeuten sie wirklich Infrastruktur als Bash-Verlauf oder Dokumentation als Bash-Verlauf . Dies ist fast eine reale Situation. Zum Beispiel beschrieb Denis Lysenko diese Situation in seiner Rede Wie man die Infrastruktur ersetzt und aufhört, sich Sorgen zu machen (RU) . Denis erzählte, wie man die Bash-Geschichte in eine gehobene Infrastruktur umwandelt.


Lassen Sie uns die Definition des Quellcodes überprüfen: a text listing of commands to be compiled or assembled into an executable computer program . Wenn wir wollen, können wir die Infrastruktur als Bash-Verlauf wie Code präsentieren. Dies ist ein Text und eine Liste von Befehlen. Es beschreibt, wie ein Server konfiguriert wurde. Darüber hinaus ist es:


  1. Reproduzierbar : Sie können den Bash-Verlauf abrufen, Befehle ausführen und wahrscheinlich eine funktionierende Infrastruktur erhalten.
  2. Versionierung : Sie wissen, wer sich wann und was angemeldet hat.
    Wenn Sie den Server verlieren, können Sie leider nichts tun, da es keinen Bash-Verlauf gibt. Sie haben ihn mit dem Server verloren.

Was ist zu tun?


Infrastruktur als Code



Einerseits kann dieser ungewöhnliche Fall, Infrastruktur als Bash-Verlauf , als Infrastruktur als Code dargestellt werden. Wenn Sie jedoch etwas Komplexeres als den LAMP-Server ausführen möchten, müssen Sie den Code verwalten, warten und ändern . Lassen Sie uns über Parallelen zwischen Infrastruktur als Codeentwicklung und Softwareentwicklung sprechen.


TROCKEN



Wir haben SDS (Software Defined Storage) entwickelt. Das Sicherheitsdatenblatt bestand aus benutzerdefinierten Betriebssystem-Distributionsservern, gehobenen Servern und viel Geschäftslogik. Daher musste echte Hardware verwendet werden. In regelmäßigen Abständen gab es eine Unteraufgabe zur Installation von SDS . Vor der Veröffentlichung der neuen Version mussten wir sie installieren und auschecken. Zuerst sah es so aus, als wäre es eine sehr einfache Aufgabe:


  • SSH zum Hosten und Ausführen des Befehls.
  • SCP eine Datei.
  • Ändern Sie eine Konfiguration.
  • Führen Sie einen Dienst aus.
  • ...
  • GEWINN!

Ich glaube, dass Make CM, nicht Bash, ein guter Ansatz ist. Bash wird jedoch nur in extremen, begrenzten Fällen verwendet, wie zu Beginn eines Projekts. Bash war also zu Beginn des Projekts eine ziemlich gute und vernünftige Wahl. Die Zeit tickte. Wir hatten unterschiedliche Anforderungen, neue Installationen in einer etwas anderen Konfiguration zu erstellen. Wir haben SSHing in Installationen durchgeführt und die Befehle ausgeführt, um die gesamte benötigte Software zu installieren, die Konfigurationsdateien mithilfe von Skripten zu bearbeiten und schließlich SDS über die Web-HTTP-Rest-API zu konfigurieren. Nach all dem war die Installation konfiguriert und funktionierte. Dies war eine ziemlich übliche Praxis, aber es gab viele Bash-Skripte und die Installationslogik wurde von Tag zu Tag komplexer.


Leider war jedes Skript wie eine kleine Schneeflocke, je nachdem, wer es kopierte. Es war auch ein echtes Problem, als wir die Installation erstellten oder neu erstellten.


Ich hoffe, Sie haben die Hauptidee, dass wir zu diesem Zeitpunkt die Skriptlogik ständig optimieren mussten, bis der Dienst in Ordnung war. Aber dafür gab es eine Lösung. Es war trocken



Es gibt einen DRY-Ansatz (Wiederholen Sie sich nicht). Die Hauptidee besteht darin, bereits vorhandenen Code wiederzuverwenden. Es klingt sehr einfach. In unserem Fall bedeutete DRY: geteilte Konfigurationen und Skripte.


FEST für CFM



Das Projekt wuchs, daher entschieden wir uns für Ansible. Dafür gab es Gründe:


  1. Bash sollte keine komplexe Logik enthalten .
  2. Wir hatten einige Kenntnisse in Ansible.

Der Ansible-Code enthielt eine Menge Geschäftslogik. Es gibt einen Ansatz, um Dinge während des Softwareentwicklungsprozesses in den Quellcode einzufügen. Es heißt SOLID . Aus meiner Sicht können wir SOLID for Infrastructure als Code wiederverwenden. Lassen Sie mich Schritt für Schritt erklären.


Das Prinzip der Einzelverantwortung



Eine Klasse sollte nur eine einzige Verantwortung haben, dh nur Änderungen an einem Teil der Softwarespezifikation sollten sich auf die Spezifikation der Klasse auswirken können.


Sie sollten keinen Spaghetti-Code in Ihrem Infrastrukturcode erstellen. Ihre Infrastruktur sollte aus einfachen vorhersehbaren Bausteinen bestehen. Mit anderen Worten, es könnte eine gute Idee sein, ein immenses Ansible-Spielbuch in unabhängige Ansible-Rollen aufzuteilen. Es wird einfacher zu warten sein.


Die offen-geschlossenen Prinzipien



Software-Entitäten ... sollten zur Erweiterung geöffnet, aber zur Änderung geschlossen sein.


Am Anfang haben wir das Sicherheitsdatenblatt auf virtuellen Maschinen bereitgestellt, etwas später haben wir die Bereitstellung auf Bare-Metal-Servern hinzugefügt. Wir hatten es geschafft. Für uns war das kinderleicht, da wir gerade eine Implementierung für Bare-Metal-spezifische Teile hinzugefügt haben, ohne die SDS-Installationslogik zu ändern.


Das Liskov-Substitutionsprinzip



Objekte in einem Programm sollten durch Instanzen ihrer Untertypen ersetzt werden können, ohne die Richtigkeit dieses Programms zu ändern.


Seien wir aufgeschlossen. SOLID kann im Allgemeinen in CFM verwendet werden, es war kein glückliches Projekt. Ich möchte ein anderes Projekt beschreiben. Es ist eine Out-of-Box-Unternehmenslösung. Es unterstützt verschiedene Datenbanken, Anwendungsserver und Integrationsschnittstellen mit Systemen von Drittanbietern. Ich werde dieses Beispiel verwenden, um den Rest von SOLID zu beschreiben


In unserem Fall besteht beispielsweise eine Vereinbarung innerhalb des Infrastrukturteams: Wenn Sie eine IBM Java-Rolle oder Oracle Java oder OpenJDK bereitstellen, verfügen Sie über eine ausführbare Java-Binärdatei. Wir brauchen es, weil Ansible-Rollen auf höchster Ebene davon abhängen. Außerdem können wir die Java-Implementierung austauschen, ohne die Anwendungsinstallationslogik zu ändern.


Leider gibt es dafür in Ansible-Playbooks keinen Syntaxzucker. Dies bedeutet, dass Sie dies bei der Entwicklung von Ansible-Rollen berücksichtigen müssen.


Das Prinzip der Schnittstellentrennung



Viele clientspezifische Schnittstellen sind besser als eine Allzweckschnittstelle.


Am Anfang haben wir die Anwendungsinstallationslogik in das einzelne Playbook eingefügt und versucht, alle Fälle und Schneidkanten abzudecken. Wir hatten das Problem, dass es schwer aufrechtzuerhalten ist, und haben unseren Ansatz geändert. Wir haben verstanden, dass ein Client eine Schnittstelle von uns benötigt (dh https am 443-Port), und wir konnten unsere Ansible-Rollen für jede spezifische Umgebung kombinieren.


Das Prinzip der Abhängigkeitsinversion



Man sollte "von Abstraktionen abhängen, nicht von Konkretionen".


  • High-Level-Module sollten nicht von Low-Level-Modulen abhängen. Beides sollte von Abstraktionen abhängen (zB Schnittstellen).
  • Abstraktionen sollten nicht von Details abhängen. Details (konkrete Implementierungen) sollten von Abstraktionen abhängen.

Ich möchte dieses Prinzip über Anti-Pattern beschreiben.


  1. Es gab einen Kunden mit einer privaten Cloud.
  2. Wir haben VMs in der Cloud angefordert.
  3. Unsere Bereitstellungslogik war abhängig davon, auf welchem ​​Hypervisor sich eine VM befand.

Mit anderen Worten, wir konnten unser IaC nicht in einer anderen Cloud wiederverwenden, da die Bereitstellungslogik auf oberster Ebene von der Implementierung auf niedrigerer Ebene abhing. Bitte tu es nicht


Interaktion



Infrastruktur ist nicht nur Code, sondern auch Interaktionscode <-> DevOps, DevOps <-> DevOps, IaC <-> Personen.


Busfaktor



Stellen wir uns vor, es gibt DevOps-Ingenieur John. John weiß alles über Ihre Infrastruktur. Was passiert mit Ihrer Infrastruktur, wenn John von einem Bus angefahren wird? Leider ist es fast ein realer Fall. Manchmal passieren Dinge. Wenn dies passiert ist und Sie Ihren Teammitgliedern kein Wissen über IaC, Infrastruktur mitteilen, werden Sie mit vielen unvorhersehbaren und unangenehmen Konsequenzen konfrontiert sein. Es gibt einige Ansätze, um damit umzugehen. Lass uns darüber reden.


Pair DevOpsing



Es ist wie bei der Paarprogrammierung. Mit anderen Worten, es gibt zwei DevOps-Ingenieure, die einen einzelnen Laptop \ Tastatur zum Konfigurieren der Infrastruktur verwenden: Konfigurieren eines Servers, Erstellen einer Ansible-Rolle usw. Es klingt großartig, hat aber bei uns nicht funktioniert. Es gab einige benutzerdefinierte Fälle, in denen es teilweise funktionierte.


  • Onboarding : Mentor und neue Person erhalten eine echte Aufgabe aus einem Rückstand und arbeiten zusammen - übertragen Sie Wissen vom Mentor auf die Person.
  • Incident Call : Während der Fehlerbehebung gibt es eine Gruppe von Ingenieuren, die nach einer Lösung suchen. Der entscheidende Punkt ist, dass es eine Person gibt, die diesen Vorfall leitet. Die Person teilt Bildschirm & Ideen. Andere Leute folgen ihm aufmerksam und bemerken Bash-Tricks, Fehler, das Parsen von Protokollen usw.

Codeüberprüfung



Aus meiner Sicht ist die Codeüberprüfung eine der effizientesten Möglichkeiten, um innerhalb eines Teams Wissen über Ihre Infrastruktur auszutauschen. Wie funktioniert es


  • Es gibt ein Repository, das Ihre Infrastrukturbeschreibung enthält.
  • Jeder nimmt seine Änderungen in einer eigenen Niederlassung vor.
  • Während der Zusammenführungsanforderung können Sie das Delta der Änderungen in Ihrer Infrastruktur überprüfen.

Das Interessanteste ist, dass wir einen Rezensenten gewechselt haben. Dies bedeutet, dass wir alle paar Tage einen neuen Prüfer gewählt haben und der Prüfer alle Zusammenführungsanfragen durchgesehen hat. Theoretisch musste daher jede Person einen neuen Teil der Infrastruktur berühren und verfügte über ein durchschnittliches Wissen über unsere Infrastruktur im Allgemeinen.


Codestil



Die Zeit lief, wir haben uns manchmal während der Überprüfung gestritten , weil der Prüfer und der Committer möglicherweise einen anderen Codestil verwenden : 2 Leerzeichen oder 4, camelCase oder snake_case . Wir haben es implementiert, es war jedoch kein Picknick.


  • Die erste Idee war, die Verwendung von Lintern zu empfehlen. Jeder hatte seine eigene Entwicklungsumgebung: IDE, Betriebssystem ... es war schwierig, alles zu synchronisieren und zu vereinheitlichen.
  • Die Idee entwickelte sich zu einem schlaffen Bot. Nach jedem Commit überprüfte der Bot den Quellcode und drückte mit einer Liste von Problemen auf Slack-Nachrichten. Leider gab es in den allermeisten Fällen nach den Nachrichten keine Änderungen des Quellcodes.

Grüner Baumeister



Als nächstes bestand der schmerzhafteste Schritt darin, das Schieben für alle auf den Hauptzweig zu beschränken. Nur über Merge Requests & Green Tests muss ok sein. Dies wird als Green Build Master bezeichnet . Mit anderen Worten, Sie sind zu 100% sicher, dass Sie Ihre Infrastruktur von der Hauptniederlassung aus bereitstellen können. In der Softwareentwicklung ist dies eine weit verbreitete Praxis:


  • Es gibt ein Repository, das Ihre Infrastrukturbeschreibung enthält.
  • Jeder nimmt seine Änderungen in einer eigenen Niederlassung vor.
  • Für jeden Zweig führen wir Tests durch.
  • Sie können nicht in den Hauptzweig eingebunden werden, wenn die Tests fehlschlagen.

Es war eine schwere Entscheidung. Als Ergebnis des Überprüfungsprozesses gab es hoffentlich keine Streitigkeiten über den Codestil und die Menge des Codegeruchs nahm ab.


IaC-Tests



Neben der Überprüfung des Codestils können Sie auch überprüfen, ob Sie Ihre Infrastruktur in einer Sandbox bereitstellen oder neu erstellen können. Wofür ist das? Es ist eine anspruchsvolle Frage, und ich möchte eine Geschichte anstelle einer Antwort teilen. Wurde ein benutzerdefinierter Auto-Scaler für AWS in Powershell geschrieben. Der Auto-Scaler überprüfte die Schneidkanten nicht auf Eingabeparameter. Infolgedessen wurden Tonnen virtueller Maschinen erstellt, und der Kunde war unglücklich. Es ist eine unangenehme Situation, hoffentlich ist es möglich, sie in den frühesten Stadien zu erfassen.


Einerseits ist es möglich, das Skript und die Infrastruktur zu testen, andererseits erhöhen Sie die Codemenge und machen die Infrastruktur komplexer. Der wahre Grund dafür ist jedoch, dass Sie Ihr Wissen über die Infrastruktur auf die Probe stellen. Sie beschreiben, wie die Dinge zusammenarbeiten sollen.


IaC-Testpyramide



IaC-Test: Statische Analyse


Sie können die gesamte Infrastruktur für jedes Commit von Grund auf neu erstellen. In der Regel gibt es jedoch einige Hindernisse:


  • Der Preis ist stratosphärisch.
  • Es braucht viel Zeit.

Hoffentlich gibt es einige Tricks. Sie sollten viele einfache, schnelle und primitive Tests in Ihrer Stiftung haben.


Bash ist schwierig


Schauen wir uns ein äußerst einfaches Beispiel an. Ich möchte ein Backup-Skript erstellen:


  • Holen Sie sich alle Dateien aus dem aktuellen Verzeichnis.
  • Kopieren Sie die Dateien in ein anderes Verzeichnis mit einem geänderten Namen.

Die erste Idee ist:


 for i in * ; do cp $i /some/path/$i.bak done 

Ziemlich gut. Was ist jedoch, wenn der Dateiname Leerzeichen enthält? Wir sind kluge Jungs, wir verwenden Zitate:


 for i in * ; do cp "$i" "/some/path/$i.bak" done 

Sind wir fertig? Nein! Was ist, wenn das Verzeichnis leer ist? Das Globing schlägt in diesem Fall fehl.


 find . -type f -exec mv -v {} dst/{}.bak \; 

Sind wir fertig? Noch nicht ... Wir haben vergessen, dass der Dateiname möglicherweise \n Zeichen enthält.


 touch x mv x "$(printf "foo\nbar")" find . -type f -print0 | xargs -0 mv -t /path/to/target-dir 

Statische Analysewerkzeuge


Sie können einige Probleme aus dem vorherigen Beispiel über Shellcheck abfangen . Es gibt viele solche Tools, die als Linters bezeichnet werden, und Sie können herausfinden, welche für Ihre IDE, Ihren Stack und Ihre Umgebung am besten geeignet sind.


SpracheWerkzeug
BashShellcheck
RubyRubocop
PythonPylint
AnsibleAnsible Flusen

IaC-Tests: Unit-Tests



Wie Sie sehen können, können Linters nicht alles fangen, sondern nur vorhersagen. Wenn wir weiterhin über Parallelen zwischen Softwareentwicklung und Infrastruktur als Code nachdenken, sollten wir Unit-Tests erwähnen. Es gibt viele Unit-Test-Systeme wie Shunit , JUnit , RSpec , Pytest . Aber haben Sie jemals von Unit-Tests für Ansible, Chef, Saltstack, CFengine gehört?


Als wir über SOLID for CFM sprachen, erwähnte ich, dass unsere Infrastruktur aus einfachen Bausteinen / Modulen bestehen sollte. Jetzt ist die Zeit gekommen:


  1. Teilen Sie die Infrastruktur in einfache Module / Pausen auf, d. H. Ansible Rollen.
  2. Erstellen Sie eine Umgebung, z. B. Docker oder VM.
  3. Wenden Sie Ihre einfache Pause / Ihr Modul auf die Umgebung an.
  4. Überprüfen Sie, ob alles in Ordnung ist oder nicht.
    ...
  5. GEWINN!

IaC-Test: Unit-Testing-Tools


Was ist der Test für CFM und Ihre Infrastruktur? Das heißt, Sie können einfach ein Skript ausführen oder eine produktionsbereite Lösung wie die folgende verwenden:


CFMWerkzeug
AnsibleTestinfra
KüchenchefInspec
KüchenchefServerspez
SalzstapelGoss

Werfen wir einen Blick auf testinfra. Ich möchte überprüfen, ob die Benutzer test1 , test2 existieren und Teil der sshusers Gruppe sind:


 def test_default_users(host): users = ['test1', 'test2' ] for login in users: assert host.user(login).exists assert 'sshusers' in host.user(login).groups 

Was ist die beste Lösung? Es gibt keine einheitliche Antwort auf diese Frage. Ich habe jedoch die Heatmap erstellt und die Änderungen in diesen Projekten im Zeitraum 2018-2019 verglichen:



IaC-Test-Frameworks


Danach können Sie sich einer Frage stellen, wie Sie alles zusammen ausführen können. Einerseits können Sie alles selbst erledigen, wenn Sie über genügend großartige Ingenieure verfügen, andererseits können Sie produktionsbereite OpenSource-Lösungen verwenden:


CFMWerkzeug
AnsibleMolekül
KüchenchefKüche testen
TerraformSchrecklichste

Ich habe die Heatmap erstellt und die Änderungen in diesen Projekten im Zeitraum 2018-2019 verglichen:



Molekül vs. Testküche



Am Anfang haben wir versucht, ansible Rollen über testkitchen in hyper-v zu testen :


  1. Erstellen Sie VMs.
  2. Übernehmen Sie Ansible-Rollen.
  3. Führen Sie Inspec aus.

Es dauerte 40-70 Minuten für 25-35 Ansible-Rollen. Es war zu lang für uns.



Der nächste Schritt war die Verwendung von Jenkins / Docker / Ansible / Molekül. Es ist ungefähr die gleiche Idee:


  1. Lint Ansible Spielbücher.
  2. Lint Ansible Rollen.
  3. Führen Sie einen Docker-Container aus.
  4. Übernehmen Sie Ansible-Rollen.
  5. Führen Sie testinfra aus.
  6. Überprüfen Sie die Idempotenz.


Das Fusseln für 40 Rollen und das Testen für zehn von ihnen dauerte ungefähr 15 Minuten.



Was ist die beste Lösung? Einerseits möchte ich nicht die letzte Instanz sein, andererseits möchte ich meinen Standpunkt teilen. Es gibt keine Silberkugel, jedoch ist im Falle von Ansible Molekül eine geeignetere Lösung als Testküche.


IaC-Tests: Integrationstests



Auf der nächsten Ebene der IaC-Testpyramide gibt es Integrationstests . Integrationstests für die Infrastruktur sehen aus wie Komponententests:


  1. Teilen Sie die Infrastruktur in einfache Module / Pausen auf, d. H. Ansible Rollen.
  2. Erstellen Sie eine Umgebung, z. B. Docker oder VM.
  3. Wenden Sie eine Kombination aus einfacher Unterbrechung / Modul auf die Umgebung an.
  4. Überprüfen Sie, ob alles in Ordnung ist oder nicht.
    ...
  5. GEWINN!

Mit anderen Worten, während Unit-Tests überprüfen wir ein einfaches Modul (z. B. Ansible-Rolle, Python-Skript, Ansible-Modul usw.) einer Infrastruktur, aber bei Integrationstests überprüfen wir die gesamte Serverkonfiguration.


IaC-Tests: End-to-End-Tests



Über der IaC-Testpyramide befinden sich End-to-End-Tests . In diesem Fall überprüfen wir nicht den dedizierten Server, das Skript oder das Modul unserer Infrastruktur. Wir überprüfen, ob die gesamte Infrastruktur ordnungsgemäß funktioniert. Leider gibt es dafür keine sofort einsatzbereite Lösung, oder ich habe noch nichts davon gehört (bitte melden Sie mich, wenn Sie davon wissen). Normalerweise erfinden die Leute das Rad neu, weil End-to-End-Tests für die Infrastruktur erforderlich sind. Ich möchte meine Erfahrungen teilen und hoffe, dass sie für jemanden nützlich sind.



Zunächst möchte ich den Kontext beschreiben. Es handelt sich um eine sofort einsatzbereite Unternehmenslösung, die verschiedene Datenbanken, Anwendungsserver und Integrationsschnittstellen mit Systemen von Drittanbietern unterstützt. Normalerweise sind unsere Kunden ein immenses Unternehmen mit einer völlig anderen Umgebung. Wir haben Kenntnisse über verschiedene Umgebungskombinationen und speichern diese als verschiedene Docker-Compose-Dateien. Da dort Docker-Compose-Dateien und Tests übereinstimmen, speichern wir sie auch als Jenkins-Jobs.



Dieses Schema hatte eine ruhige Protokollperiode, in der wir während der OpenShift-Forschung versuchten, es in Openshift zu migrieren. Wir haben ungefähr die gleichen Container verwendet (wieder verdammt trocken) und nur die Umgebung verändert.



Wir recherchieren weiter und haben APB (Ansible Playbook Bundle) gefunden. Die Hauptidee ist, dass Sie alle benötigten Dinge in einen Container packen und den Container in Openshift ausführen. Dies bedeutet, dass Sie eine reproduzierbare und testbare Lösung haben.



Alles war in Ordnung, bis wir vor einem weiteren Problem standen: Wir mussten eine heterogene Infrastruktur für Testumgebungen aufrechterhalten. Infolgedessen speichern wir unser Wissen darüber, wie Infrastrukturen erstellt und Tests in den Jenkins-Jobs ausgeführt werden.


Fazit



Infrastruktur als Code ist eine Kombination aus:


  • Code
  • Menschen Interaktion.
  • Infrastrukturtests.

Source: https://habr.com/ru/post/de467169/


All Articles