Part 13 · CI/CD, Git & Cloud

Deploy nhiều microservice không downtime

Best practice không phải Jenkins deploy đồng loạt mọi service. Mỗi service phải deploy độc lập trong một khoảng compatibility, còn thay đổi schema/API/event được chia thành nhiều release nhỏ có thể cùng tồn tại.

1. Rolling update chỉ là lớp compute

v1 pods: 4 -> 3 -> 2 -> 1 -> 0
v2 pods: 0 -> 1 -> 2 -> 3 -> 4

Trong rollout luôn có lúc v1 và v2 cùng nhận traffic.

Kubernetes Deployment tạo ReplicaSet mới, thêm pod v2 rồi giảm pod v1 theo maxSurge/maxUnavailable. Readiness loại pod chưa sẵn sàng; preStop, termination grace và load-balancer drain bảo vệ in-flight request. Nhưng rolling update không giải quyết database schema, API/event compatibility hay workflow đang chạy dở.

2. Quy tắc nền tảng: N và N-1 tương thích

Version mới của producer/consumer/database schema phải làm việc được với version đang chạy và thường cả version trước đó trong rollout/rollback window:

Nếu deployment bắt buộc tất cả service đổi cùng lúc mới hoạt động, hệ thống đang có distributed-monolith coupling.

3. Jenkins/pipeline thường deploy thế nào?

service repository change
  -> build/test/contract/security
  -> publish immutable image digest
  -> update manifest for that service
  -> deploy dev -> integration -> staging
  -> canary production -> automated SLO gate -> full rollout

Shared contract/library change
  -> publish backward-compatible version
  -> consumers upgrade independently
  -> remove old contract only after usage evidence = 0

Mỗi service có pipeline/artifact/deployment riêng. Jenkins không cần “deploy hết”; nó chỉ deploy affected service. Monorepo dùng dependency graph/change detection nhưng shared change phải fan-out test đúng. Một business feature xuyên nhiều service được release bằng compatible deployments + feature flag, không bằng một transaction deploy toàn hệ thống.

4. Database migration: Expand → Migrate → Contract

Không deploy code chỉ chạy được sau destructive migration trong cùng một bước. Chia thành nhiều release:

Release A - EXPAND
  add nullable column/new table/index concurrently
  old code continues working

Release B - DUAL COMPATIBILITY
  deploy code that can read old and new
  optionally dual-write with idempotency/observability

Migration job - MIGRATE
  backfill in bounded batches, checkpoint, throttle, retry

Release C - SWITCH
  switch reads to new field behind flag
  verify metrics/data reconciliation

Release D - CONTRACT
  stop old writes, remove old code
  later drop old column/constraint after rollback window

Contract thường cách expand nhiều ngày/release, không nằm cùng pipeline run.

5. Ví dụ rename column an toàn

Sai:

alter table orders rename column total to total_amount;
// v1 pods vẫn SELECT total -> fail trong rolling update

An toàn hơn:

-- Release A
alter table orders add column total_amount numeric(19,2);

-- Backfill batches
update orders set total_amount = total
where id > :last_id and id <= :next_id and total_amount is null;

-- Release B/C: code reads COALESCE(total_amount, total), writes compatible values
-- Verify no null/mismatch, then add constraint safely
-- Release D later: remove total only after all old versions are gone

Dual-write có race/partial-write risk; tốt hơn nếu database trigger hoặc một authoritative write path phù hợp, nhưng trigger cũng cần lifecycle/observability. Luôn reconciliation trước contract.

6. Migration job chạy ở đâu?

CáchĐánh giá
Mỗi pod tự chạy Flyway lúc startupĐơn giản cho migration nhỏ/compatible, nhưng nhiều pod cạnh tranh lock; migration lâu làm readiness/startup khó đoán.
Singleton Kubernetes Job/pipeline stageOwnership/evidence rõ; phải idempotent và app versions compatible trước/sau job.
Operator/controllerPhù hợp platform trưởng thành; thêm complexity.

Best practice thường tách migration thành job một lần với lock, timeout, logs và failure policy. Tuy nhiên “migration trước hay code trước” không có câu trả lời chung: expand migration chạy trước code; contract migration chỉ chạy sau khi code cũ biến mất.

7. Migration lớn không chạy như một transaction khổng lồ

8. API compatibility giữa service

// Producer mới thêm field optional
{
  "orderId": "...",
  "status": "CONFIRMED",
  "reservationExpiresAt": "..." // consumer cũ phải ignore được
}

Gateway versioning không tự giải quyết service-to-service compatibility; semantic change vẫn cần migration path.

9. Event compatibility

OrderConfirmed v1 -> consumers A(v1), B(v1)
OrderConfirmed v2 -> producer deploy trước? chỉ khi old consumers đọc được

Ưu tiên additive schema, default và tolerant reader. Nếu breaking, publish event type/version mới song song; migrate consumers; đo không còn consumer/traffic cũ rồi ngừng v1. Event lưu lâu/replay khiến compatibility window dài hơn HTTP. Không serialize JPA entity trực tiếp.

10. Feature nhiều service được release thế nào?

  1. Deploy database expand/contract additions.
  2. Deploy downstream consumers/providers có backward-compatible support nhưng feature tắt.
  3. Deploy upstream callers/producers vẫn dùng old behavior mặc định.
  4. Bật feature cho internal tenant/canary percentage.
  5. Quan sát end-to-end business SLI và reconciliation.
  6. Tăng rollout; giữ kill switch.
  7. Sau stability/rollback window mới xóa old path/schema/event.

Deployment order thường “downstream capability trước, upstream usage sau”. Feature flag tách deploy khỏi release, nhưng flag cần owner, expiry và test cả on/off/mixed version.

11. Ví dụ Order gọi Inventory API mới

Phase 1: Inventory v2 deploy, vẫn hỗ trợ /decrement cũ,
         thêm /reservations mới.
Phase 2: Order v2 deploy với flag inventory-reservation=false.
Phase 3: bật flag 1% -> 10% -> 100%; monitor reservation leak/latency.
Phase 4: Order v1 và old path không còn traffic.
Phase 5: deprecate/remove /decrement trong Inventory release sau.

Không cần Jenkins deploy hai service cùng một lúc. Nếu Order v2 rollback, Inventory v2 vẫn hỗ trợ API cũ.

12. Rollback không đơn giản là image cũ

Thay đổiRollback strategy
Code-only compatibleRollback image/digest.
Additive schemaRollback code, giữ schema thừa.
New data format đã writeOld code phải đọc được; nếu không, forward fix/data conversion.
Destructive schemaKhó/không thể rollback nhanh; restore gây data loss và downtime.
External side effect/eventKhông rollback bằng image; cần compensation/reconciliation.

Vì vậy ưu tiên roll-forward khi migration/data side effect đã xảy ra. Rollback plan phải được thiết kế trước deploy và diễn tập.

13. Kubernetes rollout settings

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 25%
    maxUnavailable: 0
minReadySeconds: 10
progressDeadlineSeconds: 600

14. Pipeline orchestration mẫu

stages {
  stage('Build & verify')      { /* tests, contracts, scan */ }
  stage('Publish image')       { /* immutable digest */ }
  stage('Expand migration')    { /* approved singleton job */ }
  stage('Deploy canary')       { /* only this service */ }
  stage('Analyze')             { /* SLO/business/schema metrics */ }
  stage('Promote')             { /* progressive traffic */ }
}

// Backfill and contract are separate tracked workflows/releases.

Jenkins có thể trigger downstream pipeline khi thật sự cần capability ordering, nhưng tránh một “mega pipeline” deploy mọi service. Release manifest có thể ghi tập version đã verified cùng nhau mà vẫn cho phép deploy độc lập.

15. Khi nào cần coordinated release?

Đôi khi compliance, protocol switch hoặc security incident yêu cầu coordination. Vẫn dùng phases:

Zero downtime không phải giáo điều. Nếu online migration rủi ro hơn một maintenance window được kiểm soát, chọn downtime có kế hoạch và truyền thông rõ.

16. Observability và deployment gates

17. Failure cases cần drill

FailureExpected response
Migration job fail giữa batchResume checkpoint; schema vẫn compatible; app không downtime.
Canary error tăngStop traffic/promote; rollback code, giữ additive schema.
Old consumer nhận event mớiTolerant schema hoặc producer flag off; DLQ alert không phải discovery đầu tiên.
New code write format old code không đọcGate rollout/rollback; dual-read/data conversion hoặc forward fix.
Pod killed khi processingDrain/visibility timeout/redelivery + idempotent consumer.
Contract migration chạy quá sớmIncident: stop rollout, forward restore compatibility; không drop destructive tự động.

18. Bài thực hành

  1. Thêm column bắt buộc vào Order bằng expand/backfill/constraint/contract qua bốn releases.
  2. Chạy v1 và v2 pods đồng thời; test cả hai đọc/write cùng schema.
  3. Thêm Inventory reservation API trong khi giữ endpoint cũ; bật bằng feature flag canary.
  4. Publish event v2 additive; chạy consumer v1/v2 và replay event lịch sử.
  5. Inject migration failure ở batch 3; resume không duplicate/mất dữ liệu.
  6. Rollback v2 sau khi đã write data mới; chứng minh v1 vẫn đọc được hoặc ghi rõ forward-fix.
  7. Canary gate theo error/latency và business stuck-order metric.
  8. Kill pod giữa message; chứng minh redelivery không tạo double effect.

19. Checklist phỏng vấn

Tài liệu: Kubernetes Deployments · Pod Disruption Budgets · Evolutionary Database Design · Database per Service