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.

References

https://grafana.com/docs/loki/latest
https://grafana.com/docs/loki/latest/clients/promtail/
https://grafana.com/docs/loki/latest/operations/storage/
https://grafana.com/docs/loki/latest/logql/
https://grafana.com/docs/loki/latest/clients/promtail/scraping/#relabeling
https://grafana.com/blog/2018/12/12/loki-prometheus-inspired-open-source-logging-for-cloud-natives/
https://github.com/grafana/loki/tree/main/cmd/chunks-inspect
https://grafana.com/docs/loki/latest/clients/promtail/configuration/#kubernetes_sd_config

You may also like...

0 0 đánh giá
Đánh giá bài viết
Theo dõi
Thông báo của
guest
0 Góp ý
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất
Phản hồi nội tuyến
Xem tất cả bình luận
0
Rất thích suy nghĩ của bạn, hãy bình luận.x