Arhitectura datelor și a sistemelor, de la zero Lecția 53 / 80

Infrastructure as code: Terraform, Pulumi, CDK

Infrastructură declarativă, problema fișierului de state, workflow-ul GitOps. Trei unelte și unde se potrivește fiecare.

Diferența dintre o echipă care poate ridica un mediu de staging proaspăt în treizeci de minute și o echipă care are nevoie de trei zile e aproape întotdeauna felul în care e definită infrastructura. Echipa de treizeci de minute o are în cod: un repository de fișiere declarative care descrie fiecare resursă cloud, rețea, bază de date, rol IAM, înregistrare DNS și secret. Rulează o comandă, cloud-ul se reconciliază cu descrierea și un mediu funcțional există. Echipa de trei zile o are în memoria cuiva și pe o pagină de wiki pe jumătate făcută, ultima oară actualizată când consola AWS arăta diferit.

Infrastructure as code, IaC, e disciplina de a defini infrastructura la fel cum definești codul aplicației: în fișiere version-controlled, revizuite, testate. Lecția asta trece prin cele trei unelte care domină domeniul (Terraform, Pulumi, AWS CDK), problema fișierului de state pe care o au toate, workflow-ul GitOps care le împachetează și de ce contează asta mai mult pentru munca cu date decât pentru majoritatea celorlalte tipuri de inginerie.

Alternativa pre-IaC

Ca să facem cazul pentru IaC, uită-te la ce înlocuiește.

Pre-IaC, infrastructura se întâmplă într-o consolă cloud. Cineva apasă „create database” și tastează parametrii. Șase luni mai târziu, nimeni nu-și mai aduce aminte care erau parametrii. Baza de date trebuie reprodusă în staging; inginerul care face reproducerea ghicește parametrii și greșește unii. Un an mai târziu, baza de date de producție și cea de staging s-au depărtat suficient cât bug-urile din staging să nu se reproducă în producție, iar bug-urile din producție să nu se reproducă în staging. Fiecare mediu e un fulg de zăpadă unic. Fiecare modificare e un ritual manual.

Efectul cumulativ e mai rău decât durerea per incident. Fiecare cont cloud acumulează datorie: resurse orfane fără proprietar, security groups cu reguli al căror scop e uitat, politici IAM prea permisive pentru că nimeni nu știe ce permisiune minimă are nevoie de fapt serviciul. Auditul devine un exercițiu de criminalistică. Disaster recovery devine o speranță.

IaC face infrastructura reviewable, reproductibilă și capabilă de rollback. Compromisul e că trebuie să înveți tooling-ul și să accepți constrângerile configurației declarative. Pentru orice depășește un setup cu un singur VM, compromisul se plătește singur în câteva luni.

Terraform: alegerea implicită

Terraform, lansat de HashiCorp în 2014, e unealta către care reach-uiesc majoritatea echipelor. Folosește un DSL declarativ numit HCL (HashiCorp Configuration Language) și un model de provider care îi permite să vorbească cu aproape orice cloud sau serviciu: AWS, GCP, Azure, Kubernetes, Cloudflare, Datadog, GitHub și o coadă lungă de altele. Scrii fișiere HCL care descriu state-ul dorit, rulezi terraform plan ca să vezi ce s-ar schimba, rulezi terraform apply ca să faci modificările.

Un mic exemplu pentru un bucket S3:

resource "aws_s3_bucket" "data_lake" {
  bucket = "company-data-lake-prod"
}

resource "aws_s3_bucket_versioning" "data_lake" {
  bucket = aws_s3_bucket.data_lake.id
  versioning_configuration {
    status = "Enabled"
  }
}

Punctele forte sunt reale. Ecosistemul de provider-i e enorm. Comunitatea are decenii de pattern-uri și module partajate. Workflow-ul plan-then-apply oferă reviewer-ilor un diff concret a ce se va schimba în realitatea cloud-ului. Același cod Terraform funcționează pentru dev, staging și prod cu variabile diferite, ceea ce face posibil mediul proaspăt în treizeci de minute.

Situația licenței merită cunoscută. În 2023, HashiCorp a re-licențiat Terraform de la MPL la Business Source License, restricționând o parte din uzul comercial. Comunitatea a răspuns făcând fork la codul open-source în OpenTofu, acum un proiect Linux Foundation. OpenTofu e un drop-in replacement și calea open-source înainte; echipele noi care aleg azi adesea îl preferă implicit. Fișierele HCL funcționează în ambele.

Pentru majoritatea echipelor, în majoritatea timpului, Terraform sau OpenTofu e punctul de pornire potrivit. Acoperirea de provider-i și comunitatea îl fac calea cu cea mai mică rezistență.

Pulumi: infrastructură în limbaje reale

Pulumi, lansat în 2017, a făcut un pariu diferit. În loc de un DSL custom, îți permite să definești infrastructura în limbaje de programare reale: TypeScript, Python, Go, C#, Java. Același model declarativ dedesubt, același workflow plan-then-apply, dar input-ul e cod într-un limbaj pe care echipa ta îl știe deja.

Același bucket S3 în TypeScript:

import * as aws from "@pulumi/aws";

const dataLake = new aws.s3.Bucket("data-lake", {
    bucket: "company-data-lake-prod",
    versioning: { enabled: true },
});

Avantajele apar când infrastructura are vreo complexitate. Loop-uri, condiționale, abstracții, type checking, autocomplete în IDE, framework-uri reale de testare: toate gratis, pentru că e doar cod. Echipele care au depășit for_each și blocurile dynamic din Terraform găsesc adesea Pulumi mai clar pentru aceeași logică.

Costul e că abstracția e mai leaky. Cu HCL, fișierul e adevărul: citești fișierul, știi ce infrastructură există. Cu Pulumi, codul e un program care produce adevărul, iar un program suficient de inteligent poate fi greu de citit. Disciplina care păstrează Pulumi curat e disciplina oricărei codebase: simplu e mai bun decât deștept, abstracțiile își câștigă pâinea, noul angajat ar trebui să poată citi ce se întâmplă.

Pulumi e alegerea potrivită pentru echipele ai căror ingineri sunt mai puternici în cod decât în DSL-uri și a căror logică de infrastructură a crescut suficient cât HCL să se opună. Pentru o echipă mică cu nevoi simple, puterea în plus e supradimensionată.

AWS CDK: doar AWS, cu ergonomie nativă pe cod

AWS Cloud Development Kit, lansat în 2019, e răspunsul Amazon la același pitch de „limbaje reale” ca Pulumi, cu un scop mai strâns. Codul CDK în TypeScript, Python, Java, C# sau Go compilează în template-uri CloudFormation pe care AWS le deploy-uiește.

Același bucket în CDK:

import * as s3 from "aws-cdk-lib/aws-s3";

new s3.Bucket(this, "DataLake", {
    bucketName: "company-data-lake-prod",
    versioned: true,
});

CDK e doar pentru AWS. Ăsta e tot rostul. Dacă infrastructura ta e AWS de sus până jos și vrei cele mai înalte abstracții pentru resurse AWS, CDK îți oferă construct-uri care împachetează pattern-uri comune (un serviciu ECS cu un load balancer și un dashboard CloudWatch, totul configurat sensibil) în obiecte unice. Integrarea cu funcționalitățile specifice AWS e mai profundă decât ce oferă provider-ul AWS al Terraform, pentru că CDK e construit de AWS.

Compromisul e lock-in-ul. CDK nu e portabil. Dacă firma se mută la multi-cloud, codul CDK nu vine cu ea. Pentru o echipă angajată pe AWS pe termen lung și care vrea ergonomia de developer, lock-in-ul ăla e o funcționalitate, nu un bug. Pentru o echipă care vrea să țină opțiunile deschise, Terraform sau Pulumi e pariul mai sigur.

Problema fișierului de state

Fiecare unealtă IaC trebuie să țină evidența la „ce există în prezent în cloud” ca să știe ce să schimbe la următorul apply. Terraform scrie un fișier de state: un document JSON care mapează resursele din codul tău la resursele care există în realitate, cu ID-urile lor, atributele lor, dependențele lor. Pulumi are același concept sub un nume diferit. CDK delegă la CloudFormation, care își ține propriul state.

Fișierul de state e locul de unde vine majoritatea durerii operaționale a IaC.

E o piesă critică de state. Pierde-l și pierzi legătura dintre cod și realitate. Următorul apply va încerca să creeze totul din nou, va eșua pentru că resursele există deja, și te va lăsa într-un state stricat. Tratează fișierul de state ca pe un backup de bază de date: stocat remote (S3 cu versioning e setup-ul canonic Terraform), criptat și nu în repo-ul de git.

Poate fi blocat. Doi ingineri care rulează terraform apply pe același state în același timp produc corupție. Terraform suportă locking prin DynamoDB; OpenTofu prin DynamoDB sau HTTP. Lock-ul e esențial pentru orice setup de echipă; din momentul în care două persoane rulează apply, lock-ul e cel care te ține sănătos.

Poate să se depărteze de realitate. Cineva apasă în consolă și schimbă o regulă de security group. Viziunea Terraform asupra lumii e acum stale. Următorul plan raportează modificări neașteptate; apply-ul fie revertează modificarea manuală, fie, mai rău, se luptă cu ea. Disciplina care previne drift-ul e „toate modificările trec prin IaC, fără excepții”. Realitatea pragmatică e că drift-ul se întâmplă, iar unelte ca terraform refresh și refresh-ul Pulumi îți permit să reconciliezi.

Fișierul de state e și locul de unde pot scăpa secrete. Unele atribute de resurse conțin valori sensibile: parole de bază de date, token-uri generate, chei TLS. Astea ajung în fișierul de state. Criptează state-ul în repaus. Restricționează accesul la el. Tratează bucket-ul de state cum tratezi un secrets manager.

GitOps: workflow-ul care leagă totul

GitOps e pattern-ul operațional care împachetează IaC în ceva pe care o echipă îl poate folosi fără să-și calce reciproc pe picioare. Ideea e simplă: repository-ul git e singura sursă de adevăr pentru infrastructură. Fiecare modificare e un pull request. Un controller reconciliază continuu state-ul cloud cu state-ul din git. Dacă realitatea se depărtează de git, controller-ul fie alertează, fie corectează.

Pentru Terraform, Atlantis e unealta canonică. Un pull request care atinge fișiere Terraform declanșează Atlantis să ruleze terraform plan și să posteze output-ul ca un comentariu de PR. Reviewer-ii văd diff-ul exact care va fi aplicat. Odată ce PR-ul e aprobat, Atlantis rulează terraform apply din PR-ul însuși. Urma e în istoricul git; lock-ul e ținut în timpul apply-ului; nimeni nu rulează Terraform de pe laptop.

Pentru Kubernetes, Flux și ArgoCD joacă același rol. Urmăresc un repo git de manifeste Kubernetes și reconciliază continuu cluster-ul ca să se potrivească. Faci push la o modificare în git, cluster-ul o preia. Drift-ul în cluster e corectat înapoi spre git.

Flow-ul arată cam așa:

flowchart TB
    DEV[Developer writes IaC change] --> PR[Open pull request]
    PR --> PLAN[CI runs terraform plan]
    PLAN --> REVIEW[Reviewer reads plan diff]
    REVIEW --> MERGE[Merge to main]
    MERGE --> APPLY[CI runs terraform apply]
    APPLY --> CLOUD[Cloud reaches desired state]
    CLOUD --> DRIFT[Periodic drift detection]
    DRIFT --> ALERT[Alert if drift found]

Schimbarea culturală pe care o impune GitOps e reală. Gata cu „tocmai am schimbat-o în consolă”. Fiecare modificare e un commit. Fiecare commit e revizuit. Fiecare state al lumii e accesibil dintr-un istoric git.

Când IaC e exagerat

Lista cinstită a momentelor când să sari peste IaC e scurtă.

Un proiect solo cu un VM și o bază de date. Un prototip de aruncat care nu va supraviețui săptămânii. Un exercițiu de învățare unde scopul e să înțelegi primitivele cloud, nu să le livrezi.

Aproape orice altceva beneficiază. Pragul e jos: doi ingineri și trei resurse sunt suficiente cât gestionarea manuală să fie o alegere mai proastă decât IaC. Argumentul „suntem prea mici pentru asta” e argumentul care produce infrastructură de tip fulg de zăpadă șase luni mai târziu.

Unghiul de data engineering

Munca cu date are propriul ei beneficiu specific din IaC. Infrastructura pentru o platformă de date e multe resurse: warehouse-ul (cu dataset-urile, schemele, grant-urile IAM), orchestratorul (Airflow sau Dagster sau Prefect, cu secretele, conexiunile, deployments-urile lor), compute-ul (config-uri de cluster Spark, node pool-uri Kubernetes), storage-ul (bucket-uri S3 cu lifecycle policies și reguli de replicare), monitorizarea (dashboard-uri Datadog, alerte, SLO-uri).

Provisionarea unui mediu proaspăt de staging care oglindește producția e use case-ul în care investiția se plătește cel mai mult. Fără IaC, mediul de staging niciodată nu se potrivește chiar cu producția, iar gap-ul e locul unde se ascund bug-urile. Cu IaC, staging-ul e o clonă a producției cu variabile diferite, iar gap-ul se închide aproape de zero.

Celălalt use case e disaster recovery. Dacă regiunea de producție pică, IaC plus un backup recent e calea înapoi. Click-ops plus o pagină de wiki nu e.

Ce înseamnă asta în practică

Pentru o echipă care începe azi, calea realistă e:

  1. Alege Terraform sau OpenTofu dacă nu există un motiv puternic pentru Pulumi sau CDK.
  2. Stochează state-ul remote din ziua întâi (S3 plus DynamoDB lock pentru AWS, GCS pentru GCP).
  3. Împachetează workflow-ul cu Atlantis sau un runner similar bazat pe PR-uri înainte ca două persoane să ruleze Terraform de pe laptop.
  4. Setează detectarea de drift ca un job periodic; investighează fiecare eveniment de drift.
  5. Tratează codul IaC cu aceeași disciplină de review ca pe codul aplicației. Raza de explozie a unui apply prost e mare.

Lecția 54 acoperă containerele, unitatea sub care se livrează majoritatea job-urilor de date moderne. Lecția 55 acoperă Kubernetes, runtime-ul pe care rulează majoritatea platformelor de date bazate pe containere. Ambele straturi stau peste IaC: cluster-ul, registry-urile, rolurile IAM pentru job-uri, toate definite în Terraform sau în echivalentele lui.

Citații

  • Documentația Terraform (https://developer.hashicorp.com/terraform/docs, consultat 2026-05-01).
  • Documentația OpenTofu (https://opentofu.org/docs/, consultat 2026-05-01).
  • Documentația Pulumi (https://www.pulumi.com/docs/, consultat 2026-05-01).
  • Documentația AWS CDK (https://docs.aws.amazon.com/cdk/v2/guide/home.html, consultat 2026-05-01).
  • Documentația Atlantis (https://www.runatlantis.io/docs/, consultat 2026-05-01).
  • „GitOps principles” pe site-ul OpenGitOps (https://opengitops.dev/, consultat 2026-05-01).
Caută