OpenTelemetry LGTM Stack: Hướng dẫn triển khai Loki, Grafana, Tempo & Mimir trong môi trường Production

Mục lục bài viết(26 mục)
Khả năng quan sát ở quy mô lớn đòi hỏi một phương pháp tiếp cận thống nhất đối với nhật ký, số liệu và dấu vết. Bộ công cụ Grafana LGTM—Loki, Grafana, Tempo và Mimir—cung cấp một giải pháp mạnh mẽ, dựa trên đám mây. Hướng dẫn này trình bày chi tiết việc triển khai sản xuất, tập trung vào tích hợp OpenTelemetry (OTel), tương quan tín hiệu chéo và tối ưu hóa chi phí.
Lộ trình DevOps & Giám sát eBPF Hiện đại
Tổng quan kiến trúc
Bộ công cụ LGTM, được bổ sung bởi OpenTelemetry Collectors, tạo thành một nền tảng quan sát gắn kết.
Các thành phần chính:
- OpenTelemetry Collector: Agent phổ quát để thu thập, xử lý và xuất dữ liệu đo từ xa. Nó hoạt động như hệ thần kinh trung ương, chuẩn hóa dữ liệu trước khi chuyển tiếp đến các thành phần LGTM tương ứng.
- Mimir (Số liệu): Lưu trữ dài hạn, có khả năng mở rộng theo chiều ngang cho các số liệu Prometheus. Nó cung cấp tính sẵn sàng cao, đa người thuê và truy vấn hiệu quả.
- Loki (Nhật ký): Hệ thống tổng hợp nhật ký được thiết kế để tiết kiệm chi phí và khả năng mở rộng. Nó lập chỉ mục siêu dữ liệu (nhãn) thay vì toàn bộ nội dung nhật ký, giúp truy vấn hiệu quả.
- Tempo (Dấu vết): Backend theo dõi phân tán khối lượng lớn, chi phí thấp. Nó lưu trữ các dấu vết và cho phép truy xuất hiệu quả dựa trên ID dấu vết.
- Grafana: Lớp trực quan hóa, cung cấp bảng điều khiển, cảnh báo và khám phá thống nhất trên tất cả các tín hiệu đo từ xa.
- Lưu trữ đối tượng (S3/GCS): Lưu trữ dài hạn chính cho Mimir, Loki và Tempo, tận dụng các kho lưu trữ đối tượng dựa trên đám mây để đảm bảo độ bền và hiệu quả chi phí.
Các đường ống OpenTelemetry Collector
OTel Collector rất quan trọng để chuẩn hóa dữ liệu đo từ xa. Chúng tôi sẽ cấu hình nó để nhận OTLP, xử lý và xuất sang Mimir, Loki và Tempo.
Cấu hình Collector (otel-collector-config.yaml)
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch:
send_batch_size: 1024
timeout: 10s
resource/add_service_name: # Ensure service.name is present for correlation
attributes:
- key: service.name
value: unknown_service
action: upsert
attributes/add_k8s_labels: # Example: Add Kubernetes labels as resource attributes
actions:
- key: k8s.pod.name
from_attribute: k8s.pod.name
action: upsert
- key: k8s.namespace.name
from_attribute: k8s.namespace.name
action: upsert
transform/logs: # Transform OTel logs to Loki-compatible format
log_statements:
- context: log
statements:
- set(attributes["loki.tenant.id"], "default") # Multi-tenancy example
- set(attributes["loki.resource.labels"], Concat([resource.attributes["service.name"], resource.attributes["k8s.namespace.name"]], ","))
# Ensure trace_id and span_id are available as attributes for Loki
- set(attributes["trace_id"], SpanIDToHex(trace_id))
- set(attributes["span_id"], SpanIDToHex(span_id))
transform/metrics: # Example: Add tenant ID to metrics
metric_statements:
- context: metric
statements:
- set(attributes["tenant_id"], "default")
resource/add_tenant_id: # Add tenant ID to resource attributes for traces
attributes:
- key: tenant_id
value: default
action: upsert
exporters:
otlp/mimir:
endpoint: mimir-gateway.observability.svc.cluster.local:9090 # Mimir OTLP endpoint
tls:
insecure: true # Use proper TLS in production
otlp/loki:
endpoint: loki-gateway.observability.svc.cluster.local:3100 # Loki OTLP endpoint
tls:
insecure: true
otlp/tempo:
endpoint: tempo-gateway.observability.svc.cluster.local:4317 # Tempo OTLP endpoint
tls:
insecure: true
logging: # For debugging
verbosity: detailed
service:
pipelines:
metrics:
receivers: [otlp]
processors: [resource/add_service_name, attributes/add_k8s_labels, transform/metrics, batch]
exporters: [otlp/mimir, logging]
logs:
receivers: [otlp]
processors: [resource/add_service_name, attributes/add_k8s_labels, transform/logs, batch]
exporters: [otlp/loki, logging]
traces:
receivers: [otlp]
processors: [resource/add_service_name, resource/add_tenant_id, batch]
exporters: [otlp/tempo, logging]
Giải thích:
receivers.otlp: Cấu hình collector để chấp nhận dữ liệu OTLP qua gRPC và HTTP.processors.batch: Tập hợp dữ liệu đo từ xa để xuất hiệu quả.processors.resource/add_service_name: Đảm bảoservice.nameluôn hiện diện, rất quan trọng cho tương quan.processors.attributes/add_k8s_labels: Minh họa việc làm phong phú dữ liệu đo từ xa bằng siêu dữ liệu Kubernetes, hữu ích cho việc lọc và nhóm.processors.transform/logs: Điều này rất quan trọng đối với Loki. Nó thêmloki.tenant.idvàloki.resource.labels(mà Loki sử dụng làm nhãn được lập chỉ mục) và trích xuấttrace_idvàspan_idvào các thuộc tính nhật ký, cho phép tương quan dấu vết-sang-nhật ký.exporters.otlp/mimir,otlp/loki,otlp/tempo: Xuất OTLP trực tiếp đến các thành phần LGTM tương ứng. Đảm bảo các điểm cuối này có thể phân giải được trong cụm của bạn.service.pipelines: Xác định cách dữ liệu chảy qua các bộ nhận, bộ xử lý và bộ xuất cho từng loại tín hiệu (số liệu, nhật ký, dấu vết).
Tương quan tín hiệu chéo: Dấu vết-sang-Nhật ký & Nhật ký-sang-Dấu vết
Khả năng quan sát thống nhất phụ thuộc vào khả năng điều hướng giữa các tín hiệu. Grafana tạo điều kiện này với các cấu hình liên kết dữ liệu.
1. Truy sâu từ Dấu vết đến Nhật ký
Khi xem một dấu vết trong Tempo, bạn muốn chuyển đến các nhật ký liên quan đến một span cụ thể. Điều này yêu cầu trace_id và span_id phải có mặt dưới dạng thuộc tính trong nhật ký của bạn. Bộ xử lý transform/logs trong OTel Collector xử lý điều này.
Cấu hình liên kết dữ liệu Grafana (Nguồn dữ liệu Tempo):
{
"name": "Logs for Span",
"url": "/explore?orgId=1&left=[\"now-1h\",\"now\",\"Loki\",{\"expr\":\"{service_name=\\\"$${service.name}\\\", trace_id=\\\"$${traceID}\\\", span_id=\\\"$${spanID}\\\"}\",\"refId\":\"A\",\"queryType\":\"range\"},{\"ui\":true}]",
"targetBlank": true
}
Giải thích:
$${service.name}: Suy ra từ span dấu vết.$${traceID}: ID dấu vết của span hiện tại.$${spanID}: ID span của span hiện tại.exprsử dụng LogQL của Loki để lọc nhật ký theoservice_name,trace_idvàspan_id. Các nhãn này phải có mặt trong nhật ký Loki của bạn (như được cấu hình bởi OTel Collector).
2. Truy sâu từ Nhật ký đến Dấu vết
Từ một dòng nhật ký trong Loki, bạn muốn chuyển đến dấu vết tương ứng trong Tempo. Điều này yêu cầu trace_id phải có mặt dưới dạng thuộc tính trong nhật ký.
Cấu hình liên kết dữ liệu Grafana (Nguồn dữ liệu Loki):
{
"name": "Trace for Log",
"url": "/explore?orgId=1&left=[\"now-1h\",\"now\",\"Tempo\",{\"query\":\"$${__line.trace_id}\",\"queryType\":\"search\",\"refId\":\"A\"},{\"ui\":true}]",
"targetBlank": true
}
Giải thích:
$${__line.trace_id}: Biến Grafana đặc biệt này trích xuất thuộc tínhtrace_idtừ dòng nhật ký hiện tại. Điều này giả định OTel Collector của bạn đã thêmtrace_idlàm thuộc tính vào nhật ký.
Bảng điều khiển SLI/SLO tín hiệu cao
Tận dụng Mimir, chúng ta có thể xây dựng các bảng điều khiển SLI/SLO mạnh mẽ. Ví dụ này tập trung vào SLI độ trễ yêu cầu.
1. Định nghĩa SLI/SLO
- SLI: Tỷ lệ phần trăm yêu cầu được phục vụ với độ trễ p99 dưới 500ms.
- SLO: 99,9% yêu cầu phải đáp ứng SLI trong cửa sổ 7 ngày.
2. Quy tắc ghi Mimir cho SLI
Xác định các quy tắc ghi trong Mimir để tổng hợp trước dữ liệu SLI. Điều này làm giảm tải truy vấn trên các số liệu thô và cải thiện hiệu suất bảng điều khiển.
# rules.yaml for Mimir
groups:
- name: service-sli
rules:
- record: service_latency_sli_total
expr: |
sum by (service_name, http_route) (
rate(http_server_request_duration_bucket{le="0.5"}[5m])
)
- record: service_latency_slo_error_budget
expr: |
1 - (
sum by (service_name, http_route) (
rate(http_server_request_duration_bucket{le="0.5"}[5m])
)
/
sum by (service_name, http_route) (
rate(http_server_request_duration_count[5m])
)
)
Giải thích:
http_server_request_duration_bucket: Giả định ứng dụng của bạn xuất biểu đồ thời lượng yêu cầu máy chủ HTTP OpenTelemetry.service_latency_sli_total: Đếm các yêu cầu trong mục tiêu độ trễ.service_latency_slo_error_budget: Tính toán ngân sách lỗi đã tiêu thụ.
3. Các bảng điều khiển Grafana
- Trạng thái SLI hiện tại:
promql
( sum by (service_name, http_route) (service_latency_sli_total) / sum by (service_name, http_route) (rate(http_server_request_duration_count[5m])) ) * 100 - Tỷ lệ tiêu hao ngân sách lỗi (cửa sổ 7 ngày):
promql
sum by (service_name, http_route) ( increase(service_latency_slo_error_budget[7d]) ) - Ngân sách lỗi còn lại:
promql
100 - ( sum by (service_name, http_route) ( increase(service_latency_slo_error_budget[7d]) ) )
Tối ưu hóa chi phí lưu trữ đối tượng (S3/GCS)
Mimir, Loki và Tempo phụ thuộc nhiều vào lưu trữ đối tượng. Tối ưu hóa điều này là rất quan trọng để kiểm soát chi phí.
1. Chính sách lưu giữ dữ liệu
Cấu hình thời gian lưu giữ dựa trên mức độ quan trọng của dữ liệu và tuân thủ.
- Mimir: Ngắn hạn (ví dụ: 30 ngày) cho các số liệu độ phân giải cao, dài hơn cho các số liệu tổng hợp. Bộ ingester và compactor của Mimir quản lý điều này.
- Loki: Thường dài hơn (ví dụ: 90-180 ngày) vì nhật ký thường cần thiết cho pháp y.
table_managervàcompactorcủa Loki xử lý việc lưu giữ. - Tempo: Thường ngắn hơn (ví dụ: 7-30 ngày) do khối lượng lớn, trừ khi tuân thủ cụ thể yêu cầu dài hơn.
compactorcủa Tempo quản lý việc lưu giữ.
Ví dụ cấu hình lưu giữ Loki (loki.yaml):
table_manager:
retention_period: 90d # 90 days for index and chunks
compactor:
retention_enabled: true
retention_period: 90d
2. Các tầng lưu trữ & Chính sách vòng đời
Tận dụng các chính sách vòng đời của nhà cung cấp đám mây để chuyển dữ liệu cũ hơn sang các tầng lưu trữ rẻ hơn (ví dụ: S3 Standard-IA, Glacier, GCS Nearline, Coldline).
Ví dụ chính sách vòng đời S3 (Terraform aws_s3_bucket_lifecycle_configuration):
resource "aws_s3_bucket_lifecycle_configuration" "loki_bucket_lifecycle" {
bucket = aws_s3_bucket.loki_chunks.id
rule {
id = "loki-retention"
status = "Enabled"
transition {
days = 30
storage_class = "STANDARD_IA" # Infrequent Access
}
transition {
days = 60
storage_class = "GLACIER" # Archival
}
expiration {
days = 90 # Final deletion
}
}
}
3. Nén dữ liệu
Tất cả các thành phần LGTM đều sử dụng nén (Snappy, LZ4, Zstd) cho dữ liệu được lưu trữ trong bộ lưu trữ đối tượng. Đảm bảo cấu hình của bạn đang tận dụng các codec hiệu quả. Điều này thường là mặc định, nhưng đáng để xác minh.
4. Chiến lược phân mảnh & lập chỉ mục
- Loki: Tối ưu hóa
max_chunk_agevàchunk_target_sizeđể cân bằng hiệu suất ghi và hiệu quả truy vấn.max_chunk_agenhỏ hơn có nghĩa là xả thường xuyên hơn, có thể nhiều đối tượng nhỏ hơn, nhưng cập nhật chỉ mục nhanh hơn. - Tempo: Cân nhắc các chiến lược phân mảnh ID dấu vết nếu bạn có tốc độ nhập rất cao để phân phối tải trên các bộ ingester.
Các vấn đề sản xuất & Khắc phục sự cố
1. Áp lực ngược OTel Collector / OOMKilled
Triệu chứng: Các pod OTel Collector bị OOMKilled hoặc hiển thị mức sử dụng CPU/bộ nhớ cao, làm mất dữ liệu đo từ xa.
Nguyên nhân: Tốc độ nhập vượt quá khả năng xử lý/xuất, hoặc cài đặt bộ xử lý batch quá mạnh đối với bộ nhớ khả dụng.
Khắc phục:
- Tăng tài nguyên: Cấp phát thêm CPU/bộ nhớ cho các pod OTel Collector.
- Điều chỉnh bộ xử lý
batch: Tăngsend_batch_sizevàtimeoutđể cho phép các lô lớn hơn, nhưng theo dõi bộ nhớ. - Thêm bộ xử lý
memory_limiter: Cấu hình giới hạn mềm và cứng để ngăn OOM và loại bỏ dữ liệu một cách duyên dáng dưới áp lực cực lớn.yamlprocessors: memory_limiter: limit_mib: 256 spike_limit_mib: 64 check_interval: 1s - Mở rộng quy mô: Triển khai thêm các phiên bản OTel Collector phía sau bộ cân bằng tải.
2. Giảm hiệu suất truy vấn Loki
Triệu chứng: Các truy vấn LogQL chậm, đặc biệt là những truy vấn liên quan đến regex hoặc phạm vi thời gian lớn. Nguyên nhân:
- Quá nhiều kết hợp nhãn duy nhất (tính đa dạng cao).
- Các truy vấn quét quá nhiều luồng nhật ký.
- Các biểu thức LogQL không hiệu quả.
- Không đủ tài nguyên Loki query-frontend/querier. Khắc phục:
- Xem xét nhãn: Giảm thiểu số lượng nhãn duy nhất. Tránh thêm các thuộc tính có tính đa dạng cao (ví dụ: ID yêu cầu, đường dẫn URL đầy đủ) làm nhãn Loki. Thay vào đó, hãy sử dụng chúng làm thuộc tính nội dung nhật ký.
- Tối ưu hóa LogQL: Bắt đầu truy vấn bằng các nhãn có tính chọn lọc cao. Sử dụng
line_formatđể trích xuất dữ liệu từ nội dung nhật ký thay vì lập chỉ mục. - Tăng tài nguyên Loki: Mở rộng quy mô các thành phần
query-frontendvàquerier. - Tối ưu hóa chỉ mục: Đảm bảo
max_chunk_agevàchunk_target_sizeđược cân bằng.
3. Lỗi nhập dấu vết Tempo
Triệu chứng: Dấu vết bị thiếu hoặc không đầy đủ trong Tempo. Nguyên nhân:
- OTel Collector không chuyển tiếp dấu vết chính xác.
- Các bộ ingester của Tempo bị quá tải hoặc không khỏe mạnh.
- Công cụ dịch vụ không chính xác (ví dụ: thiếu truyền ngữ cảnh dấu vết). Khắc phục:
- Kiểm tra nhật ký OTel Collector: Tìm lỗi xuất sang Tempo.
- Giám sát các bộ ingester của Tempo: Kiểm tra tình trạng và mức sử dụng tài nguyên của chúng. Mở rộng quy mô nếu cần.
- Xác minh công cụ: Đảm bảo tất cả các dịch vụ đang truyền ngữ cảnh dấu vết chính xác (ví dụ: tiêu đề W3C Trace Context). Sử dụng
curlvới tiêu đềtraceparentđể kiểm tra. - Tăng
max_block_bytescủa bộ ingester Tempo: Nếu dấu vết rất lớn, đây có thể là một nút thắt cổ chai.
4. Các vấn đề về tính đa dạng cao của Mimir
Triệu chứng: Các bộ ingester/compactor của Mimir đang gặp khó khăn, chi phí lưu trữ cao, truy vấn số liệu chậm. Nguyên nhân: Quá nhiều kết hợp nhãn duy nhất trên các số liệu. Khắc phục:
- Ghi nhãn lại số liệu: Sử dụng bộ xử lý
metricstransformcủa OTel Collector hoặcrelabel_configscủa Prometheus để loại bỏ hoặc đổi tên các nhãn có tính đa dạng cao trước khi nhập.yamlprocessors: metricstransform: transforms: - include: "http_request_duration_seconds_bucket" action: "delete_label" label: "request_id" # Example: delete high-cardinality label - Xem xét công cụ: Giáo dục các nhà phát triển về việc tránh các nhãn có tính đa dạng cao.
- Giới hạn Mimir: Cấu hình
max_series_per_uservàmax_label_names_per_seriescủa Mimir để ngăn chặn lạm dụng.
Các câu hỏi thường gặp
Q1: Làm cách nào để xử lý đa người thuê với bộ công cụ LGTM?
A1: Mimir, Loki và Tempo được thiết kế cho đa người thuê.
- Mimir: Sử dụng tiêu đề HTTP
X-Scope-OrgID(hoặc thuộc tính OTLPtenant_id) để phân tách người thuê. Mỗi người thuê có dữ liệu riêng biệt. - Loki: Tương tự như Mimir, sử dụng
X-Scope-OrgIDhoặc thuộc tính nhật ký OTLPloki.tenant.id. - Tempo: Sử dụng
X-Scope-OrgIDhoặc thuộc tính tài nguyên OTLPtenant_id. - Grafana: Cấu hình các nguồn dữ liệu riêng biệt cho mỗi người thuê, hoặc sử dụng các biến mẫu cho
X-Scope-OrgIDtrong bảng điều khiển.
Q2: Cách khuyến nghị để triển khai bộ công cụ LGTM trong Kubernetes là gì?
A2: Helm charts là tiêu chuẩn. Grafana Labs cung cấp các biểu đồ chính thức cho Loki, Mimir và Tempo. Đối với OpenTelemetry Collector, sử dụng biểu đồ Helm opentelemetry-collector. Đảm bảo bạn cấu hình lưu trữ bền vững cho các thành phần có trạng thái (ví dụ: Mimir ingesters, Loki ingesters) và sử dụng backend lưu trữ đối tượng mạnh mẽ.
Q3: Làm cách nào để tự giám sát bộ công cụ LGTM?
A3: Các thành phần LGTM hiển thị các số liệu Prometheus. Triển khai một phiên bản Prometheus chuyên dụng (hoặc sử dụng chính Mimir) để thu thập các số liệu này. Tạo các bảng điều khiển Grafana để giám sát tình trạng, mức sử dụng tài nguyên, tốc độ nhập, độ trễ truy vấn và tỷ lệ lỗi của chúng. Các số liệu chính bao gồm loki_ingester_received_entries_total, mimir_ingester_received_samples_total, tempo_ingester_traces_received_total và các số liệu _grpc_server_handled_total khác nhau.
Q4: Tôi có thể sử dụng các số liệu Prometheus hiện có với Mimir không?
A4: Có. Mimir là một kho lưu trữ dài hạn cho Prometheus. Bạn có thể cấu hình các máy chủ Prometheus hiện có của mình để ghi từ xa vào Mimir, hoặc sử dụng bộ nhận Prometheus của OpenTelemetry Collector và bộ xuất OTLP sang Mimir. Cách thứ hai thường được ưu tiên hơn cho một phương pháp OTel thống nhất.
Q5: Những đánh đổi giữa lập chỉ mục dựa trên nhãn của Loki và lập chỉ mục nhật ký toàn văn truyền thống là gì?
A5:
| Tính năng | Loki (Dựa trên nhãn) | Truyền thống (Toàn văn) |
|---|---|---|
| Chi phí | Lưu trữ thấp hơn, ít tính toán hơn cho lập chỉ mục | Lưu trữ cao hơn, nhiều tính toán hơn cho lập chỉ mục |
| Tốc độ truy vấn | Nhanh cho các truy vấn được lọc theo nhãn, chậm hơn cho văn bản | Nhanh cho tìm kiếm toàn văn, chậm hơn cho các bộ lọc phức tạp |
| Khả năng mở rộng | Có khả năng mở rộng cao theo chiều ngang | Có khả năng mở rộng, nhưng quản lý chỉ mục có thể phức tạp |
| Tính linh hoạt | Yêu cầu các nhãn được xác định trước để tìm kiếm hiệu quả | Có thể tìm kiếm trên bất kỳ nội dung nhật ký nào |
| Trường hợp sử dụng | Gỡ lỗi hoạt động, các mẫu đã biết | Phân tích bảo mật, khám phá ngẫu hứng |
Loki vượt trội khi bạn biết mình đang tìm kiếm gì (ví dụ: nhật ký từ service=X trong namespace=Y). Đối với các tìm kiếm toàn văn tùy ý trên các nhật ký không có cấu trúc, rộng lớn, các hệ thống truyền thống có thể hoạt động tốt hơn nhưng với chi phí cao hơn đáng kể.
Free In-Browser Developer Tools
Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.
Related Articles

OpenTelemetry Collector & eBPF trong Production: Tracing, Metrics & Prometheus Pipelines Không Cần Code
Hướng dẫn toàn diện về OpenTelemetry Collector & eBPF trong môi trường production: tracing, metrics & prometheus pipelines không cần code với kiến trúc cấp độ production và các ví dụ code.
Read more
Quan sát hiệu suất cao với eBPF trong Kubernetes: Vượt qua gánh nặng Sidecar
Tìm hiểu sâu về khả năng quan sát eBPF trong Kubernetes: loại bỏ Envoy sidecar, thăm dò kernel, BPF ring buffer và đo lường từ xa không cần mã.
Read more
Triển khai bảo mật Zero-Trust trong Kubernetes: Hướng dẫn sản xuất hoàn chỉnh
Hướng dẫn thực tế để loại bỏ bảo mật chu vi mạng phẳng trong Kubernetes: NetworkPolicies mặc định từ chối, định danh workload SPIFFE/SPIRE và mTLS nghiêm ngặt.
Read more