0

🏗️🧠 Observability: hệ thống chậm nhưng lỗi nằm ở đâu? - System Design P15

Observability 101: Nhìn Thấu Hệ Thống Với Logs – Metrics – Tracing

1. Lời mở đầu: Cơn ác mộng của những Dashboard "màu xanh"

Đó là 2 giờ sáng ngày thứ Bảy. Điện thoại của bạn rung lên liên hồi vì tin nhắn cảnh báo từ hệ thống. Bạn bật máy tính, đăng nhập vào Dashboard chính: Tất cả các chỉ số CPU, RAM, Network đều hiển thị một màu xanh (Green) dịu mắt. Về mặt lý thuyết, hệ thống vẫn "sống khỏe". Nhưng trên kênh hỗ trợ khách hàng, người dùng đang than phiền dữ dội vì không thể thực hiện thanh toán, hoặc trang web cứ xoay vòng liên tục rồi báo lỗi timeout.

Bạn bắt đầu đăng nhập vào server, dùng tail -f để xem log. Một cơn lũ dữ liệu tràn qua màn hình với tốc độ chóng mặt. Hàng Gigabyte log trôi đi mỗi phút nhưng bạn không tìm thấy bất kỳ manh mối nào rõ ràng. Có hàng ngàn dòng lỗi 500 Internal Server Error, nhưng chúng đến từ đâu? Từ dịch vụ thanh toán, dịch vụ kho hàng, hay do một database lock bất chợt?

Là một kiến trúc sư hệ thống, tôi đã từng chứng kiến những đội ngũ kỹ sư giỏi nhất bị "quật ngã" bởi chính đống dữ liệu mà họ tạo ra. Sự bế tắc đó không đến từ việc thiếu công cụ, mà đến từ việc chúng ta biết hệ thống "đang hỏng" (Monitoring), nhưng lại hoàn toàn mù tịt về việc "tại sao nó hỏng" (Observability). Trong môi trường Production thực chiến, sự khác biệt giữa hai khái niệm này chính là khoảng cách giữa việc khôi phục hệ thống trong 5 phút và việc ngồi mò mẫm trong vô vọng suốt 5 tiếng đồng hồ.

2. Thay đổi tư duy: Từ Monitoring đến Observability

Trong giới kỹ thuật, chúng ta thường đánh đồng Monitoring và Observability, nhưng thực tế chúng đại diện cho hai triết lý vận hành hoàn toàn khác biệt.

Monitoring trả lời câu hỏi: "Hệ thống có đang hoạt động không?". Nó tập trung vào các triệu chứng (Symptoms) bên ngoài. Monitoring dựa trên các "Known-Unknowns" – những lỗi mà bạn đã biết trước là có thể xảy ra và đặt cảnh báo cho nó. Nếu CPU vượt 90%, báo động. Nếu tỉ lệ lỗi vượt 5%, báo động. Nhưng khi hệ thống gặp những "lỗi mềm" (soft failures) – chẳng hạn như một phần nhỏ request bị lỗi logic do điều kiện race condition hiếm gặp – Monitoring thường bị mù.

Observability trả lời câu hỏi: "Tại sao hệ thống lại hành xử như vậy?". Đây là khả năng hiểu được trạng thái nội tại của hệ thống chỉ bằng cách quan sát các dữ liệu đầu ra mà không cần phải can thiệp hay deploy thêm code để debug. Observability xử lý các "Unknown-Unknowns" – những vấn đề mà bạn thậm chí chưa bao giờ nghĩ là nó sẽ tồn tại cho đến khi nó thực sự xảy ra.

Đặc điểm Monitoring Observability
Mục tiêu chính Theo dõi trạng thái (Health check) Thấu hiểu hành vi (Behavioral Insight)
Trọng tâm Câu hỏi "Cái gì?" (Symptoms) Câu hỏi "Tại sao?" (Root cause)
Dựa trên Các ngưỡng (Thresholds) cố định Khám phá dữ liệu (Exploratory Data)
Đối tượng Đội Ops/SRE (Vận hành) Đội Dev/Architect (Giải quyết vấn đề)

Triết lý TechCraft: Observability không phải là một bộ công cụ (tooling) bạn mua về và cài đặt là xong. Nó là một năng lực thiết kế (architectural capability). Hệ thống phải được thiết kế để "có thể quan sát được". Một hệ thống câm lặng là một hệ thống không thể quản trị.

3. Trụ cột thứ nhất: Logging - Cuốn nhật ký sự kiện (The "What")

Logging là thứ gần gũi nhất với lập trình viên, nhưng cũng là thứ dễ bị lạm dụng nhất. Trong một hệ thống phân tán, Log không chỉ là dùng lệnh print hay console.log.

Định nghĩa lại về Log

Log là bản ghi về các sự kiện rời rạc (Discrete Events). Nó cho chúng ta biết "Cái gì" đã xảy ra tại một thời điểm nhất định. Tuy nhiên, Log có một nhược điểm chí mạng: Chi phí.

Sự đánh đổi: Cái giá của việc "biết quá nhiều"

Tôi đã từng thấy những hóa đơn lưu trữ Log (CloudWatch hay ELK) vượt xa cả chi phí vận hành cluster compute. Một sai lầm phổ biến của các Junior Developer là để mode DEBUG ở môi trường Production.

Dưới góc nhìn của một Senior Engineer, việc ghi log quá mức gây ra hai vấn đề nghiêm trọng:

  1. Lock Contention & Disk I/O Wait: Khi hệ thống chịu tải cao (High Throughput), việc hàng ngàn thread cùng tranh chấp ghi vào file log hoặc đẩy log qua network có thể tạo ra hiện tượng "backpressure". CPU thay vì xử lý logic nghiệp vụ thì lại phải đứng chờ (I/O Wait) để ghi log. Tôi đã từng xử lý một sự cố mà hệ thống chết đứng chỉ vì disk bị đầy do log flooding trong vòng chưa đầy 10 phút.
  2. Signal-to-Noise Ratio: Khi bạn log mọi thứ, bạn thực tế không thấy gì cả. Thông tin quan trọng bị chôn vùi trong đống "rác" dữ liệu.

Tiêu chuẩn: Structured Logging

Đừng bao giờ sử dụng Plain Text cho hệ thống lớn. Thay vì:[INFO] User 123 purchased item 456 at 10:00 AM

Chúng ta phải dùng Structured Logging (JSON):

{
  "timestamp": "2023-10-27T10:00:00Z",
  "level": "INFO",
  "event": "purchase_success",
  "user_id": 123,
  "item_id": 456,
  "service": "order-service",
  "trace_id": "a1b2c3d4e5"
}

Tại sao? Vì máy móc có thể parse và index JSON. Structured logs cho phép chúng ta thực hiện Automated AlertingAnomaly Detection. Bạn có thể đặt một query: "Báo động nếu user_id cụ thể có số lượng purchase_fail > 5 trong 1 phút". Điều này là bất khả thi với Plain Text trừ khi bạn muốn "đốt" CPU cho các biểu thức Regex đắt đỏ.

4. Trụ cột thứ hai: Metrics - Con số biết nói (The "How much")

Nếu Log là cuốn nhật ký chi tiết, thì Metrics là bảng thống kê tổng hợp (Aggregated Data). Metrics không quan tâm đến từng request; nó quan tâm đến xu hướng (trends).

4 Chỉ số vàng (Golden Signals)

Mọi Dashboard thực chiến phải xoay quanh 4 chỉ số này:

  1. Latency (Độ trễ): Thời gian xử lý request. Lưu ý: Phải đo riêng Latency của request thành công và thất bại. Một request lỗi trả về ngay lập tức (20ms) có thể làm đẹp chỉ số Latency trung bình một cách giả tạo.
  2. Traffic (Lưu lượng): Nhu cầu đặt lên hệ thống (ví dụ: HTTP requests per second).
  3. Errors (Lỗi): Tỉ lệ lỗi (Explicit như 5xx hoặc Implicit như dữ liệu sai nhưng trả về 200).
  4. Saturation (Độ bão hòa): Mức độ tài nguyên đã sử dụng (CPU, Memory, IOPS). Đây là chỉ số dự báo hệ thống sắp sập.

Phân tích sâu: Thảm họa "High Cardinality"

Đây là phần mà các Senior Engineer thường phải đau đầu nhất. Cardinality là số lượng các giá trị duy nhất của một chiều dữ liệu (label/tag).

  • Tag status_code có cardinality thấp (200, 4xx, 5xx).
  • Tag user_id có cardinality cực cao (hàng triệu user).

Cảnh báo: Nếu bạn đưa user_id hoặc order_id vào làm label cho Metrics trong các hệ thống như Prometheus (sử dụng TSDB - Time Series Database), bạn đang tạo ra một "Cardinality Explosion".

Mỗi tổ hợp tag tạo ra một "Time Series" riêng biệt trong bộ nhớ. Nếu bạn có 1 triệu user, Prometheus sẽ phải quản lý 1 triệu chuỗi dữ liệu trong RAM. Tôi đã từng chứng kiến một hệ thống sập toàn diện vì một engineer thêm email_customer vào metrics để "cho dễ theo dõi". Kết quả: TSDB nổ tung vì cạn kiệt RAM chỉ sau vài giờ. Quy tắc vàng: Metrics dùng để quan sát số đông, đừng dùng nó để theo dõi cá thể.

5. Trụ cột thứ ba: Distributed Tracing - Sợi chỉ đỏ (The "Where")

Trong kiến trúc Microservices, một request từ người dùng có thể đi qua một "mê cung": API Gateway -> Auth Service -> Order Service -> Payment Service -> Database. Khi một request bị chậm, "ai" là người có lỗi?

Tracing giải quyết câu hỏi: "Request này đã đi qua những đâu và mất bao lâu ở mỗi chặng?".

Cơ chế hoạt động

  • Trace ID: Một định danh duy nhất cho toàn bộ hành trình.
  • Span: Một đơn vị công việc (ví dụ: một câu query SQL, một cuộc gọi gRPC).
  • Context Propagation: Đây là kỹ thuật then chốt. Trace ID phải được truyền qua các dịch vụ thông qua HTTP Headers (ví dụ: X-Trace-Id hoặc chuẩn W3C Trace Context). Nếu một dịch vụ trong chuỗi không truyền Trace ID này đi tiếp, sợi chỉ sẽ bị đứt và bạn mất dấu request.

Góc nhìn Senior: Chiến lược Sampling (Lấy mẫu)

Việc trace 100% request trong hệ thống tải cao là một sự tự sát về mặt hiệu năng (Performance Overhead). Chúng ta phải dùng Sampling.

  1. Head-based Sampling: Quyết định trace hay không ngay từ khi request bắt đầu vào Gateway. Đơn giản, tiết kiệm nhưng có thể bỏ lỡ những request lỗi hiếm gặp.
  2. Tail-based Sampling: Đây là kỹ thuật cao cấp hơn. Hệ thống sẽ giữ lại toàn bộ dữ liệu của mọi request trong bộ nhớ đệm, sau khi request kết thúc, nếu thấy nó bị lỗi hoặc chậm (Latency > 2s), hệ thống mới quyết định lưu Trace đó lại. Cách này tốn kém hơn nhưng đảm bảo bạn luôn bắt được "biến số" quan trọng.

6. Sự gắn kết (Correlation): Sức mạnh thực sự

Nhiều team có cả Logs, Metrics, Tracing nhưng vẫn mất cả ngày để debug. Tại sao? Vì dữ liệu của họ bị Siloed (rời rạc). Sức mạnh thực sự nằm ở sự kết nối (Correlation) giữa chúng thông qua Trace ID.

Hãy nhìn vào quy trình debug lý tưởng của một chuyên gia:

  1. Nhận Alert (Metrics): Dashboard báo đỏ: "Tỉ lệ lỗi 5xx của dịch vụ Checkout tăng đột biến". (Biết có chuyện gì xảy ra).
  2. Kiểm tra Trace (Tracing): Bạn click vào biểu đồ, hệ thống liệt kê các Trace bị lỗi. Bạn chọn một Trace ID và thấy một hình ảnh trực quan: Request đi qua Checkout ổn, nhưng bị "treo" ở bước gọi sang dịch vụ Ngân hàng đối tác suốt 10 giây trước khi timeout. (Biết vấn đề nằm ở đâu).
  3. Lọc Log (Logs): Bạn lấy chính Trace ID đó và search trong hệ thống Log tập trung. Bạn thấy dòng log chi tiết của dịch vụ Payment: BankAPIConnectionException: TLS Handshake failed. (Biết chính xác tại sao).

Không có Correlation, bạn chỉ là một người thợ mò mẫm trong bóng tối với ba chiếc đèn pin rời rạc. Có Correlation, bạn có một hệ thống radar toàn cảnh.

7. Trade-offs & Production Reality: Không có bữa trưa miễn phí

Thiết kế hệ thống là nghệ thuật của sự đánh đổi. Để có Observability tốt, bạn phải trả giá:

  • Cost vs. Visibility: Dữ liệu quan sát thường lớn gấp nhiều lần dữ liệu nghiệp vụ. Bạn cần chính sách Retention (lưu trữ bao lâu) cực kỳ khắt khe. Log lỗi có thể giữ 30 ngày, nhưng log INFO có thể chỉ nên giữ 3 ngày hoặc bỏ qua hoàn toàn.
  • Agent vs. SDK: Sử dụng Agent (sidecar) giúp thu thập dữ liệu mà không làm phiền code của Dev, nhưng SDK (nhúng trực tiếp) lại cho phép lấy được các thông tin sâu về business logic.
  • Operational Overhead: Ai sẽ vận hành cụm Elasticsearch hay Jaeger khổng lồ đó? Đôi khi việc duy trì hệ thống quan sát còn khó hơn cả duy trì ứng dụng chính.

Những bài học xương máu:

  • Log Flooding: Đừng bao giờ log nội dung của các file upload hoặc các chuỗi Base64 lớn. Nó sẽ giết chết I/O hệ thống nhanh hơn bất kỳ cuộc tấn công DDoS nào.
  • Alert Fatigue (Sự mệt mỏi vì cảnh báo): Nếu Dashboard của bạn có 100 cái biểu đồ, bạn sẽ không nhìn cái nào cả. Hãy tập trung vào những chỉ số thực sự ảnh hưởng đến khách hàng (User-facing metrics).

8. Lời kết: Observability là một hành trình tiến hóa

Trong mô hình Evolution Thinking (Tư duy tiến hóa) của TechCraft, khả năng quan sát không xuất hiện đầy đủ ngay từ ngày đầu:

  • Stage 1: Bạn chỉ cần Log đơn giản để sửa lỗi logic.
  • Stage 2: Khi có traffic, bạn cần Metrics để biết khi nào cần scale server.
  • Stage 3: Khi chuyển sang Microservices hoặc Distributed Systems, Tracing trở thành điều kiện sống còn.

Đừng cố gắng setup mọi công cụ đắt tiền cùng một lúc. Hãy bắt đầu bằng việc thay đổi tư duy: Làm sao để mỗi dòng code mình viết ra đều có khả năng "tự khai báo" tình trạng của nó. Hãy bắt đầu với Structured Logging, thêm Trace ID vào mọi request, và đừng bao giờ để user_id vào Metrics label.


🚀 Tiếp tục hành trình cùng TechCraft

Bài viết này là một phần trong hành trình khám phá Backend Engineering, System Design và Production Systems tại TechCraft.

Nếu bạn muốn học theo một lộ trình rõ ràng hơn, TechCraft đang xây dựng Dev Insider như nơi tập trung các series chuyên sâu hơn về:

  • Backend Internals
  • Database Internals
  • Transaction & Consistency
  • Distributed Systems
  • Production System Design
  • AI-Proof Engineer

🚀 Dev Insider
https://www.patreon.com/techcraft_official/posts/vi-sao-dev-ra-161163881?collection=2220113

📘 Facebook
https://www.facebook.com/techcraft.official

🎥 YouTube
https://www.youtube.com/@techcraft.official

🎵 TikTok
https://www.tiktok.com/@techcraft.official

Hiểu hệ thống. Không chỉ framework.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí