Chuyển đến nội dung chính
  1. Bài viết/

Dựng hệ thống giám sát mà lập trình viên thật sự dùng tới

· loading · loading ·
Nhân Tài Đức
Tác giả
Nhân Tài Đức
Người dẫn dắt đội ngũ và kỹ sư phần mềm, sống tại Seoul, Hàn Quốc

Giữa “chạy được trên máy tôi” và “chạy được trên production” luôn có một khoảng cách, và hệ thống giám sát lẽ ra phải lấp được khoảng cách đó. Nhưng phần lớn thời gian nó không làm được, vì người ta xây nó như một công cụ cho đội vận hành, chứ không phải thứ lập trình viên tìm tới khi có gì đó hỏng.

Tôi vừa chuyển sang làm freelance mảng DevOps và hạ tầng, và ở bất cứ nơi nào tôi đặt chân tới, hệ thống giám sát luôn là một trong những thứ đầu tiên tôi xem xét — nó cho bạn biết về một đội còn nhiều hơn cả tệp README. Kiểu thất bại gần như lúc nào cũng giống nhau: công cụ thì có đủ, nhưng chẳng khớp gì với cách lập trình viên thật sự làm việc. Có người mất ba tiếng cho một lỗi mà nếu xem log production thì năm phút là xong. Sự cố được phát hiện qua ticket hỗ trợ thay vì qua cảnh báo. Hoặc ngược lại: mỗi ngày có tới 200 cảnh báo, phần lớn là nhiễu, nên ai cũng phớt lờ tất cả, và nửa tiếng đầu của mỗi sự cố chỉ để tìm xem cái gì bị hỏng, thay vì bắt tay vào sửa.

Metric, log và trace — không liên kết với nhau thì cũng vô dụng
#

Metric cho bạn biết có gì đó không ổn (tỷ lệ lỗi tăng vọt, latency nhảy lên). Log cho bạn biết chuyện gì đã xảy ra (stack trace, payload của request, thông báo lỗi). Trace cho bạn biết lỗi nằm ở đâu (service nào, endpoint nào, câu query database nào). Mấy điều này chẳng có gì mới. Điều quan trọng là chúng có được liên kết với nhau hay không: khi một cảnh báo bắn ra, bạn phải click được từ metric sang đúng đoạn log liên quan, rồi từ đó sang trace. Nếu lập trình viên vẫn phải tự dò khớp timestamp giữa ba công cụ khác nhau, thì hệ thống đó đã thất bại, cho dù trên giấy tờ từng công cụ trông tốt đến đâu.

Dashboard cũng phải qua bài kiểm tra tương tự. CPU, memory và disk I/O quan trọng khi lên kế hoạch dung lượng, nhưng chẳng giúp ai gỡ lỗi một response 500. Những dashboard mà mọi người tự nguyện mở ra thường đặt business metric cạnh technical metric (số lượt đăng ký mỗi giờ bên cạnh tỷ lệ lỗi), đánh dấu các lần deploy gần đây lên dòng thời gian, liệt kê năm lỗi xuất hiện nhiều nhất trong giờ vừa qua kèm link tới log, và hiển thị latency theo percentile — p50, p95, p99 — thay vì giá trị trung bình. Một dashboard mà không ai tự mở ra nếu không bị nhắc thì chẳng đáng giữ lại.

Cảnh báo cũng vậy. Mỗi cảnh báo phải trả lời được hai câu hỏi: cái gì đang hỏng, và tôi nên bắt đầu tìm từ đâu?

Cảnh báo tệ: “High CPU on web-server-3”

Cảnh báo tốt: “Error rate > 5% on /api/payments since 14:32. Last deploy: 14:15 by @sarah. [View logs] [View trace]”

Có vài cách giúp cảnh báo nghiêng về loại thứ hai: cảnh báo khi số liệu lệch khỏi baseline thay vì dựa vào một ngưỡng đặt tùy tiện; định tuyến theo người phụ trách (lỗi thanh toán thì gửi tới đội payments, chứ không phải cả công ty); và gom các lỗi liên quan vào một tin nhắn Slack thay vì 50 tin. Vòng phản hồi cũng phải thật ngắn — từ lúc “có gì đó hỏng” đến lúc “lập trình viên biết chuyện” nên dưới một phút. Muốn vậy thì cần stream log theo thời gian thực thay vì gom theo lô năm phút một lần, có deploy marker trên mọi dashboard, và tracing không bị đứt khi đi qua ranh giới giữa các service.

Xây cho chính những người sẽ dùng
#

Sai lầm lớn nhất của các đội vận hành là xây hệ thống giám sát cho chính mình. Cách sửa cũng chẳng có gì cao siêu. Hãy ngồi cạnh một lập trình viên trong lúc họ gỡ lỗi và xem họ bị kẹt ở đâu. Hỏi xem giữa lúc có sự cố thì họ hay đặt ra những câu hỏi nào — “service nào?”, “vừa thay đổi cái gì?”, “request lúc đó trông ra sao?”. Tìm hiểu xem metric nào thật sự quan trọng với sản phẩm; hiếm khi là CPU, mà thường là những thứ như tỷ lệ hoàn tất checkout hay tỷ lệ tải lên thành công.

Sau đó, gỡ bỏ những chỗ gây vướng víu. Nếu gắn instrumentation cho một service mới mà mất cả ngày, sẽ chẳng ai làm. Hãy cung cấp thư viện tự động instrument cho các framework đang dùng (Django, Express, Spring), template dashboard và cảnh báo để copy-paste, và metric tự phục vụ để không ai phải tạo ticket. Và hãy quản lý toàn bộ hệ thống như mã nguồn: cấu hình nằm trong version control, các rule cảnh báo được kiểm thử để chắc chắn chúng bắn ra đúng lúc, và nếu sau này định đi multi-region thì tính tới từ sớm.

Đội nhỏ dùng AWS? Bắt đầu với CloudWatch
#

Nếu đội của bạn dưới 50 kỹ sư và đang dùng AWS, CloudWatch là điểm khởi đầu hợp lý, và tôi sẽ cố nhịn không mua thứ gì xịn hơn ngay từ ngày đầu. EC2, Lambda, RDS và ECS đều tự động đẩy dữ liệu về đó, nên bạn quan sát được hệ thống ngay mà không phải viết instrumentation. Với một đội nhỏ, chi phí rơi vào khoảng $10–50 mỗi tháng, không có phí nền tảng, và metric, log, trace (qua X-Ray) cùng alarm đều nằm chung một chỗ. Chỉ trong một buổi chiều là bạn đã dựng xong những cảnh báo và dashboard có ý nghĩa, chứ không phải mất vài tuần.

Phần ít được để ý nhưng rất đáng dùng là CloudWatch Logs Insights — bạn truy vấn được log mà không cần dựng Elasticsearch:

fields @timestamp, @message
| filter @message like /ERROR/
| stats count() by bin(5m)

Ngoài ra còn có:

  • Composite alarm giúp giảm nhiễu — chỉ cảnh báo khi tỷ lệ lỗi cao thời gian phản hồi cũng chậm đi, thay vì cảnh báo riêng cho từng điều kiện.
  • Custom metric mới là chỗ mang lại giá trị thật. Hãy đẩy business metric (lượt đăng ký, giao dịch, mức độ sử dụng tính năng) qua CloudWatch SDK, song song với các metric hạ tầng.
  • CloudWatch Synthetics chạy canary theo lịch, đi qua các luồng thao tác thật của người dùng, để bạn biết checkout bị hỏng trước khi người dùng phát hiện ra.
  • X-Ray cho bạn distributed tracing mà gần như không tốn công thiết lập — đủ tốt cho phần lớn kiến trúc microservice.

Bạn sẽ tự biết lúc nào mình đã vượt quá khả năng của nó: khi chuyển sang multi-cloud, khi cần phát hiện bất thường cho ra hồn, khi việc tùy biến dashboard bắt đầu thành cực hình, hoặc khi đội vượt quá 50 kỹ sư và cần các tính năng cộng tác thật sự.

Đội lớn hơn: DataDog
#

DataDog đắt — với một tổ chức lớn, $20–100K+ mỗi năm là chuyện bình thường — nhưng ở quy mô lớn thì nó đáng đồng tiền. Bạn có một góc nhìn chung bao quát cả AWS, Azure, GCP, on-prem, container, serverless, database và frontend. Watchdog tự đánh dấu các bất thường mà không cần tinh chỉnh ngưỡng bằng tay, và điều này rất quan trọng vì không ai có thể tự canh hàng nghìn service bằng mắt. Bạn cũng có được lớp cộng tác mà CloudWatch còn thiếu: dashboard theo đội, RBAC, notebook điều tra dùng chung, tích hợp PagerDuty/Opsgenie, cảnh báo nhiều điều kiện, dự báo ngưỡng, cửa sổ bảo trì, và một APM đi sâu tới tận profiling ở cấp mã nguồn, kèm service map tự động sinh ra.

Về cách triển khai, đại khái theo thứ tự tôi sẽ làm: instrument các service quan trọng trước, và gắn tag mọi thứ theo đội và môi trường ngay từ ngày đầu. Thống nhất sớm quy ước đặt tên, template dashboard, các mức độ nghiêm trọng và định nghĩa SLO — để về sau mới áp chuẩn ngược lại thì rất khổ. Nối DataDog với deploy marker từ CI/CD, hệ thống quản lý sự cố và Slack. Dành thời gian cho việc đào tạo, vì DataDog mạnh nhưng không dễ tự mày mò. Và để mắt tới hóa đơn: lọc bớt các metric nhiễu, lấy mẫu trace ở những service có lưu lượng lớn, và rà soát xem mình thật sự đang trả tiền cho những tính năng nào.

Trước khi chốt, cũng nên xem qua vài lựa chọn khác: New Relic (tương tự, đôi khi rẻ hơn nếu tracing với lưu lượng lớn), Dynatrace (mạnh về AIOps, được chuộng trong ngành tài chính), Splunk (phân tích log thuộc hàng tốt nhất, nhất là khi đội bảo mật đã dùng sẵn), và Grafana Cloud (lựa chọn tự nhiên nếu bạn đang dùng Prometheus/Loki).

Trên thực tế, nhiều đội chọn mô hình lai: CloudWatch cho các service gốc của AWS vì nó tự động và rẻ, DataDog cho tầng ứng dụng, rồi nạp metric của CloudWatch vào DataDog để có một góc nhìn thống nhất. Chẳng hào nhoáng gì, nhưng chạy tốt.

Bắt đầu từ con số không
#

Nếu là tôi, tôi sẽ làm theo thứ tự này. Trước tiên, nói chuyện với lập trình viên xem điều gì làm họ khổ nhất mỗi khi có sự cố. Định nghĩa SLO cho những luồng quan trọng — với login, checkout, search thì thế nào mới gọi là “đang chạy tốt”? Instrument những luồng quan trọng đó trước. Tiếp theo là thêm distributed tracing; trong kiến trúc microservice, đây là khoản đầu tư cho việc gỡ lỗi có ROI cao nhất. Viết runbook cho mỗi cảnh báo, để bạn của lúc 3 giờ sáng biết cần kiểm tra cái gì. Sau đó đặt lịch review mỗi quý để xóa các cảnh báo đã lỗi thời, chỉnh lại các ngưỡng đã bị lệch, và xác nhận dashboard vẫn khớp với kiến trúc hiện tại.

Còn đây là những cái bẫy mà tôi cứ phải kéo các đội ra mãi: dùng quá nhiều công cụ (ba công cụ phối hợp ăn ý vẫn hơn sáu công cụ rời rạc), biểu đồ không có baseline (500 rps là bình thường hay là tăng vọt gấp 10 lần?), giám sát những thứ người dùng không hề cảm nhận được (disk I/O trên một container stateless), và không có phương án dự phòng cho chính hệ thống giám sát — nếu hệ thống cảnh báo chết giữa lúc đang có sự cố, bạn sẽ mù đúng vào lúc không thể để mù.

Không cần tới công cụ xịn nhất cho bất cứ điều gì ở trên. Điều cần là lập trình viên nhận được đúng thông tin họ cần, ở đúng chỗ họ sẽ nhìn vào, và thật nhanh.