天天看點

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

本篇給大家帶來一套輕量級的 kubernetes 日志收集方案的相關介紹。我自己也在生産環境中使用過這套方案,令我意想不到的時它占用的kubernetes的資源相比與ELK這套方案真的是小巫見大巫。那接下來就跟随這篇文章開始學習它吧……

為什麼要使用Loki

這篇文章着重介紹了grafana公司開發的loki日志收集應用。Loki是一個輕量級的日志收集、分析的應用,采用的是promtail的方式來擷取日志内容并送到loki裡面進行存儲,最終在grafana的datasource裡面添加資料源進行日志的展示、查詢。

loki的持久化存儲支援azure、gcs、s3、swift、local這5中類型,其中常用的是s3、local。另外,它還支援很多種日志搜集類型,像最常用的logstash、fluentbit也在官方支援的清單中。

那它有哪些優點呢?

  • 支援的用戶端,如Promtail,Fluentbit,Fluentd,Vector,Logstash和Grafana Agent
  • 首選代理Promtail,可以多來源提取日志,包括本地日志檔案,systemd,Windows事件日志,Docker日志記錄驅動程式等
  • 沒有日志格式要求,包括JSON,XML,CSV,logfmt,非結構化文本
  • 使用與查詢名額相同的文法查詢日志
  • 日志查詢時允許動态篩選和轉換日志行
  • 可以輕松地計算日志中的需要的名額
  • 引入時的最小索引意味着您可以在查詢時動态地對日志進行切片和切塊,以便在出現新問題時回答它們
  • 雲原生支援,使用Prometheus形式抓取資料

各日志收集元件簡單對比

Loki工作方式解惑日志解析格式

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

從上面的圖中我們可以看到,它在解析日志的時候是以index為主的,index包括時間戳和pod的部分label(其他label為filename、containers等),其餘的是日志内容。具體查詢效果如下:

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!
扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

{app="loki",namespace="kube-public"}為索引

日志搜集架構模式

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

在使用過程中,官方推薦使用promtail做為agent以DaemonSet方式部署在kubernetes的worker節點上搜集日志。另外也可以用上面提到的其他日志收集工具來收取,這篇文章在結尾處會附上其他工具的配置方式。

Loki部署模式都有哪些呢

Loki由許多元件微服務建構而成,微服務元件有5個。在這5個裡面添加緩存用來把資料放起來加快查詢。資料放在共享存儲裡面配置memberlist_config部分并在執行個體之間共享狀态,将Loki進行無限橫向擴充。

在配置完memberlist_config部分後采用輪詢的方式查找資料。為了使用友善官方把所有的微服務編譯成一個二進制,可以通過指令行參數-target控制,支援all、read、write,我們在部署時根據日志量的大小可以指定不同模式

all(讀寫模式)

服務啟動後,我們做的資料查詢、資料寫入都是來自這一個節點。請看下面的這個圖解:

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

read/write(讀寫分離模式)

在讀寫分離模式下運作時fronted-query查詢會将流量轉發到read節點上。讀節點上保留了querier、ruler、fronted,寫節點上保留了distributor、ingester

微服務模式運作

微服務模式運作下,通過不同的配置參數啟動為不同的角色,每一個程序都引用它的目标角色服務。

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

大顯身手之服務端部署

上面我們講了那麼多關于loki的介紹和它的工作模式,你也一定期待它是怎麼部署的吧?!該怎麼部署、部署在哪裡、部署後怎麼使用等等問題都會出現在你的腦海裡。

在部署之前你需要準備好一個k8s叢集才行哦。那好,接下來耐着性子往下看……

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

AllInOne部署模式

① k8s部署

我們從github上下載下傳的程式是沒有配置檔案的,需要提前将檔案準備一份。這裡提供了一份完整的allInOne配置檔案,部分内容進行了優化。

配置檔案内容如下所示

auth_enabled: false
target: all
ballast_bytes: 20480
server:
  grpc_listen_port: 9095
  http_listen_port: 3100
  graceful_shutdown_timeout: 20s
  grpc_listen_address: "0.0.0.0"
  grpc_listen_network: "tcp"
  grpc_server_max_concurrent_streams: 100
  grpc_server_max_recv_msg_size: 4194304
  grpc_server_max_send_msg_size: 4194304
  http_server_idle_timeout: 2m
  http_listen_address: "0.0.0.0"
  http_listen_network: "tcp"
  http_server_read_timeout: 30s
  http_server_write_timeout: 20s
  log_source_ips_enabled: true
  # http_path_prefix如果需要更改,在推送日志的時候字首都需要加指定的内容
  # http_path_prefix: "/"
  register_instrumentation: true
  log_format: json
  log_level: info
distributor:
  ring:
    heartbeat_timeout: 3s
    kvstore:
      prefix: collectors/
      store: memberlist
      # 需要提前建立好consul叢集
    #   consul:
    #     http_client_timeout: 20s
    #     consistent_reads: true
    #     host: 127.0.0.1:8500
    #     watch_burst_size: 2
    #     watch_rate_limit: 2
querier:
  engine:
    max_look_back_period: 20s 
    timeout: 3m0s 
  extra_query_delay: 100ms 
  max_concurrent: 10 
  multi_tenant_queries_enabled: true
  query_ingester_only: false
  query_ingesters_within: 3h0m0s
  query_store_only: false
  query_timeout: 5m0s
  tail_max_duration: 1h0s
query_scheduler:
  max_outstanding_requests_per_tenant: 2048
  grpc_client_config:
    max_recv_msg_size: 104857600
    max_send_msg_size: 16777216
    grpc_compression: gzip
    rate_limit: 0
    rate_limit_burst: 0
    backoff_on_ratelimits: false
    backoff_config:
      min_period: 50ms
      max_period: 15s
      max_retries: 5 
  use_scheduler_ring: true
  scheduler_ring:
    kvstore:
      store: memberlist
      prefix: "collectors/"
    heartbeat_period: 30s
    heartbeat_timeout: 1m0s
    # 預設第一個網卡的名稱
    # instance_interface_names
    # instance_addr: 127.0.0.1
    # 預設server.grpc-listen-port
    instance_port: 9095
frontend:
  max_outstanding_per_tenant: 4096
  querier_forget_delay: 1h0s
  compress_responses: true
  log_queries_longer_than: 2m0s
  max_body_size: 104857600
  query_stats_enabled: true
  scheduler_dns_lookup_period: 10s 
  scheduler_worker_concurrency: 15
query_range:
  align_queries_with_step: true
  cache_results: true
  parallelise_shardable_queries: true
  max_retries: 3
  results_cache:
    cache:
      enable_fifocache: false
      default_validity: 30s 
      background:
        writeback_buffer: 10000
      redis:
        endpoint: 127.0.0.1:6379
        timeout: 1s
        expiration: 0s 
        db: 9
        pool_size: 128 
        password: 1521Qyx6^
        tls_enabled: false
        tls_insecure_skip_verify: true
        idle_timeout: 10s 
        max_connection_age: 8h
ruler:
  enable_api: true
  enable_sharding: true
  alertmanager_refresh_interval: 1m
  disable_rule_group_label: false
  evaluation_interval: 1m0s
  flush_period: 3m0s
  for_grace_period: 20m0s
  for_outage_tolerance: 1h0s
  notification_queue_capacity: 10000
  notification_timeout: 4s
  poll_interval: 10m0s
  query_stats_enabled: true
  remote_write:
    config_refresh_period: 10s
    enabled: false
  resend_delay: 2m0s
  rule_path: /rulers
  search_pending_for: 5m0s
  storage:
    local:
      directory: /data/loki/rulers
    type: configdb
  sharding_strategy: default
  wal_cleaner:
    period:  240h
    min_age: 12h0m0s
  wal:
    dir: /data/loki/ruler_wal
    max_age: 4h0m0s
    min_age: 5m0s
    truncate_frequency: 1h0m0s
  ring:
    kvstore:
      store: memberlist
      prefix: "collectors/"
    heartbeat_period: 5s
    heartbeat_timeout: 1m0s
    # instance_addr: "127.0.0.1"
    # instance_id: "miyamoto.en0"
    # instance_interface_names: ["en0","lo0"]
    instance_port: 9500
    num_tokens: 100
ingester_client:
  pool_config:
    health_check_ingesters: false
    client_cleanup_period: 10s 
    remote_timeout: 3s
  remote_timeout: 5s 
ingester:
  autoforget_unhealthy: true
  chunk_encoding: gzip
  chunk_target_size: 1572864
  max_transfer_retries: 0
  sync_min_utilization: 3.5
  sync_period: 20s
  flush_check_period: 30s 
  flush_op_timeout: 10m0s
  chunk_retain_period: 1m30s
  chunk_block_size: 262144
  chunk_idle_period: 1h0s
  max_returned_stream_errors: 20
  concurrent_flushes: 3
  index_shards: 32
  max_chunk_age: 2h0m0s
  query_store_max_look_back_period: 3h30m30s
  wal:
    enabled: true
    dir: /data/loki/wal 
    flush_on_shutdown: true
    checkpoint_duration: 15m
    replay_memory_ceiling: 2GB
  lifecycler:
    ring:
      kvstore:
        store: memberlist
        prefix: "collectors/"
      heartbeat_timeout: 30s 
      replication_factor: 1
    num_tokens: 128
    heartbeat_period: 5s 
    join_after: 5s 
    observe_period: 1m0s
    # interface_names: ["en0","lo0"]
    final_sleep: 10s 
    min_ready_duration: 15s
storage_config:
  boltdb:
    directory: /data/loki/boltdb 
  boltdb_shipper:
    active_index_directory: /data/loki/active_index
    build_per_tenant_index: true
    cache_location: /data/loki/cache 
    cache_ttl: 48h
    resync_interval: 5m
    query_ready_num_days: 5
    index_gateway_client:
      grpc_client_config:
  filesystem:
    directory: /data/loki/chunks
chunk_store_config:
  chunk_cache_config:
    enable_fifocache: true
    default_validity: 30s
    background:
      writeback_buffer: 10000
    redis:
      endpoint: 192.168.3.56:6379
      timeout: 1s
      expiration: 0s 
      db: 8 
      pool_size: 128 
      password: 1521Qyx6^
      tls_enabled: false
      tls_insecure_skip_verify: true
      idle_timeout: 10s 
      max_connection_age: 8h
    fifocache:
      ttl: 1h
      validity: 30m0s
      max_size_items: 2000
      max_size_bytes: 500MB
  write_dedupe_cache_config:
    enable_fifocache: true
    default_validity: 30s 
    background:
      writeback_buffer: 10000
    redis:
      endpoint: 127.0.0.1:6379
      timeout: 1s
      expiration: 0s 
      db: 7
      pool_size: 128 
      password: 1521Qyx6^
      tls_enabled: false
      tls_insecure_skip_verify: true
      idle_timeout: 10s 
      max_connection_age: 8h
    fifocache:
      ttl: 1h
      validity: 30m0s
      max_size_items: 2000
      max_size_bytes: 500MB
  cache_lookups_older_than: 10s 
# 壓縮碎片索引
compactor:
  shared_store: filesystem
  shared_store_key_prefix: index/
  working_directory: /data/loki/compactor
  compaction_interval: 10m0s
  retention_enabled: true
  retention_delete_delay: 2h0m0s
  retention_delete_worker_count: 150
  delete_request_cancel_period: 24h0m0s
  max_compaction_parallelism: 2
  # compactor_ring:
frontend_worker:
  match_max_concurrent: true
  parallelism: 10
  dns_lookup_duration: 5s 
# runtime_config 這裡沒有配置任何資訊
# runtime_config:
common:
  storage:
    filesystem:
      chunks_directory: /data/loki/chunks
      fules_directory: /data/loki/rulers
  replication_factor: 3
  persist_tokens: false
  # instance_interface_names: ["en0","eth0","ens33"]
analytics:
  reporting_enabled: false
limits_config:
  ingestion_rate_strategy: global
  ingestion_rate_mb: 100
  ingestion_burst_size_mb: 18
  max_label_name_length: 2096
  max_label_value_length: 2048
  max_label_names_per_series: 60
  enforce_metric_name: true
  max_entries_limit_per_query: 5000
  reject_old_samples: true
  reject_old_samples_max_age: 168h
  creation_grace_period: 20m0s
  max_global_streams_per_user: 5000
  unordered_writes: true
  max_chunks_per_query: 200000
  max_query_length: 721h
  max_query_parallelism: 64 
  max_query_series: 700
  cardinality_limit: 100000
  max_streams_matchers_per_query: 1000 
  max_concurrent_tail_requests: 10 
  ruler_evaluation_delay_duration: 3s 
  ruler_max_rules_per_rule_group: 0
  ruler_max_rule_groups_per_tenant: 0
  retention_period: 700h
  per_tenant_override_period: 20s 
  max_cache_freshness_per_query: 2m0s
  max_queriers_per_tenant: 0
  per_stream_rate_limit: 6MB
  per_stream_rate_limit_burst: 50MB
  max_query_lookback: 0
  ruler_remote_write_disabled: false
  min_sharding_lookback: 0s
  split_queries_by_interval: 10m0s
  max_line_size: 30mb
  max_line_size_truncate: false
  max_streams_per_user: 0

# memberlist_conig子產品配置gossip用于在分發伺服器、攝取器和查詢器之間發現和連接配接。
# 所有三個元件的配置都是唯一的,以確定單個共享環。
# 至少定義了1個join_members配置後,将自動為分發伺服器、攝取器和ring 配置memberlist類型的kvstore
memberlist:
  randomize_node_name: true
  stream_timeout: 5s 
  retransmit_factor: 4
  join_members:
  - 'loki-memberlist'
  abort_if_cluster_join_fails: true
  advertise_addr: 0.0.0.0
  advertise_port: 7946
  bind_addr: ["0.0.0.0"]
  bind_port: 7946
  compression_enabled: true
  dead_node_reclaim_time: 30s
  gossip_interval: 100ms
  gossip_nodes: 3
  gossip_to_dead_nodes_time: 3
  # join:
  leave_timeout: 15s
  left_ingesters_timeout: 3m0s 
  max_join_backoff: 1m0s
  max_join_retries: 5
  message_history_buffer_bytes: 4096
  min_join_backoff: 2s
  # node_name: miyamoto
  packet_dial_timeout: 5s
  packet_write_timeout: 5s 
  pull_push_interval: 100ms
  rejoin_interval: 10s
  tls_enabled: false
  tls_insecure_skip_verify: true
schema_config:
  configs:
  - from: "2020-10-24"
    index:
      period: 24h
      prefix: index_
    object_store: filesystem
    schema: v11
    store: boltdb-shipper
    chunks:
      period: 168h
    row_shards: 32
table_manager:
  retention_deletes_enabled: false
  retention_period: 0s
  throughput_updates_disabled: false
  poll_interval: 3m0s
  creation_grace_period: 20m
  index_tables_provisioning:
    provisioned_write_throughput: 1000
    provisioned_read_throughput: 500
    inactive_write_throughput: 4
    inactive_read_throughput: 300
    inactive_write_scale_lastn: 50 
    enable_inactive_throughput_on_demand_mode: true
    enable_ondemand_throughput_mode: true
    inactive_read_scale_lastn: 10 
    write_scale:
      enabled: true
      target: 80
      # role_arn:
      out_cooldown: 1800
      min_capacity: 3000
      max_capacity: 6000
      in_cooldown: 1800
    inactive_write_scale:
      enabled: true
      target: 80
      out_cooldown: 1800
      min_capacity: 3000
      max_capacity: 6000
      in_cooldown: 1800
    read_scale:
      enabled: true
      target: 80
      out_cooldown: 1800
      min_capacity: 3000
      max_capacity: 6000
      in_cooldown: 1800
    inactive_read_scale:
      enabled: true
      target: 80
      out_cooldown: 1800
      min_capacity: 3000
      max_capacity: 6000
      in_cooldown: 1800
  chunk_tables_provisioning:
    enable_inactive_throughput_on_demand_mode: true
    enable_ondemand_throughput_mode: true
    provisioned_write_throughput: 1000
    provisioned_read_throughput: 300
    inactive_write_throughput: 1
    inactive_write_scale_lastn: 50
    inactive_read_throughput: 300
    inactive_read_scale_lastn: 10
    write_scale:
      enabled: true
      target: 80
      out_cooldown: 1800
      min_capacity: 3000
      max_capacity: 6000
      in_cooldown: 1800
    inactive_write_scale:
      enabled: true
      target: 80
      out_cooldown: 1800
      min_capacity: 3000
      max_capacity: 6000
      in_cooldown: 1800
    read_scale:
      enabled: true
      target: 80
      out_cooldown: 1800
      min_capacity: 3000
      max_capacity: 6000
      in_cooldown: 1800
    inactive_read_scale:
      enabled: true
      target: 80
      out_cooldown: 1800
      min_capacity: 3000
      max_capacity: 6000
      in_cooldown: 1800
tracing:
  enabled: true
           

注意:

ingester.lifecycler.ring.replication_factor的值在單執行個體的情況下為1

ingester.lifecycler.min_ready_duration的值為15s,在啟動後預設會顯示15秒将狀态變為ready

memberlist.node_name的值可以不用設定,預設是目前主機的名稱

memberlist.join_members是一個清單,在有多個執行個體的情況下需要添加各個節點的主機名/IP位址。在k8s裡面可以設定成一個service綁定到StatefulSets。

query_range.results_cache.cache.enable_fifocache建議設定為false,我這裡設定成了true

instance_interface_names是一個清單,預設的為["en0","eth0"],可以根據需要設定對應的網卡名稱,一般不需要進行特殊設定。

建立configmap

說明:将上面的内容寫入到一個檔案——>loki-all.yaml,把它作為一個configmap寫入k8s叢集。可以使用如下指令建立:

kubectl create configmap --from-file ./loki-all.yaml loki-all
           

可以通過指令檢視到已經建立好的configmap,具體操作詳見下圖

建立持久化存儲

在k8s裡面我們的資料是需要進行持久化的。Loki收集起來的日志資訊對于業務來說是至關重要的,是以需要在容器重新開機的時候日志能夠保留下來。

那麼就需要用到pv、pvc,後端存儲可以使用nfs、glusterfs、hostPath、azureDisk、cephfs等20種支援類型,這裡因為沒有對應的環境就采用了hostPath方式。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: loki
  namespace: default
spec:
  hostPath:
    path: /glusterfs/loki
    type: DirectoryOrCreate
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: loki
  namespace: default
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
  volumeName: loki
           

建立應用

準備好k8s的StatefulSet部署檔案後就可以直接在叢集裡面建立應用了。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app: loki
  name: loki
  namespace: default
spec:
  podManagementPolicy: OrderedReady
  replicas: 1
  selector:
    matchLabels:
      app: loki
  template:
    metadata:
      annotations:
        prometheus.io/port: http-metrics
        prometheus.io/scrape: "true"
      labels:
        app: loki
    spec:
      containers:
      - args:
        - -config.file=/etc/loki/loki-all.yaml
        image: grafana/loki:2.5.0
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /ready
            port: http-metrics
            scheme: HTTP
          initialDelaySeconds: 45
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        name: loki
        ports:
        - containerPort: 3100
          name: http-metrics
          protocol: TCP
        - containerPort: 9095
          name: grpc
          protocol: TCP
        - containerPort: 7946
          name: memberlist-port
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /ready
            port: http-metrics
            scheme: HTTP
          initialDelaySeconds: 45
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources:
          requests:
            cpu: 500m
            memory: 500Mi
          limits:
            cpu: 500m
            memory: 500Mi
        securityContext:
          readOnlyRootFilesystem: true
        volumeMounts:
        - mountPath: /etc/loki
          name: config
        - mountPath: /data
          name: storage
      restartPolicy: Always
      securityContext:
        fsGroup: 10001
        runAsGroup: 10001
        runAsNonRoot: true
        runAsUser: 10001
      serviceAccount: loki
      serviceAccountName: loki
      volumes:
      - emptyDir: {}
        name: tmp
      - name: config
        configMap:
          name: loki
      - persistentVolumeClaim:
          claimName: loki
        name: storage
---
kind: Service
apiVersion: v1
metadata:
  name: loki-memberlist
  namespace: default
spec:
  ports:
    - name: loki-memberlist
      protocol: TCP
      port: 7946
      targetPort: 7946
  selector:
    kubepi.org/name: loki
---
kind: Service
apiVersion: v1
metadata:
  name: loki
  namespace: default
spec:
  ports:
    - name: loki
      protocol: TCP
      port: 3100
      targetPort: 3100
  selector:
    kubepi.org/name: loki
           

在上面的配置檔案中我添加了一些pod級别的安全政策,這些安全政策還有叢集級别的PodSecurityPolicy,防止因為漏洞的原因造成叢集的整個崩潰,關于叢集級别的psp,可以詳見官方文檔

驗證部署結果

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

當看到上面的Running狀态時可以通過API的方式看一下分發器是不是正常工作,當顯示Active時正常才會正常分發日志流到收集器(ingester)

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

② 裸機部署

将loki放到系統的/bin/目錄下,準備grafana-loki.service控制檔案重載系統服務清單

[Unit]
Description=Grafana Loki Log Ingester
Documentation=https://grafana.com/logs/
After=network-online.target

[Service]
ExecStart=/bin/loki --config.file /etc/loki/loki-all.yaml
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target
           

重載系統清單指令,可以直接系統自動管理服務:

systemctl daemon-reload
# 啟動服務
systemctl start grafana-loki
# 停止服務
systemctl stop grafana-loki
# 重載應用
systemctl reload grafana-loki
           

大顯身手之Promtail部署

部署用戶端收集日志時也需要建立一個配置檔案,按照上面建立服務端的步驟建立。不同的是需要把日志内容push到服務端

① k8s部署

建立配置檔案

server:
  log_level: info
  http_listen_port: 3101
clients:
  - url: http://loki:3100/loki/api/v1/push
positions:
  filename: /run/promtail/positions.yaml
scrape_configs:
  - job_name: kubernetes-pods
    pipeline_stages:
      - cri: {}
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels:
          - __meta_kubernetes_pod_controller_name
        regex: ([0-9a-z-.]+?)(-[0-9a-f]{8,10})?
        action: replace
        target_label: __tmp_controller_name
      - source_labels:
          - __meta_kubernetes_pod_label_app_kubernetes_io_name
          - __meta_kubernetes_pod_label_app
          - __tmp_controller_name
          - __meta_kubernetes_pod_name
        regex: ^;*([^;]+)(;.*)?$
        action: replace
        target_label: app
      - source_labels:
          - __meta_kubernetes_pod_label_app_kubernetes_io_instance
          - __meta_kubernetes_pod_label_release
        regex: ^;*([^;]+)(;.*)?$
        action: replace
        target_label: instance
      - source_labels:
          - __meta_kubernetes_pod_label_app_kubernetes_io_component
          - __meta_kubernetes_pod_label_component
        regex: ^;*([^;]+)(;.*)?$
        action: replace
        target_label: component
      - action: replace
        source_labels:
        - __meta_kubernetes_pod_node_name
        target_label: node_name
      - action: replace
        source_labels:
        - __meta_kubernetes_namespace
        target_label: namespace
      - action: replace
        replacement: $1
        separator: /
        source_labels:
        - namespace
        - app
        target_label: job
      - action: replace
        source_labels:
        - __meta_kubernetes_pod_name
        target_label: pod
      - action: replace
        source_labels:
        - __meta_kubernetes_pod_container_name
        target_label: container
      - action: replace
        replacement: /var/log/pods/*$1/*.log
        separator: /
        source_labels:
        - __meta_kubernetes_pod_uid
        - __meta_kubernetes_pod_container_name
        target_label: __path__
      - action: replace
        regex: true/(.*)
        replacement: /var/log/pods/*$1/*.log
        separator: /
        source_labels:
        - __meta_kubernetes_pod_annotationpresent_kubernetes_io_config_hash
        - __meta_kubernetes_pod_annotation_kubernetes_io_config_hash
        - __meta_kubernetes_pod_container_name
        target_label: __path__
           

用上面的内容建立一個configMap,方法同上

建立DaemonSet檔案

Promtail是一個無狀态應用不需要進行持久化存儲隻需要部署到叢集裡面就可以了,還是同樣的準備DaemonSets建立檔案。

kind: DaemonSet
apiVersion: apps/v1
metadata:
  name: promtail
  namespace: default
  labels:
    app.kubernetes.io/instance: promtail
    app.kubernetes.io/name: promtail
    app.kubernetes.io/version: 2.5.0
spec:
  selector:
    matchLabels:
      app.kubernetes.io/instance: promtail
      app.kubernetes.io/name: promtail
  template:
    metadata:
      labels:
        app.kubernetes.io/instance: promtail
        app.kubernetes.io/name: promtail
    spec:
      volumes:
        - name: config
          configMap:
            name: promtail
        - name: run
          hostPath:
            path: /run/promtail
        - name: containers
          hostPath:
            path: /var/lib/docker/containers
        - name: pods
          hostPath:
            path: /var/log/pods
      containers:
        - name: promtail
          image: docker.io/grafana/promtail:2.3.0
          args:
            - '-config.file=/etc/promtail/promtail.yaml'
          ports:
            - name: http-metrics
              containerPort: 3101
              protocol: TCP
          env:
            - name: HOSTNAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: spec.nodeName
          volumeMounts:
            - name: config
              mountPath: /etc/promtail
            - name: run
              mountPath: /run/promtail
            - name: containers
              readOnly: true
              mountPath: /var/lib/docker/containers
            - name: pods
              readOnly: true
              mountPath: /var/log/pods
          readinessProbe:
            httpGet:
              path: /ready
              port: http-metrics
              scheme: HTTP
            initialDelaySeconds: 10
            timeoutSeconds: 1
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 5
          imagePullPolicy: IfNotPresent
          securityContext:
            capabilities:
              drop:
                - ALL
            readOnlyRootFilesystem: false
            allowPrivilegeEscalation: false
      restartPolicy: Always
      serviceAccountName: promtail
      serviceAccount: promtail
      tolerations:
        - key: node-role.kubernetes.io/master
          operator: Exists
          effect: NoSchedule
        - key: node-role.kubernetes.io/control-plane
          operator: Exists
          effect: NoSchedule
           

建立promtail應用

kubectl apply -f promtail.yaml
           

使用上面這個指令建立後就可以看到服務已經建立好了。接下來就是在Grafana裡面添加DataSource檢視資料了。

② 裸機部署

如果是裸機部署的情況下,需要對上面的配置檔案做一下稍微的改動,更改clients得位址就可以,檔案存放到/etc/loki/下,例如改成:

clients:
  - url: http://ipaddress:port/loki/api/v1/push
           

添加系統開機啟動配置,service配置檔案存放位置/usr/lib/systemd/system/loki-promtail.service内容如下

[Unit]
Description=Grafana Loki Log Ingester
Documentation=https://grafana.com/logs/
After=network-online.target

[Service]
ExecStart=/bin/promtail --config.file /etc/loki/loki-promtail.yaml
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target
           

啟動方式同上面服務端部署内容

Loki in DataSource

添加資料源

具體步驟: Grafana->Setting->DataSources->AddDataSource->Loki

注意點:

http的URL位址,應用、服務部署在哪個namespace下,就需要指定它的FQDN位址,它的格式是ServiceName.namespace。如果預設在default下、建立的端口号是3100,就需要填寫為http://loki:3100,這裡為什麼不寫IP位址而寫成服務的名字,是因為在k8s叢集裡面有個dns伺服器會自動解析這個位址。

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

查找日志資訊

扔掉 ELK!來試試這套我司使用 7 年多的 kubernetes 日志解決方案!

其他用戶端配置

Logstash作為日志收集用戶端

安裝插件

在啟動Logstash後我們需要安裝一個插件,可以通過這個指令安裝loki的輸出插件,安裝完成之後可以在logstash的output中添加資訊。

bin/logstash-plugin install logstash-output-loki
           

添加配置進行測試

完整的logstash配置資訊,可以參考官網給出的内容LogstashConfigFile

output {
  loki {
    [url => "" | default = none | required=true]
    [tenant_id => string | default = nil | required=false]
    [message_field => string | default = "message" | required=false]
    [include_fields => array | default = [] | required=false]
    [batch_wait => number | default = 1(s) | required=false]
    [batch_size => number | default = 102400(bytes) | required=false]
    [min_delay => number | default = 1(s) | required=false]
    [max_delay => number | default = 300(s) | required=false]
    [retries => number | default = 10 | required=false]
    [username => string | default = nil | required=false]
    [password => secret | default = nil | required=false]
    [cert => path | default = nil | required=false]
    [key => path | default = nil| required=false]
    [ca_cert => path | default = nil | required=false]
    [insecure_skip_verify => boolean | default = false | required=false]
  }
}
           

或者采用logstash的http輸出子產品,配置如下:

output {
    http {
        format => "json"
        http_method => "post"
        content_type => "application/json"
        connect_timeout => 10
        url => "http://loki:3100/loki/api/v1/push"
        message => '"message":"%{message}"}'
    }
}
           

Helm安裝

如果你想簡便安裝的話,可以采用helm來安裝。helm将所有的安裝步驟都進行了封裝,簡化了安裝步驟。

對于想詳細了解k8s的人來說,helm不太适合。因為它封裝後自動執行,k8s管理者不知道各元件之間是如何依賴的,可能會造成誤區。

廢話不多說,下面開始helm安裝

添加repo源

helm repo add grafana https://grafana.github.io/helm-charts
           

更新源

helm repo update
           

部署

預設配置

helm upgrade --install loki grafana/loki-simple-scalable
           

自定義namespace

helm upgrade --install loki --namespace=loki grafana/loki-simple-scalable
           

自定義配置資訊

helm upgrade --install loki grafana/loki-simple-scalable --set "key1=val1,key2=val2,..."
           

8故障解決方案

1.502 BadGateWay

loki的位址填寫不正确

在k8s裡面,位址填寫錯誤造成了502。檢查一下loki的位址是否是以下内容:

http://LokiServiceName
http://LokiServiceName.namespace
http://LokiServiceName.namespace:ServicePort
           

grafana和loki在不同的節點上,檢查一下節點間網絡通信狀态、防火牆政策

2.Ingester not ready: instance xx:9095 in state JOINING

耐心等待一會,因為是allInOne模式程式啟動需要一定的時間。

3.too many unhealthy instances in the ring

将ingester.lifecycler.replication_factor改為1,是因為這個設定不正确造成的。這個在啟動的時候會設定為多個複制源,但目前隻部署了一個是以在檢視label的時候提示這個

4.Data source connected, but no labels received. Verify that Loki and Promtail is configured properly

  • promtail無法将收集到的日志發送給loki,許可檢查一下promtail的輸出是不是正常
  • promtail在loki還沒有準備就緒的時候把日志發送過來了,但loki沒有接收到。如果需要重新接收日志,需要删除positions.yaml檔案,具體路徑可以用find查找一下位置
  • promtail忽略了目标日志檔案或者配置檔案錯誤造成的無法正常啟動
  • promtail無法在指定的位置發現日志檔案

官方文檔:

  • https://kubernetes.io/docs/concepts/security/pod-security-policy/
感謝閱讀,希望對你有所幫助 :) 來源:juejin.cn/post/7150469420605767717

今天的分享就到這裡了,如有幫助,歡迎一鍵三連(點贊、評論、轉發)支援一下!

讀者專屬群:誠邀你加入技術交流群,一起卷!

如有錯誤或其它問題,捐迎小夥伴留言評論、指正。如有幫助,歡迎點贊+轉發分享。更多相關開源技術文章,請持續關注!資源分享(小編為你精心準備了2048G的各類學習資料。包括系統運維、資料庫、redis、MogoDB、電子書、Java基礎課程、Java實戰項目、架構師綜合教程、架構師實戰項目、大資料、Docker容器、ELK Stack、機器學習、BAT面試精講視訊等。)