Tự dựng dashboard giám sát uptime cho 30 website khách hàng
Khách gọi báo web sập trước khi tôi biết — đó là lý do có cái dashboard này. Ghi chép về cách theo dõi uptime, tốc độ phản hồi và hạn SSL mà không tạo ra một núi cảnh báo rác.
Bàn giao website không phải là kết thúc. Server sập lúc hai giờ sáng, chứng chỉ SSL hết hạn vào đúng cuối tuần, trang chậm dần sau mỗi lần khách tự cài thêm plugin — và người biết cuối cùng thường là tôi, sau khi khách gọi điện.
Dashboard này ra đời để đảo ngược thứ tự đó. Nó không có gì mới về công nghệ; giá trị nằm ở vài quyết định nhỏ khiến nó thật sự dùng được hằng ngày thay vì bị tắt thông báo sau một tuần.
Ba thứ cần theo dõi, không hơn
Cám dỗ khi làm công cụ giám sát là đo mọi thứ đo được. Tôi giới hạn còn ba chỉ số, vì đó là ba thứ khiến khách hàng gọi điện:
Site còn sống không — kiểm tra HTTP status mỗi phút.
Trang có chậm đi không — đo TTFB, lưu chuỗi số liệu theo thời gian để nhìn ra xu hướng. Một site chậm dần đều trong ba tuần nguy hiểm hơn một site sập mười phút, vì không ai để ý.
SSL còn hạn bao lâu — cảnh báo trước 14 ngày. Đây là loại sự cố tự gây ra hoàn toàn phòng được, nhưng nếu không có nhắc thì năm nào cũng dính một lần.
Phần backend là một worker Node.js chạy cron, ghi vào SQLite. Quy mô vài chục site với một điểm dữ liệu mỗi phút thì SQLite thoải mái — không cần dựng Postgres cho việc này. Dashboard là Next.js, biểu đồ render phía client.
Cảnh báo sai còn tệ hơn không cảnh báo
Tuần đầu tiên, mạng phía máy giám sát chập chờn khoảng ba mươi giây. Hệ quả: mười mấy site "sập" cùng lúc, Telegram nổ liên tục, rồi tất cả tự "hồi phục". Không site nào có vấn đề thật.
Đó là cách nhanh nhất để một hệ thống cảnh báo trở nên vô dụng — vài lần như vậy là người ta tắt thông báo, và lần sập thật sẽ trôi qua trong im lặng.
Quy tắc sau khi sửa: chỉ báo động khi fail liên tiếp N lần, và luôn báo khi phục hồi.
// worker/check.js — chống cảnh báo giả do lỗi mạng thoáng qua
const FAIL_THRESHOLD = 3
async function checkSite(site) {
const started = Date.now()
try {
const res = await fetch(site.url, { signal: AbortSignal.timeout(10_000) })
await recordMetric(site.id, { status: res.status, ttfb: Date.now() - started })
// Chỉ báo phục hồi nếu trước đó đã thật sự báo động
if (site.failCount >= FAIL_THRESHOLD) notify(`✅ ${site.name} đã phục hồi`)
site.failCount = 0
} catch {
site.failCount += 1
// Dùng === thay vì >= : chỉ bắn đúng một lần khi vượt ngưỡng
if (site.failCount === FAIL_THRESHOLD) {
notify(`🔴 ${site.name} không phản hồi (${FAIL_THRESHOLD} lần liên tiếp)`)
}
}
}
Chi tiết dễ bỏ sót nằm ở dấu ===. Nếu viết >=, mỗi lần kiểm tra tiếp theo trong lúc site vẫn đang sập sẽ bắn thêm một tin nhắn — site sập nửa tiếng thành ba mươi thông báo. Với ===, một sự cố chỉ sinh đúng một cảnh báo lúc bắt đầu và một lúc kết thúc.
Ba lần liên tiếp với chu kỳ một phút nghĩa là phát hiện sự cố sau khoảng ba phút. Đổi lại là gần như không còn báo động giả — đánh đổi đáng giá với loại website này.
Nghìn điểm dữ liệu và một trình duyệt bị treo
Sau vài tuần chạy, mỗi site tích luỹ hàng chục nghìn điểm đo. Chọn khoảng "30 ngày" là trang đứng hình vài giây.
Tôi không đổi thư viện biểu đồ. Vấn đề không nằm ở đó — không màn hình nào hiển thị nổi 43.000 điểm trên một biểu đồ rộng 600px, và mắt người cũng không đọc được. Cách sửa là gộp dữ liệu trước khi vẽ, ngay từ phía API:
// Tối đa ~200 điểm cho mỗi biểu đồ, gộp trung bình theo bucket thời gian
const points = useMemo(
() => downsample(metrics, { maxPoints: 200, by: 'avg' }),
[metrics]
)
return <ResponseChart data={points} className="h-40 w-full" />
Biểu đồ nhìn y hệt, trang phản hồi tức thì. Một lưu ý khi gộp: dùng trung bình thì các đỉnh nhọn sẽ bị san phẳng — nếu bạn cần nhìn thấy spike, hãy giữ thêm giá trị max của mỗi bucket và vẽ chồng lên.
Bài học nằm ngoài code
Dashboard chỉ có giá trị nếu nó là tab đầu tiên tôi mở mỗi sáng. Điều đó buộc mọi quyết định thiết kế phải phục vụ đúng một câu hỏi: hôm nay có site nào cần đụng tay không?
Nên trang chủ không có biểu đồ đẹp. Nó là một danh sách xếp theo mức độ nghiêm trọng — site đang có vấn đề nằm trên cùng, site khoẻ mạnh thu lại thành một dòng. Nhìn năm giây là xong. Biểu đồ chi tiết nằm ở trang con, dành cho lúc thật sự cần điều tra.
Công cụ nội bộ hay chết vì được thiết kế như sản phẩm thương mại: nhiều màn hình, nhiều tuỳ chọn, nhiều thứ để cấu hình. Cái dùng được lâu thường là cái trả lời đúng một câu hỏi và trả lời thật nhanh.
Website của bạn đang vận hành mà chưa có ai theo dõi? Tôi có dịch vụ quản lý và giám sát website đi kèm sau bàn giao — hoặc liên hệ để trao đổi cụ thể.
Bài viết liên quan