Observability LGTM — Kiến trúc, triển khai và vận hành
DOC PATH deployments/observability/lgtm-lab
Bài này tập hợp kiến trúc, triển khai, tra cứu và vận hành Observability LGTM trong cùng một trang. Nội dung đi từ đường đi của metrics, logs và traces đến cấu hình collector, kiểm tra dữ liệu và xử lý sự cố.
Đọc Observability — Tổng quan và nền tảng để tra cứu lý thuyết về signals, instrumentation, labels, sampling và correlation.
System Scope
Mục có tiêu đề “System Scope”Stack gồm Grafana, Prometheus, Loki, Tempo và Grafana Alloy. Metrics cho biết hệ thống thay đổi như thế nào theo thời gian; logs ghi lại sự kiện; traces thể hiện đường đi và thời gian xử lý của một request qua các service. Grafana là nơi query và đối chiếu ba loại dữ liệu này.
Sơ đồ giữ hai vùng application host và backend host để đọc rõ đường đi của telemetry. Đường kết nối giữa hai vùng dùng endpoint có DNS hoặc routing phù hợp; Docker bridge cùng tên trên hai host không tạo kết nối liên host.
Architecture Diagram
System Flow
Topology
Mục có tiêu đề “Topology”| Layer | Thành phần | Vai trò |
|---|---|---|
| Application | Ứng dụng và exporter | Expose metrics, ghi logs và phát sinh spans qua instrumentation. |
| Collector | Grafana Alloy | Thu thập, xử lý và chuyển telemetry đến backend theo các component được khai báo. |
| Metrics backend | Prometheus | Lưu time series, nhận samples và phục vụ PromQL. |
| Logs backend | Loki | Nhận log streams và phục vụ LogQL. |
| Traces backend | Tempo | Nhận, lưu và tra cứu traces bằng TraceQL hoặc trace ID. |
| UI | Grafana | Query các datasource, hiển thị dashboard và đối chiếu dữ liệu trong Explore. |
| Storage | Storage của từng backend | Giữ telemetry và metadata theo mount, retention và policy của backend. |
Telemetry Paths
Mục có tiêu đề “Telemetry Paths”Arrow Semantics
Mục có tiêu đề “Arrow Semantics”Nét đứt trong sơ đồ thể hiện request hoặc query; nét liền thể hiện chiều truyền telemetry. Điểm chuyển động đi theo mũi tên và giúp theo dõi từng tuyến.
| Flow | Chiều và ý nghĩa | Component / API |
|---|---|---|
| 1 · Metrics scrape | Alloy → application: HTTP GET đến metrics endpoint. Application → Alloy: samples trong response. | prometheus.scrape |
| 2 · Docker logs | Application ghi stdout/stderr; Alloy đọc log stream qua Docker API/socket. | loki.source.docker |
| 3 · OTLP receiver | Application SDK → Alloy: gửi traces bằng OTLP HTTP hoặc gRPC theo receiver đã cấu hình. | otelcol.receiver.otlp |
| 4 · Remote write | Alloy → Prometheus: gửi samples đến remote-write endpoint. | prometheus.remote_write |
| 5 · Export | Alloy → Loki: log push. Alloy → Tempo: OTLP export. | loki.write, otelcol.exporter.otlp |
| 6 · Grafana queries | Grafana → Prometheus/Loki/Tempo: HTTP query. Backend trả kết quả về Grafana; sơ đồ lược bỏ đường response. | Grafana data sources |
Metrics
Mục có tiêu đề “Metrics”Pipeline scrape dùng cơ chế pull: Alloy gọi metrics endpoint theo scrape interval, nhận samples rồi chuyển tới prometheus.remote_write. Backend cần nhận đúng remote-write protocol và endpoint; scrape thành công chưa chứng minh samples đã đến Prometheus.
Metric name và labels lấy từ exporter hoặc instrumentation thực tế. Counter dùng để tính tốc độ request bằng rate; histogram dùng để phân tích phân bố thời gian xử lý. Không dùng tên metric của ứng dụng khác khi xác minh dữ liệu.
Application ghi logs ra stdout/stderr; Docker cung cấp log stream cho collector. loki.source.docker đọc qua Docker API/socket, không tail trực tiếp file log của Docker. Alloy lọc target, gắn labels rồi gửi log qua loki.write đến Loki.
systemd journal và PM2/file logs dùng source riêng, nhưng có thể chuyển đến cùng Loki writer. Mỗi nguồn cần labels đủ để phân biệt host, môi trường và service khi query.
Traces
Mục có tiêu đề “Traces”OpenTelemetry SDK phát sinh spans và gửi OTLP đến receiver. Pipeline trên Alloy chuyển spans qua processor rồi exporter tới Tempo. Protocol, receiver bind address và endpoint exporter phải khớp ở từng chặng; OTLP gRPC và HTTP không dùng thay thế trực tiếp cho nhau.
Trace generation phụ thuộc instrumentation và sampling của ứng dụng. Kiểm tra endpoint thực tế được instrument, service.name, trace ID và sampling policy khi đối chiếu request với trace.
Query and Explore
Mục có tiêu đề “Query and Explore”Grafana query từng backend qua datasource tương ứng: PromQL cho metrics, LogQL cho logs và TraceQL cho traces. Khi điều tra, giữ cùng time range và service identity để chuyển từ metric bất thường sang log hoặc trace liên quan.
Việc liên kết logs với traces cần trace ID trong log và cấu hình liên kết ở datasource. Chỉ có ba datasource trong Grafana chưa đủ để tạo correlation tự động.
Host and Port Map
Mục có tiêu đề “Host and Port Map”Bảng này tra cứu port dịch vụ thường dùng và các API liên quan. Listen address và published port phải lấy từ cấu hình backend đang vận hành; port dịch vụ bên trong container không đồng nghĩa port đã được publish trên host.
| Thành phần | Port dịch vụ | Endpoint / mục đích |
|---|---|---|
| Grafana | 3000 | UI, Explore và /api/health. |
| Prometheus | 9090 | PromQL API, /-/ready; remote-write receiver dùng /api/v1/write khi được bật. |
| Loki | 3100 | /ready, /loki/api/v1/push, /loki/api/v1/query_range. |
| Tempo HTTP | 3200 | /ready, API tra cứu traces. |
| OTLP gRPC / HTTP | 4317 / 4318 | Receiver nhận telemetry theo protocol được khai báo. |
| Alloy UI/debug | 12345 | UI component graph và /-/ready. Compose đính kèm publish trên loopback. |
| Application | Theo cấu hình ứng dụng | Metrics endpoint, health endpoint và các request đã được instrument. |
Datasource URLs
Mục có tiêu đề “Datasource URLs”Datasource URL phải truy cập được từ network namespace của Grafana. 127.0.0.1 bên trong container Grafana trỏ về container đó; dùng DNS service trên cùng Docker network hoặc endpoint có routing đến backend.
| Datasource | Đích kết nối | Ngôn ngữ query |
|---|---|---|
| Prometheus | HTTP API của Prometheus | PromQL |
| Loki | HTTP API của Loki | LogQL |
| Tempo | HTTP query API của Tempo, không dùng OTLP ingest endpoint | TraceQL / trace ID |
Đối chiếu URL, TLS, authentication và tenant header với cấu hình backend trước khi dùng chức năng kiểm tra datasource trong Grafana.
Configuration Alignment
Mục có tiêu đề “Configuration Alignment”| Hạng mục | Cần đối chiếu |
|---|---|
| Config mount | File trên host tồn tại và đúng đường dẫn mà Alloy đọc trong container. |
| Network | Endpoint resolve và reachable từ component gửi dữ liệu; Docker network có phạm vi host. |
| Metrics pipeline | Scrape target, metrics path, labels, remote-write URL và trạng thái receiver trên Prometheus. |
| Logs pipeline | Nguồn log, quyền đọc, rule lọc, labels và Loki push URL. |
| Traces pipeline | Application SDK, protocol OTLP, receiver, processor, exporter và Tempo ingest endpoint. |
| Grafana | Datasource URL, quyền truy cập và labels/service identity dùng để liên kết dữ liệu. |
| Persistence | Mount dữ liệu, quyền ghi, dung lượng và retention của từng backend. |
Preflight
Mục có tiêu đề “Preflight”Các lệnh sau chỉ đọc thông tin Docker, network, disk và thời gian hệ thống. Chạy trên host cần kiểm tra:
docker versiondocker compose versiondocker network lsdf -htimedatectlXác nhận route và firewall cho đúng chiều gửi/query; đồng bộ thời gian giữa các host để đối chiếu telemetry. Kiểm tra Compose backend hiện có trước khi thay đổi service, mount hoặc published port.
Kiến trúc và phạm vi thu thập
Mục có tiêu đề “Kiến trúc và phạm vi thu thập”Bộ Compose/Alloy dưới đây ghi lại cấu hình collector đã được cung cấp. Tên ứng dụng, host và môi trường được ẩn bằng demo-app, demo-node-01 và demo-prod; endpoint nội bộ dùng <loki-host>.
| Thành phần | Cấu hình đang dùng |
|---|---|
| Collector | grafana/alloy:v1.17.1, container logportal-alloy, user 0:0. |
| Nguồn log | Docker API qua /var/run/docker.sock. |
| Bộ lọc | Tên container hoặc Compose service chứa demo-app. |
| Đích gửi | Loki qua HTTP port 3100, endpoint /loki/api/v1/push. |
| Giao diện Alloy | Host chỉ publish 127.0.0.1:12345; process trong container listen 0.0.0.0:12345. |
| Runtime data | Bind mount ./data/alloy vào /var/lib/alloy/data. |
| Giới hạn khai báo | 0,50 CPU và 512M RAM trong Compose. |
| Restart policy | unless-stopped. |
Luồng xử lý: Docker discovery → lọc target và gắn labels → đọc Docker logs → gửi tới Loki. Chu kỳ discovery là 10 giây; đây không phải cam kết độ trễ log đến Loki.
Các nguồn chưa bật
Mục có tiêu đề “Các nguồn chưa bật”| Nội dung | Trạng thái trong bộ file |
|---|---|
| systemd journal | Component và journal mounts đang bị comment. |
| PM2/file logs | Component và các mount đường dẫn log đang bị comment. |
| Metrics và traces | Bộ file đính kèm không khai báo Prometheus pipeline, OTLP receiver hoặc Tempo exporter. |
| LGTM backend | Compose này chỉ triển khai Alloy; Loki là endpoint bên ngoài collector. |
Luồng Docker logs được bật mặc định; systemd và PM2 có cấu hình đầy đủ để bật theo nguồn log thực tế.
Bộ file triển khai
Mục có tiêu đề “Bộ file triển khai”Hai file dưới đây chứa pipeline Docker đang bật và các phần systemd, PM2 đang comment. Đường dẫn mount, image tag và cơ chế xử lý được giữ theo file gốc; tên dùng trong regex và labels đã được thay bằng giá trị trung tính. Khi áp dụng, điền tên container/service, host và môi trường của hệ thống. IP nội bộ của Loki được ẩn bằng <loki-host>; điền lại endpoint từ hồ sơ vận hành trước khi sử dụng bản tải xuống. Comment trong hai file dùng tiếng Anh để tránh lỗi hiển thị tiếng Việt.
Đặt file Alloy trong thư mục config để khớp bind mount của Compose:
alloy/├── docker-compose.yml├── config/│ └── alloy-config.alloy└── data/ └── alloy/Docker Compose
Mục có tiêu đề “Docker Compose”services: alloy: image: grafana/alloy:v1.17.1 container_name: logportal-alloy user: "0:0" # Optional journal access: use the actual host group IDs if required. # Check with: getent group adm; getent group systemd-journal # Set these variables in the Compose .env file before enabling group_add. # group_add: # - "${ALLOY_ADM_GID:?Set the host adm group ID}" # - "${ALLOY_JOURNAL_GID:?Set the host systemd-journal group ID}" command: - run - /etc/alloy/config.alloy - --server.http.listen-addr=0.0.0.0:12345 - --storage.path=/var/lib/alloy/data ports: - "127.0.0.1:12345:12345" volumes: - ./config/alloy-config.alloy:/etc/alloy/config.alloy:ro - ./data/alloy:/var/lib/alloy/data # SECTION A - Docker logs. Disable with Alloy SECTION A if not needed. - /var/run/docker.sock:/var/run/docker.sock:ro
# SECTION B - systemd journal logs. Enable with Alloy SECTION B. # Enable only journal directories that exist on the host. # - /run/log/journal:/run/log/journal:ro # - /var/log/journal:/var/log/journal:ro # - /etc/machine-id:/etc/machine-id:ro
# SECTION C - PM2 file logs. Enable with Alloy SECTION C. # Root-owned PM2 logs. # - /root/.pm2/logs:/host/root/.pm2/logs:ro
# PM2 logs for multiple app users. This exposes all of /home read-only. # Prefer a specific user's log directory and adjust the Alloy target. # - /home:/host/home:ro
# Optional app log locations: enable only when these paths are needed. # - /opt:/host/opt:ro # - /var/www:/host/var/www:ro deploy: resources: limits: cpus: '0.50' memory: 512M restart: unless-stoppedCompose chỉ publish UI/debug trên loopback của host. Docker socket cho phép Alloy đọc danh sách container và log; chỉ cấp quyền sửa Compose/config và truy cập host cho admin được phép. Không bật thêm mount journal, /home hoặc PM2 khi các nguồn đó chưa được triển khai.
Cấu hình Alloy
Mục có tiêu đề “Cấu hình Alloy”logging { level = "info" format = "logfmt"}
// The internal Loki address is redacted in this documentation.// Set the Loki endpoint from your deployment before using this file.loki.write "central_loki" { endpoint { url = "http://<loki-host>:3100/loki/api/v1/push" }}
// SECTION A - Docker logs (enabled).// To disable Docker collection, comment all components in SECTION A// and disable the Docker socket mount in docker-compose.yml.discovery.docker "local_docker" { host = "unix:///var/run/docker.sock" refresh_interval = "10s"}
discovery.relabel "docker_selected" { targets = discovery.docker.local_docker.targets
// Keep containers whose name or Compose service contains "demo-app". rule { source_labels = [ "__meta_docker_container_name", "__meta_docker_container_label_com_docker_compose_service", ] separator = ";" action = "keep" regex = ".*(demo-app).*" }
rule { source_labels = ["__meta_docker_container_name"] regex = "/(.*)" target_label = "container" }
rule { source_labels = ["__meta_docker_container_label_com_docker_compose_service"] target_label = "compose_service" }
rule { target_label = "job" replacement = "docker" }
rule { target_label = "host" replacement = "demo-node-01" }
rule { target_label = "env" replacement = "demo-prod" }
rule { target_label = "source" replacement = "docker" }}
loki.source.docker "docker_logs" { host = "unix:///var/run/docker.sock" targets = discovery.relabel.docker_selected.output labels = {} forward_to = [loki.write.central_loki.receiver]}
// SECTION B - systemd journal logs (disabled).// Enable the journal mounts in docker-compose.yml first.// Uncomment one leading "//" from each line in the component block below.// Keep the remaining inline comments. Set the service regex, host and env.// Use relabel rules for OR matching across multiple systemd units.// BEGIN SYSTEMD CONFIG// loki.relabel "systemd_selected_services" {// // This component exports rules; the journal source forwards logs directly.// forward_to = []//// rule {// source_labels = ["__journal__systemd_unit"]// action = "keep"// regex = "(demo-systemd-log[.]service|docker[.]service|ssh[.]service)"// }//// rule {// source_labels = ["__journal__systemd_unit"]// target_label = "unit"// }//// rule {// source_labels = ["__journal__hostname"]// target_label = "journal_host"// }//// rule {// source_labels = ["__journal_priority_keyword"]// target_label = "level"// }//// rule {// target_label = "job"// replacement = "systemd"// }//// rule {// target_label = "env"// replacement = "demo-prod"// }//// rule {// target_label = "source"// replacement = "systemd"// }// }//// loki.source.journal "systemd_logs" {// // Use /run/log/journal instead when the host uses volatile journals only.// path = "/var/log/journal"// forward_to = [loki.write.central_loki.receiver]// relabel_rules = loki.relabel.systemd_selected_services.rules// max_age = "2h"//// labels = {// job = "systemd",// host = "demo-node-01",// env = "demo-prod",// source = "systemd",// }// }// END SYSTEMD CONFIG
// SECTION C - PM2 file logs (disabled).// Enable only the required PM2 mounts in docker-compose.yml.// Uncomment one leading "//" from each line in the component block below.// Keep only targets that match actual log paths. Set host and env.// The optional /opt and /var/www targets retain a second comment prefix.// BEGIN PM2 CONFIG// loki.source.file "pm2_logs" {// targets = [// {// __path__ = "/host/root/.pm2/logs/*.log",// job = "pm2",// host = "demo-node-01",// env = "demo-prod",// pm2_user = "root",// source = "pm2",// },//// // Match PM2 logs for users under /home.// // Narrow the mount and glob when only one app user needs collection.// {// __path__ = "/host/home/*/.pm2/logs/*.log",// job = "pm2",// host = "demo-node-01",// env = "demo-prod",// pm2_user = "home-users",// source = "pm2",// },//// // Optional: enable with the /opt mount only when apps log there.// // {// // __path__ = "/host/opt/**/logs/*.log",// // job = "pm2",// // host = "demo-node-01",// // env = "demo-prod",// // pm2_user = "opt-apps",// // source = "pm2",// // },//// // Optional: enable with the /var/www mount only when apps log there.// // {// // __path__ = "/host/var/www/**/logs/*.log",// // job = "pm2",// // host = "demo-node-01",// // env = "demo-prod",// // pm2_user = "www-apps",// // source = "pm2",// // },// ]//// forward_to = [loki.write.central_loki.receiver]// // Start at the end only when no saved position exists for the file.// tail_from_end = true//// file_match {// enabled = true// sync_period = "10s"// }// }// END PM2 CONFIGBộ lọc, labels và đường đi của log
Mục có tiêu đề “Bộ lọc, labels và đường đi của log”Discovery và relabel
Mục có tiêu đề “Discovery và relabel”discovery.docker.local_dockerlấy target từ Docker daemon qua Unix socket, refresh mỗi 10 giây.discovery.relabel.docker_selectedghép container name và Compose service bằng dấu;. Rulekeepchỉ giữ target khớp.*(demo-app).*.- Rule
containerbỏ dấu/đầu tên container.compose_servicelấy từ labelcom.docker.compose.service; container không chạy bằng Compose có thể không có label này. loki.source.docker.docker_logsđọc log của target đã lọc rồi chuyển tớiloki.write.central_loki.receiver.
Trong bản tài liệu đã ẩn thông tin doanh nghiệp, regex lọc theo demo-app. Các tên demo-nginx, front-end, back-end và database từng xuất hiện trong comment của file gốc không phải danh sách được rule hiện tại thu thập.
Labels dùng để truy vấn
Mục có tiêu đề “Labels dùng để truy vấn”| Label | Giá trị / nguồn |
|---|---|
container |
Tên container đã bỏ dấu / đầu. |
compose_service |
Docker Compose service label, nếu có. |
job |
docker |
host |
demo-node-01 |
env |
demo-prod |
source |
docker |
host và env là giá trị gán cố định trong file, không được Alloy tự suy ra từ host. Khi dùng cấu hình cho server khác, cập nhật hai label để log không bị gắn sai nguồn.
Cơ chế các component được mô tả tại discovery.relabel và loki.source.docker.
Bật thêm systemd và PM2
Mục có tiêu đề “Bật thêm systemd và PM2”Hai nguồn được giữ ở trạng thái comment như file gốc. Mỗi nguồn cần bật cả component trong Alloy và mount trong Compose. Việc bật nguồn sẽ mở rộng phạm vi log được đọc và gửi tới Loki; chỉ chọn service và đường dẫn cần thu thập.
systemd journal
Mục có tiêu đề “systemd journal”- Trong Alloy, bỏ một lớp
//ở đầu các dòng nằm giữaBEGIN SYSTEMD CONFIGvàEND SYSTEMD CONFIG; giữ hai dòng đánh dấu và các comment giải thích còn lại. - Chỉnh regex thành các systemd unit thực tế. Rule
keepdùng phép OR giữa các tên unit;loki.relabelcung cấp.rulescho journal source, còn source gửi log trực tiếp tới Loki.forward_to = []trong component relabel là có chủ đích. - Trong Compose, bật mount journal có trên host và mount
/etc/machine-id. Giữpath = "/var/log/journal"nếu journal nằm ở đó; đổi thành/run/log/journalnếu host chỉ dùng journal tạm thời. Khi cần đọc cả hai, đặtpath = ""để Alloy dùng các đường dẫn mặc định. - Kiểm tra quyền đọc journal. Nếu cần
group_add, lấy GID thực tế bằng hai lệnh dưới, khai báoALLOY_ADM_GIDvàALLOY_JOURNAL_GIDtrong file.envcạnh Compose rồi bật nhóm tương ứng. Không dùng GID của server khác. - Cập nhật
host,envvà giữmax_age = "2h"nếu phù hợp với khoảng log cần đọc khi collector khởi động.
Các lệnh sau chỉ đọc thông tin group và log hiện có; thay docker.service bằng unit cần thu thập:
getent group admgetent group systemd-journaljournalctl -u docker.service --since "5 minutes ago" --no-pager -n 20Các label unit, journal_host và level được lấy từ journal trước khi Alloy bỏ internal labels. Xem loki.source.journal.
PM2 và file logs
Mục có tiêu đề “PM2 và file logs”- Trong Alloy, bỏ một lớp
//ở đầu các dòng nằm giữaBEGIN PM2 CONFIGvàEND PM2 CONFIG; giữ hai dòng đánh dấu. Các target/optvà/var/wwwvẫn còn một lớp comment để bật riêng khi cần. - Bật mount tương ứng trong Compose theo bảng dưới; comment lại target không sử dụng.
__path__luôn là đường dẫn bên trong container Alloy. - Cập nhật
host,env,pm2_uservà glob theo vị trí log thực tế. Mount toàn bộ/home,/opthoặc/var/wwwcho phép collector đọc phạm vi rộng; ưu tiên mount thư mục log cụ thể nếu chỉ thu một ứng dụng. file_matchtìm file mới mỗi 10 giây.tail_from_end = truebắt đầu từ cuối file khi chưa có vị trí đọc đã lưu; vì vậy cần tạo log mới khi kiểm tra nguồn vừa bật. Xem loki.source.file.
| Nguồn | Mount trong Compose | Target trong Alloy |
|---|---|---|
| PM2 của root | /root/.pm2/logs:/host/root/.pm2/logs:ro |
/host/root/.pm2/logs/*.log |
| PM2 của nhiều user | /home:/host/home:ro |
/host/home/*/.pm2/logs/*.log |
App logs dưới /opt |
/opt:/host/opt:ro |
/host/opt/**/logs/*.log |
App logs dưới /var/www |
/var/www:/host/var/www:ro |
/host/var/www/**/logs/*.log |
Sau khi chỉnh file, thực hiện bước validate và triển khai bên dưới. Trong Grafana Explore, đối chiếu log mới của từng nguồn bằng labels đã cấu hình:
{job="systemd",host="demo-node-01",env="demo-prod"}{job="systemd",host="demo-node-01",unit="docker.service"}{job="pm2",host="demo-node-01",env="demo-prod"}Nếu tắt Docker collection, comment toàn bộ component trong SECTION A và mount Docker socket; bỏ riêng bước kiểm tra socket trong preflight bên dưới. Compose vẫn cần Docker để chạy container Alloy.
Kiểm tra file và khởi động collector
Mục có tiêu đề “Kiểm tra file và khởi động collector”Xác nhận file có sẵn
Mục có tiêu đề “Xác nhận file có sẵn”Chạy tại thư mục chứa docker-compose.yml. Các lệnh sau đọc file và kiểm tra Compose; chỉ tiếp tục khi tất cả kiểm tra thành công.
test -s docker-compose.yml && test -s config/alloy-config.alloy && test -S /var/run/docker.sock && docker compose -f docker-compose.yml config --quietXác nhận endpoint Loki đã được điền đúng, host có route tới port 3100 và thư mục data/alloy là vị trí runtime data cần giữ. Không đưa raw log hoặc output chứa thông tin nội bộ vào nơi công khai.
Validate và triển khai
Mục có tiêu đề “Validate và triển khai”Lệnh pull tải đúng image tag. Bước validate chạy container tạm để kiểm tra file Alloy; không khởi chạy pipeline thu log. Chỉ chạy up -d sau khi validate trả thành công; thao tác này khởi động hoặc tạo lại collector và có thể làm gián đoạn thu log trong thời gian ngắn.
docker compose -f docker-compose.yml pull alloydocker compose -f docker-compose.yml run --no-deps alloy validate /etc/alloy/config.alloySau khi validate thành công:
docker compose -f docker-compose.yml up -d alloydocker compose -f docker-compose.yml psdocker compose -f docker-compose.yml logs --tail=200 alloycurl -fsS http://127.0.0.1:12345/-/readyvalidate kiểm tra cấu trúc và component của file; nó không chứng minh Loki truy cập được hoặc đã nhận log. Xem Alloy validate.
Backend Health
Mục có tiêu đề “Backend Health”Kiểm tra backend trước khi xác minh telemetry. Điền base URL của từng service từ cấu hình vận hành, gồm protocol và port; không thêm dấu / cuối URL. Các lệnh này chỉ đọc trạng thái HTTP, không thay đổi dữ liệu:
GRAFANA_URL='<grafana-base-url>'PROMETHEUS_URL='<prometheus-base-url>'LOKI_URL='<loki-base-url>'TEMPO_URL='<tempo-http-base-url>'
curl -fsS "${GRAFANA_URL}/api/health"curl -fsS "${PROMETHEUS_URL}/-/ready"curl -fsS "${LOKI_URL}/ready"curl -fsS "${TEMPO_URL}/ready"Với backend yêu cầu authentication hoặc tenant header, sử dụng cơ chế truy cập của hệ thống. Readiness thành công xác nhận service sẵn sàng phục vụ; cần query dữ liệu để xác nhận ingestion.
API tham khảo: Grafana health, Prometheus readiness, Loki API, Tempo API.
Verification Order
Mục có tiêu đề “Verification Order”Thực hiện theo thứ tự để khoanh vùng lỗi theo từng chặng:
- Backend: Grafana health; Prometheus, Loki và Tempo readiness; log khởi động và quyền ghi storage.
- Collector: Alloy ready, config load thành công và các component cần dùng hoạt động.
- Application: metrics endpoint trả samples, nguồn log đọc được và SDK gửi spans đến đúng receiver.
- Metrics: kiểm tra scrape target rồi remote-write ingestion trên Prometheus.
- Logs: đối chiếu log mới của nguồn với Loki range query.
- Traces: tìm trace theo service identity, time range và trace ID.
- Grafana: kiểm tra datasource, Explore và liên kết giữa metrics, logs, traces.
Để tạo dữ liệu kiểm tra, thực hiện một request đã được phép trên ứng dụng và ghi lại thời điểm, service, response status và trace ID khi có. Đối chiếu telemetry của chính request đó. Endpoint được instrument và metric name lấy từ ứng dụng đang chạy; không cần một service demo riêng.
Xác minh log từ Docker đến Loki
Mục có tiêu đề “Xác minh log từ Docker đến Loki”Kiểm tra target và collector
Mục có tiêu đề “Kiểm tra target và collector”Trên host chạy Alloy, xác định container có tên hoặc Compose service khớp regex:
docker ps --format '{{.Names}} {{.Label "com.docker.compose.service"}}'docker inspect <container-name> --format '{{.HostConfig.LogConfig.Type}}'docker logs --since 5m <container-name>Mở UI Alloy tại http://127.0.0.1:12345 trên host để kiểm tra target sau relabel và trạng thái component. Từ máy quản trị, dùng SSH tunnel tới host theo quyền đã cấp; port 12345 không được publish ra network trong Compose này.
Truy vấn Loki
Mục có tiêu đề “Truy vấn Loki”Trong Grafana Explore, chọn datasource Loki của hệ thống và dùng labels đang được Alloy gắn:
{job="docker",host="demo-node-01",env="demo-prod"}{job="docker",host="demo-node-01",source="docker"} |= "error"Hoặc chạy API query từ host được phép kết nối tới Loki. Thay <loki-host> bằng endpoint trong cấu hình vận hành:
curl -G -fsS "http://<loki-host>:3100/loki/api/v1/query_range" \ --data-urlencode 'query={job="docker",host="demo-node-01",env="demo-prod"}' \ --data-urlencode 'limit=20'Tạo một thao tác đã được phép trên ứng dụng có container khớp bộ lọc, ghi lại thời điểm và đối chiếu cùng dòng log giữa Docker với Loki. Chọn đúng time range; trạng thái Alloy ready hoặc container running chưa đủ xác nhận log đã được ingest.
Validate Metrics
Mục có tiêu đề “Validate Metrics”Scrape và remote write
Mục có tiêu đề “Scrape và remote write”Trong UI Alloy, kiểm tra targets của prometheus.scrape, lỗi scrape, labels và component prometheus.remote_write. Trên Prometheus, query các series scrape đã được chuyển tới backend:
upscrape_duration_secondsup = 1 cho biết lần scrape gần nhất thành công; up = 0 cho biết scrape thất bại. Thiếu series cần kiểm tra target, relabel, khoảng thời gian và remote-write ingestion. Kết quả scrape trên Alloy và kết quả query trên Prometheus phải được kiểm tra riêng.
Counter và histogram
Mục có tiêu đề “Counter và histogram”Lấy tên counter, histogram và labels từ metrics endpoint hoặc danh sách series trên Prometheus. Các truy vấn dưới là mẫu cú pháp; thay <request-counter>, <duration-bucket> và <scrape-job> bằng các giá trị đã xác nhận:
rate({__name__="<request-counter>",job="<scrape-job>"}[5m])sum by (job) (rate({__name__="<request-counter>",job="<scrape-job>"}[5m]))histogram_quantile(0.95, sum by (le) (rate({__name__="<duration-bucket>",job="<scrape-job>"}[5m])))Hai truy vấn đầu dùng cho counter; truy vấn cuối dùng cho classic histogram có series _bucket và label le. Giữ labels phân nhóm cần thiết khi query nhiều service. Xem Prometheus query functions.
Validate Traces
Mục có tiêu đề “Validate Traces”Trong Grafana Explore, chọn datasource Tempo, đặt time range quanh request đã kiểm tra và query theo resource.service.name thực tế. Thay <service-name> bằng tên từ instrumentation:
{ resource.service.name = "<service-name>" }{ resource.service.name = "<service-name>" && status = error }{ resource.service.name = "<service-name>" && duration > 200ms }Mở trace để đối chiếu span name, status, duration và các service tham gia. Nếu đã có trace ID từ log hoặc response, tìm trực tiếp ID đó để kiểm tra đường đi của request.
duration trong truy vấn trên là thời gian của span. Dùng trace:duration khi cần lọc theo thời gian của toàn bộ trace.
Khi không có trace, lần lượt kiểm tra SDK và sampling, endpoint/protocol OTLP, Alloy receiver, processor/exporter và Tempo ingestion. Backend ready hoặc logs có dữ liệu chưa chứng minh pipeline traces hoạt động. Xem TraceQL.
Vận hành và quản lý dữ liệu
Mục có tiêu đề “Vận hành và quản lý dữ liệu”Persistence and Retention
Mục có tiêu đề “Persistence and Retention”| Thành phần | Dữ liệu cần giữ / cấu hình cần kiểm tra |
|---|---|
| Grafana | Database, datasource provisioning và dashboard configuration. |
| Prometheus | TSDB storage, retention theo thời gian/dung lượng và mức tăng time series. |
| Loki | Log storage, index, retention và các thành phần thực hiện retention theo deployment. |
| Tempo | Trace storage, retention và cấu hình compaction theo deployment. |
| Alloy | Runtime data tại ./data/alloy theo bind mount trong Compose đính kèm. |
Retention và volume name lấy từ cấu hình của từng backend. Khi thay đổi thời gian lưu, đối chiếu ingest rate, disk/storage growth, khoảng dữ liệu cần cho điều tra và backup policy. Thay đổi container phải giữ đúng mount dữ liệu hiện có.
- Giữ
./data/alloykhi tạo lại container; đây là bind mount runtime data của collector. - Backup Compose và cấu hình Alloy trước khi đổi regex, labels hoặc endpoint. Nếu thay đổi làm mất target, khôi phục file đã backup, validate và triển khai lại collector.
- Theo dõi restart, lỗi gửi log và mức sử dụng CPU/RAM. Đối chiếu giới hạn runtime bằng
docker inspect logportal-alloy. - Log retention do backend Loki quản lý. File Compose/Alloy này không cấu hình thời hạn lưu log của Loki.
loki.writeđang dùng HTTP và không khai báo thông tin xác thực. Giới hạn đường truy cập đến Loki theo network policy của hệ thống.- systemd và PM2 được comment mặc định trong bản tải xuống. Khi bật, cập nhật đồng thời component, mount và quyền đọc tương ứng, sau đó xác minh log của từng nguồn.
Xử lý sự cố
Mục có tiêu đề “Xử lý sự cố”| Hiện tượng | Kiểm tra |
|---|---|
| Alloy không khởi động | Image tag, cú pháp file, bind mount ./config/alloy-config.alloy và log khởi động. |
| Không có Docker target | Socket tồn tại, container Alloy được mount socket, Docker daemon hoạt động. |
| Có container nhưng không có target sau lọc | Container name và Compose service có khớp demo-app hay không. |
| Target có nhưng không có log | Container có phát sinh stdout/stderr, Docker API đọc được log và loki.source.docker hoạt động. |
| Loki không nhận log | URL push, route/firewall, phản hồi HTTP và lỗi retry của loki.write. |
| Query log không có kết quả | Đúng datasource, time range và các labels thực tế: host, env, job, container. |
| Prometheus không có samples | Metrics endpoint, scrape target, relabel, remote-write URL, receiver trên Prometheus và lỗi gửi samples. |
| Tempo không có traces | Instrumentation/sampling, OTLP protocol, receiver bind address, route/firewall, processor và exporter endpoint. |
| Grafana datasource lỗi | URL từ Grafana container, DNS/routing, backend readiness, TLS, authentication và tenant header. |
| Có từng signal nhưng không liên kết được | Service identity, time range, trace ID trong logs và cấu hình correlation của datasource. |
| Disk/storage tăng nhanh | Ingest rate, cardinality, log volume, retention và dung lượng storage của từng backend. |
| Không truy cập UI từ máy khác | Compose chỉ publish loopback; truy cập trên host hoặc qua SSH tunnel. |
| Không có systemd logs | Bật cả component và journal mounts, kiểm tra quyền đọc, journal path và regex tên unit. |
| Không có PM2 logs | Bật component và mount tương ứng, kiểm tra glob bên trong container, quyền đọc và log mới do tail_from_end = true. |