Docker và Application Ops

Nâng cấp Docker image an toàn: pull, migrate và xác nhận ra sao?

Quy trình ghi nhận phiên bản, đọc release note, kiểm soát migration, pull và deploy image, xác nhận website rồi rollback khi cần.

Nâng cấp Docker image an toàn
Mục lục

Một phiên bản mới vừa được phát hành. Bạn đã đọc qua release note, thấy vài lỗi quan trọng được sửa và muốn cập nhật website trong tối nay. Công việc tưởng như chỉ có hai lệnh: pull image mới rồi tạo lại container.

Với một ứng dụng không có dữ liệu, cách làm đó đôi khi vẫn ổn. Website đang chạy thực tế thường phức tạp hơn. Phiên bản mới có thể đòi thêm biến môi trường, đổi quyền truy cập thư mục hoặc sửa cấu trúc database. Container mới có thể khởi động bình thường nhưng form liên hệ không gửi được, file cũ không đọc được hoặc worker ngừng nhận việc.

Vì vậy, hãy coi đây là một lần phát hành phần mềm, không phải thao tác thay container. Bài viết này trình bày cách thực hiện trên một VPS chạy Docker Compose. Mô hình một máy chủ vẫn có thể gián đoạn trong lúc cập nhật; yêu cầu không gián đoạn cần một kiến trúc triển khai khác.

Quy trình nâng cấp Docker image từ ghi nhận phiên bản tới xác nhận và rollback

Pull chỉ là một bước giữa quá trình chuẩn bị, xử lý migration, triển khai và xác nhận.

Ghi lại phiên bản đang chạy

Giả sử file Compose đang dùng image example/web:2.4.1 và nhà phát hành vừa đưa ra bản 2.5.0. Trước khi sửa cấu hình, hãy ghi lại tag và digest của bản hiện tại. Làm tương tự với bản sắp cài.

Tag giúp con người nhận ra phiên bản. Digest cho biết chính xác nội dung image. Hai thông tin này không hoàn toàn thay thế nhau vì một registry có thể cho phép ghi đè image dưới cùng một tag. Nếu chỉ ghi latest, vài tuần sau bạn khó xác định máy chủ từng chạy image nào.

Pin image bằng digest giúp lần triển khai sau vẫn lấy đúng artifact đã chọn. Đổi lại, bản vá mới sẽ không tự xuất hiện trên máy chủ; người quản trị phải cập nhật digest khi quyết định nhận bản vá.

Thông tin phiên bản nên được lưu cùng ngày thực hiện và người phụ trách lần cập nhật. Đây là dữ liệu rất hữu ích khi cần đối chiếu log hoặc quay về bản trước.

Đọc release note để biết phần nào sẽ thay đổi

Đừng chỉ nhìn danh sách lỗi đã sửa. Phần đáng quan tâm hơn là hướng dẫn nâng cấp và các thay đổi không tương thích.

Bạn cần biết phiên bản hiện tại có thể nâng thẳng lên bản mới hay phải qua một bản trung gian. Tìm các thay đổi liên quan đến biến môi trường, port, volume, phiên bản database, plugin và API bên ngoài. Nếu release có migration, tài liệu phải cho biết lệnh chạy và điều kiện áp dụng. Cảnh báo về downgrade hoặc rollback cũng cần được đọc trước khi chọn lịch bảo trì.

Image do doanh nghiệp tự build vẫn phải có ghi chú tương tự. Ngay cả khi mã nguồn ứng dụng không đổi, việc nâng base image cũng có thể thay runtime, package hệ thống hoặc bộ certificate.

Kết quả quét lỗ hổng và SBOM hỗ trợ kiểm tra thành phần bên trong image. Chúng không cho biết image có đọc đúng dữ liệu hiện tại hay có tương thích với cấu hình production hay không. Hai việc này phải được kiểm tra riêng.

Đối chiếu image mới với môi trường hiện tại

Container lấy khá nhiều thứ từ bên ngoài image. Nó đọc cấu hình và secret, kết nối database, gắn volume rồi gọi sang cache, queue hoặc các API khác. Chỉ một điểm không khớp cũng đủ làm website lỗi sau khi container được thay.

Bạn có thể đối chiếu theo bảng dưới đây:

Thành phầnNội dung cần xem
ImageRegistry, tag, digest và kiến trúc CPU
Cấu hìnhBiến mới, biến bị bỏ, port và feature flag
SecretTên secret và quyền đọc của container
DatabasePhiên bản engine và schema mà ứng dụng hỗ trợ
VolumeĐường dẫn mount, owner, permission và dữ liệu bên trong
Dịch vụ liên quanAPI, cache, queue, plugin hoặc extension

Ví dụ, image mới chuyển sang chạy bằng một user khác. Container vẫn lên nhưng không đọc được file upload trong volume cũ. Một thay đổi đường dẫn cũng có thể khiến ứng dụng nhìn thấy thư mục trống, trong khi dữ liệu thật vẫn nằm ở vị trí trước đây.

Nếu không có lý do bắt buộc, đừng nâng database engine trong cùng lần đổi image ứng dụng. Thay từng phần giúp việc tìm nguyên nhân dễ hơn khi có lỗi.

Chuẩn bị cho trường hợp migration không thành công

Migration làm thay đổi dữ liệu hoặc cấu trúc database. Đây cũng là lúc việc quay lại bản cũ trở nên khó nhất.

Trước ngày cập nhật, hãy xác định bản cũ có đọc được schema sau migration hay không. Có migration chỉ thêm bảng hoặc thêm cột nên hai phiên bản vẫn chạy được. Cũng có migration xóa cột, đổi định dạng dữ liệu hoặc biến đổi nội dung theo cách không thể đảo ngược. Trong trường hợp thứ hai, khởi động lại container cũ chưa chắc cứu được website.

Với dữ liệu quan trọng, hãy tạo recovery point trước khi chạy migration và kiểm tra rằng bản backup đã tới nơi lưu dự kiến. Đường dẫn restore cũng phải dùng được; trạng thái “backup thành công” chưa chứng minh dữ liệu có thể phục hồi.

Kế hoạch xử lý nên trả lời được những việc rất cụ thể: dừng ở đâu khi migration chạy quá lâu, có cho phép website tiếp tục nhận dữ liệu hay không, khi nào sửa tiếp trên bản mới và khi nào phải restore. Restore về recovery point đồng nghĩa với việc dữ liệu phát sinh sau thời điểm đó có thể mất, vì vậy quyết định này phải tính đến hoạt động kinh doanh trong khoảng thời gian bảo trì.

Bài dữ liệu persistent của container cần backup ở đâu? giúp xác định lại vị trí database, file upload và những dữ liệu nằm ngoài container.

Pull image trước giờ bảo trì

Với service có tên web, bạn có thể tải image bằng lệnh:

bash
docker compose pull web

Lệnh này chỉ tải image về máy chủ. Container hiện tại vẫn chạy, vì thế bạn có thể pull sớm để tránh dành thời gian bảo trì cho việc chờ tải các layer.

Sau khi pull, đối chiếu digest với bản đã chọn và xem lại dung lượng đĩa. Máy chủ cần đủ chỗ giữ cả image mới lẫn image cũ cho tới khi lần cập nhật được xác nhận ổn định.

Registry riêng thường yêu cầu đăng nhập. Hãy kiểm tra quyền truy cập trước giờ bảo trì, đặc biệt khi credential vừa được thay đổi hoặc tài khoản triển khai ít khi sử dụng. Password và token phải được lấy từ nơi lưu bí mật, không đặt trực tiếp trong câu lệnh dễ lưu vào lịch sử shell và không để xuất hiện trong log.

Một release có nhiều service thì nên pull đủ các image đi cùng nhau trước khi bắt đầu. Nếu thiếu một image bắt buộc, hãy xử lý việc đó trước thay vì để hệ thống chạy một tổ hợp phiên bản chưa từng được thử.

Xem cấu hình Compose sẽ được áp dụng

Production thường ghép file Compose gốc với một file override và bộ biến môi trường riêng. Hãy kiểm tra đúng tổ hợp mà máy chủ đang dùng:

bash
docker compose -f compose.yaml -f compose.production.yaml config --quiet

Lệnh trên kiểm tra cấu hình sau khi Compose đã ghép file và thay biến. Thứ tự file và project name phải giống lần triển khai thật.

Sau đó, xem lại image, port, volume, network, healthcheck và tên secret của service sắp cập nhật. docker compose config có thể hiển thị cả giá trị đã được nội suy từ environment, nên không sao chép toàn bộ kết quả vào ticket hoặc log dùng chung.

Nếu còn bind mount mã nguồn, chế độ hot reload hoặc debug log của môi trường phát triển, đây là lúc phát hiện trước khi chúng đi vào production.

Xử lý database migration trước khi đổi container

Nếu bản cập nhật có sửa database, đây là khâu dễ làm hỏng việc nhất.

Docker chỉ quản lý việc bật tắt container, nó không biết code của bạn đang chạy migration theo cách nào. Tùy framework, lệnh migration có thể được cấu hình tự chạy lúc container khởi động, hoặc bạn phải chủ động chạy thủ công bằng một container phụ:

bash
docker compose run --rm web python manage.py migrate

(hoặc lệnh php artisan migrate, prisma migrate deploy tương ứng với ứng dụng của bạn).

Trước khi bấm chạy lệnh đó, bạn cần biết chắc một điều: nếu migration này xong mà app mới gặp lỗi, container cũ có còn đọc được database nữa không?

  • Nếu migration chỉ thêm bảng hoặc thêm cột mới: Code cũ thường vẫn chạy bình thường vì nó chỉ đơn giản là bỏ qua những cột nó không biết. Trong tình huống này, nếu image mới bị lỗi, bạn vẫn có đường lùi là bật lại image cũ ngay lập tức.
  • Nếu migration đổi tên cột, xóa dữ liệu hoặc tách bảng: Một khi đã chạy xong, code của bản cũ sẽ gãy ngay vì không còn tìm thấy cấu trúc cũ. Lúc này, bạn không còn đường "quay xe" bằng cách đổi lại tag image đơn thuần nữa. Nếu có sự cố, bạn chỉ có hai lựa chọn: hoặc sửa tiếp trên bản mới, hoặc phải chấp nhận dừng web để restore database từ bản backup vừa tạo.

Với những bảng lớn có hàng triệu bản ghi, việc thêm index hay đổi kiểu dữ liệu có thể khóa bảng (table lock) trong vài phút. Hãy ước lượng trước thời gian này để không bất ngờ khi thấy kết nối vào database bị nghẽn.

Bật container mới mà không làm sập các dịch vụ khác

Khi database đã sẵn sàng và image mới đã pull về máy, bạn kích hoạt phiên bản mới bằng lệnh:

bash
docker compose up -d --no-deps web

Hãy chú ý cờ --no-deps. Tùy chọn này chỉ thị Compose chỉ dừng và tạo lại riêng container web, giữ nguyên các container khác như MySQL, Redis hay Nginx. Cần tránh thói quen gõ docker compose down rồi mới up lại trên môi trường production, vì down sẽ làm sập toàn bộ hệ thống, ngắt các kết nối database đang dở dang và khiến thời gian website gián đoạn kéo dài không cần thiết.

Lệnh up -d sẽ tự tạo container mới và hoán đổi vị trí với container cũ. Lúc này, đừng vội mừng khi thấy terminal báo Started hay trạng thái hiển thị Up (healthy). Healthcheck màu xanh chỉ mới chứng minh tiến trình bên trong container chưa bị crash. Nó chưa chứng minh được website đang phục vụ người dùng bình thường.

Kiểm tra website từ góc nhìn người dùng thực tế

Ngay sau khi container mới chạy, việc đầu tiên là mở trình duyệt ẩn danh và truy cập domain thật của website, không kiểm tra qua localhost hay IP máy chủ.

Hãy đi qua một lượt các thao tác mà người dùng hay làm nhất: mở trang chủ, đăng nhập thử một tài khoản, vào trang chi tiết sản phẩm và thử gửi một form liên hệ hoặc tạo đơn hàng test. Nếu hệ thống có gửi email xác nhận hoặc gọi API thanh toán, hãy dùng tài khoản test nội bộ để tránh gửi nhầm thông báo thật cho khách hàng.

Khi giao diện bên ngoài có vẻ ổn, quay lại terminal để soi log bên trong container:

bash
docker compose logs --tail=100 -f web

Xem log lúc này để bắt những lỗi âm thầm:

  • Có lỗi kết nối database hoặc Redis không?
  • Có dòng nào báo lỗi quyền ghi file (Permission denied) vào thư mục upload không?
  • Các tiến trình nền như worker xử lý hàng đợi (queue) và cron job có đang nhận việc bình thường không? Rất nhiều trường hợp web chạy mượt nhưng worker bên trong bị treo, sáng hôm sau mới phát hiện hàng nghìn email hay đơn hàng bị kẹt lại.

Phải làm gì nếu phiên bản mới phát sinh lỗi?

Nếu website báo lỗi 502, trắng trang hoặc chức năng chính không chạy được, hãy bình tĩnh quyết định đường lùi dựa trên tình trạng database:

  • Nếu chưa chạy migration hoặc migration tương thích hai chiều: Cách nhanh nhất là sửa lại tag image trong file Compose về phiên bản cũ rồi chạy lại docker compose up -d --no-deps web. Hệ thống sẽ quay về bản ổn định chỉ sau vài giây.
  • Nếu migration đã làm thay đổi cấu trúc database: Đừng vội rollback image. Lúc này code cũ kéo lên cũng sẽ chết vì không đọc được database mới. Bạn phải cân nhắc nhanh: nếu lỗi ở bản mới là lỗi nhỏ sửa được ngay (như thiếu một biến môi trường trong file .env), hãy sửa và khởi động lại container. Nhưng nếu lỗi phức tạp không rõ nguyên nhân, phương án an toàn duy nhất là kích hoạt kịch bản khôi phục: tạm dừng web, restore database về bản backup trước khi nâng cấp, rồi mới đưa container cũ lên lại.

Trong suốt quá trình này, hãy tránh chạy các lệnh dọn dẹp như docker system prune hay xóa image cũ. Image cũ và các volume dữ liệu chính là phao cứu sinh để quay về trạng thái ban đầu bất cứ lúc nào nếu cần.

Khi nào có thể yên tâm kết thúc ca cập nhật?

Đừng tắt máy đi ngủ ngay khi vừa thấy web chạy lại được vài phút. Lỗi thường chỉ lộ ra khi bộ nhớ đệm (cache) bắt đầu hết hạn, hoặc khi các cron job chạy vào đầu giờ tiếp theo.

Hãy để mắt tới log và mức tiêu hao CPU, RAM của máy chủ thêm khoảng 15 đến 30 phút. Nếu website vẫn êm, log không nhả thêm lỗi ngoại lệ và hàng đợi công việc được giải phóng đều đặn, bạn có thể yên tâm ca cập nhật tối nay đã thành công.

Những image cũ của phiên bản trước không cần dọn dẹp ngay trong đêm. Hãy để nó ở đó vài ngày. Khi hệ thống đã vận hành trơn tru suốt một tuần, bạn hãy quay lại dọn đĩa sau cũng chưa muộn. Để chuẩn bị bài bản mọi khâu từ cấu hình, secret đến giám sát và phục hồi trước mỗi lần deploy, bạn nên đối chiếu với Checklist đưa Docker Compose lên production.

Nguồn tham khảo

Chia sẻ bài viết hữu ích

Gửi bài viết này cho đồng nghiệp