Hướng dẫn Docker Autoheal: Tự động khởi động lại container khi bị Unhealthy

Khi container “sống dở chết dở” mà Docker không hề hay biết

Hẳn bạn từng gặp tình cảnh trớ trêu này: Khách hàng nhắn tin báo hệ thống sập. Bạn mở terminal, chạy docker ps và thấy container vẫn ghi Up 3 days.

Vỏ bọc bên ngoài vẫn chạy. Thế nhưng bên trong, app đã dính deadlock, rò rỉ RAM (leak memory) hoặc nghẽn kết nối database. Mọi request gửi tới đều timeout.

Tình huống này tương tự nhân viên thu ngân ngất xỉu ngay tại quầy. Nhìn từ xa, ghế vẫn có người ngồi. Khách đứng đợi dài nhưng chẳng ai tính tiền. Cơ chế mặc định của Docker chỉ nhìn vào tiến trình chính (PID 1). Chừng nào tiến trình này chưa thoát mã lỗi crash, Docker vẫn đinh ninh mọi thứ bình thường.

Trước đây khi mới dùng Docker Compose, mình từng mắc bẫy này. Mình thêm restart: always vào file cấu hình rồi yên tâm đi ngủ. Đúng 2 giờ sáng, Node.js app ngốn sạch 2GB RAM rồi treo cứng. Vì tiến trình không sập hẳn, Docker kiên quyết không khởi động lại. Sáng ra, điện thoại ngập tin nhắn réo từ ban giám đốc. Lúc đó, giải pháp chữa cháy chỉ là SSH vào gõ đúng một lệnh: docker restart.

Chúng ta cần cơ chế chủ động hơn. Hệ thống phải liên tục kiểm tra xem ứng dụng có thực sự trả lời request hay không. Đó là lúc cặp đôi Docker Healthcheck và Docker Autoheal giải quyết vấn đề.

Bộ đôi cứu tinh: Healthcheck và Docker Autoheal

Để container tự phục hồi, bạn cần hai thành phần: cơ chế thăm khám bệnh và người thực thi hành động.

1. Docker Healthcheck

Healthcheck là khối chỉ thị khai báo trong Dockerfile hoặc docker-compose.yml. Định kỳ theo chu kỳ bạn chọn (chẳng hạn 10 giây một lần), Docker chạy một lệnh kiểm tra bên trong container.

  • Healthy: Lệnh trả về exit code 0 (HTTP 200). App hoạt động tốt.
  • Unhealthy: Lệnh trả về lỗi (exit code 1) vượt số lần thử cho phép. Docker dán nhãn container là unhealthy.

Điểm yếu chí mạng: Docker nguyên bản chỉ dừng ở việc dán nhãn cảnh báo. Thấy container bị unhealthy, Docker Compose vẫn mặc kệ chứ không hề restart container giúp bạn.

2. Docker Autoheal

Docker Autoheal (tạo bởi Will Farrell) là container giám sát cực nhẹ, ngốn chưa tới 20MB RAM. Nó kết nối trực tiếp với Docker Socket trên host.

Autoheal theo dõi trạng thái các container theo chu kỳ. Vừa thấy container mang cờ unhealthy, Autoheal gửi ngay lệnh restart tới Docker daemon. Quá trình diễn ra tự động trong vài giây mà bạn không cần thức đêm gõ lệnh.

Cấu hình Docker Autoheal với Docker Compose

Dưới đây là kịch bản dựng web app chạy Nginx kèm Autoheal giám sát tự động.

Bước 1: Viết file docker-compose.yml

Tạo thư mục mới và mở file docker-compose.yml:

services:
  # 1. Ứng dụng web cần giám sát
  my-web-app:
    image: nginx:alpine
    container_name: my-web-app
    ports:
      - "8080:80"
    restart: always
    labels:
      # Báo cho Autoheal theo dõi container này
      - "autoheal=true"
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
      interval: 10s       # Kiểm tra mỗi 10 giây
      timeout: 3s         # Chờ phản hồi tối đa 3 giây
      retries: 3          # Lỗi liên tiếp 3 lần thì đánh dấu unhealthy
      start_period: 5s    # Cho app 5 giây khởi động trước khi check

  # 2. Container tự động restart khi có sự cố
  autoheal:
    image: willfarrell/autoheal:latest
    container_name: autoheal
    restart: always
    environment:
      - AUTOHEAL_INTERVAL=5        # Quét trạng thái mỗi 5 giây
      - AUTOHEAL_START_PERIOD=10    # Đợi 10 giây sau khi bật mới quét
      - AUTOHEAL_DEFAULT_STOP_TIMEOUT=10 # Thời gian chờ app tắt an toàn
    volumes:
      # Mount Docker socket để Autoheal có quyền restart container
      - /var/run/docker.sock:/var/run/docker.sock

Các thông số cần lưu ý

  • autoheal=true: Nhãn bắt buộc để Autoheal nhận diện mục tiêu. Cách này tránh việc Autoheal tự ý restart nhầm các worker xử lý batch job đang chạy dở.
  • /var/run/docker.sock: Socket giao tiếp trực tiếp với Docker daemon. Lưu ý bảo mật: container nào nắm socket này sẽ có quyền hạn tương đương user root trên máy chủ.
  • curl -f http://localhost/: Flag -f (fail silently) khiến curl trả về mã lỗi khi gặp HTTP 4xx hoặc 5xx, kích hoạt điều kiện exit 1.

Bước 2: Chạy hệ thống

Khởi động hai service bằng lệnh:

docker compose up -d

Đợi khoảng 15 giây, kiểm tra danh sách container:

docker ps

Quan sát cột STATUS của my-web-app, bạn sẽ thấy trạng thái Up ... (healthy). Endpoint kiểm tra đã hoạt động chính xác.

Bước 3: Thử nghiệm thực tế cơ chế tự phục hồi

Để mô phỏng sự cố web sập (trả về mã 404), ta tạm thời đổi tên file index.html bên trong container:

docker exec -it my-web-app mv /usr/share/nginx/html/index.html /usr/share/nginx/html/index.html.bak

Bật tab terminal khác và theo dõi log của Autoheal:

docker logs -f autoheal

Sau 3 lần curl thất bại (khoảng 30 giây), Docker dán nhãn unhealthy. Autoheal bắt được sự kiện và can thiệp ngay:

Container my-web-app (9a1f2b3c4d5e) found to be 'unhealthy'
Restarting container my-web-app (9a1f2b3c4d5e)...
Container my-web-app (9a1f2b3c4d5e) restarted

Lệnh restart được kích hoạt hoàn toàn tự động. Sau khi quan sát xong quá trình restart, bạn đổi tên file lại như cũ để app trở về trạng thái khỏe mạnh:

docker exec -it my-web-app mv /usr/share/nginx/html/index.html.bak /usr/share/nginx/html/index.html

Với các sự cố thực tế như nghẽn connection pool hoặc đầy heap memory, hành động restart này sẽ giải phóng tài nguyên và đưa ứng dụng trở lại vận hành ngay lập tức.

Tổng kết

Bạn không nhất thiết phải dựng cụm Kubernetes cồng kềnh chỉ để có tính năng tự phục hồi. Với các hệ thống vừa và nhỏ chạy trên VPS đơn lẻ, cặp bài trùng Docker Healthcheck và Docker Autoheal là phương án gọn nhẹ và hiệu quả rõ rệt.

Tuy vậy, Autoheal chỉ đóng vai trò băng gạc sơ cứu tạm thời. Nếu kiểm tra log thấy Autoheal phải restart container liên tục mỗi giờ, bạn cần debug lại mã nguồn để xử lý triệt để nguyên nhân gốc rễ.

Share: