Umgebungen trennen: Warum Dev- und Prod-Zustände niemals in Kontakt kommen sollten

Sie haben drei Verzeichnisse: dev, staging und prod. Jedes enthält Konfigurationsdateien, Zustandsaufzeichnungen und Ressourcendefinitionen. Wenn Sie eine Firewall-Regel aktualisieren müssen, öffnen Sie alle drei Verzeichnisse und nehmen dieselbe Änderung dreimal vor. Eines Nachmittags vergessen Sie, staging zu aktualisieren. Zwei Wochen später schlägt ein Deployment auf Staging fehl, weil die Firewall eine Verbindung blockiert, die überall sonst funktioniert. Niemand bemerkt es, bis die Veröffentlichung blockiert ist.

Diese Situation ist häufig. Teams beginnen mit einer Umgebung, fügen dann eine weitere hinzu, dann noch eine. Die Trennung fühlt sich klar an, weil die Ordner unterschiedliche Namen haben. Aber die Trennung ist nur kosmetisch. Das eigentliche Problem ist, dass dieselbe Person, dasselbe Tool und dasselbe Zugriffsmuster jede Umgebung ohne Schutzmechanismen erreichen kann.

Das eigentliche Problem mit der Umgebungstrennung

Umgebungstrennung hat nichts mit Ordnernamen zu tun. Es geht darum sicherzustellen, dass Änderungen in der Entwicklung niemals versehentlich die Produktion beeinträchtigen und dass Tests im Staging tatsächlich die Produktionsbedingungen widerspiegeln.

Wenn Sie dieselbe Zustandsdatei für alle Umgebungen verwenden, schaffen Sie einen Single Point of Failure. Ein Fehler in der Entwicklung kann eine Produktionsressource löschen. Eine falsch konfigurierte CI-Pipeline kann Staging-Werte auf die Produktionsinfrastruktur anwenden. Dies sind keine theoretischen Risiken. Sie treten auf, wenn Teams Zustandsdateien, Backends oder Zugangsdaten über Umgebungen hinweg teilen.

Das Ziel ist es, es unmöglich zu machen, versehentlich die Produktion zu ändern, während man an der Entwicklung arbeitet. Dafür ist mehr als Disziplin erforderlich. Es erfordert strukturelle Trennung.

Drei Ansätze zur Umgebungstrennung

1. Separate Verzeichnisse

Das folgende Diagramm zeigt jeden Ansatz mit seiner Struktur und seinen Hauptmerkmalen:

flowchart TD subgraph Approach1["1. Separate Verzeichnisse"] A1[Code + Konfiguration + Zustand pro Umgebung] --> D1[dev] A1 --> S1[staging] A1 --> P1[prod] end subgraph Approach2["2. Gemeinsame Struktur + Konfigurationsdateien"] A2[Gemeinsamer Code] --> C2[Konfigurationsdateien] C2 --> D2[dev] C2 --> S2[staging] C2 --> P2[prod] A2 -.->|gleiches Zustands-Backend| D2 A2 -.->|gleiches Zustands-Backend| S2 A2 -.->|gleiches Zustands-Backend| P2 end subgraph Approach3["3. Separate Zustands-Backends"] A3[Gemeinsamer Code + Konfiguration] --> D3[dev] A3 --> S3[staging] A3 --> P3[prod] D3 --> B1[Backend dev] S3 --> B2[Backend staging] P3 --> B3[Backend prod] end Approach1 -.->|hohe Redundanz| L1[Einfach, aber Drift] Approach2 -.->|mittlere Isolation| L2[Weniger Redundanz, geteiltes Risiko] Approach3 -.->|volle Isolation| L3[Am besten für Skalierung & Sicherheit]

Dies ist der einfachste Ansatz. Sie erstellen einen Ordner für jede Umgebung: dev/, staging/, prod/. Jeder Ordner enthält seine eigenen Konfigurationsdateien und seine eigene Zustandsdatei. Wenn Sie einen Befehl ausführen, geben Sie an, welchen Ordner Sie verwenden möchten.

Dies funktioniert gut für kleine Teams mit wenigen Umgebungen. Sie können die Unterschiede zwischen Umgebungen erkennen, indem Sie Dateien nebeneinander vergleichen. Die Struktur ist leicht verständlich. Neue Teammitglieder finden, was sie brauchen, ohne fragen zu müssen.

Das Problem tritt auf, wenn Umgebungen den Großteil ihrer Konfiguration teilen. Entwicklung und Staging unterscheiden sich möglicherweise nur in der Servergröße und dem Datenbanknamen. Wenn Sie eine Sicherheitseinstellung ändern müssen, müssen Sie drei Dateien aktualisieren. Das Vergessen einer erzeugt eine unsichtbare Abweichung. Mit der Zeit divergieren die Umgebungen, bis Staging die Produktion nicht mehr repräsentiert.

2. Gemeinsame Struktur mit separaten Konfigurationsdateien

Anstatt die gesamte Konfiguration zu duplizieren, behalten Sie einen Satz von Ressourcendefinitionen und separate Dateien für umgebungsspezifische Werte. Die Hauptdateien definieren die Logik und Struktur. Die Konfigurationsdateien enthalten Werte wie Servernamen, Instanzgrößen und Verbindungszeichenfolgen.

Dieser Ansatz reduziert Redundanz. Sie schreiben die Ressourcendefinitionen einmal. Wenn Sie eine Firewall-Regel ändern, ändern Sie sie an einer Stelle. Die umgebungsspezifischen Dateien enthalten nur die Werte, die sich tatsächlich unterscheiden.

Der Nachteil ist die Komplexität. Sie müssen verstehen, wie die Konfigurationsdateien mit den Hauptdefinitionen zusammengeführt werden. Sie müssen sicherstellen, dass jede Umgebung die richtige Konfigurationsdatei hat. Ein fehlender Wert kann dazu führen, dass ein Deployment fehlschlägt oder, schlimmer noch, ein Standardwert verwendet wird, der für diese Umgebung falsch ist.

3. Separate Zustands-Backends

Dies ist der ausgereifteste Ansatz. Jede Umgebung verwendet ein anderes Backend, um ihre Zustandsdatei zu speichern. Die Zustandsdatei zeichnet jede Ressource auf, die erstellt und verwaltet wurde. Wenn die Backends getrennt sind, kann sich der Entwicklungszustand nicht versehentlich mit dem Produktionszustand vermischen.

Hier ist ein konkretes Beispiel, wie Sie separate Zustands-Backends in Terraform mit S3 konfigurieren:

# Backend-Konfiguration für die Produktionsumgebung
terraform {
  backend "s3" {
    bucket = "my-company-tfstate"
    key    = "prod/terraform.tfstate"
    region = "us-east-1"
  }
}

Für die Staging-Umgebung würden Sie denselben Bucket, aber einen anderen Schlüssel verwenden:

terraform {
  backend "s3" {
    bucket = "my-company-tfstate"
    key    = "staging/terraform.tfstate"
    region = "us-east-1"
  }
}

Und für die Entwicklung:

terraform {
  backend "s3" {
    bucket = "my-company-tfstate"
    key    = "dev/terraform.tfstate"
    region = "us-east-1"
  }
}

Die Zustandsdatei jeder Umgebung wird unter einem separaten Präfix im selben S3-Bucket gespeichert. Sie können dann Zugriffsrichtlinien auf Bucket- oder Präfixebene durchsetzen, sodass Entwickler nur das dev/-Präfix lesen und schreiben können, während der Produktionszugriff auf Ihre CI/CD-Pipeline beschränkt ist.

Sie können Zugriffsrichtlinien auf Backend-Ebene durchsetzen. Entwicklerteammitglieder können nur auf das Entwicklungs-Backend zugreifen. Operations- oder SRE-Teams haben Zugriff auf das Produktions-Backend. Ein Entwickler, der einen Befehl von seinem Laptop aus ausführt, kann keine Produktionsressourcen berühren, weil seine Anmeldeinformationen keinen Zugriff auf das Produktions-Backend haben.

Diese Trennung ist entscheidend, weil Zustandsdateien sensible Informationen enthalten. Der Produktionszustand zeichnet typischerweise Server-IP-Adressen, Datenbankverbindungen und Sicherheitskonfigurationen auf. Wenn Produktions- und Entwicklungszustände am selben Ort gespeichert werden, wirkt sich ein Leck oder eine versehentliche Änderung auf beide Umgebungen aus. Mit separaten Backends können Sie unterschiedliche Sicherheitsrichtlinien für jede Umgebung anwenden.

Wann welcher Ansatz verwendet werden sollte

Wählen Sie separate Verzeichnisse, wenn Ihr Projekt klein ist, Sie wenige Umgebungen haben und sich die Ressourcen zwischen den Umgebungen erheblich unterscheiden. Dies ist ein guter Ausgangspunkt, aber planen Sie, davon abzuweichen, sobald das Team wächst.

Wählen Sie eine gemeinsame Struktur mit separaten Konfigurationsdateien, wenn Ihre Umgebungen dieselbe Struktur, aber unterschiedliche Werte haben. Dies funktioniert gut für Teams, die ihre Infrastruktur standardisiert haben und nur Parameter wie Größe, Region oder Benennung variieren müssen.

Wählen Sie separate Zustands-Backends, wenn Ihr Team groß ist, die Produktion streng geschützt werden muss oder Sie einen Prüfpfad für jede Änderung benötigen. Dies ist der Ansatz, der mit der organisatorischen Komplexität skaliert.

Die Richtlinienseite der Umgebungstrennung

Technische Trennung ist nur die Hälfte der Lösung. Sie können eine perfekte Backend-Trennung haben, aber wenn jedes Teammitglied von seinem Laptop aus Zugriff auf das Produktions-Backend hat, ist die Trennung bedeutungslos.

Umgebungstrennung erfordert Richtlinien. Wer kann den Produktionszustand lesen? Wer kann ihn ändern? Wer kann Änderungen an der Produktionsinfrastruktur genehmigen? Diese Fragen müssen beantwortet und durch Zugriffskontrollen durchgesetzt werden, nicht durch mündliche Vereinbarungen.

Einige Teams verwenden eine separate CI/CD-Pipeline für Produktions-Deployments. Die Produktions-Pipeline läuft nur von einem bestimmten Branch, erfordert Genehmigungen und verwendet Anmeldeinformationen, die Entwicklern nicht zur Verfügung stehen. Dies fügt eine weitere Trennungsebene über das Backend hinaus hinzu.

Praktische Checkliste

  • Jede Umgebung verwendet ein anderes Zustands-Backend
  • Das Produktions-Backend ist nur von einer CI/CD-Pipeline aus zugänglich, nicht von lokalen Maschinen
  • Entwicklungs- und Staging-Backends sind für Entwickler zugänglich
  • Zustandsdateien werden im Ruhezustand verschlüsselt
  • Zugriff auf den Produktionszustand erfordert Genehmigung und wird protokolliert
  • Konfigurationswerte werden validiert, bevor sie die Produktion erreichen

Die konkrete Erkenntnis

Umgebungstrennung ist kein Ordnerstrukturproblem. Es ist ein Zustandsisolationsproblem. Wenn Sie jede Umgebung als ein völlig separates System mit eigenem Zustands-Backend, eigenen Zugriffskontrollen und eigener Deployment-Pipeline behandeln, beseitigen Sie die Möglichkeit versehentlicher umgebungsübergreifender Änderungen. Beginnen Sie mit separaten Verzeichnissen, wenn es sein muss, aber wechseln Sie zu separaten Backends, bevor Ihr Team mehr als fünf Personen umfasst. Die Kosten für die Behebung eines Produktionsausfalls, der durch einen gemeinsamen Zustand verursacht wurde, sind immer höher als die Kosten für die Einrichtung einer ordnungsgemäßen Trennung von Anfang an.