CI/CD explicat: de la commit la producție
Ce înseamnă CI, Continuous Delivery și Continuous Deployment, cum arată un pipeline GitHub Actions și ce strategii de deploy există.
Într-o echipă care livrează des, nimeni nu mai copiază fișiere pe server de mână. Fiecare commit trece automat printr-o serie de verificări (build, teste, analiză statică) și, dacă totul e în regulă, ajunge în producție cu efort minim. Acest ghid explică ce înseamnă CI/CD, cum arată un pipeline real și ce practici îl fac sigur și rapid.
CI, CD și CD: trei termeni înrudiți
Abrevierea „CI/CD” ascunde de fapt trei practici diferite, care se construiesc una peste alta:
- Continuous Integration (CI): fiecare dezvoltator integrează des codul în ramura principală (cel puțin o dată pe zi), iar fiecare integrare este verificată automat prin build și teste. Scopul este ca problemele de integrare să fie descoperite în minute, nu după săptămâni.
- Continuous Delivery: codul care trece de CI este împachetat și pregătit automat pentru lansare, astfel încât deploy-ul în producție să fie oricând o decizie de business, de obicei un singur click.
- Continuous Deployment: pasul final, în care fiecare schimbare care trece toate verificările ajunge automat în producție, fără intervenție umană.
| Practică | Ce e automatizat | Cine decide lansarea în producție |
|---|---|---|
| Continuous Integration | Build și teste la fiecare push sau pull request | Nu se ocupă de lansare |
| Continuous Delivery | Tot drumul până la un artefact gata de lansare, adesea și deploy pe staging | Un om, printr-o aprobare |
| Continuous Deployment | Tot drumul, inclusiv producția | Pipeline-ul, pe baza testelor |
Multe echipe încep cu CI, trec la Continuous Delivery și ajung la Continuous Deployment doar după ce au teste și monitorizare în care au cu adevărat încredere.
Drumul unui commit până în producție
Un pipeline este o succesiune de etape automate. Detaliile diferă de la proiect la proiect, dar structura e aproape mereu aceeași:
- Trigger: un push, un pull request, un tag sau un program fix (de exemplu, în fiecare noapte).
- Checkout și instalare: runner-ul descarcă codul și instalează dependențele, ideal dintr-un cache.
- Verificări statice: linting, formatare, verificarea tipurilor (de exemplu
tsc --noEmit), scanarea dependențelor pentru vulnerabilități cunoscute. - Teste: unitare și de integrare. Dacă pică ceva, pipeline-ul se oprește aici.
- Build și artefact: se produce ceva livrabil (o imagine Docker, un pachet, un bundle static), cu o versiune unică, de obicei hash-ul commit-ului.
- Deploy pe staging: artefactul ajunge într-un mediu cât mai asemănător cu producția, unde rulează testele end-to-end.
- Deploy în producție: automat sau după o aprobare manuală.
- Verificare după deploy: smoke tests, urmărirea erorilor și a metricilor și, la nevoie, rollback.
Principiul-cheie aici este build once, deploy many. Construiești artefactul o singură dată, apoi promovezi același artefact din staging în producție. Dacă îl reconstruiești pentru fiecare mediu, riști ca în producție să ajungă altceva decât ce ai testat.
Un pipeline real cu GitHub Actions
GitHub Actions este una dintre cele mai folosite platforme de CI/CD, mai ales pentru proiectele găzduite pe GitHub. Workflow-urile sunt fișiere YAML în directorul .github/workflows/. Iată un exemplu pentru o aplicație Node.js:
# .github/workflows/ci-cd.yml
name: CI/CD
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
deploy:
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v5
- name: Deploy
run: ./scripts/deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
Ce se întâmplă aici, pe rând:
ondefinește când rulează workflow-ul: la fiecare push pemainși la fiecare pull request.permissionsrestrânge drepturile token-ului automat la citirea codului. Pornește de la minimum și adaugă doar ce îți trebuie.- Job-ul
testpornește o mașină virtuală Ubuntu, descarcă codul, instalează Node.js cu cache pentru npm și rulează pe rând lint, teste și build.npm ciinstalează exact versiunile dinpackage-lock.json, deci build-urile sunt reproductibile. - Job-ul
deployareneeds: test, deci pornește doar dacă testele au trecut, iar condițiaifîl limitează la push-urile pemain, nu și la pull request-uri. environment: productionleagă job-ul de un environment GitHub, pentru care poți configura reguli de protecție, de exemplu aprobarea unui reviewer înainte de deploy (disponibilitatea depinde de planul GitHub și de vizibilitatea repository-ului).- Secretele (token-uri, parole) nu stau niciodată în cod; le citești din
secrets, configurate în setările repository-ului.
Într-un proiect real, job-ul de deploy nu ar reconstrui aplicația, ci ar prelua artefactul produs anterior: fie prin acțiunile oficiale de upload și download pentru artefacte, fie publicând o imagine Docker într-un registry și făcând deploy cu exact acea imagine.
Versiunile acțiunilor (
@v5) avansează în timp. Verifică pe pagina fiecărei acțiuni care este cea mai recentă versiune majoră.
Testare pe mai multe versiuni cu matrix
Dacă o bibliotecă trebuie să funcționeze pe mai multe versiuni de runtime, folosește o matrice. Job-ul se multiplică automat pentru fiecare combinație și rulează în paralel:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -r requirements.txt
- run: pytest
Alte platforme de CI/CD
GitHub Actions nu este singura opțiune. Iată cele mai întâlnite platforme:
| Platformă | Unde rulează | Fișier de configurare | Când o alegi |
|---|---|---|---|
| GitHub Actions | Runneri GitHub sau self-hosted | .github/workflows/*.yml | Codul e pe GitHub; ecosistem mare de acțiuni gata făcute |
| GitLab CI/CD | Cloud sau self-hosted | .gitlab-ci.yml | Codul e pe GitLab; vrei totul într-o singură platformă |
| Jenkins | Self-hosted | Jenkinsfile | Infrastructură proprie, nevoi foarte personalizate |
| Azure Pipelines | Cloud sau self-hosted | azure-pipelines.yml | Ecosistem Microsoft și Azure |
| CircleCI | Cloud sau self-hosted | .circleci/config.yml | Accent pe viteză și caching |
Conceptele se transferă aproape integral: job-uri, etape, runneri, artefacte, secrete, cache. Dacă înveți bine o platformă, pe a doua o deprinzi în câteva zile.
Strategii de deploy
Modul în care înlocuiești versiunea veche cu cea nouă influențează direct riscul fiecărei lansări.
| Strategie | Cum funcționează | Avantaj | Dezavantaj |
|---|---|---|---|
| Recreate | Oprești versiunea veche, pornești versiunea nouă | Foarte simplă | Downtime |
| Rolling | Înlocuiești instanțele treptat, câteva odată | Fără downtime, puține resurse în plus | Două versiuni rulează simultan o vreme |
| Blue-green | Pornești noul mediu („green”) lângă cel vechi („blue”) și muți tot traficul dintr-odată | Rollback aproape instant | Dublezi temporar infrastructura |
| Canary | Trimiți un procent mic din trafic spre versiunea nouă, apoi crești treptat | Problemele afectează puțini utilizatori | Necesită monitorizare și rutare mai complexe |
Complementar, feature flags separă deploy-ul de lansare: codul nou ajunge în producție dezactivat, iar tu îl pornești pentru anumiți utilizatori când ești pregătit. Așa poți integra des în main chiar și funcționalități neterminate.
Securitatea pipeline-ului
Pipeline-ul are acces la producție, deci e o țintă atractivă pentru atacatori. Câteva practici de bază:
- Secrete în afara codului. Folosește mecanismul de secrete al platformei și nu le afișa niciodată în log-uri.
- Credențiale de scurtă durată. GitHub Actions suportă OpenID Connect (OIDC) pentru autentificarea la furnizorii de cloud, așa că nu mai trebuie să stochezi chei de acces permanente.
- Least privilege. Token-ul pipeline-ului primește doar permisiunile de care are nevoie, iar credențialele de producție sunt disponibile doar job-ului de deploy.
- Dependențe verificate. Fixează acțiunile terțe la un commit SHA, nu doar la un tag, și ține dependențele la zi cu un tool precum Dependabot sau Renovate.
- Atenție la pull request-urile din fork-uri. Nu expune secrete workflow-urilor declanșate de cod pe care nu l-ai verificat.
Cum menții pipeline-ul rapid
Un pipeline lent descurajează commit-urile mici și frecvente, adică exact ce încearcă CI să încurajeze. Un obiectiv rezonabil pentru multe echipe este ca feedback-ul pe un pull request să vină în câteva minute, nu în zeci de minute.
- Cache pentru dependențe: instalarea de la zero la fiecare rulare irosește timp.
- Job-uri în paralel: lint-ul, testele unitare și verificarea tipurilor nu depind unele de altele.
- Fail fast: rulează întâi verificările ieftine, ca să nu aștepți build-ul pentru o eroare de formatare.
- Rulare selectivă: într-un monorepo, testează doar pachetele afectate de schimbare.
- Teste stabile: un test flaky care te obligă la re-run dublează timpul real de feedback.
Rollback și observabilitate
Oricât de bun ar fi pipeline-ul, unele probleme apar doar în producție. Pregătește-te dinainte:
- Păstrează artefactele anterioare, ca să poți reveni rapid la versiunea precedentă.
- Fă migrările de bază de date compatibile cu versiunea anterioară a codului. De exemplu, adaugi o coloană nouă și abia într-un deploy ulterior o ștergi pe cea veche. Altfel, rollback-ul codului devine imposibil.
- Urmărește după fiecare deploy rata de erori, latența și metricile de business. Un deploy nu e „gata” când pipeline-ul devine verde, ci când ai confirmat că producția e sănătoasă.
Greșeli frecvente
- Branch-uri care trăiesc săptămâni întregi și se integrează greu. CI funcționează cel mai bine cu schimbări mici, integrate des.
- Un pipeline roșu ignorat de echipă pentru că „testele oricum pică uneori”.
- Pași manuali ascunși, de tipul „după deploy, intră pe server și rulează scriptul X”.
- Configurări diferite între staging și producție, care fac testele de pe staging aproape irelevante.
- Deploy-uri făcute vineri seara, fără nimeni disponibil să urmărească producția.
Pașii următori
- Adaugă un workflow GitHub Actions la un proiect personal, care să ruleze lint și teste la fiecare pull request.
- Împachetează aplicația într-o imagine Docker și publică imaginea din pipeline. Roadmap-ul Docker te ajută cu bazele.
- Continuă cu roadmap-ul DevOps pentru infrastructură, monitorizare și infrastructure as code.
- Citește documentația oficială GitHub Actions și articolul lui Martin Fowler despre Continuous Integration.