DevOps — pregătire
Manual de lucru · actualizat 25 septembrie 2026

Ce face, de fapt,
un inginer DevOps

Pagina asta explică meseria de la zero: ce se întâmplă între momentul în care un programator apasă „commit” și momentul în care codul rulează la client. Fiecare capitol are trei lucruri: explicația conceptului, exemplul de cod și o casetă verde care arată unde se leagă de ce ai făcut tu deja — la Volvo, la Metro, la DISH, la RapidBid.

Kubernetes GitHub Actions Docker Jenkins Terraform Linux & Bash Monitorizare
Mergi la pagina de interviu →
1 2 3 4 cod → client scrie construiește livrează supraveghează

1 Ce face un DevOps, de fapt

Definiția de manual („cultură care unește dezvoltarea cu operațiunile”) nu te ajută la interviu, pentru că nu spune ce faci luni dimineață. Uite formularea care te ajută:

🗣️ De spus la interviu

„Un DevOps se ocupă de drumul dintre codul scris de programatori și codul care rulează la client. Construiește drumul ăsta să fie automat și repetabil, construiește mediile în care rulează, și se asigură că atunci când ceva se strică se vede repede și se repară repede.”

Rolul are patru coloane. Toate patru apar în anunțurile de job, în ordini diferite:

🚚 Livrare automată (CI/CD)

Construiești conducta care ia codul din Git, îl compilează, îl testează, îl împachetează și îl pune pe servere. Unelte: GitHub Actions, Jenkins, GitLab CI, Azure DevOps.

🏗️ Infrastructură ca mediu de rulare

Creezi și menții locurile unde rulează codul: mașini virtuale, clustere Kubernetes, baze de date, rețele. Descrise în cod, nu din interfață. Unelte: Terraform, Kubernetes, Docker, Ansible.

👁️ Observabilitate

Faci sistemul să-și spună starea: metrici, loguri, alerte, tablouri de bord. Fără asta, primul care află că e o problemă e clientul. Unelte: Prometheus, Grafana, Datadog, ELK.

🚨 Fiabilitate și incidente

Ești de gardă, iei incidentul, îl comunici, îl închizi, apoi scrii ce s-a întâmplat și ce schimbi ca să nu se repete. Asta e partea pe care tu o faci de 20 de ani.

✅ Unde te afli tu

Coloana 4 o ai solidă: control de producție 24/7 la Metro, migrare de mesagerie fără oprire la Volvo Cars, descoperire de aplicații la Etihad. Coloana 3 o ai parțial — ai lucrat cu Nagios și Tivoli, care sunt aceeași idee cu Prometheus, dar cu generația anterioară de unelte. Coloanele 1 și 2 sunt cele pe care le-ai atins la DISH și RapidBid și pe care trebuie să le poți explica cel mai bine, pentru că acolo va insista interviul.

Ce NU e un DevOps

  • Nu e „cel care are acces la producție”. Accesul e o consecință, nu rolul. Un DevOps bun reduce numărul de oameni care trebuie să intre manual pe servere, inclusiv pe el.
  • Nu e sysadmin cu nume nou. Sysadminul administrează servere existente. DevOps le creează din cod, le distruge, le recreează identic. Dacă un server nu poate fi recreat de la zero automat, nu e făcut cum trebuie.
  • Nu scrie funcționalitatea produsului. Scrie cod, dar cod de automatizare: pipeline-uri, manifeste, module de infrastructură, scripturi.
  • Nu e o echipă separată care primește tichete. Când devine asta, s-a reinventat exact zidul pe care DevOps trebuia să-l dărâme.

De ce a apărut rolul

Istoric, existau două echipe cu obiective opuse. Dezvoltarea era plătită să schimbe lucruri. Operațiunile erau plătite ca nimic să nu se schimbe, pentru că orice schimbare putea pica producția. Rezultatul: livrări rare, mari și riscante, la două-trei luni, noaptea, cu toată lumea în ședință.

Răspunsul DevOps e contraintuitiv: livrezi mai des, nu mai rar. O livrare mică, automată, de douăzeci de linii, e ușor de verificat și ușor de dat înapoi. O livrare de trei luni de muncă nu poate fi nici verificată, nici dată înapoi. Riscul nu vine din frecvență, vine din dimensiune.

💡 Propoziția care impresionează

„Paradoxul e că stabilitatea crește când livrezi mai des, nu mai rar. O schimbare mică, automată și reversibilă e mai sigură decât un release trimestrial în care se adună trei luni de modificări.”

2 Ziua de lucru a unui DevOps

Asta e ce apare constant în descrierile de rol și în relatările celor din meserie. Nu e o zi tipică — e un amestec care se schimbă zilnic, și întreruperea e parte din meserie.

IntervalCe se întâmplăDe ce contează
DimineațaVerifici tablourile de bord și alertele din noapte. Te uiți dacă a picat vreo livrare automată, dacă a crescut vreo eroare, dacă s-a umplut vreun disc.E momentul în care prinzi problemele înainte să le prindă clientul.
Stand-up15 minute cu echipa de dezvoltare. Ce blochează pe cine. Ce se livrează azi.DevOps stă în echipa de produs, nu într-un turn separat.
Blocul de construcțieMunca planificată: un pipeline nou, un modul Terraform, o migrare, un upgrade de cluster, automatizarea unei operațiuni manuale.Aici produci valoare pe termen lung. E și partea cea mai ușor de mâncat de întreruperi.
Sprijin pentru dezvoltatori„Nu-mi trece build-ul”, „nu pot să mă conectez la baza de date din staging”, „de ce a picat deploy-ul”. Depanare cu altcineva.Jumătate din reputația ta în echipă se face aici.
LivrăriUrmărești livrările spre staging și producție. Dacă ceva nu arată bine, oprești și dai înapoi.La o echipă matură, asta e apăsat un buton și te uiți la grafice.
OricândIncident. Se oprește tot ce era planificat.Vezi capitolul 18.
PeriodicAnaliză de cost cloud, aplicare de actualizări de securitate, revizuire de permisiuni, exercițiu de restaurare din backup.Muncă pe care nu ți-o cere nimeni și care te salvează o dată pe an.
⚠️ Dacă te întreabă „cum arată o zi pentru tine”

Nu recita lista. Alege trei lucruri și leagă-le: „Dimineața mă uit la ce s-a întâmplat peste noapte, apoi am un bloc de muncă planificată — de obicei automatizare sau infrastructură — și restul zilei e împărțit între cereri de la dezvoltatori și livrări. Când apare un incident, tot ce era planificat se oprește.”

3 Harta mare: de la commit la producție

Dacă reții o singură imagine din pagina asta, asta să fie. Fiecare capitol care urmează explică o cutie din desenul de mai jos.

1. Dezvoltator scrie cod git commit + push deschide Pull Request 2. Git (GitHub) păstrează istoricul cere review declanșează pipeline 3. CI — verificare compilează · teste analiză statică · scanare GitHub Actions / Jenkins 4. Artefact imagine Docker etichetată cu commit urcată în registry 5. Aprobare automată în test umană în prod 6. CD — livrare în Kubernetes se actualizează Deployment-ul cu noua imagine rolling update: pod nou → gata → pod vechi oprit dacă nu devine gata, livrarea se blochează 7. Infrastructura cluster, rețea, baze de date descrise în Terraform create înainte, nu manual 8. Observabilitate metrici · loguri · urme · alerte Prometheus · Grafana · Datadog semnalul se întoarce la dezvoltator 9. Incident alertă → gardă → revenire apoi analiză fără vinovați
Drumul complet. Capitolele 4–8 acoperă partea de sus, 9–14 cutia 6, capitolul 15 cutia 7, 17–18 partea de jos.
💡 Folosește desenul ăsta la interviu

Dacă te întreabă ceva larg („povestește-mi despre un pipeline”), descrie fluxul în ordinea asta, cu voce tare, cutie cu cutie. Ai nouă propoziții gata făcute și nu te mai poți bloca la mijloc, pentru că fiecare cutie ți-o dă pe următoarea.

4 Git și fluxul de lucru

Git e locul unde stă adevărul. Tot ce urmează — pipeline, imagine, livrare — pornește de la un commit. Dacă nu e în Git, nu există.

commit

O fotografie a codului la un moment dat, cu un identificator unic (un hash). Ăsta e bobul de nisip din care se construiește tot.

branch

O linie paralelă de lucru. Pleci din main, lucrezi izolat, te întorci. Nimeni nu scrie direct în main.

Pull Request

Cererea de a aduce ramura ta în main. Aici se face revizia de cod și aici rulează automat verificările.

merge

Unirea efectivă. De obicei blocată până când testele trec și cel puțin un coleg a aprobat.

tag

O etichetă pe un commit, de obicei un număr de versiune: v1.4.2. Livrările spre producție se fac pe etichete, nu pe ramuri.

revert

Un commit nou care anulează efectul unuia vechi. Nu șterge istoria — asta contează, pentru că istoria e dovada.

Două modele de ramificare

Trunk-based (recomandat azi)GitFlow (mai vechi)
Cum aratăRamuri scurte, de 1–2 zile, care intră repede în main. main e mereu livrabil.Ramuri lungi: develop, release/*, hotfix/*, feature/*.
Potrivit cândLivrezi des, ai teste automate bune.Livrezi rar, pe versiuni, ai nevoie să menții mai multe versiuni în paralel.
ProblemaCere disciplină de testare; fără teste, rupi main.Ramurile lungi se depărtează și unirea devine dureroasă („merge hell”).
🗣️ Dacă te întreabă ce model preferi

„Trunk-based, cu ramuri de maximum o zi-două și cu main protejat: verificări automate obligatorii și cel puțin o aprobare. Motivul e că ramurile lungi se depărtează de trunchi și problema apare toată deodată, la unire. GitFlow are sens dacă trebuie să susții mai multe versiuni în paralel la clienți.”

GitOps — ideea care leagă Git de Kubernetes

În GitOps, starea dorită a clusterului stă într-un depozit Git, iar un agent care rulează în cluster (Argo CD sau Flux) compară permanent ce e în Git cu ce e în cluster și corectează diferența. Consecințe practice:

  • Nimeni nu mai dă kubectl apply manual în producție. Modifici fișierul, deschizi PR, se aprobă, agentul aplică.
  • Dacă cineva modifică ceva direct pe cluster, agentul îl dă înapoi — deviația se autocorectează.
  • Revenirea la versiunea anterioară e un git revert. Istoricul livrărilor e istoricul Git.

5 CI/CD — conceptele

Cele trei litere care se confundă

CI

Integrare continuă

La fiecare push, codul e compilat și testat automat. Scopul: afli în minute dacă ai stricat ceva, nu în săptămâni.

CD

Livrare continuă

Continuous delivery. Orice commit care trece verificările e gata de pus în producție. Apăsarea butonului rămâne la om.

CD

Desfășurare continuă

Continuous deployment. Același lucru, dar fără buton: ce trece testele ajunge automat la client.

⚠️ Capcană clasică de interviu

„Care e diferența dintre continuous delivery și continuous deployment?” Răspuns scurt: aprobarea umană. La delivery, artefactul e gata oricând, dar cineva apasă. La deployment, nu apasă nimeni. Foarte puține companii fac deployment adevărat în producție, și e absolut normal să spui asta.

Etapele unui pipeline, în ordine

#EtapăCe faceDacă pică
1CheckoutAduce codul din depozit pe mașina de build.Problemă de acces sau de rețea.
2Lint / formatVerifică stilul și greșelile evidente. Rulează în secunde.Oprește imediat. E cel mai ieftin filtru.
3BuildCompilează, instalează dependențele.Nu are rost să testezi ceva ce nu se construiește.
4Teste unitareTestează bucăți mici, izolate. Minute.Oprește. Aici prinzi 80% din regresii.
5Scanare securitateDependențe vulnerabile, secrete uitate în cod, imagini cu CVE-uri.De obicei oprește pe vulnerabilități critice.
6ÎmpachetareConstruiește imaginea Docker și o urcă în registry, etichetată cu hash-ul commitului.Artefactul e ce se va livra. Nu se reconstruiește pentru fiecare mediu.
7Livrare în stagingPune artefactul într-un mediu cât mai apropiat de producție.Aici prinzi problemele de configurație.
8Teste de integrare / e2eVerifică sistemul întreg, cu baze de date reale.Lente și fragile. De aceea sunt puține și bine alese.
9AprobareUn om apasă. Sau o fereastră de timp permisă.Poarta de control pentru producție.
10Livrare în producțieRolling, blue-green sau canary.Vezi mai jos.
11Verificare post-livrareTeste de fum plus urmărirea metricilor câteva minute.Dacă nu arată bine, dai înapoi automat.
💡 Regula artefactului unic

Construiești o singură dată, livrezi peste tot. Aceeași imagine Docker merge în dev, staging și producție; doar configurația diferă, injectată din exterior. Dacă reconstruiești imaginea pentru fiecare mediu, n-ai testat ce ai livrat. E o propoziție care se reține la interviu.

Strategii de livrare

Rolling update — înlocuiești treptat v2 v2 v2? v1 v1 Un pod nou pornește, trece de readiness, abia apoi se oprește unul vechi. Zero downtime. Dacă noul nu devine gata, procesul se OPREȘTE — nu revine singur. Blue-green — două medii complete, comuți traficul BLUE (v1)primește trafic GREEN (v2)gata, fără trafic Comuți balansorul dintr-o dată pe GREEN. Revenirea = comuți înapoi, în secunde. Cost: ții două medii în paralel. Atenție la migrările de bază de date. Canary — dai versiunea nouă la o felie mică v1 — 95% din trafic v2 5% Urmărești erorile și latența pe felia mică. Dacă e curat, crești la 25%, 50%, 100%. Dacă nu, oprești și ai afectat 5% din utilizatori.
Cele trei strategii pe care trebuie să le poți descrie. Kubernetes face rolling din start; blue-green și canary cer unelte în plus (Argo Rollouts, un service mesh, sau două Deployment-uri și un Service comutat).
✅ Legătura cu CV-ul tău

La RapidBid ai făcut blue-green cu Jenkins. E strategia cea mai ușor de explicat dintre cele trei și ai trăit-o. Pregătește-ți o propoziție despre cum arăta comutarea și una despre ce faci cu baza de date, pentru că acolo duce mereu întrebarea următoare: schema trebuie să fie compatibilă cu ambele versiuni în momentul comutării, altfel revenirea nu mai e posibilă.

6 GitHub Actions, în detaliu

E sistemul de automatizare integrat în GitHub. Rulează pe mașini pe care le pune GitHub la dispoziție („runner”), declanșat de evenimente din depozit. A devenit implicit în majoritatea echipelor noi, și e ce cer cele mai multe anunțuri.

6.1 Vocabularul, de la mare la mic

WORKFLOW — un fișier în .github/workflows/ se declanșează la un eveniment: push, pull_request, schedule, workflow_dispatch JOB „build” rulează pe un runner propriu (o mașină curată) runs-on: ubuntu-latest STEP 1 — uses: actions/checkout@v4 STEP 2 — run: npm ci && npm test STEP 3 — run: docker build ... JOB „deploy” alt runner, alt spațiu de lucru — NU vede fișierele lui build needs: build environment: production STEP 1 — autentificare OIDC în cloud STEP 2 — kubectl set image ... STEP 3 — kubectl rollout status Fără „needs”, joburile rulează în paralel. Ca să treacă fișiere între ele, folosești artifacts.
Ierarhia: workflow conține joburi, jobul conține pași. Fiecare job pornește pe o mașină curată — greșeala numărul unu a începătorilor e să presupună că jobul al doilea vede ce a produs primul.
TermenCe e
workflowUn fișier YAML în .github/workflows/. Un depozit poate avea oricâte.
eventCe îl pornește: push, pull_request, schedule (cron), workflow_dispatch (buton manual), release.
jobUn grup de pași care rulează pe aceeași mașină. Joburile rulează în paralel implicit.
runnerMașina care execută. Fie găzduit de GitHub (ubuntu-latest), fie al tău (self-hosted), pentru acces la rețeaua internă sau hardware special.
stepO comandă (run:) sau o acțiune refolosită (uses:).
actionO bucată de automatizare împachetată, a ta sau a altcuiva: actions/checkout, docker/build-push-action.
artifactFișiere salvate la finalul unui job, ca să le poată lua alt job sau ca să le descarci tu.
environmentUn mediu numit (staging, production) care poate avea secrete proprii, reguli de protecție și aprobatori umani.

6.2 Un workflow complet, comentat linie cu linie

.github/workflows/deploy.ymlYAML
# Numele care apare în fila Actions
name: Build and deploy

# ---- CÂND rulează ----
on:
  push:
    branches: [main]          # doar pe main
    paths:                    # și doar dacă s-a schimbat ceva relevant
      - 'src/**'
      - 'Dockerfile'
  pull_request:              # și la fiecare PR, ca să prindem devreme
  workflow_dispatch:         # plus un buton manual în interfață

# ---- Permisiuni: minimul necesar, explicit ----
permissions:
  contents: read            # poate citi codul
  id-token: write          # poate cere un token OIDC (pentru cloud, fără parole)

# ---- Un singur rulaj pe ramură; cel vechi se anulează ----
concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: true

env:
  IMAGE: ghcr.io/${{ github.repository }}

jobs:

  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:                 # același job, pe 3 versiuni de Node, în paralel
        node: [18, 20, 22]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: npm          # păstrează dependențele între rulaje = build mai rapid
      - run: npm ci
      - run: npm test

  build:
    needs: test                # pornește doar dacă „test” a trecut
    runs-on: ubuntu-latest
    outputs:
      tag: ${{ steps.meta.outputs.tag }}
    steps:
      - uses: actions/checkout@v4
      - id: meta
        run: echo "tag=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          push: true
          tags: ${{ env.IMAGE }}:${{ steps.meta.outputs.tag }}

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'   # NU livrăm din PR-uri
    runs-on: ubuntu-latest
    environment: production    # aici se cere aprobarea umană
    steps:
      - uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
          service_account: ${{ secrets.GCP_SA }}
      - run: |
          kubectl set image deployment/api \
            api=${{ env.IMAGE }}:${{ needs.build.outputs.tag }} -n prod
          kubectl rollout status deployment/api -n prod --timeout=180s
💡 Ultima linie e cea mai importantă

kubectl rollout status --timeout=180s transformă livrarea într-una care poate să eșueze. Fără ea, comanda set image întoarce succes instantaneu — ea doar cere schimbarea — iar pipeline-ul se colorează verde chiar dacă niciun pod nou n-a pornit. E exact genul de detaliu pe care îl caută un intervievator bun.

6.3 Secretele și autentificarea

Trei niveluri, în ordinea în care sunt căutate: organizație (partajate de mai multe depozite), depozit, mediu (cele mai restrânse — există doar în joburile care declară environment:). Cele de mediu sunt cele mai sigure pentru producție, fiindcă se pot lega de aprobatori.

Ce trebuie știut:

  • Secretele sunt criptate și ascunse în loguri, dar un pas care rulează cod arbitrar le poate scoate. De aceea contează în ce pași le dai.
  • Nu se transmit automat către workflow-uri din alte depozite (fork-uri) — asta e protecția împotriva unui PR malițios.
  • secrets.GITHUB_TOKEN e generat automat pentru fiecare rulaj și expiră la final. Setează-l implicit pe „read-only” la nivel de organizație și cere explicit mai mult cu permissions:.

OIDC — cum scapi complet de chei

Varianta veche: pui un AWS_SECRET_ACCESS_KEY în secretele depozitului. Cheia e permanentă; dacă scapă, e valabilă până o rotești manual. Varianta actuală, recomandată: OpenID Connect. Workflow-ul cere de la GitHub un token cu durată scurtă care descrie cine este („depozitul X, ramura main”), iar cloudul e configurat să aibă încredere în GitHub și să dea un rol temporar în schimbul acelui token. Nicio parolă nu mai stă nicăieri.

⚠️ Detaliul de securitate care se întreabă

Condiția de încredere din cloud trebuie scrisă pe identificatori imuabili. Dacă scrii repo:firma/serviciu-plati și depozitul e șters, cineva poate crea alt depozit cu exact același nume și moștenește accesul. Forma sigură folosește identificatorii numerici: repository_owner_id:12345:repository_id:67890. Și mai e o subtilitate: la workflow-urile refolosibile, id-token: write trebuie dat în workflow-ul apelant, nu în cel apelat.

6.4 Securitatea acțiunilor externe

  • Fixează pe hash, nu pe etichetă. uses: cutare/actiune@v3 înseamnă „ce e acum în v3” — iar eticheta poate fi mutată de autor peste cod nou. uses: cutare/actiune@a81bbbf... înseamnă exact acel cod. Lași Dependabot să-ți propună actualizările.
  • Evită pull_request_target dacă nu știi exact ce faci. Spre deosebire de pull_request, rulează cu secretele depozitului și cu permisiuni de scriere, în contextul PR-ului — inclusiv al unuia venit de la un străin.
  • Nu pune date din PR direct în run:. Un titlu de PR care conține caractere de shell devine cod executat. Trece valoarea printr-o variabilă de mediu și folosește variabila.

6.5 Refolosire: două mecanisme diferite

Reusable workflow

Un workflow întreg, apelat din altul cu uses: la nivel de job. Conține joburi complete, poate cere aprobări, poate folosi medii. Începe să merite de la trei-patru depozite care fac același lucru.

Composite action

Un grup de pași împachetat ca o acțiune, inserat în interiorul unui job existent. Bun pentru secvențe scurte și repetitive („autentifică-te și configurează unealta”).

6.6 Depanare

  • Adaugă secretul de depozit ACTIONS_STEP_DEBUG=true și logurile devin mult mai detaliate.
  • Un job care „nu pornește” are aproape întotdeauna o condiție if: falsă sau un filtru paths: care nu s-a potrivit — nu o defecțiune.
  • „Merge local, nu merge în CI” înseamnă de obicei o dependență instalată pe calculatorul tău și absentă pe runnerul curat, sau o variabilă de mediu care există doar la tine.
  • Pentru un rulaj lent, uită-te întâi la cache: fără actions/cache sau fără cache: în acțiunea de setup, reinstalezi tot de fiecare dată.
✅ Cum stai tu aici

Asta e zona pe care trebuie să o exersezi cu mâna, nu doar să o citești. Un depozit personal cu un workflow care construiește o imagine și o urcă în GitHub Container Registry îți dă dreptul să spui „am scris workflow-uri” fără să exagerezi. Formularea onestă, dacă te întreabă: „În proiectele mele lucrez cu GitHub Actions; experiența mea de pipeline în producție, la scară, e pe Jenkins și Azure DevOps, iar conceptele se transferă direct — etapele, artefactul unic, poarta de aprobare sunt aceleași.”

7 Jenkins și Azure DevOps — ce ai tu deja

Jenkins e sistemul clasic de automatizare, din 2011, instalat pe servere proprii. Are un controler și agenți care execută. Diferența majoră față de GitHub Actions: Jenkins e o aplicație pe care o administrezi tu — cu pluginuri, cu actualizări, cu backup — în timp ce Actions e un serviciu.

7.1 Anatomia unui Jenkinsfile

Jenkinsfile — declarative pipelineGroovy
pipeline {
  agent { label 'linux-docker' }        // pe ce agent rulează

  environment {                           // variabile pentru tot pipeline-ul
    IMAGE = "registry.intern/api"
    REG   = credentials('registry-creds') // secret din Jenkins Credentials
  }

  triggers { pollSCM('H/5 * * * *') }    // sau webhook din Git

  stages {
    stage('Build') {
      steps { sh 'mvn -B clean package' }
    }
    stage('Test') {
      steps { sh 'mvn test' }
      post { always { junit 'target/surefire-reports/*.xml' } }
    }
    stage('Image') {
      steps { sh "docker build -t $IMAGE:${GIT_COMMIT} ." }
    }
    stage('Deploy green') {
      steps { sh './deploy.sh green' }
    }
    stage('Switch traffic') {
      input { message 'Comut traficul pe green?' }  // aprobare umană
      steps { sh './switch.sh green' }
    }
  }

  post {
    failure { mail to: 'echipa@firma.ro', subject: "Pipeline picat: ${env.JOB_NAME}" }
    always  { cleanWs() }
  }
}

7.2 Traducerea Jenkins → GitHub Actions

Dacă te întreabă „ai lucrat cu GitHub Actions?”, răspunsul bun nu e da sau nu. E să arăți că știi corespondența — pentru că exact asta face cineva care migrează, iar migrarea e o sarcină frecventă în anunțuri.

JenkinsGitHub ActionsObservație
agent { label 'x' }runs-on: sau container:Agentul Jenkins e o mașină pe care o întreții tu; runnerul GitHub e curat la fiecare rulaj.
stages { stage {...} }jobs:Atenție: etapele Jenkins împart implicit acelaşi spațiu de lucru. Joburile Actions nu. Dependențele se redesenează cu needs: plus artefacte.
environment { ... }env: la nivel de job sau de pasAproape identic.
credentials('id')${{ secrets.NUME }}Actions adaugă nivelul „environment secrets”, pe care Jenkins nu-l are nativ.
triggers { pollSCM }on: push / on: scheduleActions e declanșat de eveniment, nu interoghează depozitul.
input { message }environment: cu required reviewersAprobarea umană se mută din pipeline în configurarea mediului.
sh 'comanda'run: comandaIdentic în practică.
pluginuri (peste 1800)acțiuni din MarketplaceAmbele sunt cod al altcuiva. La Actions îl fixezi pe hash; la Jenkins actualizezi și speri.
post { always { } }if: always() pe un pasAceeași idee, altă sintaxă.
⚠️ Capcana numărul unu la migrare

În Jenkins, etapa „Test” vede fișierele produse de etapa „Build”, fiindcă rulează în același director de lucru. În GitHub Actions, fiecare job pornește pe o mașină goală. Dacă traduci etapă-cu-job fără să te gândești, jobul de test nu găsește nimic. Soluția: ori pui totul într-un singur job cu mai mulți pași, ori urci rezultatul cu actions/upload-artifact și îl descarci în jobul următor.

7.3 Azure DevOps, pe scurt

Suita Microsoft: Repos (Git), Pipelines (CI/CD), Boards (tichete), Artifacts (pachete), Test Plans. Pipeline-urile sunt YAML, apropiate de Actions ca formă: trigger, pool, stages, jobs, steps. Ce e specific: service connection (identitatea cu care pipeline-ul intră în Azure), variable group (variabile partajate, legate opțional de Key Vault), environment cu approvals and checks — echivalentul porții de producție.

✅ Legătura cu CV-ul tău

Pe RapidBid ai avut Azure DevOps și Jenkins cu blue-green. Nu minimaliza asta pentru că nu e GitHub Actions. Un pipeline e un pipeline: etape, artefact, poartă, revenire. Formulare care funcționează: „Pipeline-urile mele de producție au fost pe Jenkins și Azure DevOps. Conceptele sunt aceleași, sintaxa diferă; ce e specific la Actions e modelul de joburi izolate și autentificarea OIDC, și pe astea le-am studiat pentru că sunt diferențele care te pot încurca la migrare.”

8 Docker și containerele

8.1 Ce e, de fapt, un container

Un container este un proces obișnuit pe mașina gazdă, care vede doar o felie din sistem: propriul său sistem de fișiere, propria rețea, propria listă de procese. Izolarea e făcută de nucleul Linux (namespaces și cgroups). Nu are nucleu propriu — îl folosește pe al gazdei.

Mașini virtuale — fiecare cu sistem de operare propriu Hardware Sistem gazdă + hypervisor SO complet~2 GB aplicație SO complet~2 GB aplicație SO complet~2 GB aplicație Pornire în minute. Izolare foarte puternică. Containere — nucleu comun Hardware Sistem gazdă (un singur nucleu Linux) Motor de containere (containerd) aplicație~50 MB aplicație~50 MB aplicație~50 MB aplicație~50 MB Pornire în secunde. Izolare mai slabă: o breșă în nucleu afectează tot.
Întrebarea „VM versus container” apare aproape sigur. Răspunsul complet include și compromisul de securitate, nu doar „containerul e mai mic”.

8.2 Imagine și container

Imaginea e șablonul: un pachet nemodificabil cu aplicația și tot ce-i trebuie. Containerul e o instanță care rulează din acea imagine. O imagine, oricâte containere. Analogia care se reține: imaginea e clasa, containerul e obiectul.

8.3 Straturi și de ce contează ordinea

Fiecare instrucțiune din Dockerfile creează un strat. Straturile se pun în memoria-tampon și se refolosesc — dar când un strat se schimbă, toate cele de după el se reconstruiesc. De aici regula practică: pune întâi ce se schimbă rar.

❌ Greșit — reinstalează tot la fiecare modificareDockerfile
FROM node:20
WORKDIR /app
COPY . .           # orice fișier schimbat
RUN npm install    # invalidează asta
CMD ["node","server.js"]
✅ Corect — dependențele se refolosescDockerfile
FROM node:20
WORKDIR /app
COPY package*.json ./   # se schimbă rar
RUN npm ci
COPY . .               # se schimbă des, dar e ultimul
CMD ["node","server.js"]

8.4 Build în mai multe etape

Construiești într-o imagine mare, cu compilator și unelte, apoi copiezi doar rezultatul într-o imagine mică. Imaginea finală nu conține compilatorul, deci e mai mică și are o suprafață de atac mai redusă.

Dockerfile multi-stage + bune practici de securitateDockerfile
# --- etapa 1: construire ---
FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/api ./cmd/api

# --- etapa 2: rulare ---
FROM gcr.io/distroless/static:nonroot
COPY --from=build /out/api /api
USER nonroot:nonroot      # NU rulăm ca root
EXPOSE 8080
ENTRYPOINT ["/api"]

8.5 Reguli de securitate pentru imagini

  • Nu rula ca root. Implicit, procesul din container e root. Dacă cineva iese din container, iese ca root.
  • Sistem de fișiere doar-citire pentru procesul principal, plus volume pentru ce chiar trebuie scris.
  • Nu băga secrete în imagine. Chiar dacă le ștergi într-un strat ulterior, stratul în care au fost adăugate rămâne și poate fi extras. Secretele se injectează la rulare.
  • Fixează versiunile de bază. FROM node:20.11.1-alpine, nu FROM node:latest — altfel build-ul de azi diferă de cel de ieri.
  • Scanează imaginea (Trivy, Grype) ca pas în pipeline.

8.6 Ce se pierde când moare containerul

Sistemul de fișiere al containerului e efemer: dispare odată cu el. Ce trebuie să supraviețuiască merge într-un volum sau într-un serviciu extern (bază de date, obiect storage). Logurile se scriu pe ieșirea standard, nu în fișiere din container — le colectează platforma.

✅ Legătura cu CV-ul tău

La DISH ai avut aplicațiile în containere, în Kubernetes. Dacă te întreabă despre Docker, ancora ta e practică: „Am lucrat cu imaginile în contextul livrării în GKE — o imagine per serviciu, etichetată cu commitul, aceeași imagine promovată între medii.” Apoi arată că știi cele trei reguli de igienă: multi-stage, utilizator non-root, fără secrete în straturi.

9 Kubernetes — fundamentele

9.1 Problema pe care o rezolvă

Ai cincizeci de containere care trebuie să ruleze pe douăzeci de mașini. Cine decide pe care mașină merge fiecare? Ce se întâmplă când o mașină moare la trei noaptea? Cum trimiți trafic către containere care apar și dispar și își schimbă adresa? Cum livrezi o versiune nouă fără să oprești serviciul? Kubernetes e răspunsul la toate astea, la un loc.

9.2 Ideea centrală: stare dorită și reconciliere

💡 Dacă reții un singur lucru despre Kubernetes

Tu nu dai comenzi lui Kubernetes. Tu declari cum vrei să arate lumea — „vreau trei copii ale aplicației, versiunea 2.1, cu atâta memorie” — și un set de bucle de control compară permanent realitatea cu declarația ta și acționează ca să le apropie. Ștergi un pod, apare altul. Moare un nod, podurile lui se recreează în altă parte. Nu pentru că cineva a reacționat, ci pentru că bucla nu se oprește niciodată.

STARE DORITĂ fișierul YAML scris de tine „replicas: 3, imagine v2.1” CONTROLLER compară permanent dorit ≠ real ? → acționează (la nesfârșit) STARE REALĂ ce rulează acum „2 poduri, unul a murit” observă din nou, la fiecare câteva secunde
Bucla de reconciliere. Din ea decurge tot restul: autovindecarea, scalarea, livrarea treptată.

9.3 Arhitectura clusterului

CONTROL PLANE — creierul în cloud administrat (GKE/EKS/AKS) nu-l vezi și nu-l întreții kube-apiserver singura ușă de intrare. Tot ce faci trece pe aici. Validează și autorizează. etcd baza de date a clusterului scheduler alege nodul pentru pod controller-manager buclele de reconciliere: replici, noduri, servicii, joburi cloud-controller-manager cere balansoare și discuri de la furnizorul de cloud NODURI — mușchii. Aici rulează aplicațiile. Nod 1 kubelet — execută ce-i spune apiserver kube-proxy — rutare spre servicii containerd — pornește containerele pod pod Nod 2 kubelet kube-proxy containerd pod pod kubectl — unealta ta vorbește DOAR cu apiserver, prin HTTPS. Nu se conectează la noduri. Dacă moare un nod: controllerul observă, podurile lui sunt recreate pe alt nod. Dacă moare control plane-ul: ce rulează continuă, dar nu se mai poate schimba nimic.
Întrebare frecventă: „ce se întâmplă dacă pică control plane-ul?” Răspuns: aplicațiile continuă să ruleze, dar clusterul nu mai reacționează la schimbări și nu mai poți livra.
🗣️ Explicația de 30 de secunde

„Kubernetes e un sistem declarativ. Descriu starea dorită într-un fișier, o trimit la API server, care o salvează în etcd. De acolo, controllerele compară permanent ce am cerut cu ce există și corectează diferența: schedulerul alege nodul, kubelet-ul de pe nod pornește containerul. Consecința practică e că nu trebuie să reacționez eu când moare ceva — bucla o face.”

10 Kubernetes — obiectele

INGRESS — ușa dinspre internet rutare după nume de domeniu și cale · termină TLS-ul SERVICE — adresă stabilă un IP și un nume care nu se schimbă · distribuie spre podurile GATA DEPLOYMENT — ce vrei să ruleze versiunea imaginii · câte copii · cum se face actualizarea REPLICASET — ține numărul de copii creat automat de Deployment · unul nou la fiecare versiune POD 1+ containere IP propriu, efemer POD rulează pe un nod nu se repară, se înlocuiește POD poate primi ConfigMap / Secret NAMESPACE o partiție logică dev / staging / prod sau o echipă poartă cote de resurse NU e graniță de securitate CONFIGURAȚIE ConfigMap = setări Secret = parole montate ca fișiere sau ca variabile de mediu ATENȚIE: Secret e doar base64
Ierarhia completă. Când explici la interviu, mergi de jos în sus sau de sus în jos — dar mergi ordonat, nu sări.

10.1 Pod

Cea mai mică unitate pe care Kubernetes o poate programa pe un nod. Conține unul sau mai multe containere care împart aceeași adresă IP, aceleași porturi și pot împărți volume — containerele dintr-un pod se văd între ele pe localhost. În practică, un container principal, plus eventual unul ajutător (sidecar): colector de loguri, proxy de rețea.

Un pod este de unică folosință. Nu se repară: se șterge și se creează altul, cu alt IP. Asta e și motivul pentru care nu te conectezi niciodată direct la IP-ul unui pod, ci la un Service.

10.2 Deployment

Obiectul cu care lucrezi zilnic. Îi spui ce imagine, câte copii și cum să facă actualizarea; el creează un ReplicaSet, care creează podurile. La fiecare versiune nouă creează un ReplicaSet nou și îl reduce pe cel vechi — de aici vine și posibilitatea de revenire.

deployment.yaml — comentatYAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: prod
spec:
  replicas: 3                 # câte copii vreau
  selector:
    matchLabels: { app: api }  # ce poduri îmi aparțin
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1             # am voie cu 1 pod în plus în timpul livrării
      maxUnavailable: 0       # dar NICIUNUL în minus → zero downtime
  template:                    # de aici în jos e rețeta unui pod
    metadata:
      labels: { app: api }
    spec:
      containers:
        - name: api
          image: registry/api:9f2c1ab   # NU „latest”: vrei să știi ce rulează
          ports: [{ containerPort: 8080 }]
          env:
            - name: DB_HOST
              valueFrom:
                configMapKeyRef: { name: api-config, key: db_host }
            - name: DB_PASS
              valueFrom:
                secretKeyRef: { name: api-secret, key: db_pass }
          resources:
            requests: { cpu: "200m", memory: "256Mi" }  # pentru planificare
            limits:   { cpu: "1",    memory: "512Mi" }  # plafon la rulare
          readinessProbe:                            # pot primi trafic?
            httpGet: { path: /ready, port: 8080 }
            initialDelaySeconds: 5
            periodSeconds: 5
          livenessProbe:                             # mai sunt viu?
            httpGet: { path: /healthz, port: 8080 }
            initialDelaySeconds: 20
            periodSeconds: 10

10.3 Service — cele patru tipuri

TipCe faceCând îl folosești
ClusterIPIP intern, vizibil doar din cluster. Implicit.Comunicare între servicii. Majoritatea cazurilor.
NodePortDeschide același port pe fiecare nod.Rar, în dezvoltare sau în spatele unui balansor propriu.
LoadBalancerCere furnizorului de cloud un balansor real, cu IP public.Expunere directă. Costă: un balansor per serviciu.
ExternalNameDoar un alias DNS către un nume extern.Ca să numești la fel o bază de date gestionată din afara clusterului.

Serviciul găsește podurile prin etichete (selector), nu prin nume. Dacă schimbi eticheta din pod și uiți selectorul, serviciul rămâne fără destinații și primești eroare de conectare deși toate podurile rulează perfect. E una dintre cele mai frecvente cauze de „merge pe podul X, nu merge prin serviciu”.

10.4 Ingress

Un Service de tip LoadBalancer per aplicație devine scump și greu de administrat. Ingress e stratul de deasupra: un singur punct de intrare, care rutează după numele domeniului și după cale către servicii interne diferite, și care termină TLS-ul într-un singur loc. Are nevoie de un ingress controller instalat în cluster (nginx, Traefik, cel al furnizorului) — obiectul Ingress singur nu face nimic.

10.5 ConfigMap și Secret

Configurația nu se pune în imagine. Se pune în ConfigMap (setări obișnuite) sau Secret (parole, chei, certificate) și se injectează în pod, fie ca variabile de mediu, fie ca fișiere montate.

⚠️ Întrebarea-capcană preferată a intervievatorilor

„Care e diferența între ConfigMap și Secret?” Răspunsul greșit e „Secret e criptat”. Secret e doar codificat base64, ceea ce înseamnă zero protecție — oricine îl poate citi cu base64 -d. Ce aduce în plus: e tratat diferit (nu apare în loguri, poate fi criptat în etcd dacă activezi encryption at rest, poate fi restricționat separat prin RBAC). Pentru secrete adevărate se folosesc soluții externe: External Secrets Operator cu Vault, AWS Secrets Manager sau Google Secret Manager, și Sealed Secrets dacă vrei secretele în Git.

10.6 Celelalte tipuri de workload

ObiectPentru ceDiferența esențială
DeploymentAplicații fără stare: API-uri, site-uri, servicii.Podurile sunt interschimbabile, primesc nume aleatorii.
StatefulSetBaze de date, cozi, orice are identitate: PostgreSQL, Kafka, Elasticsearch.Nume stabile și ordonate (db-0, db-1), fiecare cu discul lui, pornire și oprire în ordine.
DaemonSetUn pod pe fiecare nod: colector de loguri, agent de monitorizare, plugin de rețea.Nu specifici numărul de copii — depinde de numărul de noduri.
JobO sarcină care rulează o dată și se termină: o migrare, un import.Urmărește terminarea cu succes, nu menținerea în funcțiune.
CronJobUn Job pe program: backup nocturn, raport zilnic.Sintaxă cron obișnuită.
🗣️ Deployment sau StatefulSet — răspunsul complet

„Deployment pentru tot ce n-are stare, fiindcă podurile sunt interschimbabile și pot fi înlocuite în orice ordine. StatefulSet când fiecare instanță are identitate proprie și disc propriu — o bază de date, un Kafka. Diferența practică apare la un nod căzut: cu Deployment podul se recreează imediat oriunde. Cu StatefulSet, Kubernetes nu recreează automat podul cât timp nu e sigur că cel vechi a murit, tocmai ca să nu ajungă două instanțe să scrie pe același disc. Asta înseamnă că recuperarea e mai lentă, intenționat.”

10.7 Stocare

Trei obiecte legate: PersistentVolume (discul propriu-zis), PersistentVolumeClaim (cererea aplicației: „vreau 20 GB rapizi”) și StorageClass (rețeta prin care se creează automat discul la furnizor). În practică scrii doar PVC-ul; restul se rezolvă singur. De reținut: un disc în cloud e de obicei legat de o zonă — un pod care are nevoie de el poate fi programat doar în acea zonă.

11 Livrare și sănătate: cele două lucruri care se întreabă cel mai des

11.1 Rolling update, pas cu pas

T0 — starea inițială: 3 poduri v1, toate primesc trafic v1 ✓ v1 ✓ v1 ✓ T1 — pornește un pod v2 (maxSurge: 1). NU primește încă trafic. v1 ✓ v1 ✓ v1 ✓ v2 … readinessProbe încă nu răspunde cu succes T2 — v2 devine GATA → intră în Service. Abia ACUM se oprește un v1. v1 ✓ v1 ✓ v1 ✗ v2 ✓ podul v1 primește SIGTERM și are timp să termine cererile în curs T3 — se repetă până toate sunt v2. Dacă un v2 NU devine gata, procesul SE OPREȘTE aici. v2 ✓ v2 ✓ v2 ✓ Revenirea NU e automată: o ceri tu cu kubectl rollout undo
Cheia întregului mecanism e readinessProbe. Fără ea, Kubernetes consideră podul gata din secunda în care containerul pornește și îi trimite trafic înainte ca aplicația să fie pregătită — de aici erorile în timpul livrării.
⚠️ Cea mai valoroasă propoziție despre rolling update

„Kubernetes nu revine automat la versiunea anterioară. Dacă podurile noi nu devin gata, livrarea se blochează și rămâi cu versiunea veche în funcțiune — ceea ce e un comportament bun, pentru că serviciul nu cade. Dar cineva trebuie să observe și să dea kubectl rollout undo. De aceea pipeline-ul meu are kubectl rollout status --timeout, care transformă blocajul într-un eșec vizibil de pipeline.”

Comenzile de livrarebash
# schimbă imaginea
kubectl set image deployment/api api=registry/api:9f2c1ab -n prod

# urmărește — iese cu cod diferit de zero dacă nu reușește în 180s
kubectl rollout status deployment/api -n prod --timeout=180s

# istoricul versiunilor
kubectl rollout history deployment/api -n prod

# revenire la versiunea anterioară
kubectl rollout undo deployment/api -n prod

# revenire la o revizie anume
kubectl rollout undo deployment/api --to-revision=4 -n prod

# repornire curată, fără schimbare de imagine (util după schimbarea unui Secret)
kubectl rollout restart deployment/api -n prod

11.2 Probe — cele trei și la ce folosesc

readinessProbe Întrebarea: „pot primi trafic ACUM?” Dacă eșuează: podul e scos din Service. Rămâne pornit. Folosire: aștept baza de date, încarc memoria-tampon, sunt supraîncărcat temporar. Aici PUI verificarea dependențelor. livenessProbe Întrebarea: „mai sunt viu?” Dacă eșuează: containerul e OMORÂT și repornit. Folosire: proces blocat, memorie epuizată, blocaj intern. NU pui aici verificarea bazei de date — vezi capcana de mai jos. startupProbe Întrebarea: „am terminat de pornit?” Cât timp rulează ea, celelalte două sunt suspendate. Folosire: aplicații care pornesc lent (Java vechi, migrări la boot). Rezolvă situația în care liveness omoară podul înainte să pornească.
Trei întrebări diferite, trei consecințe diferite. Confuzia dintre primele două produce cele mai multe pene autoprovocate din Kubernetes.
🛑 Capcana livenessProbe — merită să o povestești

Dacă pui în livenessProbe o verificare a bazei de date, iată ce se întâmplă când baza are o secundă proastă: toate podurile eșuează proba în același timp, Kubernetes le omoară pe toate, ele repornesc, se conectează iar la baza deja încărcată, eșuează iar. Ai transformat o degradare temporară într-o pană totală, cu bucla de repornire ca amplificator. Regula: liveness verifică doar dacă procesul e sănătos; dependențele externe se verifică în readiness, unde consecința e retragerea din trafic, nu execuția.

11.3 Oprirea elegantă

Când un pod e oprit, Kubernetes face două lucruri în paralel: îl scoate din lista destinațiilor Service și trimite SIGTERM procesului. Fiindcă sunt în paralel, există o fereastră de câteva sute de milisecunde în care podul încă primește cereri noi dar deja a început să se oprească. Soluția standard e un preStop care așteaptă câteva secunde înainte de oprire, plus o aplicație care tratează SIGTERM: nu mai acceptă conexiuni noi, termină ce are în lucru, apoi iese. terminationGracePeriodSeconds (implicit 30) e răbdarea; după el vine SIGKILL.

12 Resurse și scalare

12.1 requests și limits

requests — „rezerv atât” Folosit de SCHEDULER ca să aleagă nodul. E o rezervare: nodul garantează atâta. Prea mare → podul nu încape nicăieri → Pending, chiar dacă nodurile au loc liber real. Prea mic → podul e programat pe un nod aglomerat și e primul evacuat când nodul rămâne fără resurse. Se stabilește după consumul REAL măsurat. limits — „nu depăși atât” Plafonul la rulare. Aplicat de nucleu. CPU peste limită → ESTE ÎNCETINIT (throttling). Procesul trăiește, dar devine lent. Se vede ca latență mare fără nicio eroare — greu de diagnosticat. Memorie peste limită → ESTE OMORÂT. OOMKilled, cod de ieșire 137. Memoria nu poate fi încetinită — ori o ai, ori nu.
Asimetria CPU/memorie e una dintre cele mai întrebate chestiuni. Dacă o explici corect, arăți că ai depanat, nu doar citit.
Clasă QoSCum o obțiiCe înseamnă
Guaranteedrequests = limits, pentru toate resursele.Ultimul evacuat când nodul e sub presiune. Pentru componentele critice.
Burstablerequests < limits.Cazul obișnuit. Poate depăși temporar rezervarea dacă nodul are loc.
BestEffortFără requests și fără limits.Primul omorât când nodul rămâne fără memorie. De evitat în producție.

12.2 Cele trei feluri de scalare

HPA — orizontal

Schimbă numărul de poduri după o metrică (CPU, memorie sau una proprie). Cea folosită cel mai des.

VPA — vertical

Schimbă requests/limits ale podurilor. Utilă ca să afli valorile corecte; în modul automat repornește podurile.

Cluster Autoscaler

Adaugă sau scoate noduri. Se aprinde când poduri rămân Pending pentru că nu mai e loc.

⚠️ Când HPA pe CPU e greșit

Dacă serviciul e limitat de altceva decât procesor — numărul de conexiuni către baza de date, lungimea unei cozi, un API extern lent — atunci CPU-ul rămâne mic în timp ce serviciul suferă, iar HPA nu se aprinde. Mai rău: dacă se aprinde, podurile noi cer și ele conexiuni la aceeași bază de date și înrăutățesc situația. Răspunsul bun: scalezi pe metrica care descrie de fapt încărcarea — adâncimea cozii, cereri în așteptare — prin metrici personalizate sau KEDA.

12.3 Unde pun podurile — plasare

  • nodeSelector / nodeAffinity — „pune podul doar pe noduri cu GPU” sau „preferabil în zona A”.
  • podAntiAffinity — „nu pune două copii ale aceluiași serviciu pe același nod”. Fără asta, cele trei replici pot ajunge pe un singur nod și un nod căzut înseamnă serviciu căzut.
  • topologySpreadConstraints — distribuie uniform peste zone de disponibilitate.
  • taints și tolerations — mecanismul invers: nodul respinge tot, mai puțin podurile care „tolerează” explicit. Așa se rezervă noduri pentru sarcini speciale.
  • PodDisruptionBudget — „minimum două poduri disponibile oricând”. Protejează în timpul operațiunilor planificate, ca actualizarea nodurilor.

13 Securitate în cluster

13.1 RBAC — cine are voie ce

Patru obiecte, două perechi. Role dă permisiuni într-un singur namespace; ClusterRole în tot clusterul. RoleBinding și ClusterRoleBinding leagă rolul de cineva: un utilizator, un grup sau un ServiceAccount (identitatea cu care rulează un pod).

RBAC minim pentru o echipăYAML
kind: Role
metadata: { namespace: echipa-a, name: dezvoltator }
rules:
  - apiGroups: [""]
    resources: [pods, pods/log, services, configmaps]
    verbs: [get, list, watch]          # doar citire
  - apiGroups: [apps]
    resources: [deployments]
    verbs: [get, list, watch, patch]   # poate schimba imaginea, nu poate șterge

Comanda pe care o folosești ca să verifici: kubectl auth can-i delete pods --as=ana -n prod.

13.2 Pod Security Admission

PodSecurityPolicy a fost scos din uz în versiunea 1.21 și eliminat în 1.25. Înlocuitorul integrat e Pod Security Admission: pui o etichetă pe namespace și clusterul refuză podurile care nu respectă nivelul.

NivelCe permite
privilegedTot. Pentru componente de sistem.
baselineBlochează escaladările evidente: containere privilegiate, namespace-ul de procese al gazdei.
restrictedCel mai strict: fără root, fără escaladare de privilegii, sistem de fișiere doar-citire. Ținta pentru aplicații.

Se aplică în trei moduri: enforce (respinge), audit (scrie în jurnal) și warn (avertizează utilizatorul). Calea obișnuită de adopție e să pornești cu warn, să vezi ce s-ar strica, apoi să treci pe enforce. Pentru reguli mai complexe se folosesc OPA Gatekeeper sau Kyverno.

13.3 NetworkPolicy

Implicit, în Kubernetes orice pod poate vorbi cu orice pod, din orice namespace. E cea mai surprinzătoare valoare implicită din sistem și un răspuns bun la „ce ai îmbunătăți într-un cluster moștenit”. NetworkPolicy schimbă asta: descrii ce trafic e permis, iar restul e blocat. Atenție: are efect doar dacă plugin-ul de rețea îl susține (Calico, Cilium; unele configurații simple îl ignoră tăcut).

13.4 Namespace-ul nu e graniță de securitate

⚠️ Formularea corectă

Namespace-ul e o partiție logică: grupează obiecte, susține cote de resurse și dă un punct de aplicare pentru RBAC și NetworkPolicy. Dar podurile din namespace-uri diferite rulează pe aceleași noduri și împart același nucleu. Izolare reală între chiriași diferiți înseamnă noduri separate, sau clustere separate. Dacă spui asta la interviu, te desparți imediat de cei care au citit doar tutoriale.

13.5 Restul listei de igienă

  • Dezactivează montarea automată a tokenului de ServiceAccount în podurile care nu vorbesc cu API-ul (automountServiceAccountToken: false).
  • Criptează etcd în repaus, sau ține secretele într-un seif extern.
  • Nu folosi :latest. Nu poți ști ce rulează și nu poți reproduce o problemă.
  • Scanează imaginile în pipeline și blochează pe vulnerabilități critice.
  • Ține jurnalul de audit al API-ului pornit. E singura sursă care îți spune cine a schimbat ce.

14 Depanare pe cluster — partea cea mai întrebată

La interviurile de Kubernetes din 2026, întrebările de tip „ce e un pod” sunt doar încălzirea. Partea care contează e scenariul: „ți se spune că serviciul e căzut, ce faci?”. Se testează metoda, nu memoria.

14.1 Ordinea în care investighezi

Secvența de bază, în ordinebash
# 1. Ce stare au podurile? (aici afli 60% din răspuns)
kubectl get pods -n prod -o wide

# 2. De ce e în starea aia? Uită-te la EVENIMENTE, la finalul ieșirii.
kubectl describe pod api-7d9f-x2k -n prod

# 3. Ce spune aplicația?
kubectl logs api-7d9f-x2k -n prod
kubectl logs api-7d9f-x2k -n prod --previous   # logurile containerului MORT — esențial la CrashLoop

# 4. Ce s-a întâmplat recent în tot namespace-ul?
kubectl get events -n prod --sort-by=.lastTimestamp | tail -30

# 5. Serviciul are destinații? (lista goală = selector greșit sau poduri ne-gata)
kubectl get endpoints api -n prod

# 6. Din interiorul unui pod: DNS și conectivitate
kubectl exec -it api-7d9f-x2k -n prod -- sh
  nslookup api.prod.svc.cluster.local
  wget -qO- http://api:8080/healthz

# 7. Consumul real
kubectl top pods -n prod
kubectl top nodes

# 8. Nodurile sunt sănătoase?
kubectl get nodes
kubectl describe node nod-3 | grep -A5 Conditions

14.2 Tabelul de diagnostic

Ce veziCe înseamnăCauze frecventeUnde te uiți
PendingPodul nu a fost programat pe niciun nod.Requests prea mari; nu există nod care să tolereze taint-ul; PVC nelegat; zonă greșită.kubectl describe pod → secțiunea Events spune exact de ce a refuzat schedulerul.
ImagePullBackOff
ErrImagePull
Nu poate descărca imaginea.Nume sau etichetă greșită; registry privat fără imagePullSecret; limită de descărcări atinsă.Events; verifică manual numele imaginii.
CrashLoopBackOffContainerul pornește și moare repetat; Kubernetes așteaptă tot mai mult între încercări.Eroare de configurație; lipsește o variabilă; nu se poate conecta la baza de date; comandă greșită; liveness prea agresivă.kubectl logs --previous. Fără --previous vezi doar containerul nou, care abia a pornit.
OOMKilled (137)A depășit limita de memorie și a fost omorât de nucleu.Limită prea mică; scurgere de memorie; un lot de date prea mare încărcat în memorie.kubectl describe pod → Last State: Terminated, Reason: OOMKilled. Compară cu kubectl top.
Running dar 0/1 READYRulează, dar readinessProbe nu trece. Nu primește trafic.Calea sau portul probei greșit; aplicația chiar nu e gata; o dependență lipsește.kubectl describe pod → mesajul probei; apoi cheamă manual calea din interiorul podului.
Terminating blocatNu se poate șterge.Un finalizer care așteaptă ceva; un volum care nu se poate demonta; procesul ignoră SIGTERM.kubectl get pod -o yaml → câmpul finalizers.
EvictedNodul a rămas fără resurse și a dat podul afară.Presiune de memorie sau disc pe nod; podul era BestEffort.kubectl describe node → Conditions.
Node NotReadyNodul nu mai raportează.kubelet oprit; disc plin; rețea; mașina a dispărut.Consolă cloud; jurnalele kubelet.
🗣️ Răspunsul complet la „serviciul e căzut, ce faci?”

„Întâi confirm ce înseamnă căzut — eroare, lentoare, sau doar pentru unii utilizatori — și de când. Apoi mă uit la poduri: câte sunt gata din câte ar trebui. Dacă podurile sunt bine, problema e mai sus: serviciu, ingress, DNS, certificat. Dacă podurile nu sunt bine, descriu podul și citesc evenimentele, pentru că starea îmi spune deja clasa problemei.

În paralel întreb ce s-a schimbat în ultimele ore, fiindcă în majoritatea cazurilor există o schimbare. Dacă e o livrare recentă, prima mea mișcare e să revin la versiunea anterioară și abia apoi să înțeleg de ce — restabilesc serviciul întâi, diagnostichez după. Comunic pe canalul de incident la început și la fiecare pas important, ca să nu fie nevoie să mă întrerupă cineva ca să întrebe.”

✅ Aici ești tare

Metoda de mai sus e exact ce ai făcut 24/7 la Metro și la Volvo, doar cu alte unelte. Spune-o ca pe o metodă pe care o ai, nu ca pe o listă pe care ai învățat-o: „Ordinea mea e aceeași de mulți ani — confirm simptomul, restabilesc serviciul, apoi caut cauza. Pe Kubernetes se schimbă comenzile, nu metoda.”

14.3 Un traseu de depanare de rețea, până la capăt

„Serviciul A nu poate ajunge la serviciul B.” Ordinea care găsește problema fără ghicit:

  1. kubectl get pods -l app=b — există poduri și sunt READY? Un pod ne-gata nu e în serviciu.
  2. kubectl get endpoints b — lista e goală? Atunci selectorul serviciului nu se potrivește cu etichetele podurilor. Asta e cauza cea mai frecventă.
  3. Din podul A: nslookup b.namespace.svc.cluster.local — dacă DNS-ul nu răspunde, problema e CoreDNS, nu serviciul tău.
  4. Din podul A: wget -qO- http://b:8080/ — dacă DNS-ul merge dar conexiunea e refuzată, verifică portul: port din Service trebuie să corespundă cu targetPort și cu portul real al aplicației.
  5. kubectl get networkpolicy -n namespace — există o politică ce blochează? Dacă e prima politică din namespace, a schimbat implicitul din „totul permis” în „doar ce e scris”.

15 Terraform și infrastructura ca și cod

15.1 Ideea

Infrastructura nu se creează cu mâna din interfața cloudului. Se descrie în fișiere, fișierele stau în Git, iar unealta aplică diferența dintre ce e scris și ce există. Consecințe: mediile sunt reproductibile, schimbările trec prin revizie ca orice cod, și există un istoric al motivelor.

15.2 Ciclul

write descrii resursele în fișiere .tf init descarcă providerii leagă starea la distanță plan ce s-ar schimba, fără să schimbe nimic apply execută planul actualizează starea destroy șterge tot ce e în stare planul se pune în Pull Request și se citește de un om ÎNAINTE de apply
Fluxul standard: plan automat pe fiecare PR, apply doar după aprobare și doar din pipeline, niciodată de pe laptop.

15.3 Starea — subiectul care separă

Terraform ține un fișier de stare care leagă ce ai scris de ce există în realitate. Fără el nu știe dacă trebuie să creeze sau să modifice.

  • Starea nu stă local. Stă într-un depozit comun (S3 cu blocare în DynamoDB, un container Azure Storage, GCS), altfel doi colegi care aplică simultan își calcă starea unul altuia.
  • Blocarea e obligatorie. Două apply în paralel pe aceeași stare o pot corupe.
  • Starea conține secrete în clar. Parola unei baze de date creată de Terraform apare în fișierul de stare. Deci: criptare, acces restrâns, și niciodată în Git.
  • Deviația apare când cineva modifică manual în consolă. terraform plan o arată. Remediul tehnic e import sau realiniere; remediul real e organizațional — se scot drepturile de scriere manuală în producție.
main.tf — structura obișnuităHCL
terraform {
  required_version = "~> 1.9"
  backend "s3" {                    # starea, la distanță și blocabilă
    bucket         = "tf-state-firma"
    key            = "prod/retea.tfstate"
    region         = "eu-central-1"
    dynamodb_table = "tf-locks"
    encrypt        = true
  }
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.60" }
  }
}

variable "mediu" {
  type    = string
  default = "prod"
}

module "retea" {                     # module = refolosire între medii
  source = "./modules/retea"
  mediu  = var.mediu
  cidr   = "10.20.0.0/16"
}

resource "aws_db_instance" "principal" {
  identifier        = "db-${var.mediu}"
  engine            = "postgres"
  instance_class    = "db.t4g.medium"
  allocated_storage = 50
  lifecycle {
    prevent_destroy = true            # plasă: nu se poate șterge din greșeală
  }
}

output "db_endpoint" {
  value = aws_db_instance.principal.endpoint
}
💡 Trei răspunsuri care sună a experiență

Cum separi mediile? Directoare separate cu fișiere de stare separate și module comune. Workspace-urile sunt utile pentru variații mici, dar dev și prod în același fișier de stare e o rețetă de dezastru.
Ce faci cu o resursă creată manual? terraform import o aduce sub control, apoi scriu definiția ca să se potrivească.
Cum eviți un apply distrugător? Planul e citit de un om, prevent_destroy pe resursele cu date, și apply doar din pipeline, cu o identitate care nu are drept de ștergere pe ce e critic.

✅ Legătura cu CV-ul tău

Pe RapidBid ai scris Terraform pentru AWS și Azure. Ai deci și un subiect suplimentar pe care mulți nu-l au: ce înseamnă să susții doi furnizori — module separate per furnizor, pentru că resursele nu sunt echivalente, cu o interfață comună deasupra. Dacă te întreabă despre multi-cloud, asta e experiență reală, nu teorie.

16 Cloud: ce trebuie să știi din fiecare

Nu-ți trebuie certificare ca să răspunzi la interviu. Îți trebuie harta serviciilor de bază și echivalențele, pentru că anunțurile cer „AWS sau Azure sau GCP”, iar cine știe unul îl învață pe celălalt.

Ce trebuieAWSAzureGoogle Cloud
Mașini virtualeEC2Virtual MachinesCompute Engine
Kubernetes administratEKSAKSGKE
Registru de imaginiECRACRArtifact Registry
Stocare de obiecteS3Blob StorageCloud Storage
Baze relaționaleRDS / AuroraAzure SQL / DB for PostgreSQLCloud SQL
Rețea privatăVPCVNetVPC
Identitate și drepturiIAMEntra ID + RBACIAM
SecreteSecrets ManagerKey VaultSecret Manager
MonitorizareCloudWatchAzure MonitorCloud Monitoring
Funcții fără serverLambdaFunctionsCloud Functions / Run
CI/CD propriuCodePipelineAzure DevOpsCloud Build
✅ Ce ai bifat deja

Îngroșatele din tabel sunt din CV-ul tău: GKE și Cloud SQL de la DISH, Azure DevOps de la RapidBid, plus Terraform pe AWS. Asta înseamnă că ai atins toate cele trei clouduri. Formularea onestă și puternică: „Am lucrat pe Google Cloud în producție — GKE și Cloud SQL — și pe AWS și Azure prin Terraform și Azure DevOps. Serviciile de bază se corespund; ce diferă e modelul de identitate și drepturi, care e partea în care trebuie să fii atent când treci de la unul la altul.”

Costul, subiectul care apare la senior

Un DevOps senior e întrebat despre cost, pentru că e o pârghie reală. Principalele surse de risipă și ce faci cu ele:

  • Resurse rezervate mult peste consumul real. Se corectează măsurând consumul efectiv și ajustând requests. Aici e cea mai mare economie, de obicei.
  • Medii de test care rulează noaptea și în weekend. Se opresc pe program.
  • Discuri și adrese IP rămase orfane după ștergerea resurselor care le foloseau.
  • Trafic între zone de disponibilitate, care se taxează și pe care nu-l vede nimeni până nu se uită la factură.
  • Etichetare pe echipă și pe mediu, altfel factura nu poate fi atribuită nimănui și nimeni nu o optimizează.

17 Monitorizare, loguri și SLO

17.1 Cei trei piloni

📊 Metrici

Numere în timp: cereri pe secundă, latență, erori, memorie. Ieftine de păstrat mult timp. Răspund la „ce nu merge bine și de când”.

📜 Loguri

Evenimente cu text. Scumpe de păstrat. Răspund la „de ce”. Se scriu structurat (JSON) ca să poată fi căutate.

🔗 Urme (tracing)

Drumul unei cereri prin toate serviciile, cu timpul petrecut în fiecare. Răspund la „unde se pierde timpul”. Indispensabil la microservicii.

17.2 Ce se monitorizează, concret

Două seturi de reguli care se citează des:

Cele patru semnale de aur

Latență (cât durează), trafic (cât de mult se cere), erori (cât eșuează), saturație (cât de plină e resursa cea mai limitată).

Metoda RED, pentru servicii

Rate — cereri pe secundă. Errors — câte eșuează. Duration — distribuția timpilor de răspuns.

⚠️ Media ascunde problema

Nu te uita la latența medie, uită-te la percentile: p50, p95, p99. Cu o medie de 200 ms poți avea 1% din utilizatori care așteaptă 8 secunde — și ăia sunt cei care sună. Formularea la interviu: „urmăresc p95 și p99, nu media, pentru că media ascunde exact utilizatorii afectați.”

17.3 SLI, SLO, SLA și bugetul de erori

TermenCe eExemplu
SLIIndicatorul măsurat.Procentul de cereri care răspund sub 300 ms.
SLOȚinta internă pe acel indicator.99,9% din cereri sub 300 ms, pe 30 de zile.
SLAPromisiunea contractuală către client, cu penalizări.99,5% disponibilitate lunar. Întotdeauna mai relaxat decât SLO-ul.
Buget de eroriCât ai voie să greșești: 100% minus SLO.Un SLO de 99,9% pe lună îți dă circa 43 de minute de nerespectare.
🗣️ De ce contează bugetul de erori

„Bugetul de erori transformă o discuție de opinii într-una cu cifre. Dacă mai avem buget, echipa poate livra agresiv. Dacă l-am consumat, oprim funcționalitățile noi și lucrăm la stabilitate până se reface. Nu mai e nevoie ca cineva să convingă pe altcineva că «e prea riscant» — decizia o dă măsurătoarea.”

17.4 Alertele

Cea mai frecventă problemă în echipele reale nu e lipsa alertelor, ci prea multe. Când primești patruzeci de alerte pe zi și treizeci și nouă nu cer nimic, încetezi să le citești — și o ratezi pe a patruzecea. Regulile care rezolvă:

  • Alertează pe simptom, nu pe cauză. „Rata de erori a crescut peste prag” e util. „CPU la 90%” nu e — poate fi perfect normal.
  • Fiecare alertă care sună noaptea trebuie să ceară o acțiune umană imediată. Dacă poate aștepta până dimineață, nu e alertă, e tichet.
  • Fiecare alertă are un runbook — un document scurt care spune ce verifici și ce faci.
  • Alertează pe rata de consum a bugetului de erori, nu pe fiecare vârf.

17.5 Unelte

Prometheus colectează metrici trăgându-le periodic de la aplicații și se interoghează cu PromQL. Grafana desenează. Alertmanager rutează alertele și le grupează. Loki sau ELK pentru loguri. Jaeger sau Tempo pentru urme. OpenTelemetry e standardul de instrumentare care le alimentează pe toate. Alternativa comercială care le face pe toate într-un singur produs: Datadog, New Relic, Dynatrace.

✅ Legătura cu CV-ul tău

Ai lucrat cu Nagios și cu suita Tivoli. Sunt generația anterioară, dar ideea e identică: colectezi semnale, definești praguri, rutezi alerta către cine e de gardă. Dacă te întreabă de Prometheus, nu spune „n-am lucrat”. Spune ce ai făcut și unde e diferența: „Monitorizarea am făcut-o cu Nagios și Tivoli. Modelul e același — semnal, prag, alertă, cine răspunde. Ce e diferit la Prometheus e că el trage metricile de la aplicații și că interogarea e un limbaj propriu, PromQL, în loc de verificări scrise una câte una.”

18 Incidente și gardă

Detectare alertă sau raport MTTD Confirmare preiau, anunț canalul „mă ocup eu” oprește dublarea muncii Triere cât de grav, cine e afectat severitate → escaladare Atenuare revenire / repornire / redirectare MTTR — aici se măsoară SERVICIUL ÎNTÂI, cauza după Rezolvare cauza reală, reparată poate fi a doua zi Analiză fără vinovați acțiuni cu termen
Mediile care se măsoară: MTTD (până la detectare), MTTA (până la preluare), MTTR (până la restabilire). Un DevOps senior e judecat pe MTTR mai mult decât pe orice altceva.

18.1 Rolurile într-un incident mare

  • Comandantul incidentului — coordonează, nu depanează. Ia deciziile și ține ordinea.
  • Responsabilul de comunicare — informează părțile interesate și clienții la intervale fixe, ca nimeni să nu întrerupă echipa tehnică ca să întrebe „cum merge”.
  • Cei care repară — se uită efectiv în sistem.
  • Secretarul — notează cronologia cu ore exacte. Fără ea, analiza de după e o discuție din amintiri.

În echipe mici, o persoană poate purta mai multe pălării. Ce nu se face niciodată: comandantul să intre și el cu mâinile în sistem, pentru că atunci nimeni nu mai are imaginea de ansamblu.

18.2 Analiza de după, fără vinovați

Principiul: oamenii nu sunt cauza; sistemul care a permis greșeala este. Dacă o comandă greșită a putut șterge producția, problema nu e omul care a scris-o, e faptul că sistemul a acceptat-o fără confirmare, fără limită de rază și fără posibilitate de revenire. Dacă în analiză apare numele cuiva în loc de o cauză de sistem, oamenii vor ascunde incidentele data viitoare, și pierzi exact informația care ți-ar fi trebuit.

Structura documentului: rezumat, impact (cine, cât timp, ce nu a mers), cronologie cu ore, cauze contributive (la plural), ce a mers bine, ce a mers prost, acțiuni cu responsabil și termen. Fără ultima secțiune, e literatură.

✅ Aici ai cea mai mare vechime din tot CV-ul

Control de producție 24/7 la Metro, gardă, escaladare, comunicare către client. Asta nu se învață dintr-un curs și e exact ce caută o echipă care are un serviciu în funcțiune. Când ajungi la subiectul incidente, încetinește și dă un exemplu concret — e terenul tău.

19 Securitate în pipeline (DevSecOps)

Ideea într-o frază: verificările de securitate se mută devreme în procesul de dezvoltare, unde sunt ieftine, în loc să fie un audit la final.

UndeCe verificiCu ce
Înainte de commitSecrete scrise din greșeală în cod.gitleaks, git-secrets, ca hook local.
La fiecare PRVulnerabilități în codul propriu (SAST).CodeQL, Semgrep, SonarQube.
La fiecare PRDependențe cu vulnerabilități cunoscute (SCA).Dependabot, Snyk, npm audit.
După buildVulnerabilități în imaginea de container.Trivy, Grype.
După buildLista completă de componente (SBOM) și semnătura imaginii.Syft pentru SBOM, Cosign pentru semnătură.
Înainte de applyConfigurații nesigure în Terraform și în manifestele Kubernetes.Checkov, tfsec, kube-score.
În funcționareCe rulează efectiv și dacă se comportă anormal.Falco, agenți de securitate ai furnizorului.
⚠️ Un secret ajuns în Git nu se șterge cu un commit

Dacă o cheie a fost urcată în depozit, ea rămâne în istoric și în toate clonele existente. Ordinea corectă e: întâi o invalidezi la furnizor și generezi alta, abia apoi cureți istoricul. Invers, cureți istoricul degeaba — cheia e deja copiată. E o întrebare care apare și care separă răspunsul învățat de cel trăit.

Lanțul de aprovizionare software

Subiect tot mai prezent după incidentele din ultimii ani. Punctele principale: fixează acțiunile și imaginile pe hash, nu pe etichetă; semnează artefactele și verifică semnătura la livrare; generează SBOM ca să știi în cinci minute dacă ești afectat de o vulnerabilitate nouă; dă pipeline-ului permisiuni minime, obținute prin OIDC în loc de chei permanente.

20 Bash și Python

Aproape orice anunț cere „scripting”. Nu ți se cere să fii programator: ți se cere să automatizezi ceva repetitiv și să nu faci pagubă când scriptul dă greș la jumătate.

20.1 Antetul care te salvează

Șablonul de scriptbash
#!/usr/bin/env bash
set -euo pipefail
# -e  : oprește la prima comandă care eșuează
# -u  : eroare la folosirea unei variabile nedefinite (prinde typo-urile)
# -o pipefail : o conductă eșuează dacă ORICARE element eșuează, nu doar ultimul

IFS=$'\n\t'                  # nu mai despărți pe spații — protejează căile cu spații

# curățenie garantată, orice s-ar întâmpla
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT

# argument obligatoriu, cu mesaj clar dacă lipsește
MEDIU=${1:?folosire: $0 <mediu>}

log() { printf '%s [%s] %s\n' "$(date +%FT%T)" "$1" "$2" >&2; }

# reîncercare cu așteptare crescătoare — obligatoriu la orice apel de rețea
retry() {
  local n=0 max=5 delay=2
  until "$@"; do
    n=$((n+1))
    [ "$n" -ge "$max" ] && { log ERROR "eșec după $max încercări: $*"; return 1; }
    log WARN "încercarea $n a eșuat, reiau în ${delay}s"
    sleep "$delay"; delay=$((delay*2))
  done
}

retry curl -fsS "https://api.intern/$MEDIU/status" > "$TMP/status.json"
log INFO "gata"
💡 Detaliile pe care le observă un intervievator
  • curl fără -f întoarce succes chiar și la un răspuns 500, pentru că a descărcat pagina de eroare cu succes. Aproape toată lumea greșește asta.
  • "$@" păstrează argumentele separate; "$*" le lipește într-un singur șir. La căi cu spații, diferența e între „merge” și „șterge altceva”.
  • Ghilimelele în jurul variabilelor nu sunt stil, sunt corectitudine.
  • Idempotență: scriptul rulat de două ori dă același rezultat. Și flock pe fișier ca să nu ruleze două instanțe simultan din cron.
  • shellcheck pus în pipeline prinde majoritatea acestor greșeli automat.

20.2 Când treci la Python

Regula practică: Bash până la o sută de linii sau până când începi să parsezi JSON serios; de acolo, Python. Semnalele că ai depășit: ai nevoie de structuri de date, de tratare de erori pe cazuri, de apeluri HTTP cu antete și reîncercări, de teste.

Python pentru operațiuni — forma obișnuităpython
import os, sys, json, logging
import requests
from tenacity import retry, stop_after_attempt, wait_exponential

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")

TOKEN = os.environ["API_TOKEN"]      # din mediu, NU din cod

@retry(stop=stop_after_attempt(5), wait=wait_exponential(min=1, max=30))
def ia_starea(mediu: str) -> dict:
    r = requests.get(f"https://api.intern/{mediu}/status",
                     headers={"Authorization": f"Bearer {TOKEN}"},
                     timeout=10)          # timeout SIEMPRE — altfel atârnă la infinit
    r.raise_for_status()
    return r.json()

if __name__ == "__main__":
    try:
        print(json.dumps(ia_starea(sys.argv[1]), indent=2))
    except Exception as e:
        logging.error("nu am putut citi starea: %s", e)
        sys.exit(1)                        # cod de ieșire corect, ca pipeline-ul să știe

20.3 Linux — ordinea în care investighezi un server

ÎntrebareaComanda
Procesul rulează?systemctl status serviciu · ps aux | grep nume
Ascultă pe port?ss -ltnp · lsof -i :443
Se poate ajunge la el?curl -v · telnet gazda port · traceroute
Numele se rezolvă?dig nume · nslookup
A rămas fără ceva?df -h (disc) · free -h (memorie) · top · iostat
Ce spune jurnalul?journalctl -u serviciu -n 100 --no-pager · dmesg -T | tail
Cine ocupă discul?du -sh /* 2>/dev/null | sort -h
Ce s-a schimbat?last · istoricul shell · jurnalul de livrări
💡 Răspunsul la „serverul nu răspunde, ce verifici primul?”

„Discul.” E banal și e cauza cea mai des întâlnită: un disc plin oprește aproape orice serviciu, și adesea în moduri care par să nu aibă legătură — baza de date nu mai scrie, jurnalele se opresc, procesele se blochează. E prima comandă pe care o dau, pentru că durează o secundă și elimină cea mai probabilă cauză.

21 Experiența ta, tradusă în limbajul rolului

Asta e secțiunea de care ai cea mai mare nevoie. Nu îți lipsește experiența — îți lipsește obiceiul de a o numi în cuvintele pe care le caută cel din fața ta. Un om care a ținut producția în picioare la Metro și a migrat cozi de mesaje la Volvo are exact fondul pe care se construiește rolul de DevOps; dacă îl povestește cu termenii din 2012, ascultătorul aude „administrator de sistem”, nu „inginer de fiabilitate”.

Ce ai făcutCum se numește aziFraza pe care o spui
Control de producție 24/7, gardă, escaladare (Metro, IBM) Inginerie de fiabilitate, răspuns la incidente, gardă „Am lucrat ani în control de producție non-stop. Am preluat incidente, am escaladat pe severitate și am comunicat către client cât timp problema era deschisă.”
Nagios, suita Tivoli Monitorizare și alertare „Monitorizarea am făcut-o cu Nagios și Tivoli — semnale, praguri, rutarea alertei. Modelul e același cu Prometheus, diferă modul de colectare și limbajul de interogare.”
Migrare WebSphere MQ v8 → v9, manageri de coadă în mai multe instanțe (Volvo Cars) Modernizare de platformă, disponibilitate ridicată, migrare fără întrerupere „Am migrat platforma de mesagerie de la o versiune majoră la alta, pe o configurație în mai multe instanțe, adică exact cazul în care nu ai voie să oprești fluxul.”
TADDM (Etihad) Descoperire automată, inventar de configurație, hărți de dependențe „Am lucrat la descoperirea automată a infrastructurii și la maparea dependențelor între aplicații — la ce vorbește cu ce, ceea ce contează enorm când planifici o schimbare.”
AIX, Linux, shell Administrare de sistem, scripting „Am administrat sisteme UNIX în producție. Depanarea la nivel de sistem — disc, memorie, procese, rețea — o fac fără să mă uit pe documentație.”
GKE, 17 pod-uri, 44 de baze Cloud SQL (DISH Digital Solutions) Kubernetes administrat, operare pe Google Cloud „Am operat pe Google Kubernetes Engine, cu 17 pod-uri și 44 de baze Cloud SQL în spate, și am făcut livrări fără întrerupere de serviciu.”
Terraform pe AWS și Azure (RapidBid) Infrastructură ca și cod, multi-furnizor „Am scris infrastructura ca și cod în Terraform, pe doi furnizori — AWS și Azure. Ce e diferit între ei sunt furnizorii și modul de autentificare; fluxul plan–apply și disciplina stării sunt identice.”
Azure DevOps, Jenkins cu livrare albastru-verde (RapidBid) CI/CD, strategii de livrare „Am construit conducte în Azure DevOps și în Jenkins, inclusiv o livrare albastru-verde, cu comutarea traficului după ce mediul nou trecea verificările.”
Servere proprii, n8n, integrare cu modele de limbaj (ultimii ani) Automatizare, integrare de sisteme, operare de la un capăt la altul „În ultimii ani am construit și am ținut în funcțiune sisteme proprii, de la server până la automatizări și integrări. Am făcut toate rolurile, inclusiv pe cele care mă interesează aici.”
⚠️ Regula de aur: nu adăuga nimic peste ce e în tabel

Fiecare rând de mai sus e verificabil în CV-ul tău. Dacă la interviu te trezești adăugând un detaliu care sună bine — un procent, un mecanism, un instrument — și nu ești sigur că s-a întâmplat exact așa, oprește-te. Un intervievator tehnic sapă exact acolo unde detaliul sună impresionant, și dacă a doua întrebare te prinde fără răspuns, pierzi și ce era adevărat înainte. „Nu-mi mai amintesc exact, ar trebui să mă uit” e un răspuns care nu a costat niciodată pe nimeni un post.

21.1 Unde ești tare și unde vei fi presat

🟢 Teren solid

Incidente și gardă. Depanare la nivel de sistem. Migrări pe sisteme care nu au voie să cadă. Comunicare cu clientul sub presiune. Vechime într-o multinațională, cu proceduri reale. Operare pe Kubernetes administrat.

🟠 Unde vor săpa

Cât Kubernetes ai scris tu, nu doar operat. Câte conducte ai construit de la zero. GitHub Actions. Cât cod scrii efectiv. Ce ai făcut în pauza din multinațională.

🗣️ Cum declari o lipsă fără să pierzi terenul

„Pe partea asta am lucrat mai puțin decât pe restul. Ce am făcut concret e [lucrul exact]. Ce nu am făcut e [lucrul exact]. Cunosc modelul, iar diferența până la ce cereți voi o închid în câteva săptămâni de lucru efectiv — am mai făcut trecerea asta când am intrat pe Google Cloud.”

Structura are trei bucăți și fiecare face o treabă: recunoști (nu te prind ei), delimitezi exact (arăți că știi ce știi, ceea ce e mai rar decât pare), arăți traseul (dai motivul pentru care riscul lor e mic). Ce nu faci niciodată: nu spui doar „nu am lucrat cu asta” și nu taci după.

22 Plan de învățare

Nu ai nevoie să știi tot. Ai nevoie ca lucrurile pe care le spui să fie susținute de ceva ce ai atins cu mâna. Planul de mai jos e construit pe asta: fiecare bloc se termină cu un lucru care există pe calculatorul tău și despre care poți vorbi la trecut.

BlocCe faciCe rămâne în urmă
1Instalezi un cluster local (kind sau minikube). Scrii de mână un Deployment, un Service și un Ingress pentru o aplicație banală.Poți spune „am scris manifestele” fără să exagerezi.
2Strici lucrurile intenționat: imagine inexistentă, sondă de pregătire pe o cale greșită, limită de memorie prea mică. Repari fiecare caz citind describe și logs.Ai văzut cu ochii tăi ImagePullBackOff, CrashLoopBackOff și OOMKilled. Astea sunt întrebări sigure la interviu.
3Un depozit pe GitHub cu un workflow care construiește o imagine, o urcă în ghcr și rulează teste. Adaugi needs, o matrice și memorie tampon.Răspunsul onest la „ai lucrat cu GitHub Actions?” devine mai puternic.
4Conectezi workflow-ul la clusterul local sau la unul gratuit și faci o actualizare progresivă. Apoi provoci o eșuare și dai rollout undo.Ai dus un lanț complet de la commit până în producție.
5Terraform: o resursă simplă la un furnizor, cu stare la distanță și blocare. Faci o modificare manuală în consolă și vezi driftul în plan.Povestea despre drift devine trăită, nu citită.
6Prometheus și Grafana peste clusterul local. Un tablou de bord cu cele patru semnale de aur și o alertă care chiar se declanșează.Poți lega Nagios de Prometheus într-o singură frază credibilă.
7Repetiție cu voce tare: capitolele 9–14 din pagina asta, explicate ca și cum ai preda.Frazele ies fluent. Ăsta e scopul întregului document.
💡 Ce contează mai mult decât cât citești

Un lucru stricat și reparat cu mâna ta valorează cât zece capitole citite. Intervievatorii tehnici recunosc imediat diferența dintre cineva care a văzut un pod blocat în Pending și cineva care a citit ce înseamnă. Primul spune „m-am uitat în evenimente și nu avea nod care să-l primească”. Al doilea spune definiția.

Certificări, dacă vrei să pui ceva pe hârtie

CKA (Certified Kubernetes Administrator) e cea mai bine văzută dintre toate, pentru că e examen practic — stai în terminal și rezolvi. KCNA e varianta de intrare, doar cu întrebări. Terraform Associate e ieftin și rapid. Certificările de furnizor (AWS, Azure, Google Cloud) contează mai ales când firma lucrează pe acel furnizor. Nicio certificare nu înlocuiește un cluster stricat și reparat, dar CKA trece filtrele automate de selecție, ceea ce e o problemă reală și separată.

23 Glosar

Termenii care apar în conversație și se presupun cunoscuți. Dacă unul dintre ei te blochează în timpul interviului, ai pierdut firul propoziției — de aceea sunt aici.

Infrastructură

Idempotent — rulat de două ori, dă același rezultat. Declarativ — descrii starea dorită, nu pașii. Imperativ — dai pașii. Drift — realitatea a plecat de la ce zice codul. Provizionare — crearea resurselor. Efemer — care dispare fără urmări.

Livrare

Artefact — rezultatul construirii (imagine, arhivă). Registru — depozitul de imagini. Etichetă imutabilă — o etichetă care nu se mai suprascrie niciodată. Revenire — întoarcerea la versiunea anterioară. Raza de acțiune — cât se strică dacă schimbarea e greșită.

Fiabilitate

Disponibilitate — procentul de timp în care serviciul răspunde. Redundanță — exemplare de rezervă. Punct unic de eșec — piesa care, dacă pică, oprește tot. Degradare grațioasă — funcționează parțial în loc să cadă. Presiune inversă — sistemul refuză cereri ca să nu se prăbușească. Efect de turmă — toți clienții reîncearcă simultan și dărâmă serviciul care tocmai revenea.

Kubernetes

Manifest — fișierul YAML care descrie un obiect. Reconciliere — bucla care aduce realul la dorit. Etichetă — perechea cheie-valoare pe care o folosesc selectoarele. Adnotare — metadate fără rol de selecție. Sidecar — container auxiliar în același pod. Evacuare — scoaterea unui pod de pe un nod sub presiune. Cordonare — nodul nu mai primește pod-uri noi.

Rețea

Proxy invers — stă în față și distribuie către servicii. Terminare TLS — locul unde criptarea se oprește. Gazdă virtuală — mai multe domenii pe un IP. CIDR — notația pentru intervale de adrese. Egress / Ingress — trafic care iese / care intră.

Proces

Fără vinovați — analiză pe sistem, nu pe persoană. Runbook — instrucțiunile pentru o situație cunoscută. Interval de nelivrare — perioada în care nu se pune nimic în producție (sărbători, vârfuri de trafic). Comutator de funcționalitate — pornești sau oprești o funcție fără să livrezi cod.

🗣️ Ultimul lucru, și cel mai important

„Poți să repeți întrebarea? Vreau să fiu sigur că răspund la ce m-ai întrebat.”

Fraza asta îți cumpără cinci secunde, sună profesionist și nu te costă nimic. Nimeni, niciodată, nu a fost respins pentru că a cerut o clarificare. Oamenii sunt respinși pentru că au început să vorbească fără să știe unde ajung. Dacă reții un singur lucru din toată pagina, reține-l pe ăsta.