Terraformer — Infrastructure To Code

Terraformer — Infrastructure To Code
Ich möchte von einem neuen CLI-Tool erzÀhlen, das ich entwickelt habe, um ein altes Problem zu lösen.

Problem

Terraform ist lÀngst zum Standard in der DevOps-/Cloud-/IT-Community geworden. Es ist sehr praktisch und hilfreich, um Infrastruktur als Code zu verwalten. Es gibt viele Vorteile bei Terraform, aber auch viele Fallstricke, scharfe Messer und Stolpersteine.
Mit Terraform ist es sehr einfach, neue Dinge zu erstellen und diese dann zu verwalten, zu Àndern oder zu löschen. Aber was ist mit denen, die eine riesige Infrastruktur in der Cloud haben, die nicht mit Terraform erstellt wurde? Alles neu zu schreiben und die gesamte Cloud neu zu erstellen, ist ziemlich teuer und riskant.
Ich habe dieses Problem in zwei Jobs erlebt, das einfachste Beispiel ist, wenn man möchte, dass alles in Git in Form von Terraform-Dateien ist, aber man hat ĂŒber 250 Buckets und es ist irgendwie viel, sie manuell fĂŒr Terraform zu schreiben.
Ja issue Seit 2014 gab es Terraform, das 2016 eingestellt wurde, in der Hoffnung, dass eine Importfunktion kommen wĂŒrde.

Kurz gesagt, es ist alles wie auf dem Bild, nur von rechts nach links.

Warnung: Der Autor lebt seit einem halben Leben nicht in Russland und schreibt nicht viel auf Russisch. Vorsicht, Fehler in der Rechtschreibung.

Lösungen

1. Es gibt fertige und alte Lösungen fĂŒr AWS. terraforming. Als ich versuchte, ĂŒber 250 Buckets damit zu erhalten, stellte ich fest, dass es viele Probleme gab. AWS hat seit langem viele neue Optionen hinzugefĂŒgt, von denen terraforming nichts weiß, und außerdem ist es in Ruby geschrieben. Die Templates sehen spĂ€rlich aus.. Nach zwei Abenden habe ich einen Pull-Request gesendet, um mehr Funktionen hinzuzufĂŒgen, und verstand, dass solche Lösungen ĂŒberhaupt nicht geeignet sind.
So funktioniert terraforming: Es nimmt Daten von SDK AWS und generiert tf und tfstate durch Templates.
Hier gibt es drei Probleme:
1. Es wird immer eine Verzögerung bei den Updates geben.
2. Die tf-Dateien sind manchmal beschÀdigt.
3. tfstate wird separat von tf gesammelt und stimmt nicht immer ĂŒberein.
Es ist insgesamt schwierig, ein Ergebnis zu erzielen, bei dem `terraform plan` sagt, dass es keine Änderungen gibt.

2. `terraform import` — ist ein integrierter Befehl in Terraform. Wie funktioniert das?
Man schreibt eine leere TF-Datei mit dem Namen und Typ der Ressource, dann fĂŒhrt man `terraform import` aus und ĂŒbergibt die ID der Ressource. Terraform kontaktiert den Provider, erhĂ€lt die Daten und erstellt die tfstate-Datei.
Hier gibt es drei Probleme:
1. Man bekommt nur die tfstate-Datei, und die tf ist leer, muss manuell geschrieben oder von der tfstate konvertiert werden.
2. Es kann immer nur mit einer Ressource gleichzeitig arbeiten und unterstĂŒtzt nicht alle Ressourcen. Und was soll ich wieder mit ĂŒber 250 Buckets tun?
3. Man muss die ID der Ressourcen kennen - das heißt, man muss es in den Code einwickeln, der die Liste der Ressourcen abruft.
Insgesamt ist das Ergebnis teilweise und nicht gut skalierbar.

Meine Lösung

Anforderungen:
1. Möglichkeit, tf- und tfstate-Dateien basierend auf Ressourcen zu erstellen. Zum Beispiel alle Buckets/Sicherheitsgruppen/Load Balancer herunterzuladen und was `terraform plan` zurĂŒckgibt, dass keine Änderungen vorhanden sind.
2. Zwei Clouds GCP + AWS
3. Globale Lösung, die leicht aktualisiert werden kann, ohne jedes Mal drei Tage fĂŒr jede Ressource zu verschwenden.
4. Open Source machen – jeder hat dieses Problem.

Die Sprache Go – deshalb liebe ich sie, und es gibt eine Bibliothek zur Erstellung von HCL-Dateien, die in Terraform verwendet wird + viel Code in Terraform, der nĂŒtzlich sein kann.

Weg

Erster Versuch
Ich begann mit einer einfachen Variante. Anfragen an die Cloud ĂŒber das SDK nach der benötigten Ressource und deren Umwandlung in Felder fĂŒr Terraform. Der Versuch starb sofort an der Sicherheitsgruppe, weil mir nicht gefiel, dass ich 1,5 Tage benötigte, nur um die Sicherheitsgruppe zu konvertieren (es gibt viele Ressourcen). Es dauert lange und spĂ€ter könnten sich die Felder Ă€ndern/hinzugefĂŒgt werden.

Zweiter Versuch
Basierend auf der beschriebenen Idee. hier. Einfach die tfstate in tf konvertieren. Alle Daten sind dort vorhanden und die Felder sind die gleichen. Wie bekommt man einen vollstĂ€ndigen tfstate fĂŒr viele Ressourcen?? Hier kommt der Befehl `terraform refresh` ins Spiel. Terraform nimmt alle Ressourcen im tfstate und zieht anhand der ID die Daten ab und schreibt alles in den tfstate. Das bedeutet, man erstellt einen leeren tfstate nur mit Namen und ID, startet `terraform refresh` und erhĂ€lt vollstĂ€ndige tfstates. Hurra!
Jetzt kĂŒmmern wir uns um die rekursive Herausforderung, einen Konverter fĂŒr tfstate in tf zu schreiben. FĂŒr diejenigen, die tfstate noch nie gelesen haben: Das ist JSON, aber etwas speziell.
Hier ist der wichtige Teil attributes

 "attributes": {
                            "id": "default/backend-logging-load-deployment",
                            "metadata.#": "1",
                            "metadata.0.annotations.%": "0",
                            "metadata.0.generate_name": "",
                            "metadata.0.generation": "24",
                            "metadata.0.labels.%": "1",
                            "metadata.0.labels.app": "backend-logging",
                            "metadata.0.name": "backend-logging-load-deployment",
                            "metadata.0.namespace": "default",
                            "metadata.0.resource_version": "109317427",
                            "metadata.0.self_link": "\/apis\/apps\/v1\/namespaces\/default\/deployments\/backend-logging-load-deployment",
                            "metadata.0.uid": "300ecda1-4138-11e9-9d5d-42010a8400b5",
                            "spec.#": "1",
                            "spec.0.min_ready_seconds": "0",
                            "spec.0.paused": "false",
                            "spec.0.progress_deadline_seconds": "600",
                            "spec.0.replicas": "1",
                            "spec.0.revision_history_limit": "10",
                            "spec.0.selector.#": "1",

Hier gibt es:
1. id – string
2. metadata – Array der GrĂ¶ĂŸe 1, darin ein Objekt mit den unten beschriebenen Feldern
3. spec – Hash der GrĂ¶ĂŸe 1, darin Key, Value
Kurz gesagt, ein lustiges Format, alles kann auch mehrere Ebenen tief sein.

                   "spec.#": "1",
                            "spec.0.min_ready_seconds": "0",
                            "spec.0.paused": "false",
                            "spec.0.progress_deadline_seconds": "600",
                            "spec.0.replicas": "1",
                            "spec.0.revision_history_limit": "10",
                            "spec.0.selector.#": "1",
                            "spec.0.selector.0.match_expressions.#": "0",
                            "spec.0.selector.0.match_labels.%": "1",
                            "spec.0.selector.0.match_labels.app": "backend-logging-load",
                            "spec.0.strategy.#": "0",
                            "spec.0.template.#": "1",
                            "spec.0.template.0.metadata.#": "1",
                            "spec.0.template.0.metadata.0.annotations.%": "0",
                            "spec.0.template.0.metadata.0.generate_name": "",
                            "spec.0.template.0.metadata.0.generation": "0",
                            "spec.0.template.0.metadata.0.labels.%": "1",
                            "spec.0.template.0.metadata.0.labels.app": "backend-logging-load",
                            "spec.0.template.0.metadata.0.name": "",
                            "spec.0.template.0.metadata.0.namespace": "",
                            "spec.0.template.0.metadata.0.resource_version": "",
                            "spec.0.template.0.metadata.0.self_link": "",
                            "spec.0.template.0.metadata.0.uid": "",
                            "spec.0.template.0.spec.#": "1",
                            "spec.0.template.0.spec.0.active_deadline_seconds": "0",
                            "spec.0.template.0.spec.0.container.#": "1",
                            "spec.0.template.0.spec.0.container.0.args.#": "3",

Im Grunde genommen, wer eine Programmieraufgabe fĂŒr ein VorstellungsgesprĂ€ch möchte, der kann einfach darum bitten, einen Parser dafĂŒr zu schreiben 🙂
Nach vielen Versuchen, einen fehlerfreien Parser zu schreiben, fand ich einen Teil davon im Terraform-Code, und zwar den wichtigsten Teil. Und alles schien normal zu funktionieren.

Versuch drei
Ein Terraform-Provider ist eine BinĂ€rdatei, die Code mit allen Ressourcen und der Logik zur Arbeit mit Cloud-APIs enthĂ€lt. Jede Cloud hat ihren eigenen Provider, und Terraform ruft sie ĂŒber sein RPC-Protokoll zwischen zwei Prozessen auf.
Jetzt habe ich beschlossen, direkt ĂŒber RPC-Aufrufe auf die Terraform-Provider zuzugreifen. Das hat sich schön dargestellt und ermöglicht es, Terraform-Provider auf neuere Versionen zu wechseln und neue Funktionen zu erhalten, ohne den Code zu Ă€ndern. Es stellte sich auch heraus, dass nicht alle Felder im tfstate im tf enthalten sein mĂŒssen, und wie erfĂ€hrt man das? Nur indem man den Provider danach fragt. Danach begann eine weitere Ă€rgerliche Rekursion beim Zusammenstellen von regulĂ€ren AusdrĂŒcken und dem Suchen von Feldern innerhalb des tfstate in alle Ebenen nach innen.

Am Ende entstand ein nĂŒtzliches CLI-Tool, dessen Infrastruktur fĂŒr alle Terraform-Provider gemein ist und man kann leicht neue hinzufĂŒgen. Auch die HinzufĂŒgung von Ressourcen erfordert wenig Code. Außerdem gibt es allerlei zusĂ€tzliche Funktionen wie Verbindungen zwischen Ressourcen. NatĂŒrlich gab es viele verschiedene Probleme, die man nicht alle beschreiben kann.
Ich nannte das Tool Terrafomer.

Finale

Mit Terrafomer haben wir 500-700 Tausend Zeilen Code tf + tfstate in zwei Clouds generiert. Wir konnten Legacy-Dinge ĂŒbernehmen und sie nur ĂŒber Terraform angehen, wie es die besten Ideen der Infrastruktur als Code vorschlagen. Es ist einfach Magie, wenn man eine riesige Cloud nimmt und sie ĂŒber das Kommando in Form von funktionierenden Terraform-Dateien erhĂ€lt. Und dann grep/replace/git und so weiter.

Ich habe alles durchforstet und in Ordnung gebracht, die Genehmigungen eingeholt. Am Donnerstag (02.05.19) auf GitHub veröffentlicht. github.com/GoogleCloudPlatform/terraformer
Ich habe bereits 600 Sterne, 2 Pull-Requests zur UnterstĂŒtzung von OpenStack und Kubernetes erhalten. Gute RĂŒckmeldungen. Das Projekt ist im Allgemeinen nĂŒtzlich fĂŒr die Menschen.
Ich empfehle es jedem, der mit Terraform anfangen möchte und nicht alles dafĂŒr neu schreiben will.
Ich freue mich ĂŒber Pull-Requests, Issues, Sterne.

Demo
Terraformer — Infrastructure To Code

Quelle: habr.com

60GB SSD 8Gb DDR4