Logging System cho k8s với Loki
Giới thiệu
Loki là hệ thống thu thập và tra cứu log tập trung được thiết kế từ Prometheus, có tính sẵn sàng cao (high-available) và có khả năng scale tốt. Loki được thiết kế để dễ dàng vận hành và hiệu quả về chi phí.
So sánh với các hệ thống log khác, Loki có một vài điểm khác:
- Không index fulltext log.
- Lưu trữ với thuật toán nén, không có cấu trúc log, chỉ index metadata.
- Loki dễ dàng vận hành và chỉ cần resource nhỏ là có thể chạy.
- Index và group các luồng log stream dựa vào các nhãn giống nhau.
- Đặc biệt rất phù hợp với lưu trữ log của các Pod chạy trong Kubernetes.
Kiến trúc
Hệ thống logging của Loki gồm 3 thành phần chính:
- Promtail là agent, có trách nhiệm đi thu gom log và gửi về cho Loki.
- Loki là thành phần chính, có trách nhiệm lưu trữ log và xử lý các câu query.
- Grafana là công cụ query và hiển thị log.
Loki có thể hiểu là kho lưu trữ dữ liệu được tối ưu hóa để lưu log một cách hiệu quả nhằm giúp công việc tra cứu nhanh hơn các hệ thống log thông thường.
Để có thể tăng tốc độ tra cứu loki không chỉ lưu lại các dòng log thông thường mà đánh chỉ mục các log dựa trên các metadata. Ví dụ như sau:
Labels:
__name__ = logs
app = graphite
cluster = us-central1
color = b
container_name = graphite
filename = /var/log/pods/metrictank_graphite-og
hosted_metrics = 1
instance = graphite-1-large-mdb68b5c-jh769
job = metrictank/graphite
namespace = metrictank
org = 1
plan = large
pod_template_hash = 5f9db68b5c
stream = stderr
Promtail
Loki cần các agent chạy độc lập để đẩy log về lưu trữ. Có thể tham khảo một vài các công cụ thu thập log khác như Fluent Bit, Fluentd, Logstash..
Đa phần các công cụ sẽ có một vài điểm chung trong kiến trúc. Trong bài giới thiệu này chúng tôi sẽ nói cụ thể hơn về promtail. 3 tính năng chính của promtail là:
- Tìm kiếm các targets (container, app, pod …)
- Gán nhãn và trích xuất metadata của các log stream.
- Đẩy các dòng log và nhãn về cho Loki thông qua HTTP API.
Trong các hệ thống sử dụng k8s Promtail sẽ được chạy dưới dạng các Daemonset trên mỗi node và gửi trả log về cho Loki dưới dạng HTTP API. Promtail có thể theo dõi log từ hai nguồn: local log files và systemd log.
Scraping log (tìm kiếm các log)
Promtail tìm vị trí của các tệp log và trích xuất nhãn từ chúng thông qua phần scrape_configs trong YAML cấu hình. Cú pháp giống hệt với những gì Prometheus sử dụng.
scrape_configs chứa một hoặc nhiều phần được thực thi cho từng mục tiêu (VD cho từng container trong mỗi pod chạy trên một node):
scrape_configs:
- job_name: local
static_configs:
- ...
- job_name: kubernetes
kubernetes_sd_config:
- ...
Nếu có nhiều hơn một scrape_configs với một mục tiêu (cùng một container), sẽ nhận được các dòng log trùng lặp vì log sẽ được gửi theo các luồng khác nhau có thể trả ra các label khác nhau.
Kubernetes Discovery
Promtail có thể sử dụng API Kubernetes để khám phá các pod làm mục tiêu, nhưng nó chỉ có thể đọc các tệp log từ các pod đang chạy trên cùng một node với Promtail đang chạy trên đó. Promtail tìm kiếm nhãn __host__ trên mỗi mục tiêu và xác thực rằng nó được đặt thành cùng tên máy chủ như của Promtail.
relabel_configs:
- source_labels: ['__meta_kubernetes_pod_node_name']
target_label: '__host__'
Kubernetes config
Cấu hình này cho phép lấy các metadata của kubernetes thông qua REST API và các dữ liệu này được đồng bộ với cluster
Một vài các config để lấy các metadata thường dùng như:
Đối với Node:
__meta_kubernetes_node_name: tên của node
__meta_kubernetes_node_address_<address_type>: The first address for each node address type, if it exists.
Service:
__meta_kubernetes_namespace: tên namespace của service
__meta_kubernetes_service_cluster_ip: cluster_ip của service
__meta_kubernetes_service_name: tên của service
pod
__meta_kubernetes_pod_name: tên của pod
__meta_kubernetes_pod_container_name: tên container
ingress:
__meta_kubernetes_ingress_name: tên của ingress
__meta_kubernetes_ingress_path
Loki
Lưu trữ
Grafana Loki cần lưu trữ hai loại dữ liệu khác nhau: chunk và index.
Loki nhận log trong các luồng riêng biệt, trong đó mỗi luồng được xác định duy nhất bằng ID đối tượng và bộ label của nó.
Khi các log từ một luồng đến, chúng được nén thành chunk và được lưu trong chunk store.
Index sẽ lưu trữ bộ label của từng luồng và liên kết chúng với các chunk riêng lẻ.
Table Manager
Hai tác dụng chính
- Thay đổi schema_config: mỗi table được giới hạn với một phiên bản và schema, do đó ta có thể thay đổi schema theo thời gian, và với mỗi khoảng thời gian khác nhau có thể dùng các version schema khác nhau
- Retention: tự động xóa các table khi chúng expire
Các backend được Loki hỗ trợ để lưu trữ index bao gồm
- Single Store (boltdb-shipper)
- Amazon DynamoDB
- Google Bigtable
- Apache Cassandra
- BoltDB (thường dùng local)
Các backend được Loki hỗ trợ để lưu trữ chunk bao gồm
- Amazon DynamoDB
- Google Bigtable
- Apache Cassandra
- Amazon S3
- Google Cloud Storage
- Filesystem (thường dùng local)
- Baidu Object Storage
Retention
Thao tác retention là thao tác xóa các table cũ, đã expire và không còn cần thiết, để giữ cho chunk store và index store của Loki không bị đầy lên theo thời gian, giảm hiệu quả store/query và tốn chi phí lưu trữ.
Thao tác này được quản lý bởi Table Manager (theo mặc định sẽ không được bật lên do tính chất xóa file cũ).
Có thể kích hoạt tính năng này trong configuration và đặt retention_period > 0:
table_manager:
retention_deletes_enabled: true
retention_period: 336h
Table Manager sẽ thực hiện xóa toàn bộ bảng có dữ liệu timestamp vượt quá retention_period tính tới thời điểm chạy. Thiết kế này cho phép thực hiện các thao tác xóa nhanh, kiểm soát bởi thời gian tồn tại của bảng.
Table Manager sẽ tính toán giữ cho các bảng cuối cùng tồn tại bằng công thức sau:
number_of_tables_to_keep = floor(retention_period / table_period) + 1
LogQL: Log query language
LogQL có thể hiểu đơn giản giống như PromQL sử dụng khi truy vấn với Prometheus
Có hai loại truy vấn LogQL: Log query và metric query
- Log query: trả về nội dung của các dòng nhật ký.
- Metric query: để tính toán giá trị dựa trên kết quả truy vấn.
Demo
Sử dụng docker-compose tạo một stack các service bao gồm: grafana, loki, promtail
Config loki
Tạo file loki-config.yml
auth_enabled: false
chunk_store_config:
max_look_back_period: 0s
compactor:
shared_store: filesystem
working_directory: /data/loki/boltdb-shipper-compactor
ingester:
chunk_block_size: 262144
chunk_idle_period: 3m
chunk_retain_period: 1m
lifecycler:
ring:
kvstore:
store: inmemory
replication_factor: 1
max_transfer_retries: 0
limits_config:
enforce_metric_name: false
reject_old_samples: false
reject_old_samples_max_age: 4320h
max_entries_limit_per_query: 5000
schema_config:
configs:
- from: "2020-10-24"
index:
period: 24h
prefix: index_
object_store: filesystem
schema: v11
store: boltdb-shipper
server:
http_listen_port: 3100
storage_config:
boltdb_shipper:
active_index_directory: /data/loki/boltdb-shipper-active
cache_location: /data/loki/boltdb-shipper-cache
cache_ttl: 24h
shared_store: filesystem
filesystem:
directory: /data/loki/chunks
table_manager:
retention_deletes_enabled: true
retention_period: 168h
Một vài cài đặt cần lưu ý
- ingester cấu hình để đẩy ra file chunk vd thời gian giữ file, dung lượng file tối đa
- schema_config khai báo sử dụng phương pháp lưu index dùng boltdb-shipper và lưu chunk dùng filesystem
- http_listen_port loki chạy HTTP API ở cổng 3100 lưu ý khi add vào Grafana sẽ cần endpoint này
- storage_config khai báo thư mục lưu index và chunk
- table_manager bật tính năng retention với thời gian lưu file chunk cũ
Config promtail
Tạo file promtail-config.yml
server:
http_listen_address: 0.0.0.0
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://localhost:3100/loki/api/v1/push
scrape_configs:
- job_name: kubernetes-pods-name
pipeline_stages:
- docker: {}
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_namespace]
action: drop
regex: auth|cert-manager
- source_labels:
- __meta_kubernetes_pod_label_name
target_label: __service__
- source_labels:
- __meta_kubernetes_pod_node_name
target_label: __host__
- action: replace
source_labels:
- __meta_kubernetes_namespace
target_label: namespace
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- action: replace
source_labels:
- __meta_kubernetes_pod_name
target_label: pod
- action: replace
source_labels:
- __meta_kubernetes_pod_container_name
target_label: container
- replacement: /var/log/pods/*$1/*.log
separator: /
source_labels:
- __meta_kubernetes_pod_uid
- __meta_kubernetes_pod_container_name
target_label: __path__
Mô tả một vài các stages trong pipeline được sử dụng
- parsing stage
- docker: Trích xuất dữ liệu bằng cách phân tích log bằng định dạng Docker tiêu chuẩn.
- action.replace: thay thế các label gốc bằng label tự tạo. Khai báo source_labels và target_label
- filtering stage
- action.drop: bỏ đi các log phù hợp với regex phía dưới (regex: auth|cert-manager)
Tạo các service
docker-compose up -d
version: "3.3"
networks:
monitor:
driver: bridge
services:
grafana:
restart: always
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- $PWD/grafana:/var/lib/grafana
networks:
- monitor
loki:
image: grafana/loki:2.1.0
container_name: loki
restart: unless-stopped
ports:
- 3100:3100
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./loki-config.yaml:/etc/loki/local-config.yaml
networks:
- monitor
promtail:
image: grafana/promtail:latest
container_name: promtail
restart: unless-stopped
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers
- ./promtail-config.yml:/etc/promtail/promtail-config.yml
command: -config.file=/etc/promtail/promtail-config.yml
networks:
- monitor
Thêm datasource loki vào grafana
Truy cập localhost:3000 mặc định admin/admin
Click Save & test => Explore
Tra cứu log với loki
VD Query log theo tên của namespace và tên pod, thường dùng khi muốn xem chi tiết log của 1 pod đang có lỗi
VD khi muốn lọc các kết quả có chứa chữ “endpointslice”, thường dùng khi muốn filter các entries của 1 log stream theo 1 keyword cụ thể (vd xem các log DEBUG, WARNING hoặc ERROR)
Metric queries:
VD khi muốn đếm các dòng log của từng pod, thường dùng nếu muốn tìm xem pod nào đang có khả năng gặp lỗi. Pod gặp lỗi thường sẽ bắn ra nhiều log hơn các pod khác.