Cài đặt hệ thống giám sát MongoDB và truy vết query chậm

Cài đặt hệ thống giám sát MongoDB và truy vết query chậm

1. Bối cảnh

Dũng Lê đây, khoảng ba tuần trước, một buổi sáng đẹp trời – thực ra không đẹp lắm , team dev báo mình server dev bị đuối cái gì cũng chậm, mình điều tra thì thấy CPU ngốn 100%, load tăng đột biến. Check htop thì thấy container MongoDB chiếm gần như toàn bộ tài nguyên. Vì server dev nên nhiều app hit vào con DB này, và cho dù biết được app nào đang hit nặng nhất cũng chẳng biết query nào làm full CPU?

2. Vấn đề và câu hỏi đặt ra

Chúng ta cần trả lời:

  • Query nào đang chiếm tài nguyên?
  • Thời điểm nó xảy ra?
  • Tác động kéo dài bao lâu?

Giải pháp lý tưởng là một hệ thống observability tập trung – nơi mọi query đều được trace lại, truy xuất được lịch sử, lọc theo latency hoặc mức độ ảnh hưởng. Đó là lúc PMM xuất hiện.

3. Giới thiệu PMM

PMM – Percona Monitoring and Management là một hệ thống monitoring open-source dành cho database, kết hợp giữa Prometheus + Grafana + exporters chuyên dụng. PMM hỗ trợ MongoDB, MySQL, PostgreSQL, v.v…

Thành phần chính:

  • pmm-server: trung tâm điều phối và visualization trên dashboard.
  • pmm-client: chạy gần database, thu thập metrics và gửi về pmm-server.

Nghĩa là thằng client(agent) đi thu thập data, sau đó quăng về thẳng server, thằng server nó sẽ có nhiệm vụ biểu diễn ra

4. Cài đặt hệ thống

Trong bài viết này thì mình dùng docker để các bạn dễ setup và tiếp cận, còn thực tế mình setup bằng tay rất tốn công và gặp nhiều lỗi vặt.

Hệ thống sẽ bao gồm 3 service chính chạy bằng Docker Compose:

  • MongoDB: database cần giám sát. Chúng ta bật chế độ profiler để theo dõi các truy vấn chậm(ở bước test mình sẽ hướng dẫn).
  • PMM Server: trung tâm thu thập và hiển thị dữ liệu giám sát, gồm Prometheus + Grafana + exporters.
  • PMM Client: agent cài đặt kề bên MongoDB, có nhiệm vụ collect metrics và gửi về PMM Server.

Các thành phần được kết nối với nhau qua Docker network mặc định. PMM Client sẽ chỉ khởi động sau khi MongoDBPMM Server đã sẵn sàng, nhờ vào depends_on + healthcheck của Docker cung cấp. Trên thực tế khi chúng ta dựng các hệ thống phụ thuộc vào nhau cũng rất cần đến các hệ thống healthcheck liên tục

Giải thích healthcheck: là cách Docker Compose kiểm tra xem service đã “ready” chưa, ví dụ:

  • PMM Server thì phải trả về thông tin version từ endpoint /v1/version
  • MongoDB thì phải phản hồi được lệnh db.adminCommand('ping')
  • Xa hơn thì ví dụ chúng ta có 1 app NodeJs, 1 con redis thì chúng ta cần đảm bảo redis ping pong oce mới start con NodeJs
version: '3.8'

services:
  pmm-server:
    image: percona/pmm-server:3
    container_name: pmm-server
    ports:
      - "8080:8080"  # Giao diện web
      - "8443:8443"  # Endpoint cho client agent kết nối TLS nghĩa là https ấy, thằng này cũng làm UI cho web được luôn
    healthcheck:
      test: ["CMD-SHELL", "curl -ksu admin:admin https://localhost:8443/v1/version | grep version"] # nghĩa là không quan tâm container chạy cái gì, chỉ quan tâm api cuối cùng trả về oce thì là service đã chạy ổn
      interval: 30s
      timeout: 10s
      retries: 5

  mongodb:
    image: mongo:8
    container_name: mongodb
    ports:
      - "27017:27017"t
    healthcheck: # health check cho mongodb, như bên trên mình nói
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5

  pmm-client:
    image: percona/pmm-client:3
    container_name: pmm-client
    restart: unless-stopped
    privileged: true  # cần cho một số exporter truy cập resource hệ thống
    depends_on: # phần này có nghĩa là thằng pmm-server và mongodb phải passed hết health check mới bắt đầu chạy con pmm-client lên 
      pmm-server:
        condition: service_healthy
      mongodb:
        condition: service_healthy
    environment:
      PMM_AGENT_SERVER_ADDRESS: pmm-server:8443
      PMM_AGENT_SERVER_USERNAME: admin
      PMM_AGENT_SERVER_PASSWORD: admin
      PMM_AGENT_SERVER_INSECURE_TLS: "1"  # cái này có nghĩa là mình pass https cert
      PMM_AGENT_SETUP: "1"
      PMM_AGENT_SETUP_FORCE: "1"
      PMM_AGENT_CONFIG_FILE: /config/pmm-agent.yaml # file này là chỗ chứa nội dung file config của pmm-agent, khi service start nó sẽ tự ghi vào file này
      PMM_AGENT_PRERUN_SCRIPT: >
        pmm-admin status --wait=10s && # check trạng thái agent đã chạy chưa
        pmm-admin add mongodb --host=mongodb --port=27017 --query-source=profiler # thêm service mongodb vào để check bắt sự kiện
    volumes:
      - ./config:/config

Cấu trúc thư mục của chúng ta sẽ thế này:

  • File pmm-agent.yaml bạn cứ để rỗng, sau khi start docker compose bạn check lại sẽ thấy content

5. Cơ chế hoạt động

  • pmm-client dùng pmm-admin để register MongoDB instance với chế độ query-source=profiler.
  • MongoDB sẽ log các truy vấn chậm dựa trên slowms, và exporter sẽ scrape rồi đẩy về server.
  • Trên pmm-server, bạn sẽ thấy các panel như Query Analytics (QAN), CPU, Memory, v.v…

6. Kiểm chứng hoạt động

Truy cập PMM:

  • Mở trình duyệt: http://localhost:8080
  • Tài khoản mặc định: admin / admin
  • Pmm sẽ yêu cầu đổi mật khẩu, mình đổi thành admin/admin luôn, nếu các bạn đổi khác đi thì ở docker compose các bạn cũng phải đổi theo nhé

Chuẩn bị dữ liệu test

Vì mình có sẵn Navicat nên mình sẽ dùng nó để tạo dữ liệu và query vào mongo, các bạn có thể dùng bất kỳ trình quản lý NoSql nào để test, hoặc đơn giản là mongosh(mình chưa test với mongosh)

Mọi người nên chạy từng câu lệnh dưới đây để hiểu rõ nhé, mình cũng note rất kỹ từng dòng

use test // Sử dụng data lên là test, nếu chưa có nó sẽ tự tạo
db.test.insertOne({ name: "mrd", from: "VietNamLinuxFamily" }) // thêm 1 record vào collection test
db.setProfilingLevel(1, { slowms: 400 })  // BẮT BUỘC, khi có lệnh này mongodb sẽ bắt những query chậm hơn 400ms = 0.4s, nghĩa là câu query nào chậm hơn 0.4 giây thì sẽ bị bắt lại
db.getProfilingStatus() // Câu này dùng để check xem trạng thái của profilling nó đang như nào: trả về { was: 1, slowms: 400, sampleRate: 1, ok: 1 } là oce

// lặp 100 lần câu query sleep 1 giây, mục đích để mongo profiler bắt được câu query chậm
for (let i = 1; i < 100; i++) {
  db.test.aggregate([
    {
      $addFields: {
        sleep: {
          $function: {
            body: function() { sleep(1000); return true },
            args: [],
            lang: "js"
          }
        }
      }
    }
  ])
}
// câu này dùng để check xem những query nào đã bị đánh dấu là chậm
db.system.profile.find().sort({ ts: -1 })

Kiểm tra MongoDB đã được add chưa:

  • Vào mục DashboardMongoDB, nếu thấy “Instances: 1” như hình bên dưới là OK.

Quan sát dữ liệu

  • Vào Query Analytics (QAN), ở mục filter bên trái các bạn sẽ thấy databse “test” mà chúng ta đã tạo, ở danh sách câu query bên phải các bạn sẽ thấy câu query mà chúng ta đã cố tình cho chạy chậm, ở cột Query Time chúng ta rê chuột vào thời gian mà query đã chạy sẽ hiện popup chi tiết, PMM sẽ không tách riêng các câu query ra mà gom nhóm các query giống y đúc nhau lại để phần tích: Tổng thời gian cho tất cả query giống nhau, mỗi câu trung bình bao nhiêu lâu, min và max thời gian của câu query đó
  • Ngoài ra chúng ta có rất nhiều trường để filter, hoặc tìm kiếm câu query chúng ta nghi vấn

Khi các bạn click chuột vào câu query nào đó, chúng ta sẽ có thông tin chi tiết hơn như hình bên dưới: Mục Details sẽ có metricsmetada, nói lên hiệu năng của câu query đó, mục Examples sẽ toàn bộ câu query đó, mục Explain cho chúng ta biết được câu query đó đang được thực thi với chiến lược như thế nào, chúng ta có thể nâng cấp để đạt hiệu năng tốt hơn dựa vào mục này

Thông tin về hiệu năng của instace đang chạy mongo

Ngoài việc truy suất câu query ra PMM còn giúp chúng ta biết được các thông tin khác về instance đang chạy mongodb như hình bên dưới(các bạn nhấn vào OverviewSummary ở side bar bên trái cấp dưới MongoDB):

Mục MongoDB > Overview

Đây là nơi hiển thị tổng quan các chỉ số quan trọng:

  • Services: Số lượng MongoDB service đang được giám sát. Nếu bạn chạy nhiều container/replica set, con số này sẽ tăng.
  • Min MongoDB Uptime: Thời gian instance MongoDB uptime thấp nhất (giúp phát hiện instance bị restart).
  • Total Used Resident Memory: Lượng RAM thực tế đang được MongoDB sử dụng (không tính swap).
  • Total Used Virtual Memory: Tổng memory ảo mà MongoDB sử dụng (bao gồm mmap).
  • Total Used Mapped: Nếu MongoDB dùng mmapv1 engine, chỗ này thể hiện lượng dữ liệu mapped. Với WiredTiger sẽ bằng 0.
  • Total Current QPS (Queries Per Second): Tốc độ truy vấn hiện tại trên MongoDB.

Phía dưới là các panel chi tiết:

  • Top Connections: Số lượng kết nối client hiện tại.
  • Top Opened Cursors: Cho biết số lượng cursor đang mở, có thể là do query lớn hoặc giữ kết nối lâu.
  • Min/Max QPS: Thống kê mức thấp/cao của QPS trong thời gian quan sát.
  • Max Latency: Độ trễ cao nhất được ghi nhận.

Mục MongoDB > Summary

Tổng hợp trạng thái của từng node MongoDB:

  • Node: Tên hoặc ID của node đang được theo dõi (click vào để xem chi tiết).
  • MongoDB Uptime: Thời gian hoạt động của node.
  • QPS: Tốc độ truy vấn thực tế của node này.
  • Latency: Độ trễ trung bình của query trên node.
  • ReplSet All States: Trạng thái các replica set (trong ảnh là UNKNOWN vì đang chạy MongoDB single node).
  • Current State: Trạng thái hiện tại của node (PRIMARY, SECONDARY, UNKNOWN, v.v.).

⬇️ Phía dưới:

  • Command Operations: Biểu đồ hiển thị tần suất các thao tác như find, insert, delete, update, v.v.
    • Ví dụ: ttl_delete, repl_, insert, query, update,…

7. Xong xuôi

Triển khai PMM giúp bạn có được góc nhìn vào MongoDB Server nó đang thế nào một cách rõ ràng và trực quan:

  • Theo dõi và phân tích các query chậm ngay thời điểm xảy ra
  • Biết được query nào đang ngốn tài nguyên
  • Đo được impact tới hệ thống production

Nếu bạn đang buồn vì chuyện tình cảm, hãy nhớ: ít nhất MongoDB còn cho bạn biết vì sao nó chậm, còn bạn thì đang không biết phải làm gì trong mối quan hệ của chính mình.

Leave a Reply

Your email address will not be published. Required fields are marked *