
Es scheint, dass die Terraform-Entwickler recht praktische Best Practices für die Arbeit mit AWS-Infrastruktur anbieten. Es gibt jedoch einen Haken. Mit der Zeit steigt die Anzahl der Umgebungen, und jede hat ihre eigenen Besonderheiten. Es entsteht quasi eine Kopie des Anwendungsstacks in einer benachbarten Region. Der Terraform-Code muss sorgfältig kopiert und an die neuen Anforderungen angepasst werden oder es muss eine Schneeflocke erstellt werden.
Mein Vortrag handelt von Mustern in Terraform zur Bekämpfung von Chaos und manuellen Routinen in großen und langwierigen Projekten.
Video:

Ich bin 40 Jahre alt und arbeite seit 20 Jahren in der IT. Seit 12 Jahren bin ich bei der Firma Ixtens tätig. Wir beschäftigen uns mit E-Commerce-gesteuerter Entwicklung. Und seit 5 Jahren praktiziere ich DevOps-Praktiken.

Meine Erzählung wird von meinen Erfahrungen in einem Projekt bei einer Firma handeln, deren Name ich aufgrund einer Geheimhaltungsvereinbarung nicht nennen werde.
Die Zahlen auf der Folie sind zur Veranschaulichung des Umfangs des Projekts angegeben. Alles, was ich weiter sagen werde, steht im Zusammenhang mit Amazon.

Ich habe mich vor 4 Jahren diesem Projekt angeschlossen. Zu diesem Zeitpunkt befand sich die Infrastruktur im vollständigen Refactoring, weil das Projekt gewachsen war. Die Muster, die verwendet wurden, waren nicht mehr geeignet. Angesichts des geplanten Wachstums des Projekts musste etwas Neues erfunden werden.
Danke an Matvey, der mir gestern erzählt hat, was bei Dodo Pizza passiert ist. Das war das, was bei uns vor 4 Jahren passiert ist.
Die Entwickler kamen und begannen, Infrastruktur-Code zu schreiben.
Die offensichtlichsten Gründe, warum dies nötig war, sind die Time-to-Market. Es musste sichergestellt werden, dass das DevOps-Team nicht der Engpass bei der Bereitstellung war. Außerdem wurden auf der ersten Ebene Terraform und Puppet eingesetzt.

Terraform ist ein Open-Source-Projekt von HashiCorp. Für diejenigen, die überhaupt nicht wissen, was das ist, die nächsten Folien.

Infrastruktur als Code bedeutet, dass wir unsere Infrastruktur beschreiben und einige Roboter bitten können, die Ressourcen bereitzustellen, die wir beschrieben haben.
Zum Beispiel brauchen wir eine virtuelle Maschine. Wir beschreiben es und fügen einige obligatorische Parameter hinzu.

Danach richten wir in der Konsole den Zugriff auf Amazon ein. Und wir bitten Terraform um einen Plan. Terraform wird sagen: „Okay, für Ihre Ressource können wir folgende Dinge tun“. Mindestens eine Ressource wird hinzugefügt, und es sind keine Änderungen vorgesehen.

Sobald Sie mit allem zufrieden sind, können Sie Terraform anweisen, die Änderungen anzuwenden, und Terraform erstellt Ihnen eine Instanz, und Sie erhalten eine virtuelle Maschine in Ihrer Cloud.

Dann entwickelt sich unser Projekt weiter. Wir fügen dort einige Änderungen hinzu. Wir bitten um mehr Instances, wir fügen 53 Einträge hinzu.

Und wir wiederholen. Wir bitten um einen Plan. Wir sehen, welche Änderungen geplant sind. Wir wenden sie an. So wächst unsere Infrastruktur.
Terraform verwendet so etwas wie State-Dateien. Das heißt, alle Änderungen, die an Amazon gesendet werden, werden in einer Datei gespeichert, in der für jede Ressource, die Sie beschrieben haben, die entsprechenden Ressourcen enthalten sind, die in Amazon erstellt wurden. Wenn sich also die Beschreibung einer Ressource ändert, weiß Terraform genau, was in Amazon geändert werden muss.

Diese State-Dateien waren ursprünglich einfach Dateien. Und wir haben sie in Git gespeichert, was äußerst unpraktisch war. Jemand hat ständig vergessen, die Änderungen zu committen, und es traten viele Konflikte auf.
Jetzt gibt es die Möglichkeit, ein Backend zu verwenden, d. h. Terraform wird angegeben, in welchem Bucket und unter welchem Schlüssel die State-Datei gespeichert werden soll. Terraform kümmert sich selbst darum, diese State-Datei abzurufen, die gesamte Magie durchzuführen und das Endergebnis zurückzulegen.

Unsere Infrastruktur wächst. Hier ist unser Code. Und wir möchten jetzt nicht einfach eine virtuelle Maschine erstellen, wir wollen eine Testumgebung haben.

Terraform ermöglicht das Erstellen eines Moduls, d. h. dasselbe in einem bestimmten Ordner zu beschreiben.

Und zum Beispiel im Testen dieses Modul aufzurufen und dasselbe zu erhalten, als ob wir Terraform apply im Modul selbst ausgeführt hätten. Für den Test sieht der Code so aus.

Für die Produktion können wir dort einige Änderungen senden, da wir im Test keine großen Instances benötigen, in der Produktion sind große Instances jedoch wirklich nützlich.

Und dann werde ich zurück zum Projekt kommen. Es gab eine komplexe Aufgabe, die Infrastruktur war als sehr groß geplant. Und es musste irgendwie der gesamte Code organisiert werden, damit es für alle bequem ist: sowohl für diejenigen, die Wartung an diesem Code durchführen, als auch für diejenigen, die Änderungen vornehmen. Es war geplant, dass jeder Entwickler hingehen und die Infrastruktur so anpassen kann, wie es für seinen Teil der Plattform notwendig ist.
Das ist der Ordnerbaum, der von HashiCorp selbst empfohlen wird, wenn Sie ein großes Projekt haben und es sinnvoll ist, die gesamte Infrastruktur in einige kleine Stücke zu teilen und jedes Stück in einem separaten Ordner zu beschreiben.
Mit einer umfangreichen Bibliothek von Ressourcen kann man im Test und in der Produktion ungefähr dasselbe aufrufen.

In unserem Fall passte das nicht so ganz, da der Test-Stack für Entwickler oder für Tests einfacher beschafft werden musste. Es war nicht angenehm, durch Ordner zu suchen und sie in der richtigen Reihenfolge anzuwenden und sich Sorgen zu machen, dass die Datenbank hochfährt und dann das Instance, das diese Datenbank nutzt. Daher wurde das gesamte Testing aus einem einzigen Ordner gestartet. Dort wurden dieselben Module aufgerufen, aber alles lief in einem Durchgang ab.
Terraform kümmert sich um alle Abhängigkeiten. Und es erstellt die Ressourcen immer in der Reihenfolge, sodass man beispielsweise die IP-Adresse von einem neu erstellten Instance erhalten kann und diese IP-Adresse in einer route53-Aufzeichnung erhält.
Außerdem ist die Plattform sehr groß. Und das Starten eines Test-Stacks, selbst für eine Stunde oder acht Stunden – das ist eine ziemlich kostspielige Angelegenheit.
Und wir haben das automatisiert. Der Jenkins-Job ermöglichte es, den Stack zu starten. Man musste einen Pull-Request mit den Änderungen starten, die der Entwickler testen wollte, sowie alle benötigten Optionen, Komponenten und Größen angeben. Wenn er Performance-Tests möchte, kann er mehr Instances nehmen. Wenn er nur überprüfen möchte, ob ein bestimmtes Formular geöffnet wird, konnte er mit den Minimalanforderungen starten. Außerdem konnte man angeben, ob ein Cluster benötigt wird oder nicht, usw.
Und dann pushte Jenkins ein Shell-Skript, das den Code im Terraform-Ordner ein wenig modifizierte. Es entfernte unnötige Dateien und fügte benötigte Dateien hinzu. Und dann wurde der Stack in einem Durchgang mit Terraform apply hochgefahren.
Danach folgten weitere Schritte, in die ich nicht näher einsteigen möchte.

Da wir für das Testing etwas mehr Optionen benötigten als in der Produktion, mussten wir Kopien der Module erstellen, damit wir in diesen Kopien die Funktionen hinzufügen konnten, die nur für das Testing gebraucht wurden.
So kam es, dass man im Testing gerne die Änderungen testen möchte, die letztlich in die Produktion gehen. Aber tatsächlich wurde etwas anderes getestet, und in der Produktion wurde etwas anderes angewendet. Es gab eine kleine Diskrepanz, da in der Produktion alle Änderungen vom Operationsteam umgesetzt wurden. Manchmal kam es vor, dass die Änderungen, die aus dem Testing in die Produktion übergehen sollten, in einer anderen Version blieben.
Darüber hinaus gab es das Problem, dass ein neuer Service hinzugefügt wurde, der sich ein wenig von einem bereits bestehenden unterschied. Anstatt das bestehende Modul zu modifizieren, war es notwendig, eine Kopie davon zu erstellen und die erforderlichen Änderungen vorzunehmen.
Im Grunde genommen ist Terraform keine echte Sprache. Es ist eine Deklaration. Wenn wir etwas deklarieren müssen, dann tun wir das. Und alles funktioniert.
In einem Moment, als einer meiner Pull-Requests besprochen wurde, sagte ein Kollege, dass man keine Schneeflocken erzeugen sollte. Ich war neugierig, was er damit meinte. Es gibt eine wissenschaftliche Tatsache, dass es auf der Welt keine zwei identischen Schneeflocken gibt, sie unterscheiden sich immer ein wenig. Und als ich das hörte, spürte ich sofort das Gewicht des Terraform-Codes. Denn wenn es erforderlich war, von einer Version auf die nächste zu wechseln, verlangte Terraform eine breaking chain Änderung, d. h. der Code war nicht mehr mit der nächsten Version kompatibel. Dadurch musste ein Pull-Request erstellt werden, der fast die Hälfte der Dateien in der Infrastruktur abdeckte, um die Infrastruktur auf die nächste Version von Terraform zu bringen.
Und nachdem so eine Schneeflocke entstanden war, verwandelte sich der gesamte Terraform-Code, den wir hatten, in einen riesigen Schneehaufen.
Für einen externen Entwickler, der außerhalb des Betriebs arbeitet, spielt das keine große Rolle, weil er einen Pull-Request erstellt hat, sein Ressourcen gestartet wurde. Und das war's, weiter ist es nicht seine Sorge. Das DevOps-Team, das darauf achtet, dass alles in Ordnung ist, muss all diese Änderungen vornehmen. Und die Kosten für diese Änderungen stiegen mit jeder zusätzlichen Schneeflocke erheblich.

Es gibt eine Geschichte über einen Studenten, der in einem Seminar mit Kreide zwei perfekte Kreise an die Tafel malt. Der Dozent ist erstaunt, wie es ihm gelungen ist, so gleichmäßig ohne Zirkel zu zeichnen. Der Student antwortet: "Ganz einfach, ich habe zwei Jahre lang in der Armee eine Fleischmaschine gedreht."
In den vier Jahren, in denen ich an diesem Projekt beteiligt bin, beschäftige ich mich etwa zwei Jahre mit Terraform. Und natürlich habe ich einige Tricks, einige Ratschläge, wie man Terraform-Code vereinfachen, mit ihm wie mit einer Programmiersprache arbeiten und die Belastung der Entwickler reduzieren kann, die diesen Code aktuell halten müssen.

Zuerst möchte ich mit Symlinks beginnen. Terraform hat viel sich wiederholenden Code. Zum Beispiel wird der Provider an fast jedem Punkt aufgerufen, an dem wir ein Stück Infrastruktur erstellen. Es wäre logisch, ihn in einen separaten Ordner auszulagern. Und überall dort, wo der Provider benötigt wird, sollten Symlinks auf diese Datei gemacht werden.

Zum Beispiel verwenden Sie in der Produktion eine Assume-Rolle, die es Ihnen ermöglicht, Zugriffsrechte für ein externes Amazon-Konto zu erhalten. Indem Sie eine Datei ändern, haben alle anderen im Ressourcenbaum die erforderlichen Berechtigungen, damit Terraform weiß, auf welchen Amazon-Segment zuzugreifen ist.

Wo funktionieren Symlinks nicht? Wie bereits erwähnt, gibt es in Terraform Statusdateien. Und diese sind wirklich sehr gut. Das Problem ist, dass Terraform das Backend beim ersten Mal initialisiert. Und er kann in diesen Parametern keine Variablen verwenden, diese müssen immer schriftlich angegeben werden.
Das Ergebnis ist, dass, wenn jemand eine neue Ressource erstellt, er einen Teil des Codes aus anderen Ordnern kopiert. Und er könnte einen Fehler mit dem Schlüssel oder dem Bucket machen. Zum Beispiel erstellt er etwas im Sandbox-Modus und dann in der Produktion. So könnte es passieren, dass der Bucket in der Produktion aus der Sandbox verwendet wird. Natürlich wird das schnell gefunden. Es könnte irgendwie behoben werden, aber dennoch ist das Zeitverschwendung und in gewissem Maße Ressourcenverlust.

Was können wir als Nächstes tun? Bevor Sie mit Terraform arbeiten, müssen Sie es initialisieren. Während der Initialisierung lädt Terraform alle Plugins herunter. Diese wurden irgendwann aus einem Monolithen in eine mikroserviceorientierte Architektur zerlegt. Und es ist immer erforderlich, Terraform init auszuführen, damit es alle Module und Plugins abruft.
Und Sie können ein Shell-Skript verwenden, das erstens alle Variablen abrufen kann. Ein Shell-Skript ist in nichts beschränkt. Und zweitens die Pfade. Wenn wir immer den Pfad verwenden, der im Repository als Schlüssel zur Statusdatei fungiert, wird ein Fehler in diesem Fall ausgeschlossen.

Woher kommen die Daten? Aus einer JSON-Datei. Terraform ermöglicht es, Infrastruktur nicht nur in hcl (HashiCorp Configuration Language), sondern auch in JSON zu beschreiben.
JSON lässt sich leicht aus einem Shell-Skript lesen. Dementsprechend können Sie an einem Ort eine Konfigurationsdatei mit einem Bucket ablegen. Und diesen Bucket sowohl im Terraform-Code als auch im Shell-Skript zur Initialisierung verwenden.

Warum ist es wichtig, einen Bucket für Terraform zu haben? Weil es so etwas wie Remote State-Dateien gibt. Das heißt, wenn ich eine Ressource bereitstelle, muss ich Amazon sagen: "Bitte stelle eine Instanz bereit", und dafür muss ich viele erforderliche Parameter angeben.
Diese Identifikatoren werden in einem anderen Ordner gespeichert. Ich kann also sagen: "Terraform, gehe bitte in die State-Datei dieser Ressource und hol mir diese Identifikatoren zurück." So entsteht eine gewisse Einheitlichkeit zwischen verschiedenen Regionen oder Umgebungen.
Es ist nicht immer möglich, eine Remote State-Datei zu verwenden. Zum Beispiel, wenn Sie manuell ein VPC erstellt haben. Der Terraform-Code, der das VPC erstellt, kann so unterschiedlich sein, dass Sie sehr lange brauchen werden, um ihn an das andere anzupassen. Daher gibt es einen Trick.

Das heißt, machen Sie ein Modul, das wie ein VPC funktioniert und Ihnen die Identifikatoren gibt, aber eigentlich gibt es nur eine Datei mit fest kodierten Werten, die zur Erstellung derselben Instanz verwendet werden kann.

Es ist nicht immer erforderlich, die State-Datei in der Cloud zu speichern. Wenn Sie Module testen, können Sie eine Backend-Initialisierung verwenden, bei der die Datei einfach auf der Festplatte während des Tests gespeichert wird.

Jetzt ein bisschen über das Testen. Was kann man in Terraform testen? Wahrscheinlich kann man viel testen, aber ich werde über diese vier Punkte sprechen.
HashiCorp hat ein Verständnis dafür, wie der Terraform-Code formatiert werden sollte. Und Terraform fmt ermöglicht es Ihnen, den Code, den Sie bearbeiten, gemäß dieser Auffassung zu formatieren. Daher sollten die Tests unbedingt überprüfen, ob die Formatierung dem entspricht, was HashiCorp hinterlassen hat, damit Sie nicht die Position der Klammern ändern müssen.

Der nächste Punkt ist Terraform validate. Es macht ein bisschen mehr als nur eine Syntaxprüfung – etwa, ob alle Klammern passend sind. Was ist hier wichtig? Unsere Infrastruktur ist sehr weit verbreitet. Es gibt viele verschiedene Ordner. In jedem dieser Ordner muss Terraform validate ausgeführt werden.
Um das Testen zu beschleunigen, führen wir mehrere Prozesse parallel aus, indem wir Parallelität nutzen.
Parallelität ist eine großartige Funktion, nutzen Sie sie.
Aber jedes Mal, wenn die Initialisierung von Terraform erfolgt, geht er zu HashiCorp und fragt: „Welche sind die neuesten Versionen der Plugins? Ist das Plugin, das ich im Cache habe, das richtige oder nicht?“. Und das hat bei jedem Schritt seine Verzögerung mit sich gebracht.

Wenn Terraform gesagt wird, wo die Plugins liegen, dann sagt Terraform: „Okay, das ist wahrscheinlich das Neueste, was es gibt. Ich werde nicht woanders hingehen, ich werde sofort mit der Validierung Ihres Terraform-Codes beginnen.“

Um den benötigten Plugins-Ordner zu füllen, haben wir einen sehr einfachen Terraform-Code, den man einfach initialisieren muss. Hier müssen natürlich alle Provider angegeben werden, die irgendwie in Ihrem Code verwendet werden, sonst sagt Terraform: „Ich kenne diesen Provider nicht, weil er nicht im Cache ist.“

Der nächste Schritt ist Terraform Plan. Wie bereits erwähnt, ist die Entwicklung zyklisch. Wir erstellen Code mit Änderungen. Und dann muss man herausfinden, welche Änderungen an der Infrastruktur geplant sind.
Und wenn die Infrastruktur sehr, sehr groß ist, kann man ein Modul ändern, eine Testumgebung reparieren oder eine spezifische Region beeinflussen und damit etwas Nachbarliches zerstören. Daher sollte Terraform Plan für die gesamte Infrastruktur durchgeführt werden und zeigen, welche Änderungen geplant sind.
Das kann man schlau machen. Wir haben zum Beispiel ein Skript in Python geschrieben, das Abhängigkeiten auflöst. Je nachdem, was geändert wurde: Terraform-Modul oder einfach eine spezifische Komponente, macht es Pläne für alle abhängigen Ordner.
Terraform Plan sollte auf Anfrage durchgeführt werden. Zumindest ist das, was wir tun.
Tests sind natürlich gut für jede Änderung, jeden Commit zu machen, aber Pläne sind eine recht kostspielige Angelegenheit. Und wir sagen in Pull Requests: „Bitte gib mir die Pläne.“ Ein Roboter wird aktiviert. Und er schickt in die Kommentare oder im Anhang alle Pläne, die sich aus Ihrer Änderung erwarten lassen.
Der Plan ist eine ziemlich teure Angelegenheit. Er benötigt Zeit, weil Terraform zu Amazon geht und fragt: „Existiert dieser Instance noch? Hat dieser Autoscale genau diese Parameter?“ Und um das zu beschleunigen, kann man einen Parameter wie refresh=false verwenden. Das bedeutet, dass Terraform den State aus S3 herunterladen wird. Und es wird glauben, dass der State genau mit dem in Amazon übereinstimmt.
Ein solcher Terraform-Plan läuft deutlich schneller ab, aber der Zustand muss Ihrer Infrastruktur entsprechen, d. h. irgendwann muss ein Terraform-Refresh durchgeführt werden. Terraform-Refresh sorgt dafür, dass der Zustand dem entspricht, was sich in der realen Infrastruktur befindet.
Und ich muss über die Sicherheit sprechen. Damit hätte man anfangen müssen. Dort, wo Sie Terraform ausführen und Terraform mit Ihrer Infrastruktur arbeitet, gibt es eine Sicherheitsanfälligkeit. Das heißt, Sie führen im Grunde genommen Code aus. Und wenn der Pull-Request schadhafter Code enthält, kann dieser in der Infrastruktur ausgeführt werden, die zu viel Zugriff hat. Deshalb seien Sie vorsichtig, wo Sie den Terraform-Plan ausführen.

Als Nächstes möchte ich über das Testen von User-Data sprechen.
Was ist User-Data? Bei Amazon, wenn wir eine Instanz erstellen, können wir von der Instanz eine Art Mitteilung senden – Metadaten. Wenn die Instanz gestartet wird, ist cloud-init normalerweise immer auf diesen Instanzen vorhanden. Cloud-init liest diese Mitteilung und sagt: „Okay, heute bin ich ein Lastenausgleich“. Und in Übereinstimmung mit diesen Vorgaben führt es bestimmte Aktionen aus.

Aber leider sieht User-Data, wenn wir Terraform-Plan und Terraform-Anwendung durchführen, wie ein solcher Brei aus Zahlen aus. Das heißt, es schickt Ihnen einfach einen Hash. Und das Einzige, was Sie im Plan sehen können, ist, ob es irgendwelche Änderungen gibt oder ob der Hash gleich bleibt.
Und wenn man dem keine Beachtung schenkt, kann auf Amazon ein kleiner beschädigter Textdatei in die echte Infrastruktur gelangen.

Als Option kann man bei der Ausführung angeben, nicht die gesamte Infrastruktur, sondern nur das Template. Und im Code sagen: „Bitte zeige mir dieses Template an“. So kann man einen Ausdruck erhalten, wie Ihre Daten auf Amazon aussehen werden.

Eine andere Möglichkeit besteht darin, ein Modul zur Generierung von User-Data zu verwenden. Sie setzen dieses Modul ein, erhalten eine Datei auf der Festplatte und vergleichen sie mit dem Referenzdokument. Und so, wenn irgendein Junior beschließt, ein bisschen User-Data zu ändern, werden Ihre Tests sagen: „Okay, hier und hier gibt es einige Änderungen – das ist in Ordnung“.

Als Nächstes möchte ich über die Automatisierung von Terraform-Anwendungen sprechen.
Natürlich ist es ziemlich beängstigend, Terraform-Anwendungen automatisch auszuführen, denn wer weiß schon, welche Änderungen dort vorgenommen wurden und wie verheerend sie für die Live-Infrastruktur sein können.
Für die Testumgebung ist das alles in Ordnung. Das heißt, der Job, der die Testumgebung erstellt, ist das, was alle Entwickler brauchen. Und solche Ausdrücke wie „bei mir hat alles funktioniert“ sind kein witziges Meme, sondern der Beweis, dass der Mensch sich damit beschäftigt hat, den Stack hochgefahren hat, irgendwelche Tests auf diesem Stack ausgeführt hat und sich vergewissert hat, dass alles in Ordnung ist, und gesagt hat: „Okay, der Code, den ich veröffentlichte, wurde getestet.“
In Produktions-, Sandbox- und anderen Umgebungen, die für das Geschäft wichtiger sind, kann man teilweise einige Ressourcen ausreichend sicher anwenden, da dies nicht dazu führt, dass jemand stirbt. Dazu gehören: Autoscale-Gruppen, Sicherheitsgruppen, Rollen, Route53, und die Liste kann ziemlich lang sein. Aber achten Sie darauf, was passiert, lesen Sie Berichte über automatische Anwendungen.
Dort, wo die Anwendung gefährlich oder riskant ist, zum Beispiel wenn es um persistente Ressourcen aus Datenbanken geht, erhalten Sie Berichte darüber, dass in einem bestimmten Teil der Infrastruktur nicht angewendete Änderungen vorliegen. Und der Ingenieur startet unter Aufsicht Jobs, um diese anzuwenden, oder führt dies über seine Konsole durch.
Bei Amazon gibt es so etwas wie Terminate Protection. Und es kann in einigen Fällen vor unerwünschten Änderungen schützen. Das heißt, Terraform geht zu Amazon und sagt: „Ich muss diese Instanz töten, um eine andere zu machen.“ Und Amazon sagt: „Entschuldigung, nicht heute. Wir haben die Terminate Protection aktiviert.“

Und das Sahnehäubchen ist die Code-Optimierung. Wenn wir mit Terraform-Code arbeiten, müssen wir in das Modul eine sehr große Anzahl von Parametern übergeben. Das sind die Parameter, die erforderlich sind, um eine Ressource zu erstellen. Und der Code verwandelt sich in große Listen von Parametern, die von Modul zu Modul, von Modul zu Modul übergeben werden müssen, besonders wenn die Module verschachtelt sind.
Und das ist sehr schwer lesbar. Es ist sehr schwierig, eine Überprüfung durchzuführen. Und es kommt sehr oft vor, dass einige Parameter die Überprüfung bestehen und sie nicht ganz die sind, die benötigt werden. Das kostet Zeit und Geld, um sie später zu korrigieren.

Deshalb empfehle ich Ihnen, so etwas wie einen komplexen Parameter zu verwenden, der eine Art Baum von Werten enthält. Das heißt, Sie benötigen einen bestimmten Ordner, in dem alle Werte angegeben sind, die Sie in einer bestimmten Umgebung haben möchten.

Durch den Aufruf dieses Moduls kann man einen Baum erhalten, der in einem gemeinsamen Modul generiert wird, d. h. in einem Modul, das für die gesamte Infrastruktur gleich funktioniert.
In diesem Modul können einige Berechnungen angestellt werden, indem man eine so frische Funktion in Terraform wie locals nutzt. Und dann kann man mit einem Output einen komplexen Parameter ausgeben, der beispielsweise Hashes, Arrays usw. enthalten kann.

Das sind alle besten Erkenntnisse, die ich habe. Ich möchte eine Anekdote über Kolumbus erzählen. Als er nach Geldern für seine Expedition suchte, um Indien zu entdecken (so glaubte er damals), glaubte ihm niemand und man hielt es für unmöglich. Dann sagte er: 'Macht es so, dass das Ei nicht fällt.' Alle Banker, sehr reiche und wahrscheinlich kluge Menschen, versuchten auf irgendeine Weise, das Ei zu stellen, aber es fiel immer wieder. Dann nahm Kolumbus das Ei, drückte ein wenig darauf. Die Schale ließ nach und das Ei blieb stehen. Sie sagten: 'Oh, das ist viel zu einfach!' Und Kolumbus antwortete: 'Ja, das ist viel zu einfach. Und wenn ich Indien entdecke, werden alle diesen Handelsweg nutzen.'
Das, was ich Ihnen jetzt erzählt habe, sind wahrscheinlich ziemlich einfache und triviale Dinge. Und wenn man davon erfährt und beginnt, sie anzuwenden, ist das in Ordnung. Also nutzen Sie es. Und wenn das für Sie ganz normale Dinge sind, wissen Sie zumindest, wie man ein Ei stellt, damit es nicht fällt.

Lassen Sie uns zusammenfassen:
- Versuchen Sie, Schneeflocken zu vermeiden. Je weniger Schneeflocken, desto weniger Ressourcen benötigen Sie, um Änderungen in Ihrer gesamten großen Infrastruktur vorzunehmen.
- Ständige Änderungen. Das heißt, wenn es im Code Änderungen gegeben hat, müssen Sie Ihre Infrastruktur so schnell wie möglich an diese Änderungen anpassen. Es sollte nicht vorkommen, dass jemand nach zwei oder drei Monaten kommt, um sich Elasticsearch anzusehen, einen Terraform-Plan macht und dabei mit vielen Änderungen konfrontiert wird, die er nicht erwartet hat. Dabei wird viel Zeit darauf verwendet, alles wieder in Ordnung zu bringen.
- Tests und Automatisierung. Je mehr Ihr Code durch Tests und Features abgedeckt ist, desto mehr Vertrauen haben Sie in das, was Sie richtig machen. Und die automatisierte Bereitstellung wird Ihr Vertrauen erheblich steigern.
- Der Code für die Test- und Produktionsumgebung sollte praktisch identisch sein. Praktisch, weil die Produktionsumgebung dennoch etwas anders ist und es dort einige Nuancen geben wird, die über die Testumgebung hinausgehen. Dennoch kann man das plus-minus sicherstellen.
- Und wenn Sie sehr viel Terraform-Code haben und die Pflege dieses Codes sehr viel Zeit in Anspruch nimmt, ist es nie zu spät, ein Refactoring durchzuführen und ihn in eine gute Form zu bringen.

- Immutable Infrastructure. Lieferung von AMI nach Zeitplan.
- Struktur für Route53, wenn Sie sehr viele Einträge haben und möchten, dass sie in einer konsistenten Reihenfolge sind.
- Bekämpfung von API-Rate-Limits. Das passiert, wenn Amazon sagt: „Das ist alles, ich kann keine Anfragen mehr annehmen, bitte warten Sie.“ Und die Hälfte der Firma wartet, bis sie ihre Infrastruktur starten kann.
- Spot-Instanzen. Amazon ist kein günstiges Unternehmen und Spots ermöglichen es, erheblich zu sparen. Darüber könnte man einen ganzen Vortrag halten.
- Sicherheit und IAM-Rollen.
- Suche nach verlorenen Ressourcen, wenn Sie in Amazon Instanzen unbekannter Herkunft haben, die Geld kosten. Selbst wenn eine Instanz 100-150 Dollar im Monat kostet, summiert sich das über ein Jahr auf mehr als 1.000 Dollar. Die Suche nach solchen Ressourcen ist ein profitables Geschäft.
- Und reservierte Instanzen.

Das ist alles von meiner Seite. Terraform ist wirklich großartig, nutzen Sie es. Danke!
Fragen
Danke für den Vortrag! Ihre State-Datei liegt in S3, aber wie gehen Sie mit dem Problem um, dass mehrere Personen diese State-Datei nehmen und versuchen, sich bereitzustellen?
Erstens, wir hetzen nicht. Zweitens gibt es Flags, mit denen wir mitteilen, dass wir an einem bestimmten Abschnitt des Codes arbeiten. Das heißt, obwohl die Infrastruktur sehr groß ist, bedeutet das nicht, dass ständig jemand etwas anwendet. Und als die aktive Phase war, war das ein Problem; unsere State-Dateien wurden in Git gespeichert. Das war wichtig, denn sonst hätte jemand eine State-Datei erstellt, und wir hätten sie manuell zusammenstellen müssen, damit alles weitergehen kann. Dieses Problem gibt es jetzt nicht mehr. Insgesamt hat Terraform dieses Problem gelöst. Und wenn sich ständig etwas ändert, können Sie Locks verwenden, die verhindern, was Sie gesagt haben.
Verwenden Sie die Open-Version oder Enterprise?
Kein Enterprise, d.h. alles, was man herunterladen kann, ist kostenlos.
Ich heiße Stanislav. Ich wollte eine kleine Ergänzung machen. Sie haben über die Amazon-Funktion gesprochen, die es ermöglicht, eine Instanz unzerstörbar zu machen. Das gibt es auch in Terraform, im Block Life Second kann man ein Verbot zur Änderung oder zur Zerstörung festlegen.
Ich war zeitlich begrenzt. Gute Anmerkung.
Ich wollte noch zwei Dinge fragen. Erstens, Sie haben über Tests gesprochen. Haben Sie irgendwelche Tools zum Testen verwendet? Ich habe von dem Plugin Test Kitchen gehört. Vielleicht gibt es noch etwas anderes. Und ich wollte auch nach Local Values fragen. Was unterscheidet sie prinzipiell von Input Variables? Und warum kann ich nicht etwas nur über Local Values parametrieren? Ich habe versucht, mich mit diesem Thema auseinanderzusetzen, aber irgendwie nicht verstanden.
Wir können ausführlicher nach diesem Raum sprechen. Die Tools für Tests – das ist unser eigener Kram. Es gibt nichts, was getestet werden könnte. Tatsächlich gibt es Optionen, wenn automatische Tests Infrastruktur irgendwo hochfahren, überprüfen, dass sie in Ordnung ist, und dann alles mit einem Bericht löschen, dass Ihre Infrastruktur weiterhin in gutem Zustand ist. Das haben wir nicht, weil die Test-Stacks jeden Tag hochgefahren werden. Das reicht aus. Und wenn etwas zu brechen beginnt, dann beginnt es zu brechen, ohne dass wir das noch woanders überprüfen.
Zum Thema Local Values lassen Sie uns das Gespräch außerhalb des Raumes fortsetzen.
Hallo! Danke für den Vortrag! Sehr lehrreich. Du hast gesagt, dass ihr sehr viel einheitlichen Code zur Beschreibung der Infrastruktur habt. Habt ihr nicht in Erwägung gezogen, diesen Code zu generieren?
Tolle Frage, danke! Das Problem ist, dass wir, wenn wir Infrastruktur als Code verwenden, annehmen, dass wir den Code betrachten und verstehen, welche Infrastruktur hinter diesem Code steckt. Wenn der Code generiert wird, müssen wir uns vorstellen, welcher Code generiert wird, um zu verstehen, welche Infrastruktur es geben wird. Oder wir generieren den Code, committen ihn und es ist im Grunde dasselbe. Daher sind wir den Weg gegangen, den wir geschrieben haben, und so haben wir es bekommen. Außerdem sind Generatoren etwas später aufgetaucht, als wir angefangen haben. Und es war bereits zu spät, um etwas zu ändern.
Hast du schon einmal etwas über Jsonnet gehört?
Nein.
Schau mal, das ist eine echt coole Sache. Ich sehe einen konkreten Anwendungsfall, wo man es anwenden und Datenstrukturen generieren kann.
Generatoren sind gut, wie im Witz über einen Rasierer. Das heißt, beim ersten Mal sieht man unterschiedlich aus, aber danach haben alle das gleiche Gesicht. Generatoren sind echt klasse. Aber leider haben wir ein etwas anderes Gesicht. Das ist ein Problem.
Schau einfach mal. Danke!
Ich heiße Maxim, ich komme von der Sberbank. Sie haben ein wenig erzählt, dass Sie versucht haben, Terraform an eine Programmiersprache anzupassen. Ist es nicht einfacher, Ansible zu verwenden?
Das sind sehr unterschiedliche Dinge. Man kann mit Ansible Ressourcen erstellen und mit Puppet kann man Ressourcen in Amazon erstellen. Aber Terraform ist speziell dafür konzipiert.
Haben Sie nur Amazon?
Es geht nicht darum, dass wir nur Amazon haben. Fast ausschließlich Amazon. Aber das Hauptmerkmal ist, dass Terraform sich merkt. Bei Ansible, wenn du sagst: „Erstelle mir 5 Instanzen“, dann erstellt er sie, und wenn du sagst: „Jetzt brauche ich 3“, wird Terraform sagen: „Okay, ich lösche 2“, während Ansible sagen wird: „Okay, hier sind 3“. Insgesamt also 8.
Hallo! Vielen Dank für Ihren Vortrag! Es war sehr interessant, über Terraform zu hören. Ich möchte sofort einen kleinen Kommentar abgeben, dass Terraform immer noch keine stabile Version hat, also seien Sie mit Terraform vorsichtig.
Gut Ding will Weile haben. Das heißt, wenn Sie eine Lösung benötigen, dann schieben Sie manchmal das Unstabile und so weiter beiseite, aber es funktioniert und hat uns geholfen.
Ich habe eine Frage. Sie verwenden Remote-Backend, Sie verwenden S3. Warum benutzen Sie nicht das offizielle Backend?
Offiziell?
Terraform Cloud.
Wann ist es erschienen?
Vor etwa 4 Monaten.
Wenn es vor 4 Jahren erschienen wäre, hätte ich wahrscheinlich Ihre Frage beantwortet.
Dort gibt es bereits eingebaute Funktionen und Locks, und man kann die State-Datei speichern. Probieren Sie es aus. Ich habe es auch nicht getestet.
Wir fahren in einem großen Zug, der mit hoher Geschwindigkeit fährt. Man kann nicht einfach ein paar Waggons rauswerfen.
Sie sprachen über Schneeflocken, warum haben Sie keinen Branch verwendet? Warum hat das nicht funktioniert?
Wir haben den Ansatz, dass die gesamte Infrastruktur in einem Repository ist. Terraform, Puppet, alle Skripte, die irgendwie dazu gehören, sind alle in einem Repository. Auf diese Weise können wir garantieren, dass inkrementelle Änderungen nacheinander getestet werden. Wenn es viele Branches wären, wäre so ein Projekt praktisch unmöglich zu warten. Es vergehen sechs Monate, und sie weichen so weit auseinander, dass es einfach eine Qual wird. Das ist das, wovor wir uns bis zur Refaktorisierung drücken wollten.
Das heißt, es funktioniert nicht?
Das funktioniert überhaupt nicht.
Im Branch habe ich den Ordnerslide herausgeschnitten. Das heißt, wenn wir für jedes Test-Framework einen eigenen Ordner einrichten, zum Beispiel hat Team A seinen eigenen Ordner, Team B hat seinen eigenen Ordner, dann funktioniert das auch nicht. Wir haben einen einheitlichen Code für die Testumgebung erstellt, der flexibel genug war, um allen zu passen. Das heißt, wir hatten einen Code, der gewartet wurde.
Hallo! Ich heiße Jura! Danke für den Vortrag! Eine Frage zu den Modulen. Sie sagten, dass Sie Module verwenden. Wie gehen Sie damit um, wenn in einem Modul Änderungen vorgenommen wurden, die nicht mit den Änderungen eines anderen Benutzers kompatibel sind? Versionieren Sie die Module irgendwie oder versuchen Sie, eine „Wunderwaffe“ zu finden, um zwei Anforderungen gerecht zu werden?
Das ist das Problem eines großen Schneeballs. Das ist das, was wir erleiden, wenn eine harmlose Änderung einen Teil der Infrastruktur kaputt machen kann. Und das wird sich erst nach einer langen Zeit bemerkbar machen.
Das heißt, es wird bisher nicht gelöst?
Sie machen universelle Module. Vermeiden Sie Schneeflocken. Dann wird alles gut. Der zweite Teil des Vortrags handelt davon, wie man das vermeiden kann.
Guten Tag! Danke für den Vortrag! Ich wollte nachfragen. Im Hintergrund blieb ein großer Brocken, wegen dem ich hierher gekommen bin. Wie sind Puppet und die Rollenzuweisung integriert?
User-data.
Das heißt, Sie spucken einfach eine Datei aus und führen sie damit irgendwie aus?
User-data ist eine Notiz, das heißt, wenn wir ein Abbild klonen, dann wird ein Daemon gestartet, und um herauszufinden, wer er ist, liest er die Notiz, dass er ein Load Balancer ist.
Das heißt, das ist ein separater Prozess, der übergeben wird?
Wir haben ihn nicht erfunden. Wir benutzen ihn.
Guten Tag! Ich habe eine Frage zu User-data. Sie sagten, dass es dort Probleme gibt, weil jemand etwas falsch übermitteln könnte. Gibt es eine Möglichkeit, die User-data im gleichen Git zu speichern, damit immer klar ist, worauf sich die User-data bezieht?
Wir generieren User-data aus der Vorlage. Das heißt, es gibt eine bestimmte Anzahl von Variablen. Und Terraform generiert das Endergebnis. Daher kann man nicht einfach auf die Vorlage schauen und sagen, was herauskommt, denn alle Probleme hängen damit zusammen, dass der Entwickler denkt, dass er in dieser Variablen eine Zeichenkette übergibt, während es tatsächlich ein Array ist. Und dann passiert es – zack, ich habe das und das, nächste Zeile, und alles ist kaputt. Wenn es sich um eine neue Ressource handelt und jemand sie aufsetzt, sieht er, dass etwas nicht funktioniert, und das lässt sich schnell lösen. Aber wenn es sich um eine aktualisierte Autoscale-Gruppe handelt, fangen irgendwann die Instanzen in der Autoscale-Gruppe an, sich zu ändern. Und plötzlich funktioniert etwas nicht mehr. Das ist ärgerlich.
Bedeutet das, dass die einzige Lösung ist – zu testen?
Ja, du siehst das Problem, du fügst Testschritte hinzu. Das heißt, auch das Output kann getestet werden. Vielleicht nicht so bequem, aber du kannst auch einige Markierungen setzen – überprüfe, dass User-data hier mit Nägeln fixiert ist.
Mein Name ist Timur. Es ist wirklich großartig, dass es Vorträge darüber gibt, wie man Terraform richtig organisiert.
Ich habe noch nicht einmal angefangen.
Ich denke, dass es auf der nächsten Konferenz vielleicht möglich sein wird. Ich habe eine einfache Frage. Warum hardcodest du den Wert in einem separaten Modul, anstatt tfvars zu verwenden, das heißt, was ist besser an einem Modul mit Werten als an tfvars?
Das heißt, ich müsste hier (Slide: Production/environment/settings.tf) schreiben: domain = Variable, domain vpcnetwork, Variable vpcnetwork und stvars – das Gleiche herausbekommen?
Das machen wir genau so. Wir beziehen uns auf das Modul source setting, zum Beispiel.
Im Wesentlichen ist das so etwas wie tfvars. Tfvars ist im Testumfeld sehr bequem. Ich habe tfvars für große Instanzen, für kleine. Und ich habe eine Datei in den Ordner geschüttelt. Und habe das, was ich wollte. Wenn wir die Infrastruktur ausbauen, möchten wir, dass man schauen und sofort alles verstehen kann. Aber so ist es so, dass man hier einen Blick werfen und dann in die tfvars schauen muss.
Bedeutet das, dass alles an einem Ort sein sollte?
Ja, tfvars ist, wenn du einen Code hast. Und er wird an mehreren verschiedenen Stellen mit unterschiedlichen Nuancen verwendet. Dann würdest du tfvars verwenden und deine Nuancen erhalten. Und wir – das ist Infrastruktur als Code in reinster Form. Du schauest und verstehst.
Hallo! Hatten Sie schon einmal Situationen, in denen der Cloud-Anbieter in das eingreift, was Sie mit Terraform gemacht haben? Angenommen, wir ändern Metadaten. Dort sind SSH-Schlüssel. Und Google fügt ständig seine Metadaten und Schlüssel hinzu. Und Terraform sagt immer, dass es Änderungen gibt. Nach jedem Durchlauf, selbst wenn sich nichts ändert, sagt es immer, dass es dieses Feld aktualisieren wird.
Mit den Schlüsseln, aber ja, ein Teil der Infrastruktur ist von so etwas betroffen, d. h. Terraform kann nichts ändern. Wir können auch nichts manuell ändern. Wir leben einfach damit.
Das heißt, Sie sind damit konfrontiert, haben aber nichts entworfen, es macht einfach und macht einfach weiter?
Leider ja.
Hallo! Ich heiße Stanislav Starkov. Mail.ru Gruppe. Wie lösen Sie das Problem mit der Generierung des Tags auf ..., wie übertragen Sie ihn? Ich verstehe, über User — data, um den Hostnamen anzugeben, Puppet zu verwenden? Und die zweite Frage. Wie lösen Sie dieses Problem in SG, d. h. wenn Sie SG generieren, Hundert gleichartige Instanzen, wie benennen Sie sie richtig?
Die Instanzen, die uns sehr wichtig sind, benennen wir schön. Die, die nicht benötigt werden, haben einen Zusatz, dass es sich um eine Autoscale-Gruppe handelt. Und theoretisch könnte man das abschalten und eine neue erhalten.
In Bezug auf das Problem mit dem Tag gibt es kein solches Problem, sondern eine solche Aufgabe. Und Tags werden bei uns sehr stark verwendet, weil die Infrastruktur groß und teuer ist. Wir müssen sehen, wohin das Geld fließt, deshalb ermöglichen uns Tags, herauszufinden, was und wohin ausgegeben wurde. Und entsprechend die Suche nach dem, wo viel Geld ausgegeben wird.
Worüber war noch die Frage?
Wenn SG hundert Instanzen erstellt, müssen sie irgendwie unterschieden werden?
Nein, das ist nicht nötig. Auf jeder Instanz gibt es einen Agenten, der meldet, dass es ein Problem gibt. Wenn der Agent meldet, dann weiß der Agent auch von ihm und zumindest existiert seine IP-Adresse. Man kann schon loslegen. Zweitens verwenden wir Consul für Discovery, wo es kein Kubernetes gibt. Und Consul zeigt auch die IP-Adresse der Instanz an.
Das heißt, Sie orientieren sich genau an der IP, und nicht am Hostnamen?
Es ist unmöglich, sich am Hostnamen zu orientieren, d. h. es gibt sehr viele. Es gibt Instanzidentifikatoren – AE usw. Man kann ihn irgendwo finden, man kann ihn in die Suche eingeben.
Hallo! Ich habe verstanden, dass Terraform eine gute Sache ist, die für Clouds ausgelegt ist.
Nicht nur.
Genau diese Frage interessiert mich. Wenn Sie sich entscheiden, massenhaft auf Bare Metal mit all Ihren Instanzen zu migrieren? Wird es keine Probleme geben? Oder müssen Sie doch auf andere Produkte zurückgreifen, wie z. B. das hier erwähnte Ansible?
Ansible ist ein bisschen anders. Das bedeutet, Ansible arbeitet bereits, wenn die Instanz gestartet ist. Terraform arbeitet, bevor die Instanz gestartet wurde. Der Übergang zu Bare Metal – nein.
Jetzt nicht, aber das Geschäft wird kommen und sagen: „Lass uns das machen“.
Der Wechsel zu einer anderen Cloud – ja, aber hier gibt es einen etwas anderen Aspekt. Man muss den Terraform-Code so schreiben, dass man einfacher auf eine andere Cloud wechseln kann.
Ursprünglich bestand die Aufgabe darin, unsere gesamte Infrastruktur cloud-unabhängig zu gestalten, d. h. jede Cloud sollte geeignet sein, aber irgendwann gab das Geschäft auf und sagte: „Okay, in den nächsten N Jahren gehen wir nirgendwohin, wir können die Dienste von Amazon nutzen“.
Terraform ermöglicht es, Front-End-Jobs zu erstellen, PagerDuty zu konfigurieren, Daten-Dokumente usw. Es hat sehr viele Möglichkeiten. Es kann praktisch die ganze Welt kontrollieren.
Danke für den Vortrag! Ich arbeite jetzt auch seit 4 Jahren mit Terraform. In der Übergangsphase zu Terraform, zu Infrastruktur und der deklarativen Beschreibung, hatten wir Situationen, in denen jemand etwas manuell machte, während Sie versuchten, einen Plan zu erstellen. Und dabei bekamen Sie irgendeinen Fehler. Wie gehen Sie mit solchen Problemen um? Wie finden Sie verlorene Ressourcen, die angegeben waren?
Im Wesentlichen manuell und visuell, wenn wir im Bericht etwas Seltsames sehen, analysieren wir, was dort vor sich geht, oder wir eliminieren es einfach. Generell sind Pull-Requests ein alltägliches Geschäft.
Wenn ein Fehler auftritt, machen Sie dann ein Rollback? Haben Sie das schon mal versucht?
Nein, das ist eine Entscheidung des Menschen in dem Moment, wenn er das Problem sieht.
Quelle: habr.com
