Hướng dẫn giám sát PgBouncer Connection Pooler với Prometheus và Grafana

Monitoring tutorial - IT technology blog
Monitoring tutorial - IT technology blog

Bối cảnh: Vì sao PgBouncer cũng cần được giám sát?

Đợt flash sale năm ngoái, cụm PostgreSQL của bên mình suýt sập dù CPU máy chủ chỉ mới chạm 35%. Nguyên nhân đến từ một điểm nghẽn mà không ai ngờ tới: PgBouncer.

Mỗi connection tới PostgreSQL là một process backend riêng trên Linux, ngốn từ 5 đến 10 MB RAM. Khi microservices scale lên 300 pod, lượng connection dễ dàng chạm trần max_connections (thường đặt 200–300). Hậu quả là database quá tải bộ nhớ và crash. PgBouncer giải quyết triệt để vấn đề này. Nhờ cơ chế Transaction Pooling, hàng nghìn client có thể dùng chung một pool backend chỉ gồm 40–50 kết nối thực tế.

Thế nhưng, chiếc phao cứu sinh này vẫn có thể chìm. Khi backend database bị chậm, pool connection cạn rất nhanh. Các query gửi tới sau đó buộc phải xếp hàng chờ slot trống. Hàng đợi này phình to chỉ trong vài chục giây là API Gateway dính 504 Gateway Timeout ngay. Database không chết, nhưng ứng dụng thì tê liệt hoàn toàn.

Để phát hiện sự cố trước khi người dùng kịp báo lỗi, ba chỉ số bắt buộc phải theo dõi liên tục gồm:

  • Client Waiting Connections: Số lượng query đang kẹt ở hàng đợi của pooler.
  • Query Max Wait Time: Thời gian query nằm chờ lâu nhất trước khi được backend tiếp nhận.
  • Pool Utilization: Tỷ lệ connection backend đang bận so với giới hạn cấu hình.

Cài đặt pgbouncer_exporter

PgBouncer quản lý dữ liệu vận hành thông qua database ảo có tên pgbouncer. Bạn có thể tra cứu nhanh các thông số này bằng lệnh SHOW POOLS; và SHOW STATS;. Để Prometheus thu thập được dữ liệu, chúng ta dùng binary Go pgbouncer_exporter từ Prometheus Community.

Đầu tiên, tải bản binary phù hợp về server chạy PgBouncer:

# Tải binary pgbouncer_exporter
wget https://github.com/prometheus-community/pgbouncer_exporter/releases/download/v0.8.0/pgbouncer_exporter-0.8.0.linux-amd64.tar.gz
tar -xvf pgbouncer_exporter-0.8.0.linux-amd64.tar.gz
sudo mv pgbouncer_exporter-0.8.0.linux-amd64/pgbouncer_exporter /usr/local/bin/

# Tạo user hệ thống chạy service
sudo useradd --no-create-home --shell /bin/false pgbouncer_exporter

Kế tiếp, tạo file service systemd để quản lý tiến trình chạy nền:

sudo tee /etc/systemd/system/pgbouncer_exporter.service << 'EOF'
[Unit]
Description=PgBouncer Prometheus Exporter
After=network.target pgbouncer.service

[Service]
Type=simple
User=pgbouncer_exporter
EnvironmentFile=/etc/default/pgbouncer_exporter
ExecStart=/usr/local/bin/pgbouncer_exporter $PGBOUNCER_EXPORTER_OPTS
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

Cấu hình kết nối và Prometheus Scrape

PgBouncer cần cấp quyền đọc cho exporter thông qua console ảo. Mở file cấu hình /etc/pgbouncer/pgbouncer.ini:

[pgbouncer]
logfile = /var/log/postgresql/pgbouncer.log
pidfile = /var/run/postgresql/pgbouncer.pid
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt

# Khai báo user admin/stats
admin_users = postgres
stats_users = pgbouncer_exporter

Khai báo mật khẩu cho tài khoản pgbouncer_exporter trong file /etc/pgbouncer/userlist.txt:

"pgbouncer_exporter" "ExporterSecurePassword123"

Nạp lại cấu hình PgBouncer mà không làm ngắt kết nối hiện có:

sudo systemctl reload pgbouncer

Cung cấp chuỗi kết nối cho exporter qua file môi trường /etc/default/pgbouncer_exporter:

sudo tee /etc/default/pgbouncer_exporter << 'EOF'
PGBOUNCER_EXPORTER_OPTS="--pgBouncer.connectionString=postgres://pgbouncer_exporter:[email protected]:6432/pgbouncer?sslmode=disable --web.listen-address=:9127"
EOF

Khởi động exporter và kiểm tra output trực tiếp từ cổng 9127:

sudo systemctl daemon-reload
sudo systemctl enable --now pgbouncer_exporter
curl -s http://localhost:9127/metrics | grep pgbouncer_pools_server_active_connections

Khi curl trả về danh sách metric kèm status code 200, bổ sung cấu hình job vào prometheus.yml:

scrape_configs:
  - job_name: 'pgbouncer'
    scrape_interval: 10s
    scrape_timeout: 5s
    static_configs:
      - targets: ['10.10.20.15:9127']
        labels:
          environment: 'production'
          cluster: 'pg-core'

Các metric cốt lõi và kinh nghiệm trực chiến

Đưa dữ liệu vào Prometheus mới xong một nửa chặng đường. Bạn cần biết chính xác metric nào phản ánh đúng sức khỏe hệ thống.

1. Giám sát hàng đợi: Client Waiting & Maxwait

Đây là chỉ báo nhạy nhất về tình trạng nghẽn pool. Ở trạng thái bình thường, hai thông số này luôn phải bằng 0.

# Số lượng client đang chờ slot backend
sum by (database) (pgbouncer_pools_client_waiting_connections)

# Thời gian query phải chờ lâu nhất trong pool (giây)
max by (database) (pgbouncer_pools_maxwait_seconds)

Nếu client_waiting vượt quá 5 và maxwait tăng trên 2 giây, hệ thống đang gặp nguy hiểm. Hoặc pool size quá nhỏ, hoặc database backend đang bị slow query treo lock hàng loạt bảng.

2. Đo lường Pool Saturation

PgBouncer cấp phát kết nối tới PostgreSQL theo cơ chế lười (lazy allocation). Ban đầu nó chỉ mở vài connection, sau đó tăng dần lên theo tải cho đến ngưỡng default_pool_size.

Do đó, công thức tính phần trăm bận dựa trên active / (active + idle) dễ báo động giả khi tải thấp. Cách an toàn hơn là theo dõi trực tiếp số kết nối server đang hoạt động so với trần cấu hình:

# Số kết nối backend đang xử lý query thực tế
sum by (database) (pgbouncer_pools_server_active_connections)

Khi con số này chạm sát ngưỡng pool size (ví dụ 45/50 connection) trong giờ cao điểm, đã đến lúc tăng nhẹ pool size hoặc tối ưu lại thời gian giữ transaction của code.

3. Kinh nghiệm đặt alert: Tránh bẫy Alert Fatigue

Hồi đầu mới vận hành, mình đặt cảnh báo hễ client_waiting > 0 là bắn tin Slack ngay lập tức. Đó là một sai lầm. Cứ mỗi lần cronjob chạy định kỳ 5 giây hay Kubernetes scale pod mới, metric lại nhảy lên 1 rồi tụt về 0. Slack nhận vài trăm thông báo rác mỗi ngày. Đội ngũ trực ca mệt mỏi và dần bỏ qua cả cảnh báo thật.

Nguyên tắc vàng: Chỉ cảnh báo khi tình trạng bất thường kéo dài đủ lâu. Bộ rule chuẩn hóa hiện tại:

groups:
  - name: pgbouncer_alerts
    rules:
      - alert: PgBouncerHighClientWaiting
        expr: sum by (database) (pgbouncer_pools_client_waiting_connections) > 5
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "PgBouncer database {{ $labels.database }} nghẽn hàng đợi"
          description: "Có {{ $value }} client chờ connection quá 1 phút. Cần kiểm tra pool size hoặc slow queries."

      - alert: PgBouncerHighQueryWaitTime
        expr: max by (database) (pgbouncer_pools_maxwait_seconds) > 2
        for: 30s
        labels:
          severity: critical
        annotations:
          summary: "Query bị trễ tại PgBouncer pool {{ $labels.database }}"
          description: "Query wait time vượt quá 2 giây trong 30 giây liên tục."

      - alert: PgBouncerPoolSaturation
        expr: sum by (database) (pgbouncer_pools_server_active_connections) >= 45
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "PgBouncer pool gần chạm trần trên {{ $labels.database }}"
          description: "Số active backend connection duy trì sát ngưỡng tối đa (50) trong hơn 3 phút."

Trên Grafana, bạn có thể import dashboard mẫu số 11905 hoặc 14118 để dùng ngay. Hãy đặt dashboard PgBouncer cạnh dashboard hạ tầng máy chủ (CPU, Disk I/O). Khi có biến, bạn sẽ biết ngay nghẽn do thiếu connection hay do ổ đĩa database bị nghẽn IOPS.

Share: