
Ich möchte über ein neues CLI-Tool berichten, das ich geschrieben habe, um ein altes Problem zu lösen.
Das Problem
Terraform ist längst zum Standard in der DevOps-, Cloud- und IT-Community geworden. Es ist ein äußerst praktisches und hilfreiches Tool für Infrastructure as Code. Terraform hat viele Vorzüge sowie seine Fallstricke und Herausforderungen.
Mit Terraform lassen sich neue Dinge einfach umsetzen und anschließend verwalten, ändern oder löschen. Aber was ist mit denjenigen, die eine umfangreiche Cloud-Infrastruktur besitzen, die nicht mit Terraform erstellt wurde? Alles neu zu schreiben und die gesamte Cloud neu zu erstellen, ist teuer und riskant.
Ich habe dieses Problem in beiden meiner bisherigen Jobs erlebt. Ein einfaches Beispiel ist, wenn man alles in Git als Terraform-Dateien haben möchte, aber über 250 Buckets hat. Alle diese manuell für Terraform zu erstellen, ist ziemlich aufwendig.
Es gibt Seit 2014 gab es in Terraform eine Funktion, die 2016 eingestellt wurde, in der Hoffnung, dass eine Importfunktion kommen würde.
Im Grunde sieht alles wie auf dem Bild aus, nur von rechts nach links.
Hinweis: Der Autor lebt seit Jahren außerhalb von Russland und schreibt kaum auf Russisch. Seien Sie vorsichtig wegen möglicher Rechtschreibfehler.
Lösungen
1. Es gibt eine bewährte und alte Lösung für AWS. . Als ich versuchte, meine 250+ Buckets über ihn abzurufen, wurde mir klar, dass vieles nicht stimmte. AWS hat seit langem viele neue Optionen hinzugefügt, von denen Terraforming keine Kenntnis hat, und das gesamte System basiert auf Ruby. . Nach 2 Uhr nachmittags habe ich gesendet. , um mehr Funktionen hinzuzufügen, und mir wurde klar, dass dieser Ansatz überhaupt nicht geeignet ist.
Wie funktioniert Terraforming? Es nimmt Daten von der AWS SDK und generiert tf- und tfstate-Dateien über die Vorlage.
Hier gibt es 3 Probleme:
1. Es wird immer eine Verzögerung bei den Updates geben.
2. tf-Dateien sind manchmal beschädigt.
3. tfstate wird separat von tf gesammelt und stimmt nicht immer überein.
Es ist generell 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?
Du schreibst eine leere TF-Datei mit dem Namen und dem Typ der Ressource, dann startest du `terraform import` und übergibst die ID der Ressource. Terraform kontaktiert den Provider, erhält die Daten und erstellt die tfstate-Datei.
Hier gibt es 3 Probleme:
1. Wir erhalten nur die tfstate-Datei, während die tf-Datei leer bleibt und manuell geschrieben oder aus der tfstate konvertiert werden muss.
2. Es kann nur mit einer Ressource gleichzeitig arbeiten und unterstützt nicht alle Ressourcen. Was soll ich also wieder mit 250+ Buckets tun?
3. Man muss die IDs der Ressourcen kennen – das heißt, man muss diese in einen Code einbinden, der die Liste der Ressourcen abruft.
Das Ergebnis ist im Allgemeinen teilweise und lässt sich nicht gut skalieren.
Meine Lösung.
Anforderungen:
1. Möglichkeit, tf- und tfstate-Dateien für die Ressourcen zu erstellen. Zum Beispiel alle Buckets/Sicherheitsgruppen/Lastenausgleichsrampen herunterladen und sicherstellen, dass `terraform plan` anzeigt, dass keine Änderungen vorhanden sind.
2. Zwei Clouds: GCP + AWS.
3. Eine globale Lösung, die sich leicht bei jeder Aktualisierung anpassen lässt und nicht für jede Ressource drei Tage Arbeit kostet.
4. Open Source machen – dieses Problem haben viele.
Sprache Go – ich mag sie, und es gibt eine Bibliothek zur Erstellung von HCL-Dateien, die in Terraform verwendet wird, plus viel nützlichen Code in Terraform.
Weg.
Erster Versuch.
Ich begann mit einer einfachen Variante. Zugriff auf die Cloud über das SDK, um die benötigte Ressource abzurufen und sie in Felder für Terraform zu konvertieren. Der Versuch scheiterte sofort bei der Sicherheitsgruppe, weil ich 1,5 Tage damit verbracht habe, nur die Sicherheitsgruppe zu konvertieren (es gibt viele Ressourcen). Das ist langwierig, und später können die Felder geändert oder hinzugefügt werden.
Zweiter Versuch.
Basierend auf der beschriebenen Idee. . Einfach tfstate in tf konvertieren. Alle Daten sind vorhanden und die Felder sind gleich. Wie erhält man einen vollständigen tfstate für mehrere Ressourcen? Hier hilft der Befehl `terraform refresh`. Terraform entnimmt alle Ressourcen aus dem tfstate und zieht die Daten über die ID und schreibt alles in den tfstate. Das bedeutet, man erstellt einen leeren tfstate nur mit Namen und ID, führt `terraform refresh` aus und erhält einen vollständigen tfstate. Hurra!
Jetzt konzentrieren wir uns auf das rekursive Schreiben eines Konverters von tfstate zu tf. Für diejenigen, die noch nie von tfstate gehört haben, es handelt sich um JSON, aber auf besondere Weise.
Hier ist der wichtige Teil der 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 — Zeichenfolge
2. metadata — Array der Größe 1, das ein Objekt mit den folgenden Feldern enthält
3. spec — Hash der Größe 1, der key,value enthält
Kurz gesagt, ein unterhaltsames Format, das auch mehrere Ebenen tief sein kann
"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" Wenn Sie eine Programmieraufgabe für ein Vorstellungsgespräch wünschen, fragen Sie einfach nach der Erstellung eines Parsers dafür 🙂
Nach zahlreichen Versuchen, einen fehlerfreien Parser zu schreiben, habe ich einen Teil davon im Terraform-Code gefunden, und zwar den entscheidenden Teil. Und scheinbar hat alles gut funktioniert.
Versuch drei
Ein Terraform-Provider ist eine Binärdatei, die den Code mit allen Ressourcen und der Logik zur Arbeit mit den Cloud-APIs enthält. Jede Cloud hat ihren eigenen Provider, und Terraform ruft diese über sein RPC-Protokoll zwischen zwei Prozessen auf.
Jetzt habe ich beschlossen, direkt über RPC-Aufrufe auf die Terraform-Provider zuzugreifen. Das hat sich als elegant herausgestellt und ermöglicht es, Terraform-Provider gegen neuere auszutauschen und neue Funktionen zu erhalten, ohne den Code ändern zu müssen. Außerdem stellte sich heraus, dass nicht alle Felder im tfstate im tf enthalten sein müssen. Aber wie erfährt man das? Man muss nur den Provider danach fragen. Dann begann ein weiteres Rekursionsvergnügen bei der Erstellung von regulären Ausdrücken und der Suche nach Feldern im tfstate auf allen Ebenen.
Am Ende ist ein nützliches CLI-Tool entstanden, das eine gemeinsame Infrastruktur für alle Terraform-Provider bietet und das Hinzufügen neuer Provider einfach macht. Zudem erfordert das Hinzufügen von Ressourcen nur wenig Code. Außerdem gibt es nützliche Features wie Verbindungen zwischen Ressourcen. Natürlich gab es viele verschiedene Probleme, die man nicht alle beschreiben kann.
Ich habe das Tool Terrafomer genannt.
Das Finale
Mit Terrafomer haben wir 500-700.000 Zeilen tf- und tfstate-Code für zwei Clouds generiert. Wir konnten Legacy-Elemente übernehmen und sie nur über Terraform anpacken, ganz im Sinne der besten Ideen von Infrastructure as Code. Es ist einfach Magie, wenn man eine riesige Cloud hat und sie mit einem Befehl in Form von funktionierenden Terraform-Dateien erhält. Und danach geht es mit grep/replace/git und so weiter weiter.
Ich habe die Probleme gelöst und die Berechtigungen geklärt. Am Donnerstag (02.05.19) habe ich es auf GitHub veröffentlicht.
Ich habe bereits 600 Sterne erhalten, 2 Pull-Requests zur Unterstützung von OpenStack und Kubernetes. Gute Rückmeldungen. Das Projekt ist insgesamt nützlich für die Leute.
Ich empfehle es allen, die mit Terraform anfangen wollen, ohne alles neu zu schreiben.
Über Pull-Requests, Issues und Sterne würde ich mich freuen.
Demo
Quelle: habr.com
