Ai migrat aplicația pe AWS — React pe CloudFront, Node.js pe EC2 cu Auto Scaling, PostgreSQL pe RDS, Redis pe ElastiCache. Infrastructura e solidă. Dar acum vine întrebarea inevitabilă: cum faci deploy fără să pice site-ul?
Dacă fiecare deploy înseamnă câteva secunde (sau minute) de downtime, pierzi utilizatori, încredere și bani. Zero-downtime deployment nu e un lux — e un standard. În acest articol explorăm două strategii de deployment fără întrerupere pe AWS (Rolling și Blue/Green), le comparăm, și arătăm cum le integrezi cu GitHub Actions sau GitLab CI/CD.
De ce pică aplicația la deploy?
Pe un VPS clasic, deploy-ul arată de obicei așa: te conectezi prin SSH, faci git pull, rulezi npm install, restartezi procesul Node.js. Între momentul în care oprești vechiul proces și pornești noul, aplicația nu răspunde. Poate sunt 5 secunde, poate 30 — depinde de cât durează build-ul și startup-ul.
Pe AWS cu ALB și Auto Scaling Group, lucrurile stau diferit. Ai mai multe instanțe EC2 în spatele unui load balancer. Trucul e să actualizezi instanțele pe rând, astfel încât cel puțin o parte din ele servește trafic în orice moment. Există două strategii principale pentru asta.
Strategia 1: Rolling Deployment
Rolling deployment-ul actualizează instanțele una câte una (sau în batch-uri) în cadrul aceluiași Auto Scaling Group.
Procesul funcționează astfel: AWS CodeDeploy preia noua versiune a codului și o trimite către prima instanță EC2. ALB-ul scoate acea instanță din rotație (deregistration), codul se actualizează, aplicația repornește, apoi ALB-ul o readaugă după ce trece health check-ul. Procesul se repetă pentru fiecare instanță.
Practic, dacă ai 4 instanțe, la orice moment ai cel puțin 3 care servesc trafic. Utilizatorii nu observă nimic.
Avantaje: nu ai nevoie de resurse suplimentare (nu dublezi infrastructura), e simplu de configurat, și costul rămâne identic. Dezavantaje: deploy-ul durează mai mult (fiecare instanță se actualizează secvențial), și pentru câteva minute rulezi simultan două versiuni ale aplicației — trebuie să te asiguri că sunt compatibile.
Configurare CodeDeploy pentru Rolling: în deployment group, setezi tipul ca „In-place" și selectezi Auto Scaling Group-ul. La „Deployment settings" alegi CodeDeployDefault.OneAtATime pentru siguranță maximă sau CodeDeployDefault.HalfAtATime pentru viteză. ALB-ul face automat deregistration/registration.
Strategia 2: Blue/Green Deployment
Blue/Green e strategia premium. În loc să actualizezi instanțele existente, creezi un set complet nou de instanțe (mediul „green") cu noua versiune, le testezi, apoi comuți tot traficul de pe vechiul set (mediul „blue") pe cel nou.
Procesul arată așa: CodeDeploy creează un nou Auto Scaling Group cu instanțe care rulează versiunea nouă. Instanțele noi se înregistrează pe un Target Group separat al ALB-ului. După ce toate instanțele noi trec health check-urile, ALB-ul redirecționează traficul de la Target Group-ul vechi la cel nou — instantaneu. Dacă ceva nu merge, rollback-ul e un simplu switch înapoi la Target Group-ul original.
Avantaje: rollback instantaneu (vechile instanțe sunt încă active), poți testa mediul green înainte să comuți traficul, nu rulezi niciodată două versiuni simultan. Dezavantaje: pentru câteva minute plătești dublu (două seturi de instanțe rulează în paralel), și setup-ul e mai complex.
Rolling vs. Blue/Green — Când folosești ce?
Rolling deployment-ul e potrivit pentru aplicații cu toleranță la rularea simultană a două versiuni, echipe mici care vor simplicitate, și situații în care bugetul e o preocupare (fără resurse suplimentare).
Blue/Green e alegerea corectă pentru aplicații cu SLA-uri stricte, când ai nevoie de rollback instant (sub 1 minut), sau când vrei să validezi manual noua versiune înainte de switch.
Pentru aplicația noastră Node.js + PostgreSQL + Redis, recomandarea practică e să începi cu Rolling deployment. E mai simplu, mai ieftin, și acoperă 90% din nevoi. Treci la Blue/Green când ai un motiv concret — de exemplu, o migrare de bază de date care necesită testare înainte de cutover.
Piesa centrală: AWS CodeDeploy
Indiferent de strategie, AWS CodeDeploy orchestrează procesul de deployment pe instanțele EC2. Îl configurezi o singură dată, apoi îl declanșezi din GitHub Actions sau GitLab CI/CD.
CodeDeploy folosește un fișier appspec.yml în rădăcina proiectului care definește ce se întâmplă la fiecare etapă a deployment-ului:
version: 0.0
os: linux
files:
- source: /
destination: /home/app/myapp
hooks:
BeforeInstall:
- location: scripts/stop_server.sh
timeout: 60
AfterInstall:
- location: scripts/install_dependencies.sh
timeout: 120
ApplicationStart:
- location: scripts/start_server.sh
timeout: 60
ValidateService:
- location: scripts/health_check.sh
timeout: 60
Scripturile fac exact ce te-ai aștepta: stop_server.sh oprește procesul Node.js (graceful shutdown), install_dependencies.sh rulează npm ci --production, start_server.sh pornește aplicația, iar health_check.sh verifică că endpoint-ul /health răspunde cu 200.
Un detaliu critic: graceful shutdown. Când ALB-ul scoate o instanță din rotație, setarea deregistration_delay (default 300 secunde) permite request-urilor în curs să se finalizeze. Asigură-te că aplicația Node.js tratează semnalul SIGTERM corect — finalizează request-urile active, închide conexiunile la DB și Redis, apoi se oprește.
Integrare cu GitHub Actions
Flow-ul complet: push pe branch-ul main → GitHub Actions rulează teste → build-ul se împachetează și se uploadează pe S3 → CodeDeploy se declanșează → instanțele EC2 se actualizează.
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
aws-region: eu-central-1
- name: Package and upload to S3
run: |
zip -r app-${{ github.sha }}.zip . -x '.git/*'
aws s3 cp app-${{ github.sha }}.zip \
s3://my-deploy-bucket/app-${{ github.sha }}.zip
- name: Trigger CodeDeploy
run: |
aws deploy create-deployment \
--application-name my-app \
--deployment-group-name production \
--s3-location bucket=my-deploy-bucket,key=app-${{ github.sha }}.zip,bundleType=zip \
--description "Deploy ${{ github.sha }}"
Notă importantă despre autentificare: exemplul folosește OIDC (OpenID Connect) în loc de access keys stocate ca secrets. Configurezi un Identity Provider în IAM care are încredere în GitHub, și GitHub Actions primește credențiale temporare la fiecare run. Zero secrete de rotit, zero riscuri de leak.
Integrare cu GitLab CI/CD
Aceeași logică, sintaxă diferită. GitLab CI/CD folosește fișierul .gitlab-ci.yml:
# .gitlab-ci.yml
stages:
- test
- deploy
variables:
AWS_DEFAULT_REGION: eu-central-1
test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm test
deploy_production:
stage: deploy
image: amazon/aws-cli:latest
only:
- main
before_script:
- apt-get update && apt-get install -y zip
script:
- zip -r app-${CI_COMMIT_SHA}.zip . -x '.git/*'
- aws s3 cp app-${CI_COMMIT_SHA}.zip
s3://my-deploy-bucket/app-${CI_COMMIT_SHA}.zip
- aws deploy create-deployment
--application-name my-app
--deployment-group-name production
--s3-location bucket=my-deploy-bucket,key=app-${CI_COMMIT_SHA}.zip,bundleType=zip
--description "Deploy ${CI_COMMIT_SHA}"
environment:
name: production
url: https://myapp.example.com
Pentru autentificare, GitLab suportă de asemenea OIDC cu AWS. Alternativa e să setezi AWS_ACCESS_KEY_ID și AWS_SECRET_ACCESS_KEY ca variabile CI/CD protejate în Settings → CI/CD → Variables.
GitHub Actions vs. GitLab CI/CD — Comparație rapidă
Ambele platforme fac treaba excelent. Diferențele practice sunt minore: GitHub Actions folosește YAML cu structură de jobs și steps, are marketplace bogat cu acțiuni predefinite, și se integrează nativ dacă codul e deja pe GitHub. GitLab CI/CD are YAML mai compact, oferă environments cu tracking vizual al deployment-urilor, include built-in Container Registry, și e ideal dacă folosești deja ecosistemul GitLab.
Alegerea depinde de unde locuiește codul tău, nu de capabilitățile CI/CD. Ambele pot declanșa CodeDeploy identic.
Checklist înainte de primul deploy
Câteva lucruri de verificat înainte de a pune totul în producție. Endpoint-ul /health din aplicația Node.js trebuie să verifice conexiunea la DB și Redis, nu doar să returneze 200. ALB health check-ul trebuie configurat pe acest endpoint, cu interval de 15 secunde și threshold de 2 health checks. Graceful shutdown-ul trebuie implementat — aplicația trebuie să trateze SIGTERM corect. deregistration_delay pe Target Group trebuie setat la minim 30 de secunde (default 300 e prea mult pentru majoritatea aplicațiilor). Migrările de bază de date trebuie rulate separat, înainte de deploy, și trebuie să fie backward-compatible. Și nu în ultimul rând, trebuie configurat un mecanism de rollback automat — CodeDeploy poate face rollback automat dacă un deployment eșuează.
Concluzie
Zero-downtime deployment nu e rocket science — e o combinație de servicii AWS configurate corect (ALB + ASG + CodeDeploy) cu un pipeline CI/CD care le orchestrează. Începe cu Rolling deployment, automatizează cu GitHub Actions sau GitLab CI/CD, și adaugă complexitate (Blue/Green) doar când ai nevoie reală.
Cel mai important: nu încerca să faci totul de la început. Un rolling deployment funcțional bate un blue/green pe jumătate configurat în fiecare zi a săptămânii.
Publicat pe teninvent.ro — TEN INVENT S.R.L. oferă servicii de implementare CI/CD și infrastructură AWS. Contactează-ne pentru o evaluare a pipeline-ului tău de deployment.