Bản cập nhật vừa lên được vài phút thì hệ thống giám sát báo động, hoặc khách hàng bắt đầu nhắn tin báo website không vào được. Mở terminal lên, bạn thấy container liên tục khởi động lại (crash loop) hoặc Nginx trả về lỗi 502 Bad Gateway.
Phản xạ tự nhiên lúc này là muốn "quay xe" về phiên bản cũ càng nhanh càng tốt.
Nhưng trên máy chủ production, việc rollback bằng Docker Compose không đơn giản là đổi lại số tag image rồi chạy lại. Một bản deploy thường kéo theo ba thứ thay đổi cùng lúc: image ứng dụng, biến môi trường (hoặc file cấu hình) và dữ liệu trong database. Nếu chỉ vội vàng đổi lại image cũ mà không để ý hai yếu tố còn lại, website rất dễ rơi vào tình trạng tồi tệ hơn: app cũ không đọc được cấu hình mới, hoặc tệ nhất là làm hỏng dữ liệu đang có.
Trước khi gõ bất kỳ lệnh nào, hãy lưu ý hai phản xạ sai lầm rất hay gặp:
- Đừng gõ
docker compose restart web: Lệnh restart chỉ đơn thuần khởi động lại tiến trình bên trong container hiện tại. Docker sẽ không nạp lại image cũ, cũng không đọc lại các biến môi trường mà bạn vừa chỉnh sửa trong file.env. - Đừng vội chạy
docker compose down: Lệnh này sẽ kéo sập toàn bộ hệ thống, ngắt sạch các kết nối mạng nội bộ và khiến website gián đoạn hoàn toàn. Ngoài ra, việc xóa container lỗi quá sớm sẽ xóa mất dấu vết và log cần thiết để bạn hiểu chuyện gì vừa xảy ra.
Thay đổi database quyết định việc có thể rollback application trực tiếp hay phải chọn đường phục hồi khác.
Bước 1: Giữ nguyên hiện trạng và khoanh vùng lỗi trong 2 phút
Thay vì cuống cuồng thử các lệnh sửa chữa, hãy dành ra một đến hai phút đầu tiên để xem container đang ở trạng thái nào:
docker compose psLệnh này cho bạn biết service nào đang chạy (Up), service nào bị thoát (Exited) và service nào đang kẹt trong vòng lặp restart (Restarting).
Ngay sau đó, soi nhanh 50 dòng log gần nhất của service gặp sự cố:
docker compose logs --tail=50 webChỉ cần nhìn lướt qua log, bạn sẽ xác định được nguyên nhân thuộc nhóm nào:
- Lỗi cấu hình: Báo thiếu biến môi trường, sai thông tin kết nối database hoặc sai đường dẫn file.
- Lỗi ứng dụng: Báo lỗi cú pháp code, thiếu thư viện runtime hoặc crash ngay khi boot.
- Lỗi database: Báo bảng hoặc cột không tồn tại, sai định dạng dữ liệu (thường do migration chưa chạy hoặc chạy dở).
Bước 2: Cân nhắc sửa tiếp hay quyết định quay về
Không phải mọi sự cố deploy đều cần quay về bản cũ. Khi đã nhìn thấy log ở bước 1, bạn có hai hướng đi:
- Sửa tiếp tại chỗ (Roll-forward): Nếu lỗi chỉ đơn giản là quên chưa thêm một biến môi trường trong file
.env(ví dụ thiếu API key mới, hoặc sai tên host của Redis), hãy sửa ngay file cấu hình đó rồi chạy lạidocker compose up -d --no-deps web. Việc này chỉ mất khoảng một phút và giải quyết triệt để vấn đề mà không phải kéo lùi phiên bản. - Quyết định quay về bản cũ (Rollback): Nếu log báo lỗi nghiêm trọng từ bên trong mã nguồn ứng dụng, container crash liên tục không rõ nguyên nhân, hoặc các chức năng thanh toán, đăng nhập bị tê liệt, đây là lúc bạn cần kích hoạt quy trình rollback ngay để trả lại website hoạt động cho người dùng trước, việc tìm lỗi tính sau.
Bước 3: Kiểm tra database trước khi đụng vào container
Đây là ranh giới sống còn của một ca rollback: database đã bị thay đổi bởi migration của bản mới hay chưa?
- Nếu chưa chạy migration (hoặc migration chỉ thêm bảng/cột mới): Bạn hoàn toàn yên tâm. Image cũ khi khởi động lại vẫn sẽ đọc được database như bình thường vì nó chỉ đơn giản bỏ qua các dữ liệu mới mà nó không dùng đến.
- Nếu migration đã xóa cột, đổi kiểu dữ liệu hoặc tách bảng: Hãy cẩn thận. Lúc này nếu bạn vội vàng kéo image cũ lên, code cũ cũng sẽ sập ngay vì không còn tìm thấy các cột quen thuộc trong database. Trong tình huống này, bạn bắt buộc phải:
- Hoặc chạy lệnh đảo ngược migration (
migrate down/ rollback migration theo framework của app) nếu bạn đã chuẩn bị sẵn kịch bản này từ trước. - Hoặc chấp nhận dừng web vài phút để khôi phục (restore) database từ bản backup mà bạn vừa tạo trước giờ deploy. Bài Dữ liệu persistent của container cần backup ở đâu? giải thích chi tiết cách tổ chức dữ liệu volume và database để phục hồi nhanh nhất.
Bước 4: Thao tác rollback an toàn trên máy chủ
Khi đã xác nhận đường lui của database an toàn, bạn tiến hành đưa ứng dụng về phiên bản cũ theo 3 bước sau:
1. Khôi phục lại file Compose và biến môi trường: Sửa lại tag image trong file Compose (hoặc file .env) về đúng tag của phiên bản ổn định trước đó (ví dụ từ v2.4.0 quay về v2.3.9). Nếu bản cập nhật vừa rồi có sửa các file cấu hình mount ngoài máy chủ (như file config của Nginx hay PHP-FPM), hãy đưa chúng về lại nội dung cũ.
2. Kiểm tra cú pháp cấu hình: Chạy lệnh kiểm tra để chắc chắn file Compose không bị lỗi thụt dòng hay sai cú pháp:
docker compose config --quietNếu lệnh chạy êm và không báo lỗi gì ra màn hình, cấu hình đã hợp lệ.
3. Tạo lại container bằng image cũ: Kích hoạt phiên bản cũ bằng lệnh:
docker compose up -d --no-deps webCờ --no-deps đảm bảo Docker chỉ thay thế đúng container web, giữ nguyên database và các dịch vụ khác đang chạy. Docker sẽ tự động dừng container lỗi, tháo gỡ và khởi động lại container bằng image cũ mà bạn đã chỉ định. Bạn có thể xem thêm quy trình đối chiếu digest và khởi động an toàn tại bài Nâng cấp Docker image an toàn.
Bước 5: Xác nhận hệ thống đã thực sự bình phục
Container báo trạng thái Up mới chỉ là điều kiện cần. Bạn cần xác nhận website đã thực sự phục vụ khách hàng bình thường qua 3 lớp kiểm tra:
- Trải nghiệm bên ngoài: Mở trình duyệt ẩn danh vào thẳng domain chính thức. Kiểm tra trang chủ, đăng nhập vào hệ thống và thử mở một vài trang dữ liệu quan trọng xem nội dung có hiển thị đầy đủ không.
- Tiến trình xử lý nền (Worker & Queue): Nếu ứng dụng có gửi email, xử lý ảnh hay hàng đợi công việc nền, hãy kiểm tra xem worker có đang nhặt job và xử lý tiếp hay không:
docker compose logs --tail=50 workerRất nhiều ca rollback xong web chạy lại bình thường nhưng worker dùng phiên bản cũ không xử lý được các job vừa bị bản mới đẩy vào queue dở dang. Chi tiết về cách thiết lập healthcheck để tự động bắt các lỗi tiến trình này có tại bài Healthcheck trong Docker Compose nên kiểm tra gì?.
- Mức tiêu hao tài nguyên: Quan sát CPU, RAM của máy chủ trong 10 đến 15 phút. Nếu biểu đồ tải trở về mức bình thường và log không còn phun ra các dòng lỗi ngoại lệ liên tục, ca cứu hộ của bạn đã thành công.
Giữ lại bằng chứng lỗi để điều tra vào ngày mai
Khi website đã chạy êm trở lại, tâm lý chung là muốn dọn dẹp sạch sẽ máy chủ để đi nghỉ. Nhưng khoan hãy gõ các lệnh dọn dẹp như docker system prune.
Hãy xuất lại vài trăm dòng log của container bị lỗi vừa rồi và lưu vào một file text:
docker compose logs web > deploy-failed-$(date +%F).logGiữ lại file log này cùng với commit mã nguồn và file .env của lần deploy lỗi. Sáng hôm sau, đội ngũ phát triển có thể nhìn thẳng vào dữ liệu thực tế này để tái hiện và sửa lỗi trên môi trường staging, thay vì phải đoán mò xem tối qua máy chủ đã gặp chuyện gì.
5 điều cần chuẩn bị để mọi lần rollback đều êm xuôi
Để lần deploy nào cũng có đường lui an toàn mà không bị hoảng loạn, hãy biến 5 việc sau thành thói quen trước mỗi đợt phát hành:
- ☐ Không bao giờ dùng tag
latesttrên production: Luôn dùng số phiên bản rõ ràng (nhưv1.2.3hoặc commit hash ngắn) để bạn luôn biết chính xác bản ổn định trước đó là bản nào. - ☐ Không xóa image cũ ngay sau khi pull image mới: Ổ cứng máy chủ nên để dư một khoảng dung lượng đủ để giữ lại ít nhất 1–2 image phiên bản trước đó.
- ☐ Luôn tạo bản backup database trước khi chạy migration: Đặc biệt là các migration có sửa đổi cấu trúc bảng.
- ☐ Tách biệt giữa thay đổi code và thay đổi schema database: Nếu tính năng mới cần đổi cấu trúc bảng lớn, hãy triển khai migration trước một bản phát hành, đảm bảo hệ thống cũ vẫn tương thích, rồi mới phát hành code dùng tính năng mới ở lần sau.
- ☐ Lưu sẵn bộ file cấu hình của phiên bản đang chạy: Để khi cần quay xe, bạn có sẵn file Compose và biến môi trường cũ mà không mất thời gian lục lại lịch sử Git. Xem thêm Checklist đưa ứng dụng Docker Compose lên production để hoàn thiện toàn bộ quy trình phát hành.



