Producție declarativă

ForkTex Engineering · 5 septembrie 2026 · 3 min

Întreabă un repository cum rulează în producție și de obicei primești o vânătoare de comori: un Dockerfile, un fișier compose care poate fi sau nu actual, un workflow de CI cu variabilele de mediu reale, un ghid într-un wiki, și un inginer care știe părțile care nu sunt scrise nicăieri.

Fiecare consumator al acelei informații o re-deduce, iar unul dintre ei greșește.

Așa că un repository ForkTex declară cum rulează într-un singur fișier pe care o mașină îl poate citi. Un manifest descrie serviciile, infrastructura de care au nevoie și strategia de deployment. Și același fișier conduce atât stiva locală, cât și cea de producție.

Ce declară manifestul

{
  "apiVersion": "forktex.cloud/v1",
  "kind": "ProjectDeployment",
  "metadata": { "name": "example", "environment": "production" },
  "infrastructure": {
    "servers": [{
      "id": "primary",
      "primary": true,
      "provider": "hetzner",
      "flavour": "standard",
      "region": "eu-central",
      "image": "ubuntu-24.04",
      "gateway": {
        "domain": "example.forktex.com",
        "ssl": { "provider": "letsencrypt", "challenge": "dns-01" }
      }
    }]
  },
  "deployment": { "strategy": "blue-green", "gracefulStopSeconds": 30 },
  "services": [
    { "id": "api", "type": "compute", "port": 8080, "healthPath": "/api/health" },
    { "id": "db",  "type": "persistence", "image": "postgres:17-alpine" }
  ]
}

Serviciile poartă un tip, iar tipul e ceea ce raționează platforma. Un serviciu compute rulează în două sloturi și se face cutover între ele. Un serviciu persistence nu. e o bază de date, rulează o singură dată și primește volume și health checks potrivite imaginii sale. Un daemon nu are port, pentru că el consumă muncă, nu o servește.

Această tipizare e ce permite platformei să genereze părțile pe care nimeni n-ar trebui să le scrie manual. Stratul de proxy nu e infrastructură definită de utilizator; e derivat din lista de servicii. Nimeni nu scrie o configurare de rutare, ceea ce înseamnă că nimeni nu uită să o actualizeze.

forktex.jsonservices · infra+ forktex.local.jsonmounted source · mail catcher+ forktex.production.jsondomains · TLS · observabilitydocker composelocalhost, hot reloadHetzner VPSblue / green behind HAProxyone descriptionoverlays are the exhaustive list of differences
Un manifest, două suprapuneri, două medii.

Mediile sunt overlay-uri, nu copii

Un manifest de bază plus un overlay per mediu, combinate în profunzime. Obiectele se combină recursiv; serviciile se combină după id; scalarii se suprascriu.

Overlay-ul local montează directoare sursă și înlocuiește releul de mail cu un catcher. Overlay-ul de producție setează domenii reale și pornește observabilitatea. Niciunul nu e o descriere separată a sistemului. Ambele sunt diferențe față de unul singur.

Aici e diferența care contează. Când local și producție sunt două fișiere întreținute manual, ele diverg, iar divergența e invizibilă până când o cale de cod care există doar în producție eșuează în producție. Când sunt un singur fișier plus un mic overlay, overlay-ul *este* lista exhaustivă a modurilor în care mediile diferă, și e destul de scurtă ca să fie citită.

Secretele sunt referențiate, niciodată incluse direct

Valorile care nu trebuie să existe într-un repository sunt referențiate:

"environment": { "DB_PASSWORD": "${vault:DB_PASSWORD}" }

Rezolvate la momentul compilării, dintr-un depozit criptat. Manifestul rămâne comisibil în repository, ceea ce e ideea de bază. Din momentul în care un manifest nu mai poate fi comis, încetează să fie sursa de adevăr, iar o copie privată devine cea reală.

Blue-green, și de ce partea plictisitoare contează

Un deploy pornește versiunea nouă alături de cea veche. Slotul nou trebuie să treacă verificările de sănătate înainte să primească trafic. Apoi proxy-ul se mută, slotul vechi se golește pe o perioadă de grație, și se oprește.

Proprietatea interesantă nu e zero downtime. E că rollback-ul e o schimbare de proxy. Versiunea anterioară rulează încă, e încă sănătoasă, își ține încă conexiunile. A reveni înseamnă a muta traficul înapoi, ceea ce durează cam la fel de mult ca a-l muta înainte. Nu există reconstrucție, nu există re-deploy, nu există așteptare pentru tragerea unei imagini în timp ce site-ul e picat.

Asta schimbă calculul de risc al lansărilor. Când rollback-ul e o reconstrucție, un deploy prost e un incident; echipele răspund făcând deploy mai rar, în loturi mai mari, ceea ce face fiecare deploy mai riscant. Când rollback-ul e o schimbare de rută, un deploy prost e un inconvenient, iar deploy-urile devin mai mici și mai frecvente. Ceea ce chiar le face sigure.

Perioada de grație merită și ea o propoziție. Tăierea instantă a traficului rupe request-urile în curs. Golirea îi lasă să se termine. Nu e ceva spectaculos, dar e diferența dintre un cutover curat și o împrăștiere de erori 502 în log-uri, pe care cineva le va investiga săptămâna viitoare.

Suveran implicit

Infrastructura de calcul din spate e europeană. Nu ca o bifă de conformitate, deși ajută cu clienții pentru care rezidența datelor e o constrângere reală, ci pentru că valoarea platformei depinde de a nu fi blocată în planul de control al unui singur furnizor.

Manifestul descrie un sistem în termeni de servere, containere, rute și certificate. Acestea sunt concepte portabile. Un manifest care ar descrie în schimb cozi gestionate și medii de execuție de funcții proprietare ar fi descrierea unui sistem care poate exista doar în contul unui singur furnizor.

Compromisul e onest: ne asumăm munca operațională pe care o platformă gestionată ar absorbi-o. Backup-urile, patch-urile și provizionarea sunt ale noastre. Ce câștigăm e că fiecare proiect livrează pe aceeași stivă inspectabilă, și niciun sistem de client nu e la o schimbare de preț distanță de o migrare.

Eșecul pe care îl previne

Nu o pană. Ceva mai lent.

Fără un manifest, cunoașterea despre producție se difuzează. Ajunge într-un fișier de CI, un wiki și memoria cuiva, iar acestea trei diverg în ritmuri diferite. Nimic nu eșuează. Până când un server e reconstruit, sau persoana care știa partea nedocumentată e în concediu, iar sistemul se dovedește a depinde de un fapt pe care nimeni nu l-a scris nicăieri.

Un fișier pe care o mașină îl poate citi nu elimină acel risc. Dar face golul vizibil: dacă manifestul nu îl descrie, nu e lansat.