Đến nội dung chính
Infra Notes
DOCS / UTF-8
Deployment // Triển khai hệ thốngobservability

Observability LGTM — Kiến trúc, triển khai và vận hành

DOC PATH deployments/observability/lgtm-lab

Doc Type
Deployment
Technology
observability
Status
Active
Version
Alloy v1.17.1
Tags
observabilitylgtmgrafanaprometheuslokitempoalloyDockerOperations

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.

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

Open Diagram
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.

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

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.

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.

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.

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 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.

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.

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:

Terminal window
docker version
docker compose version
docker network ls
df -h
timedatectl

Xá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.

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.

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ế.

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/
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-stopped

Compose 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.

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 CONFIG
  1. discovery.docker.local_docker lấy target từ Docker daemon qua Unix socket, refresh mỗi 10 giây.
  2. discovery.relabel.docker_selected ghép container name và Compose service bằng dấu ;. Rule keep chỉ giữ target khớp .*(demo-app).*.
  3. Rule container bỏ dấu / đầu tên container. compose_service lấy từ label com.docker.compose.service; container không chạy bằng Compose có thể không có label này.
  4. loki.source.docker.docker_logs đọc log của target đã lọc rồi chuyển tới loki.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.

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.

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.

  1. Trong Alloy, bỏ một lớp // ở đầu các dòng nằm giữa BEGIN SYSTEMD CONFIG và END SYSTEMD CONFIG; giữ hai dòng đánh dấu và các comment giải thích còn lại.
  2. Chỉnh regex thành các systemd unit thực tế. Rule keep dùng phép OR giữa các tên unit; loki.relabel cung cấp .rules cho journal source, còn source gửi log trực tiếp tới Loki. forward_to = [] trong component relabel là có chủ đích.
  3. 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/journal nếu host chỉ dùng journal tạm thời. Khi cần đọc cả hai, đặt path = "" để Alloy dùng các đường dẫn mặc định.
  4. 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áo ALLOY_ADM_GID và ALLOY_JOURNAL_GID trong file .env cạnh Compose rồi bật nhóm tương ứng. Không dùng GID của server khác.
  5. Cập nhật host, env và 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:

Terminal window
getent group adm
getent group systemd-journal
journalctl -u docker.service --since "5 minutes ago" --no-pager -n 20

Các label unit, journal_host và level được lấy từ journal trước khi Alloy bỏ internal labels. Xem loki.source.journal.

  1. Trong Alloy, bỏ một lớp // ở đầu các dòng nằm giữa BEGIN PM2 CONFIG và END PM2 CONFIG; giữ hai dòng đánh dấu. Các target /opt và /var/www vẫn còn một lớp comment để bật riêng khi cần.
  2. 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.
  3. Cập nhật host, env, pm2_user và glob theo vị trí log thực tế. Mount toàn bộ /home, /opt hoặc /var/www cho 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.
  4. file_match tìm file mới mỗi 10 giây. tail_from_end = true bắ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.

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.

Terminal window
test -s docker-compose.yml &&
test -s config/alloy-config.alloy &&
test -S /var/run/docker.sock &&
docker compose -f docker-compose.yml config --quiet

Xá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.

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.

Terminal window
docker compose -f docker-compose.yml pull alloy
docker compose -f docker-compose.yml run --no-deps alloy validate /etc/alloy/config.alloy

Sau khi validate thành công:

Terminal window
docker compose -f docker-compose.yml up -d alloy
docker compose -f docker-compose.yml ps
docker compose -f docker-compose.yml logs --tail=200 alloy
curl -fsS http://127.0.0.1:12345/-/ready

validate 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.

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:

Terminal window
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.

Thực hiện theo thứ tự để khoanh vùng lỗi theo từng chặng:

  1. Backend: Grafana health; Prometheus, Loki và Tempo readiness; log khởi động và quyền ghi storage.
  2. Collector: Alloy ready, config load thành công và các component cần dùng hoạt động.
  3. Application: metrics endpoint trả samples, nguồn log đọc được và SDK gửi spans đến đúng receiver.
  4. Metrics: kiểm tra scrape target rồi remote-write ingestion trên Prometheus.
  5. Logs: đối chiếu log mới của nguồn với Loki range query.
  6. Traces: tìm trace theo service identity, time range và trace ID.
  7. 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.

Trên host chạy Alloy, xác định container có tên hoặc Compose service khớp regex:

Terminal window
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.

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:

Terminal window
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.

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:

up
scrape_duration_seconds

up = 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.

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.

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.

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/alloy khi 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.
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.