Khi hàng nghìn Task ‘biến mất’ không dấu vết
Thứ Hai đầu tuần, bạn nhận hàng chục cuộc gọi từ khách hàng phàn nàn vì không nhận được email xác nhận đơn hàng. Kiểm tra hệ thống, mọi thứ vẫn báo “Success”. Tuy nhiên, thực tế là có 5.000 email vẫn đang nằm chờ đâu đó trong hàng đợi mà không hề được xử lý.
Bạn bơi trong hàng GB log file nhưng không tìm thấy lỗi rõ ràng. Celery worker vẫn sống, nhưng tại sao task không chạy? Queue đang kẹt ở đâu? Có task nào bị loop liên tục gây nghẽn cổ chai không? Lúc này bạn mới thấy: Chạy được Celery mới chỉ là 50% chặng đường, 50% còn lại là phải nhìn thấy nó đang làm gì.
Tại sao Celery thường là một “hộp đen”?
Các tác vụ nền (background tasks) hoạt động tách biệt hoàn toàn với luồng Request-Response thông thường. Khi một request HTTP lỗi, bạn nhận ngay mã 500. Nhưng khi Celery Task chết, nó thường im lặng biến mất hoặc nằm chờ vô tận trong Broker (Redis/RabbitMQ) mà không hề báo động.
Ba nguyên nhân phổ biến nhất khiến hệ thống “đứng hình”:
- Nghẽn Broker: Task đổ vào quá nhanh (ví dụ 1000 task/giây) nhưng Worker chỉ xử lý được 100 task/giây.
- Worker bị Zombie: Tiến trình vẫn tồn tại nhưng không còn khả năng nhận việc mới do lỗi memory leak.
- Task nặng chiếm dụng tài nguyên: Một vài tác vụ xử lý ảnh nặng 20MB “nuốt” sạch CPU, khiến các task gửi email nhẹ nhàng bị đẩy xuống cuối hàng đợi.
Đừng chỉ giám sát bằng cách ‘soi log’
Nhiều anh em vẫn duy trì thói quen debug thủ công, nhưng cách này rất khó scale:
- Dùng
tail -f: Cách này chỉ hiệu quả khi dev cục bộ. Với hệ thống 20 workers chạy trên Docker, việc soi log từng container là cực hình. - Celery Inspect: Lệnh
celery -A proj inspect activecho con số tức thời nhưng thiếu cái nhìn tổng thể về lịch sử và xu hướng. - Flower đơn thuần: Công cụ này có UI rất tốt. Tuy nhiên, nếu Flower restart, toàn bộ dữ liệu lịch sử sẽ bốc hơi. Nó cũng thiếu khả năng bắn cảnh báo (alerting) chủ động.
Combo chuẩn: Flower + Prometheus + Grafana
Sau nhiều lần thức đêm xử lý sự cố, mình rút ra bộ công cụ tối ưu: Flower để quản lý trực quan, Prometheus để lưu trữ metrics theo thời gian và Grafana để vẽ dashboard. Setup này giúp mình phát hiện ngay lập tức khi một task bị retry quá 10 lần hoặc khi RAM của worker vượt ngưỡng 80%.
Bước 1: Kích hoạt Prometheus Metrics trên Flower
Flower đời mới đã tích hợp sẵn endpoint cho Prometheus. Bạn chỉ cần cài đặt phiên bản mới nhất:
pip install flower
Thay vì chạy lệnh thông thường, hãy thêm flag --prometheus_enable để Flower xuất dữ liệu định dạng Prometheus:
celery -A your_project flower --address=0.0.0.0 --port=5555 --prometheus_enable
Hãy kiểm tra tại http://localhost:5555/metrics. Nếu thấy các dòng như celery_tasks_total, bạn đã đi đúng hướng.
Bước 2: Cấu hình Prometheus thu thập dữ liệu
Chúng ta cần cấu hình để Prometheus chủ động lấy dữ liệu từ Flower sau mỗi 15 giây. Mở file prometheus.yml và thêm đoạn sau:
scrape_configs:
- job_name: 'celery-monitor'
static_configs:
- targets: ['192.168.1.10:5555'] # IP server chạy Flower
scrape_interval: 15s
Bước 3: Trực quan hóa với Grafana
Dữ liệu thô trong Prometheus rất khó đọc. Hãy dùng Grafana để tạo biểu đồ. 3 chỉ số “vàng” bạn cần quan tâm:
- celery_tasks_total (status=”failure”): Nếu biểu đồ này dốc đứng, hệ thống đang gặp lỗi nghiêm trọng.
- celery_queues_length: Con số này phải xấp xỉ bằng 0. Nếu nó tăng dần theo thời gian, bạn cần bổ sung thêm Worker ngay lập tức.
- celery_workers_online: Đảm bảo số lượng worker luôn đúng với kỳ vọng (ví dụ: luôn có 4 worker hoạt động).
Mẹo nhỏ: Bạn có thể dùng Dashboard ID 14195 trên Grafana Labs để import nhanh giao diện chuẩn cho Celery.
Kinh nghiệm ‘xương máu’ khi vận hành thực tế
Khi triển khai cho hệ thống xử lý hơn 1 triệu task mỗi ngày, mình rút ra 3 lưu ý quan trọng:
1. Giới hạn bộ nhớ cho Flower: Flower lưu lịch sử task trong RAM. Nếu không giới hạn, nó sẽ gây crash server. Hãy dùng flag --max_tasks=10000 để giữ cho Flower luôn nhẹ nhàng.
2. Bảo mật là ưu tiên số 1: Đừng bao giờ để lộ dashboard Flower ra ngoài internet mà không có mật khẩu. Sử dụng Basic Auth để chặn những vị khách không mời:
celery -A proj flower --basic_auth=admin:mypassword123
3. Cảnh báo chủ động: Đừng đợi đến lúc nhìn dashboard. Hãy setup Alertmanager để gửi tin nhắn Telegram ngay khi celery_queues_length > 500 trong vòng 5 phút. Điều này giúp bạn xử lý nghẽn cổ chai trước khi khách hàng kịp nhận ra.
Kết luận
Giám sát không đơn thuần là cài đặt công cụ, mà là hiểu rõ “sức khỏe” của luồng dữ liệu. Với Flower và Prometheus, bạn sẽ không còn phải lo lắng về việc task bị mất tích. Chỉ mất khoảng 30 phút setup nhưng nó sẽ tiết kiệm cho bạn hàng giờ debug và bảo vệ uy tín của ứng dụng trên production.

