Zero-Downtime Deployment cho Banking: Nâng cấp ứng dụng và Database an toàn
Hướng dẫn Zero-Downtime Deployment cho banking: Kubernetes rolling update, blue-green/canary, graceful shutdown, backward-compatible database migration, PostgreSQL online DDL và rollback an toàn.
ShanverseUpdated 11 min read— viewsLoading views
Deployment
PostgreSQL
Payment Systems
Vì sao deployment banking khó hơn website thông thường?
Trong một đợt rollout, phiên bản cũ và mới thường chạy đồng thời. Một giao dịch có thể:
Bắt đầu trên pod v1 nhưng callback/webhook được xử lý bởi v2.
Ghi database bằng v2 rồi được đọc bởi v1.
Nằm trong queue trước deployment và được consumer mới xử lý sau deployment.
Timeout ở client nhưng thực tế đã commit.
Đi qua fraud, ledger và notification service ở các phiên bản khác nhau.
Mục tiêu không chỉ là HTTP không trả 503. Các invariant tài chính phải giữ nguyên:
Không mất accepted command.
Không double debit/double credit.
Không chuyển trạng thái lùi hoặc nhảy qua bước không hợp lệ.
Comments
Tổng ledger vẫn cân bằng.
Webhook/event có thể xử lý lại an toàn.
Audit trail giải thích được phiên bản code/schema/rule đã xử lý.
Kiến trúc tổng thể
Mermaid
Loading diagram…
Điều kiện cốt lõi: app version N và N+1 phải cùng làm việc an toàn với schema chuyển tiếp trong toàn bộ rollout.
Chọn chiến lược rollout
Rolling update
Thay pod theo từng nhóm. Chi phí thấp và phù hợp thay đổi tương thích ngược.
Ưu điểm:
Không cần hai environment đầy đủ.
Rollout tích hợp tự nhiên với Kubernetes.
Có thể pause/rollback ReplicaSet.
Rủi ro:
Hai phiên bản chạy cùng lúc.
Traffic phân phối không chính xác cho canary analysis.
Rollback app không đảo ngược database migration.
Blue-green
Blue phục vụ production; Green chạy phiên bản mới. Test Green rồi chuyển traffic.
Ưu điểm: cutover và rollback traffic nhanh. Nhược điểm: chi phí gấp đôi, database dùng chung vẫn cần schema tương thích; database riêng đòi hỏi replication, cutover và xử lý split-brain/data divergence.
Canary
Đưa 1–5% traffic vào phiên bản mới, kiểm tra SLO/business metrics rồi tăng dần. Với banking, canary nên sticky theo account/tenant hoặc request cohort để tránh một workflow nhảy ngẫu nhiên giữa nhiều phiên bản.
Không canary chỉ dựa trên HTTP 5xx. Cần theo dõi duplicate suppression, ledger mismatch, payment pending age, decline rate và reconciliation variance.
Kubernetes rolling update an toàn
startupProbe: app đã khởi động xong chưa.
readinessProbe: pod có nên nhận traffic mới không.
livenessProbe: process có bị kẹt và cần restart không.
Không để readiness phụ thuộc trực tiếp vào mọi downstream tùy chọn; một reporting service lỗi không nên loại toàn bộ payment pod. Nhưng pod không được ready nếu không thể thực hiện critical operation an toàn.
maxUnavailable: 0 duy trì replica khả dụng theo controller, nhưng không tự bảo đảm capacity nếu pod mới chiếm CPU/connection hoặc readiness sai.
Graceful shutdown và giao dịch đang chạy
Khi nhận SIGTERM:
Đánh dấu not-ready để ngừng nhận request mới.
Dừng polling queue hoặc pause partition assignment phù hợp.
Chờ in-flight request trong giới hạn grace period.
Commit hoặc rollback database transaction rõ ràng.
Flush telemetry thiết yếu.
Đóng connection pool.
Exit trước SIGKILL.
Readiness endpoint trả failure khi draining = true. Tuy nhiên propagation từ pod đến load balancer không tức thời; endpoint vẫn phải idempotent và chịu được connection bị cắt.
Không giữ HTTP request mở hàng phút cho một transfer. Sau durable acceptance, trả 202/PENDING cùng transaction ID và cho client query/subscribe trạng thái.
Idempotency là lớp bảo hiểm của zero downtime
Deployment tạo ra timeout, retry và redelivery. Mỗi money-moving command cần idempotency key:
Lưu request fingerprint. Cùng key + cùng payload trả lại kết quả; cùng key + payload khác trả conflict. Provider call dùng idempotency key nếu hỗ trợ.
Consumer dùng inbox/unique event ID và chỉ ack/commit offset sau durable state. Transactional Outbox tránh dual-write giữa database và broker.
Database migration: Expand–Migrate–Contract
Không deploy một migration phá schema rồi hy vọng app update kịp. Chia thay đổi thành ba pha độc lập.
1. Expand
Thêm cấu trúc mới nhưng giữ cấu trúc cũ:
Thêm nullable column/table/index.
Thêm event field optional với default semantics rõ ràng.
App N vẫn chạy với schema mới.
App N+1 có thể đọc cả old/new shape.
Ví dụ đổi account_number thành tokenized identifier:
Deploy code ghi cả old và new trong cùng transaction, hoặc chọn một cơ chế backfill/change capture được kiểm soát. Dual write ở application phải có một source of truth và cơ chế phát hiện divergence.
2. Migrate
Backfill theo batch nhỏ:
Thực tế không nên gọi external tokenization function tùy ý trong transaction dài. Worker thường đọc batch, gọi vault idempotent, rồi update với checkpoint và rate limit.
Batch nhỏ và commit thường xuyên.
Giới hạn CPU/IO/WAL/replication lag.
Có checkpoint, retry và dead-letter/manual review.
Không dùng OFFSET cho bảng đang thay đổi; dùng stable cursor/keyset.
Đo số row chưa migrate và mismatch.
Sau backfill, chuyển read path sang column mới bằng feature flag/canary và so sánh shadow read.
3. Contract
Chỉ xóa column/constraint/API cũ sau khi:
100% app và worker cũ đã rời production.
Queue không còn message schema cũ ngoài retention yêu cầu.
Backfill hoàn tất và reconciliation pass.
Rollback window đã đóng.
Không còn report, ETL hoặc service phụ thuộc.
Contract là release riêng, không gộp với Expand.
PostgreSQL online-safe patterns
Tạo index
Regular CREATE INDEX có thể chặn writes. Với bảng live lớn:
CREATE INDEX CONCURRENTLY không chạy trong transaction block, tốn thêm thời gian/I/O và có thể để lại invalid index nếu thất bại; migration runner phải hỗ trợ và kiểm tra hậu điều kiện.
Thêm constraint
NOT VALID tránh quét toàn bộ dữ liệu ở bước add; row mới vẫn phải thỏa constraint. VALIDATE CONSTRAINT chạy sau, với lock profile nhẹ hơn nhưng vẫn tiêu tốn I/O.
Thay đổi kiểu dữ liệu
Tránh ALTER COLUMN TYPE có table rewrite trên bảng nóng. Tạo column mới, dual write, backfill, chuyển reads, rồi contract.
Lock timeout
Fail fast tốt hơn chờ lock hàng phút và làm hàng nghìn transaction xếp hàng. Không retry migration DDL nóng liên tục.
Luôn kiểm tra lock behavior trên đúng PostgreSQL version và kích thước production-like.
Schema compatibility matrix
Pipeline phải có compatibility tests cho ít nhất app N/N+1 với schema transition. Với microservices release độc lập, cửa sổ tương thích có thể cần N-1/N/N+1.
Không cho mọi pod tự chạy migration
Nếu mỗi replica chạy migration khi start, các pod có thể tranh DDL lock hoặc đánh dấu version sai. Dùng một migration job riêng với:
Leader/single execution.
Explicit approval cho destructive changes.
Lock timeout và observability.
Checksum/versioned scripts.
Precondition/postcondition.
Không block app startup vô thời hạn.
Pod mới chỉ ready khi schema version nằm trong range tương thích, không nhất thiết đúng tuyệt đối một version duy nhất.
Event và queue compatibility
Database không phải boundary duy nhất. Message được publish trước rollout có thể được consumer mới đọc sau rollout.
Schema Registry/contract tests.
Additive optional fields trước.
Không đổi meaning của field cũ âm thầm.
Consumer bỏ qua unknown fields an toàn.
Producer không phát schema mới trước khi consumer tương thích được deploy.
Version rule/model/action trong event.
Giữ old consumer hoặc adapter đến khi backlog cũ được drain.
Không replay event production vào live side-effect consumer nếu không có idempotency và replay isolation.
Blue-green database: không đơn giản như ứng dụng
Hai application environment có thể dùng chung database transition schema. Hai database riêng cần replication/CDC và cutover protocol:
Tạo Green database từ snapshot/replication.
Đồng bộ thay đổi liên tục.
Validate schema, row count, checksums và business totals.
Quiesce hoặc fence writes trong cửa sổ cutover rất ngắn nếu cần.
Chờ replication lag về ngưỡng cho phép.
Chuyển connection/traffic.
Ngăn Blue nhận writes mới.
Monitor rồi mới kết thúc rollback window.
Nếu rollback traffic sang Blue sau khi Green đã nhận writes, dữ liệu có thể phân kỳ. Cần reverse replication hoặc kế hoạch roll-forward. “Đổi DNS lại” không đủ.
Trình tự release tham khảo
Backup/restore drill và migration rehearsal.
Deploy Expand migration.
Deploy app N+1 ở shadow/canary, vẫn tương thích old data.
Tăng canary 1% → 5% → 25% → 50% → 100% theo gates.
Chạy backfill được throttle.
Enable read-new feature flag cho cohort nhỏ.
Reconcile old/new values và business invariants.
Dừng old writes; giữ fallback reads trong rollback window.
Xóa old pods/consumers.
Chạy Contract migration trong release sau.
Mỗi bước có automated gate và manual stop authority.
Rollback hay roll-forward?
App-only, schema additive: rollback traffic/image thường an toàn.
Backfill đã chạy: app rollback phải vẫn đọc được dữ liệu đã ghi mới.
Destructive schema: rollback có thể không thể hoặc gây mất dữ liệu; ưu tiên roll-forward.
External side effect: không rollback bằng cách gọi lại; dùng reversal/compensation có ledger entry riêng.
Feature flag phải có owner, expiry và audit. Kill switch không được bỏ qua authorization hay data invariants.
Connection pool và cache trong rollout
Surge pod có thể tạo connection storm. Ví dụ 20 pods × pool 50 = 1.000 connections, vượt DB budget.
Chia global DB connection budget cho max pod count gồm surge.
Warm up có giới hạn và jitter.
Dùng connection proxy/pooler khi phù hợp.
Đóng pool graceful.
Cache key/version phải tương thích; tránh cache stampede khi Green cold.
Không coi cache là source of truth cho balance hoặc payment status.
Observability và deployment gates
Theo dõi theo version/cohort:
Availability, p95/p99 latency và saturation.
Error rate theo endpoint/dependency.
DB lock wait, deadlock, long transaction, replication lag và WAL.
Connection count/pool wait.
Queue lag, oldest-message age và redelivery.
Idempotency conflict/duplicate suppression.
Payment PENDING age và reconciliation mismatch.
Ledger debit-credit imbalance phải bằng 0.
Business rate: authorization success/decline/refund.
Canary gate nên auto-pause khi vượt error budget. Dashboard aggregate có thể che lỗi của phiên bản mới 1%; luôn group theo build/version.
Test trước production
N và N+1 chạy đồng thời với transition schema.
Request bắt đầu trên old pod, hoàn thành khi pod terminate.
SIGTERM giữa database transaction.
Client retry sau timeout không double charge.
Old message được new consumer xử lý.
Backfill restart giữa batch.
DDL gặp lock dài và fail fast.
Invalid concurrent index được phát hiện.
Canary rollback sau new writes.
Queue backlog qua nhiều release.
DB failover trong rollout.
Webhook đến trong lúc cutover.
Restore backup và reconciliation pass.
Runbook release banking
Freeze unrelated schema/config changes.
Xác nhận backup, restore test và rollback/roll-forward plan.
Kiểm tra capacity cho surge pods và DB connections.
Pause ngay khi xuất hiện unknown payment outcome tăng bất thường.
Không drop/rename column trong cùng release với code mới.
Giữ người có quyền stop rollout và incident channel.
Reconcile trước khi đóng change window.
Anti-patterns
Chạy destructive migration trước khi app mới sẵn sàng.
Mỗi pod tự chạy DDL lúc startup.
Dùng Recreate cho critical API.
Readiness luôn trả 200 dù app chưa warm hoặc đang drain.
Kill pod ngay khi load balancer chưa ngừng route.
Tạo index thường trên bảng payment lớn giữa peak.
Backfill một transaction hàng triệu row.
Rollback database bằng script xóa dữ liệu mới.
Blue và Green cùng ghi hai database không có fencing.
Chỉ nhìn CPU/HTTP 5xx mà bỏ qua ledger/reconciliation.
Kết luận
Zero-downtime banking không đến từ một YAML RollingUpdate. Nó là chuỗi hợp đồng tương thích giữa load balancer, app versions, queue events, database schema và financial state machine.
Công thức thực tế là: rolling/blue-green/canary + graceful drain + idempotency + Expand–Migrate–Contract + online-safe DDL + automated financial gates + roll-forward plan. Khi những lớp này phối hợp, ứng dụng có thể được nâng cấp mà giao dịch đang chạy vẫn có kết quả duy nhất, bền vững và đối soát được.
Hướng dẫn HTTPS và TLS từ nền tảng đến production: rủi ro MITM của HTTP, chứng chỉ Let's Encrypt, Certbot với Nginx, ACME challenges, HSTS, renewal và troubleshooting.
Hướng dẫn lựa chọn thuật toán Load Balancing: Round Robin, Weighted Round Robin, Least Connections và IP Hash; kèm TypeScript, NGINX, health checks và HTTP/2 caveats.