Cập nhật self-hosted app không downtime cho hệ thống nhỏ và vừa: làm sao để “đổi máy khi xe vẫn chạy”?
Với hệ thống nhỏ và vừa, downtime thường bị xem là “chấp nhận được vài phút”. Nhưng thực tế, vài phút đó có thể đổi lấy mất đơn hàng, lỗi đồng bộ, gián đoạn nội bộ, mất niềm tin người dùng. Tin tốt: để cập nhật ứng dụng không downtime không nhất thiết cần hạ tầng quá lớn, Kubernetes phức tạp hay đội SRE đông người. Nhiều hệ thống SME vẫn làm được nếu thiết kế đúng quy trình.
Mấu chốt: đừng cập nhật trực tiếp lên instance đang phục vụ traffic. Thay vào đó, tạo bản mới, kiểm tra, chuyển lưu lượng, theo dõi, rồi mới loại bản cũ. Quy trình này nghe đơn giản, nhưng để chạy ổn cần xử lý đúng 4 lớp: app, DB, session/cache, reverse proxy/load balancer.
“Không downtime” thực sự nghĩa là gì?
Không downtime không phải luôn là 0 giây gián đoạn tuyệt đối. Với hệ thống nhỏ và vừa, mục tiêu thực tế hơn là:
– Người dùng không thấy lỗi 5xx
– Kết nối đang chạy không bị cắt đột ngột
– Deploy có thể rollback nhanh
– Migration DB không khóa hệ thống lâu
– Request mới luôn có instance khỏe để nhận
Nói ngắn: deploy diễn ra trong lúc hệ thống vẫn phục vụ bình thường.
Điều kiện nền tảng trước khi nghĩ tới zero-downtime update
1. Ứng dụng phải stateless tối đa
App giữ state trong RAM → thay instance khó.
State đưa ra ngoài → thay app dễ.
Nên tách:
– Session → Redis/DB
– File upload → object storage/NFS/shared volume
– Job queue → Redis/RabbitMQ
– Cache → cache ngoài app
– Config → env file/secret manager
Nếu session nằm trong memory của từng container/process, khi chuyển traffic người dùng sẽ bị đăng xuất hoặc lỗi phiên.
2. Reverse proxy phải hỗ trợ chuyển traffic mềm
Các lựa chọn phổ biến:
– Nginx
– Traefik
– HAProxy
– Caddy
Vai trò:
– route traffic tới nhiều app instance
– health check
– loại instance lỗi khỏi pool
– hỗ trợ reload config không ngắt kết nối
3. App phải có health check
2 endpoint nên có:
– liveness → app còn sống?
– readiness → app sẵn sàng nhận traffic?
Ví dụ:
– app khởi động xong process nhưng chưa kết nối DB → liveness OK, readiness FAIL
– app treo hoàn toàn → cả 2 FAIL
Không có readiness → proxy có thể route vào instance chưa sẵn sàng → lỗi ngay lúc deploy.
4. SIGTERM phải được xử lý đúng
Khi dừng instance cũ:
– ngừng nhận request mới
– xử lý nốt request đang chạy
– đóng kết nối sạch
– thoát sau grace period
Nếu app bị kill cứng → request dở dang → 502/504 → “không downtime” thất bại.
Mô hình cập nhật phù hợp cho SME
1. Rolling update
Cách chạy: cập nhật từng instance một.
Nếu có 2–3 instance app trở lên, đây là cách gọn nhất.
Quy trình:
1. thêm instance mới hoặc cập nhật 1 instance
2. chờ health check pass
3. đưa vào pool nhận traffic
4. rút 1 instance cũ khỏi pool
5. drain request cũ
6. dừng instance cũ
7. lặp lại
Ưu điểm:
– ít tốn tài nguyên hơn blue-green
– phù hợp VM nhỏ, Docker Compose mở rộng thủ công
Nhược điểm:
– cần tương thích giữa version cũ/mới trong thời gian chồng lấn
– migration DB phải rất cẩn thận
2. Blue-Green deployment
Cách chạy: giữ 2 môi trường song song.
– Blue → đang chạy production
– Green → bản mới
Deploy lên Green, test xong → đổi proxy từ Blue sang Green.
Ưu điểm:
– rollback cực nhanh
– test sát production dễ
– ít rủi ro hơn rolling
Nhược điểm:
– tốn gấp đôi tài nguyên tạm thời
– cần cơ chế đồng bộ config, secret, network
Với SME, blue-green rất hợp nếu app không quá nặng, số node ít, cần thao tác dễ hiểu.
3. Canary deployment
Chuyển một phần nhỏ traffic sang bản mới, ví dụ 5% → 20% → 50% → 100%.
Hợp khi:
– có đủ traffic để quan sát
– có metric rõ: error rate, latency, login fail, checkout fail
Với hệ thống rất nhỏ, canary đôi khi không đáng vì lượng traffic thấp → khó kết luận.
Bài toán khó nhất: cập nhật DB không làm sập hệ thống
Deploy app thường dễ hơn migration DB. Lỗi phổ biến:
– app mới cần cột mới nhưng DB chưa có
– app cũ không hiểu schema mới
– migration khóa bảng lâu
– rollback app được, rollback schema khó
Nguyên tắc “expand → migrate → contract”
Đây là quy trình an toàn nhất.
Bước 1: Expand
Thêm cấu trúc mới theo kiểu tương thích ngược:
– thêm cột mới, cho phép null
– thêm bảng mới
– thêm index mới
– không xóa cột cũ
– không đổi kiểu dữ liệu gây vỡ tương thích ngay
App cũ vẫn chạy được. App mới cũng bắt đầu dùng dần.
Bước 2: Migrate
Chuyển dữ liệu:
– backfill dữ liệu cũ sang cột mới
– chạy job nền theo batch
– tránh update toàn bảng một lần nếu dữ liệu lớn
Bước 3: Contract
Khi chắc chắn app mới ổn, không còn phụ thuộc cấu trúc cũ:
– xóa cột cũ
– bỏ code cũ
– siết constraint
Không gộp cả 3 bước vào 1 deploy nếu muốn an toàn.
Lưu ý thực chiến với migration
– tạo index lớn → cân nhắc concurrent/online index nếu DB hỗ trợ
– đổi tên cột trực tiếp → app cũ dễ chết
– thêm NOT NULL ngay → deploy dễ fail nếu dữ liệu cũ chưa sạch
– migration lâu → tách khỏi bước restart app
Nói ngắn: schema phải tương thích qua ít nhất 1–2 version app.
Quy trình deploy không downtime mẫu cho hệ thống nhỏ và vừa
Dưới đây là quy trình thực tế, đủ nhẹ để áp dụng với Docker/VM phổ thông.
Giai đoạn 1: Chuẩn bị
– build artifact/container image bất biến
– gắn version rõ ràng
– backup DB
– chạy test tự động
– xác nhận migration đã backward-compatible
– chuẩn bị dashboard log/metric
Giai đoạn 2: Deploy bản mới song song
– khởi động instance/container mới
– nạp config, secret
– kết nối DB, cache, queue
– chờ readiness pass
Chưa pass readiness → chưa cho nhận traffic.
Giai đoạn 3: Chuyển traffic có kiểm soát
– thêm instance mới vào upstream
– giữ instance cũ phục vụ tiếp
– theo dõi:
– 5xx
– CPU/RAM
– latency
– error log
– login/payment/task fail
Nếu ổn → tiếp tục tăng tỷ lệ traffic hoặc thay dần instance cũ.
Giai đoạn 4: Drain instance cũ
– đánh dấu instance cũ “không nhận request mới”
– chờ request đang xử lý xong
– dừng worker nhận job mới
– đợi job ngắn hoàn tất hoặc requeue job dài
Giai đoạn 5: Hoàn tất và hậu kiểm
– xác nhận toàn bộ traffic sang bản mới
– kiểm tra job nền, cron, webhook
– theo dõi 15–30 phút
– chỉ sau đó mới cleanup bản cũ
Công cụ tối giản vẫn làm được
Không cần bắt đầu từ Kubernetes. SME có thể đi từng mức:
Mức 1: Docker Compose + Nginx
Phù hợp:
– 1–2 máy chủ
– traffic vừa phải
– đội ngũ nhỏ
Cách làm:
– chạy 2 bản app song song khác cổng
– Nginx upstream tới cả 2
– reload Nginx khi đổi pool
– health check thủ công/bán tự động
Mức 2: Traefik hoặc HAProxy
Phù hợp khi muốn:
– service discovery gọn hơn
– health check tự động hơn
– routing linh hoạt hơn
Mức 3: Nomad/Kubernetes
Chỉ nên lên khi:
– nhiều service
– nhiều môi trường
– cần autoscaling, self-healing, rollout policy mạnh
Sai lầm phổ biến: nâng orchestration trước khi chuẩn hóa app và DB.
App không stateless, migration kém an toàn → lên nền tảng lớn vẫn downtime.
Checklist lỗi hay gặp
– Chỉ có 1 app instance → không thể zero-downtime thật
– Session lưu local memory → người dùng bị logout khi chuyển instance
– Thiếu readiness check → traffic vào app chưa sẵn sàng
– Migration phá tương thích → app cũ/mới không cùng sống được
– Không drain kết nối → request đang chạy bị cắt
– Không rollback plan → deploy lỗi kéo dài
– Không quan sát sau deploy → lỗi âm thầm lan rộng
Kết luận: SME không cần hạ tầng “khủng”, cần quy trình đúng
Không downtime không phải đặc quyền của big tech. Với hệ thống nhỏ và vừa, chỉ cần vài nguyên tắc cốt lõi:
– ít nhất 2 instance app
– reverse proxy biết health check + drain
– app stateless tối đa
– DB migration tương thích ngược
– deploy theo rolling hoặc blue-green
– theo dõi sát và rollback nhanh
Nếu chỉ chọn một việc để bắt đầu tuần này, hãy làm: tách session/file/cache ra khỏi app instance. Bước đó mở đường cho gần như toàn bộ chiến lược cập nhật không downtime về sau.
Zero-downtime không đến từ công cụ đắt tiền. Nó đến từ tư duy: không sửa cái đang chạy; dựng cái mới, kiểm tra, chuyển tải, rồi mới bỏ cái cũ. Với SME, đó là cách cân bằng tốt nhất giữa an toàn, chi phí, đơn giản, hiệu quả.