Bạn cài xong mysqld_exporter bằng Helm, pod báo 1/1 Running, nhưng Prometheus hoặc không thấy target, hoặc thấy target màu xanh mà biểu đồ vẫn trống. Cả ba lỗi dưới đây đều không in ra dòng cảnh báo nào ở bước cài đặt, nên rất dễ mất hàng giờ để lần ra. Bài này đi theo từng tầng của đường dữ liệu để bạn biết lỗi nằm ở đâu: Prometheus có target không, mysqld_exporter có nói chuyện được với MySQL không, và Helm có thật sự nhận giá trị bạn truyền vào không.
Nếu cluster của bạn chưa có Prometheus, hãy dựng trước theo bài Giám sát Kubernetes với kube-prometheus và Grafana. Bài này giả định stack đó đã chạy trong một namespace tên monitoring.
Kiến trúc: mysqld_exporter là cầu nối hai đường riêng biệt
Prometheus không biết nói giao thức MySQL. mysqld_exporter là một tiến trình nhỏ đứng giữa: nó mở kết nối tới MySQL, chạy các câu lệnh như SHOW GLOBAL STATUS, rồi trả kết quả dưới dạng text ở cổng 9104, đường dẫn /metrics. Cần tách rõ hai đường:
- Đường 1: Prometheus gọi HTTP tới
mysqld_exporter(qua Service, cổng 9104). - Đường 2:
mysqld_exporterđăng nhập vào MySQL bằng user và mật khẩu bạn cấu hình.
Đường 1 hỏng thì Prometheus không có target hoặc target down. Đường 2 hỏng thì target vẫn up, vì exporter vẫn phục vụ /metrics bình thường, chỉ là kèm mysql_up 0 và không có số liệu MySQL. Hai tình huống này nhìn từ Prometheus rất khác nhau, và đó là chìa khoá chẩn đoán.
Tạo user giám sát với quyền tối thiểu
mysqld_exporter chỉ cần đọc. Tài liệu của dự án khuyến nghị các quyền PROCESS, REPLICATION CLIENT và SELECT, kèm giới hạn số kết nối đồng thời:
CREATE USER 'exporter'@'10.0.0.%' IDENTIFIED BY '<mat-khau>'
WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'10.0.0.%';Hai điểm đáng cân nhắc. Thứ nhất, host '%' cho phép đăng nhập từ mọi nơi; hãy giới hạn theo dải mạng của pod nếu mạng cluster cho phép. Thứ hai, SELECT ON *.* là quyền rộng. Với một số collector, quyền này là cần thiết, nhưng nếu bạn tắt các collector đọc bảng thì có thể thu hẹp lại. Hãy kiểm chứng trên môi trường thử trước khi bỏ quyền.
Cài chart prometheus-mysql-exporter
Chart do cộng đồng Prometheus duy trì. Một lệnh cài mẫu, dùng tên trung tính:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install db1-exporter prometheus-community/prometheus-mysql-exporter \
-n monitoring \
--set mysql.user=exporter \
--set mysql.pass='<mat-khau>' \
--set mysql.host=db-1.db.svc.cluster.local \
--set mysql.port=3306Sau khi cài mysqld_exporter, kiểm tra theo thứ tự pod, Service và log:
kubectl -n monitoring get pods,svc
kubectl -n monitoring logs deploy/db1-exporter-prometheus-mysql-exporterLog lúc khởi động liệt kê các collector đã bật (global_status, global_variables, slave_status…) và dòng Listening on ... :9104. Một chi tiết hay bị bỏ sót: mysqld_exporter thường chỉ mở kết nối tới MySQL khi có một lần scrape, không phải lúc khởi động. Vì vậy log sạch ngay sau khi pod vừa lên chưa chứng minh gì cả. Lỗi đăng nhập chỉ xuất hiện sau lần Prometheus gọi đầu tiên.
Lỗi 1: relabel keep loại bỏ mọi pod, không sinh target
Cấu hình scrape ban đầu dùng kubernetes_sd_configs với role: pod và hai luật keep, một theo nhãn pod prometheus=true, một theo annotation prometheus.io/scrape=true:
# nằm dưới prometheus.prometheusSpec trong values của kube-prometheus-stack,
# sau đó chạy helm upgrade cho stack
additionalScrapeConfigs:
- job_name: 'mysql-exporter'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_prometheus]
action: keep
regex: "true"
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: "true"Pod của chart không có nhãn và annotation đó. Luật keep chỉ giữ lại target khớp, nên mọi pod bị loại và trang Status → Targets không có dòng nào cho job này. Không có lỗi nào được báo, vì "không khớp" là kết quả hợp lệ. Bạn có ba hướng sửa:
- Gắn nhãn hoặc annotation cho pod (qua
podLabelshoặcpodAnnotationstrong values của chart, nếu bản chart có). - Bỏ service discovery, dùng
static_configstrỏ thẳng Service (cách đơn giản nhất, xem bên dưới). - Dùng ServiceMonitor của Prometheus Operator. Đây là phần bổ sung của bài, bạn cần bật
serviceMonitor.enabledtrong values của chart và đặt nhãnreleasetrongserviceMonitor.additionalLabelssao cho khớp với selector mà Prometheus đang dùng (kiểm travalues.yamlđúng phiên bản chart).
Nhớ rằng annotation kiểu prometheus.io/scrape không phải chuẩn của Prometheus. Nó chỉ có tác dụng khi chính luật relabel của bạn đọc nó. Chi tiết các khoá __meta_kubernetes_* có trong tài liệu kubernetes_sd_config của Prometheus.
Lỗi 2: mysqld_exporter up nhưng không có số liệu vì Access denied
Sau khi chuyển sang static_configs, target hiện up, nhưng truy vấn các metric MySQL vẫn trống:
# cũng nằm dưới prometheus.prometheusSpec, rồi helm upgrade stack
additionalScrapeConfigs:
- job_name: 'mysql-exporter'
static_configs:
- targets: ['db1-exporter-prometheus-mysql-exporter:9104']Tên Service lấy từ kubectl -n monitoring get svc. Nếu Prometheus nằm ở namespace khác, dùng tên đầy đủ có hậu tố .monitoring.svc. Đây là lúc tách hai đường: target up nghĩa là đường 1 ổn. Để xem đường 2, hỏi thẳng mysqld_exporter:
# port-forward chạy ở foreground: để nền bằng &, hoặc mở terminal thứ hai để chạy curl
kubectl -n monitoring port-forward svc/db1-exporter-prometheus-mysql-exporter 9104:9104 &
sleep 2
curl -s localhost:9104/metrics | grep '^mysql_up'
# mysql_up 0 => exporter không đăng nhập được vào MySQLTrong Prometheus, truy vấn mysql_up cho kết quả tương tự. Lúc này mới đọc log của mysqld_exporter. Một dòng điển hình khi sai thông tin đăng nhập:
level=ERROR msg="Error opening connection to database"
err="Error 1045 (28000): Access denied for user 'exporter'@'10.0.0.x' (using password: YES)"Chú ý using password: YES: mysqld_exporter có gửi một mật khẩu, nhưng không phải mật khẩu bạn nghĩ. Bước kế tiếp là hỏi vì sao.
Lỗi 3: Helm im lặng nhận khoá không tồn tại
Lệnh cài ban đầu dùng --set mysql.password=.... Khoá đúng của chart là mysql.pass. Helm không hề phàn nàn, vì --set chỉ gộp giá trị vào cây values, còn template chỉ đọc những khoá nó biết. Khoá lạ bị bỏ qua, và mysql.pass giữ nguyên giá trị mặc định của chart (không phải mật khẩu thật của user). Vì thế mysqld_exporter vẫn đăng nhập "có mật khẩu" và bị MySQL từ chối.
Đổi sang đúng khoá và nâng cấp release thì lỗi hết, số liệu xuất hiện:
helm upgrade db1-exporter prometheus-community/prometheus-mysql-exporter \
-n monitoring --reuse-values \
--set mysql.pass='<mat-khau>'Cùng lệnh ban đầu còn có mysql.metrics.tls=false. Chúng tôi chưa xác nhận khoá này có hiệu lực: nhiều khả năng đây cũng là khoá lạ, tuỳ phiên bản chart. Đây là giả thuyết, bạn tự kiểm tra bằng helm show values đúng phiên bản chart đang dùng. Đừng chép khoá từ bài viết, kể cả bài này.
Bắt sớm lỗi values trước khi mất thời gian
Ba thao tác sau bắt được cả ba lỗi từ sớm:
# 1. Khoá hợp lệ của đúng phiên bản chart
helm show values prometheus-community/prometheus-mysql-exporter | grep -n -A12 '^mysql:'
# 2. Giá trị người dùng đã truyền (khoá lạ vẫn hiện ở đây, không báo lỗi)
helm get values db1-exporter -n monitoring
# 3. Xem manifest sinh ra có chứa giá trị bạn mong đợi không
helm template db1-exporter prometheus-community/prometheus-mysql-exporter \
--set mysql.pass='<mat-khau>' | grep -n -i -E 'pass|user|host'Nếu helm get values liệt kê một khoá mà helm show values không có, bạn vừa gõ sai. Chart không có values.schema.json thì Helm không kiểm tra gì; chart có schema chỉ chặn khoá lạ khi schema cấm thuộc tính thừa (additionalProperties: false), nên đừng dựa vào Helm để bắt lỗi gõ sai. Chi tiết lệnh có ở tài liệu helm get values. Bước kiểm chứng cuối luôn là mysql_up bằng 1, không phải log sạch.
Ba lưu ý khi dùng các lệnh này. Thứ nhất, mật khẩu trong Secret do chart sinh ra ở dạng base64, nên grep pass trên manifest chỉ thấy chuỗi mã hoá và chưa chứng minh giá trị đúng; hãy giải mã bằng kubectl -n monitoring get secret <ten-secret> -o jsonpath='{.data}' rồi base64 -d giá trị cần xem. Thứ hai, helm get values và helm template in mật khẩu dạng rõ ra terminal, nên đừng chạy khi đang chia sẻ màn hình hoặc ghi log. Thứ ba, helm upgrade --reuse-values giữ lại cả khoá sai cũ trong values của release; nếu muốn dọn sạch, dùng --reset-values rồi truyền lại đầy đủ các giá trị cần thiết.
Dashboard Grafana và panel SHOW PROCESSLIST
Dashboard cộng đồng mã số 14057 (loại MySQL) nhập được thẳng vào Grafana từ trang dashboards của grafana.com để hiển thị số liệu của mysqld_exporter. Chúng tôi chưa kiểm panel nào của dashboard đó hiển thị đúng với phiên bản exporter bạn cài, nên hãy thử rồi xoá panel trống.
Để xem danh sách tiến trình đang chạy, thêm data source MySQL trong Grafana, tạo panel loại table và nhập truy vấn SHOW PROCESSLIST. Dùng một user riêng cho Grafana, không dùng chung với user của mysqld_exporter:
CREATE USER 'grafana'@'10.0.0.%' IDENTIFIED BY '<mat-khau>'
WITH MAX_USER_CONNECTIONS 5;
GRANT PROCESS ON *.* TO 'grafana'@'10.0.0.%';Chỉ cần PROCESS để thấy tiến trình của mọi user. Bạn không cần SELECT ON *.* chỉ để hiển thị processlist. Hãy nhớ cột Info chứa nguyên văn câu truy vấn, có thể kèm dữ liệu nhạy cảm, nên chỉ cấp quyền xem dashboard này cho người cần.
Checklist cài mysqld_exporter
- User riêng cho
mysqld_exporter, quyền tối thiểu, cóMAX_USER_CONNECTIONS, host giới hạn theo dải pod. - Chạy
helm show valuestrước khi gõ--set, vàhelm get valuessau khi cài. - Phân tầng khi gặp trống số liệu: có target không,
upbằng 1 không,mysql_upbằng 1 không, rồi mới đọc log. - Đọc log
mysqld_exportersau một lần scrape, không phải ngay lúc khởi động. - Không dán mật khẩu vào lệnh
--settrong script dùng chung. Dùng Secret có sẵn nếu chart hỗ trợ. - Giữ user Grafana tách biệt với user của
mysqld_exporter.
mysqld_exporter cho biết MySQL đang làm gì (kết nối, truy vấn, replication), còn dung lượng volume phải theo dõi bằng metric kubelet_volume_stats_* của kubelet. Với binlog, collector binlog_size không bật mặc định. Khi volume MySQL đầy, xem bài MySQL đầy volume trên Kubernetes: binlog, dựng lại PVC và bẫy khi nới PVC.
Bài tiếp theo
Health endpoint cho Laravel trên Kubernetes: kiểm tra gì và chọn probe nào. Mỗi probe trả lời đúng một câu hỏi, và endpoint health nên kiểm tra gì để không kéo cả website xuống theo một phụ thuộc ngoài.



