Một container có thể ở trạng thái “Up” nhưng ứng dụng bên trong đã không còn phục vụ đúng. Process vẫn tồn tại, cổng vẫn mở hoặc PID 1 chưa thoát không chứng minh request HTTP, kết nối database hay queue consumer đang khỏe. Docker healthcheck giúp đo trạng thái sẵn sàng; restart policy giúp xử lý một số trường hợp process thoát. Hai cơ chế này liên quan nhưng không thay thế cho nhau.
Bài viết này giải thích cách viết probe có ý nghĩa, cấu hình HEALTHCHECK trong Dockerfile và Compose, dùng depends_on với service_healthy, chọn restart policy phù hợp và tránh các lỗi khiến container restart loop hoặc báo unhealthy giả. Mục tiêu là có một service tự phục hồi vừa đủ, đồng thời vẫn để monitoring và log chỉ ra nguyên nhân.
1. Healthcheck và restart policy khác nhau thế nào?
| Cơ chế | Nó trả lời câu hỏi nào? | Nó không tự giải quyết điều gì? |
|---|---|---|
| Healthcheck | Container có đang đáp ứng probe theo tiêu chí đã định không? | Không tự sửa ứng dụng hoặc mặc định restart container unhealthy |
| Restart policy | Khi container exit, Docker có start lại không? | Không biết endpoint có trả đúng hoặc database đã sẵn sàng |
| Monitoring | Người vận hành có nhận biết lỗi, xu hướng và thời gian gián đoạn không? | Không thay thế probe đúng hoặc runbook xử lý |
| Orchestrator | Có cần rollout, reschedule hoặc scale service không? | Không phải lựa chọn mặc định cho mọi VPS đơn lẻ |
Healthcheck đo “service có khỏe theo probe không”; restart policy xử lý “container đã thoát thì có chạy lại không”.Docker Docs — Dockerfile HEALTHCHECK và Start containers automatically.
Nếu process bị kẹt nhưng chưa thoát, restart policy có thể không làm gì. Nếu healthcheck sai hoặc thiếu tool, container có thể bị đánh dấu unhealthy dù người dùng vẫn truy cập bình thường. Vì vậy trước khi tăng retry hoặc dùng always, hãy kiểm tra probe và hành vi exit thực tế.
2. HEALTHCHECK trong Dockerfile hoạt động ra sao?
Docker hỗ trợ dạng HEALTHCHECK CMD command để chạy lệnh trong container hoặc HEALTHCHECK NONE để tắt healthcheck kế thừa từ base image. Health status ban đầu là starting, sau khi probe pass là healthy, và sau số lần fail liên tiếp theo retries là unhealthy.
FROM nginx:stable-alpine
COPY ./public /usr/share/nginx/html
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1/ || exit 1Exit code 0 nghĩa là probe thành công, 1 là unhealthy và 2 là mã reserved không nên dùng. Output stdout hoặc stderr của probe được lưu trong health status để xem bằng docker inspect, nhưng nên giữ output ngắn để log chẩn đoán không biến thành nguồn gây đầy disk.
3. Chọn interval, timeout, start-period và retries
Các option cần phản ánh thời gian khởi động thật của ứng dụng. interval là khoảng giữa các lần probe; timeout là thời gian tối đa cho một lần probe; start-period cho phép app bootstrap mà failure ban đầu chưa bị tính; retries là số lần fail liên tiếp trước khi chuyển unhealthy. Docker Engine 25 trở lên còn hỗ trợ start-interval cho tần suất probe trong start period.
| Option | Cách suy nghĩ khi chọn |
|---|---|
| interval | Không quá dày gây tải, không quá thưa khiến phát hiện lỗi chậm |
| timeout | Lớn hơn latency bình thường nhưng nhỏ hơn thời gian chờ người dùng |
| start-period | Bao phủ migration/bootstrap hợp lệ, không che lỗi khởi động vô hạn |
| retries | Đủ tránh một spike ngắn nhưng không trì hoãn cảnh báo quá lâu |
Không nên sao chép một bộ 30s/30s/0s/3 cho mọi service. Database, ứng dụng Node.js, worker và reverse proxy có thời gian sẵn sàng khác nhau. Đo trên staging, sau đó kiểm tra cả cold start, restart và tải bình thường.
4. Viết probe đúng: kiểm tra khả năng phục vụ, không chỉ kiểm tra process
Probe tốt kiểm tra một hành vi tối thiểu có ý nghĩa. Web service nên gọi endpoint nhẹ như /healthz hoặc /ready; database dùng client readiness command; worker có thể kiểm tra heartbeat hoặc queue connection. Tránh endpoint phụ thuộc vào dịch vụ ngoài nếu mục tiêu là biết process cục bộ còn hoạt động.
# Probe HTTP chỉ coi status 2xx là thành công
HEALTHCHECK --interval=20s --timeout=3s --retries=3 \
CMD wget -q -O /dev/null http://127.0.0.1:8080/healthz || exit 1
# Kiểm tra bằng docker inspect
docker inspect --format='{{json .State.Health}}' app | jqNếu image không có curl hoặc wget, cài thêm chỉ vì probe sẽ làm image phình lên. Có thể dùng binary đã có, endpoint nội bộ, hoặc tạo probe nhỏ phù hợp runtime. Tuyệt đối không để probe ghi dữ liệu, gửi email, gọi API tốn phí hoặc chạy migration mỗi chu kỳ.
5. Healthcheck trong Docker Compose
Compose có thể khai báo healthcheck ở service và dùng depends_on với condition: service_healthy. Điểm quan trọng là Compose mặc định chỉ chờ container dependency ở trạng thái running, không chờ ứng dụng bên trong sẵn sàng. Điều kiện service_healthy giúp web đợi database pass probe trước khi được tạo.
services:
db:
image: postgres:18
environment:
POSTGRES_USER: app
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
web:
build: .
depends_on:
db:
condition: service_healthy
restart: unless-stoppedTrong ví dụ, healthcheck của database chỉ kiểm tra database đã sẵn sàng nhận kết nối; nó chưa chứng minh migration đã chạy hoặc user ứng dụng có đủ quyền. Nếu web cần migration, hãy tách job migration thành bước có kiểm soát thay vì nhét vào healthcheck.
6. Bốn restart policy của Docker
Docker có các policy chính: no là mặc định và không tự restart; on-failure[:max-retries] chỉ restart khi process exit với mã khác 0; always restart khi container stop và cả khi daemon restart theo semantics của Docker; unless-stopped tương tự always nhưng tôn trọng việc người vận hành stop thủ công sau khi daemon restart.
# Service web thường dùng unless-stopped khi chạy trên VPS
docker run -d --name web --restart unless-stopped example/web:1.0
# Worker lỗi liên tục: giới hạn số lần restart
docker run -d --name worker --restart on-failure:5 example/worker:1.0
# Xem policy hiện tại
docker inspect --format='{{json .HostConfig.RestartPolicy}}' webRestart policy chỉ bắt đầu có tác dụng sau khi container khởi động thành công và Docker đã theo dõi nó ít nhất khoảng 10 giây. Nếu container thoát ngay trong bootstrap, đừng chỉ tăng policy; hãy đọc log, kiểm tra command, environment, permission và dependency. Docker cũng cảnh báo không nên kết hợp restart policy với process manager ở host vì hai lớp có thể tranh quyền điều khiển.
7. Vì sao container unhealthy không tự restart?
Health status và exit status là hai tín hiệu khác nhau. Một container có thể unhealthy nhưng process chính vẫn đang chạy, vì vậy restart policy không nhất thiết được kích hoạt. Nếu muốn unhealthy dẫn đến restart, cần một lớp điều phối hoặc automation bên ngoài có chủ đích; trên VPS nhỏ, trước hết hãy sửa probe và để monitoring cảnh báo thay vì tạo vòng restart mù.
| Tình huống | Cách điều tra đầu tiên |
|---|---|
| Up nhưng unhealthy | docker inspect State.Health, log probe, endpoint nội bộ |
| Restart loop | docker logs, exit code, env/secret, command và dependency |
| Web start trước database | depends_on condition service_healthy và migration flow |
| Probe timeout | latency, DNS, localhost/port, CPU pressure và timeout thực tế |
| Sau reboot không chạy | restart policy, Docker daemon, volume/network và boot ordering |
8. Cẩn thận với localhost, DNS và port trong container
Bên trong container, localhost là chính container đó, không phải host và cũng không phải service khác trong Compose. Vì vậy web probe vào database bằng localhost là sai nếu database nằm ở service riêng. Trong Compose, dùng tên service làm hostname; khi kiểm tra từ host, dùng port publish; khi kiểm tra nội bộ, dùng port listen bên trong container.
# Trong container web: db là tên service Compose
getent hosts db || true
nc -z -w 3 db 5432
# Từ host: kiểm tra port đã publish
curl --fail http://127.0.0.1:8080/healthzProbe cũng phải có timeout rõ ràng. Một lệnh DNS hoặc TCP treo không chỉ làm trạng thái health sai mà còn tiêu thụ process trong container. Hãy kiểm thử khi dependency down, DNS lỗi, disk đầy và app trả 500 để bảo đảm probe phân biệt được healthy/unhealthy.
9. Log, event và monitoring bên ngoài
Healthcheck là tín hiệu cục bộ; production vẫn cần log và monitoring bên ngoài container. Dùng docker events, docker inspect, log driver hoặc hệ thống monitoring để theo dõi lần chuyển trạng thái health, số lần restart, tuổi container và thời gian từ lần backup/triển khai gần nhất.
docker events --filter type=container
docker ps --format 'table {{.Names}}\t{{.Status}}'
docker inspect --format '{{.Name}} restart={{.RestartCount}} status={{.State.Status}} health={{if .State.Health}}{{.State.Health.Status}}{{end}}' appMột health endpoint nên trả thông tin tối thiểu, không lộ version nhạy cảm, token hoặc topology nội bộ. Tách liveness và readiness nếu ứng dụng lớn: liveness trả lời process có cần restart không, readiness trả lời instance có nhận traffic không. Docker HEALTHCHECK không thay thế thiết kế quan sát ở cấp ứng dụng.
10. Anti-pattern thường gặp
- Dùng ping localhost để kết luận web app hoạt động dù HTTP handler đã treo.
- Dùng endpoint kiểm tra quá nặng, gọi dependency ngoài hoặc chạy migration trong mỗi probe.
- Chọn always cho mọi container mà không giới hạn restart hoặc đọc exit code.
- Tưởng unhealthy tự động kích hoạt restart policy.
- Dùng localhost để gọi database service khác trong Compose.
- Thiếu curl/wget/pg_isready trong image nhưng vẫn copy ví dụ probe không kiểm thử.
- Kết hợp systemd/supervisor ở host với restart policy mà không có ownership rõ ràng.
- Không có alert khi container restart loop hoặc health chuyển unhealthy.
Khi job hoặc service có lịch, hãy kiểm tra cả môi trường daemon, timezone và log như trong bài cron trên Linux. Với khái niệm container, image và volume, xem thêm Docker cho người mới trước khi viết probe production.
11. Checklist triển khai an toàn
- Xác định tín hiệu cần đo: liveness, readiness hay dependency readiness.
- Viết probe nhẹ, có timeout, exit code rõ và không có side effect.
- Đặt interval, start-period, timeout và retries dựa trên đo đạc staging.
- Kiểm tra tool probe có thật trong image và đúng port/network namespace.
- Dùng service_healthy khi Compose cần chờ dependency sẵn sàng.
- Chọn restart policy theo loại service; giới hạn on-failure cho worker lỗi lặp.
- Không nhầm health status với exit status và không giả định unhealthy tự restart.
- Theo dõi restart count, health event, log và tuổi của container.
- Test cold start, dependency down, reboot host, disk đầy và rollback image.
- Ghi runbook: ai xử lý, đọc log nào, rollback ra sao và khi nào tắt automation.
12. Kết luận
Docker healthcheck giúp biến khái niệm “service còn sống” thành một phép đo có thể kiểm tra; restart policy giúp container quay lại sau một số kiểu exit. Hệ thống ổn định cần cả probe đúng, policy phù hợp, log, monitoring và runbook. Không có cấu hình restart: always nào thay thế được việc hiểu vì sao service unhealthy.
Hãy bắt đầu bằng endpoint health tối giản, test trong staging và quan sát khi dependency bị dừng. Sau đó mới chọn policy, depends_on và cảnh báo. Cách tiếp cận này tránh hai cực đoan: container chết mà không ai biết, hoặc container restart liên tục nhưng nguyên nhân thật bị che khuất.