Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Grafana Lokiで始めるログ管理

nutslove
February 22, 2023

Grafana Lokiで始めるログ管理

Grafana Lokiについての社内勉強会で使用した資料になります。
Grafana Lokiを使っている方や導入を検討されている方にお役に立てればと思います。
よろしくお願いいたします。

nutslove

February 22, 2023
Tweet

More Decks by nutslove

Other Decks in Technology

Transcript

  1. 2023/02 李 俊起
    で始めるログ管理

    View Slide

  2. 本日のゴール
    Who
    • 大量のログを(安価に)管理できるツールをお探しの方
    • Lokiを使っているシステム担当者
    • Lokiを運用している管理者
    What
    • Lokiの特性を理解したうえでLokiの導入について判断
    • Lokiの(正しい)使い方を知る
    • Lokiの仕組みを理解して安定的に運用(できる状態を目指す)

    View Slide

  3. 本日話す内容
    • Lokiとは
    • Lokiの特徴
    • Lokiアーキテクチャ/コンポーネント
    • 処理の流れ(Write/Read)
    • data lossを防ぐ仕組み
    • promtailについて
    • APIを使ったログ送信
    • Lokiでできること&使い方(LogQL)
    • Lokiアンチパターン
    • Loki/promtailのオブザーバビリティ
    • まとめ

    View Slide

  4. Lokiとは
    • Grafana LabsがPrometheusにインスパイアされて
    開発したOSSログ管理ツール
    • ログを受信して保存する/クエリーに対してデータを返す側
     ログ送信にはpromtailという送信用ツールが必要
    ※Fluentd、Fluent Bit、Logstashも利用可能
    ※APIにHTTP POSTメソッドで直接ログを送信することも可能
    • ログの可視化にはGrafanaというダッシュボードツールが必要
    ※ElasticSearch(Loki)とKibana(Grafana)のような関係
    • ログ検索にはLogQLという独自のクエリー言語を使用

    View Slide

  5. Lokiのデータフォーマット
    • Timestamp、Label、Logデータ 3つのフィールドで構成
    • TimestampとLabelがLog検索に使われるindexとなり、
    Logデータは圧縮されてchunkとなる
    この部分を
    Streamと呼ぶ
    How to manage privacy in log data with Grafana Loki
    Stream (+tenant) ごとに
    Chunkが生成される

    View Slide

  6. Lokiの特徴
    • Scalability
     各コンポーネントが独立していてWrite/Readごとにスケールアウトが可能
    • Multi tenant
     Tenant ID(X-Scope-OrgID)でデータを分割/識別することで、
    1つのLokiクラスターに複数テナントの収容を可能に
    • Low cost
     ログ(chunk)/indexともにObject Storage(e.g. AWS S3)に保存
     Metadata(Label、Timestamp)だけをindexing
    How to scale and secure your logs cost-effectively with Grafana Loki

    View Slide

  7. Lokiアーキテクチャ
    A guide to deploying Grafana Loki and Grafana Tempo without Kubernetes on AWS Fargate | Grafana Labs
    Stateless
    Stateful
    ログ送信 クエリー
    この部分がLoki

    View Slide

  8. Lokiのコンポーネント
    • Distributor
     最初にログを受信し、validation checkで問題なければ(複数の)Ingesterにデータを流す
    • Ingester
     メモリに一時的にデータを保存し、一定の条件を満たしたデータをObject Storageに流す
    メモリ上のログに対するクエリーにはログデータを返す
    • Query-frontend
     クエリーを分割しキューイングする。また、複数Querierからの結果をまとめてGrafanaに返す
    • Querier
     キューからクエリーをpullしログ検索を行う
    Ingesterに先にクエリーを投げ、Ingesterになかったらバックエンドにクエリーを投げる
    • Compactor (optional)
     複数ファイルに分散されているIndexファイルを1つのファイルにまとめ、重複排除を行う
    • Ruler (optional)
     Alert/Recording Rules関連コンポーネント • Components | Grafana Loki documentation
    • Architecture | Grafana Loki documentation

    View Slide

  9. 処理の流れ(Write Path)
    Client
    (e.g. promtail) Distributor
    Distributor
    Distributor
    Distributor
    Distributor
    Ingester
    • Validation check
     RateLimit check
     Label check
     Log Size check
     Timestamp check


    Request Validation & Rate-Limit Errors |
    Grafana Loki documentation
    • Consistent hashingにより
    データがhashedされ、
    どのIngester(複数)に送るかが決まる
    • Replication factor(Defaultは3つ)
    の数分Ingesterに送信される
    • floor(Replication factor / 2) + 1
    の数以上のIngesterへの書込みを保証
     書込みに成功したIngesterの数が
    上記未満の場合はエラーを返し、
    promtail側でリトライされる
    • ログはIngesterのメモリとDiskに保存される
    ※Diskへの書込みはdata lossを防ぐため(WAL)
    Object
    Storage
    • 以下の条件を満たしたChunkは
    IngesterのメモリからObject Storage
    にflushされる
     「chunk_idle_period」の間
    chunkにupdateがなかった時
     chunkサイズが「chunk_target_size」
    に達した時
     chunkが生成されてから
    「max_chunk_age」が経過した時
    • Disk上のログは定期的に削除される
    Configuration | Grafana Loki
    documentation

    View Slide

  10. 処理の流れ(Read Path)
    Grafana
    Distributor
    Distributor
    Query
    frontend
    Distributor
    Distributor
    Querier
    • クエリーを以下の単位で分割
     「split_queries_by_interval」
    で指定した間隔(defaultは30分)
    • 分割したクエリーをキューイング
    • 複数のQuerierから返ってきた
    データをまとめてGrafanaに返す
    Query Frontend | Grafana Loki
    documentation
    • キューからクエリーをpullし、クエリーを実行
     複数のQuerierでパラレルで実行される
    パラレルの上限は「max_query_parallelism」(defaultは32)で指定可能
    • 直近のログはObject Storageより先にIngesterにクエリーを投げて、
    IngesterになかったらObject Storageにクエリーを投げる
     現時点からマイナス何時間までIngesterに要求するかは
    「query_ingesters_within」(defaultは3h)で指定可能
    • 複数Ingesterからの以下3つが重複するデータを重複排除する
     nanosecond timestamp, label set, and log message
    Object
    Storage
    Distributor
    Distributor
    Ingester
    クエリー
    (LogQL)
    Components | Grafana Loki documentation

    View Slide

  11. data lossを防ぐ仕組み
    1. Replication factor
     ログを複数のIngesterに送り、同じデータを複数のIngesterで持つことで
    一部のIngesterが落ちてもデータが失われないようにする仕組み
     いくつのIngester障害まで許容できるかは「replication_factor」に
    指定した数により異なる
    例えば「replication_factor」 が5の場合は前ページのfloor(Replication
    factor / 2) + 1ルールにより3つのIngesterへの書込みが保証されるので、2
    つのIngesterが落ちてもデータは失われない
     Ingester(のPod)が複数のAZに分散されるように「podAffinity」や
    「topologySpreadConstraints」などを適切に設定する
    • The essential config settings you should use so you won’t drop logs in Loki | Grafana Labs
    • Components | Grafana Loki documentation

    View Slide

  12. data lossを防ぐ仕組み
    2. WAL(Write Ahead Log)
     v2.2から導入された機能
     Ingesterがcrashしてメモリ上のデータが消えても
    データを復元できるよう、データをメモリとディスク両方に書き込む
     crashしたIngesterがrestartされる時、Diskからデータを復元する
    データ復元が完了するまでIngesterはReady状態にならない
     Ingesterごとに専用のディスク(e.g. EBS)を払い出すこと
     ディスク容量が一杯になってWALへの書込みができなくても
    Lokiへの書込み自体は失敗しないのでWAL用ディスク空き容量を監視すること
    • The essential config settings you should use so you won’t drop logs in Loki | Grafana Labs
    • Write Ahead Log | Grafana Loki documentation
    • Write-Ahead Logs | Grafana Loki documentation

    View Slide

  13. promtailのログ送信
    • 1秒ごとにログを送信
     「clients.batchwait」で変更可能
    • positionファイル
     promtailがどこまでログを送ったかを記録するファイル
     promtailプロセス(再)起動時このファイルを参照してどこから送るかを決める
    • Retry
     デフォルトで8.5分に渡って10回リトライし、リトライがすべて失敗したらログをDrop
     「backoff_config.max_retries」を変えることでリトライ回数を変更可能
    ※retry時、すべてのretry失敗時のpromtailから出力されるログ
    Configuration | Grafana Loki documentation
    Jan 27 04:09:19 ip-10-111-1-195.oci.ad promtail[15505]: level=warn ts=2023-01-27T04:09:19.841944217Z caller=client.go:369 component=client
    host=lee-loki-switch-test-NLB-fc4597fd6a5ad433.elb.ap-northeast-1.amazonaws.com msg="error sending batch, will retry" status=-1 error="Post
    ¥"http://lee-loki-switch-test-NLB-fc4597fd6a5ad433.elb.ap-northeast-1.amazonaws.com/loki/api/v1/push¥": context deadline exceeded"
    Jan 27 04:09:19 ip-10-111-1-195.oci.ad promtail[15505]: level=error ts=2023-01-27T04:09:19.841997354Z caller=client.go:380 component=client
    host=lee-loki-switch-test-NLB-fc4597fd6a5ad433.elb.ap-northeast-1.amazonaws.com msg="final error sending batch" status=-1 error="Post
    ¥"http://lee-loki-switch-test-NLB-fc4597fd6a5ad433.elb.ap-northeast-1.amazonaws.com/loki/api/v1/push¥": context deadline exceeded"

    View Slide

  14. promtailのpipeline
    • Lokiに連携する前にログの編集/除外等を行う
    • 複数のstageから構成されている
     replaceステージ
     特定の文字列を置換
    ケース 設定例
    P/S番のユーザアカウントを****に
    変換(マスキング)する
    - replace:
    expression: "([SP][0-9]{6})"
    replace: "****"

    View Slide

  15. promtailのpipeline
     dropステージ
     特定の文字列を含むログをdrop
     multilineステージ
     複数行に渡って出力されるログを1つのログとして扱う
    ケース 設定例
    debugという文字列を含むログをDrop - drop:
    expression: ".*debug.*"
    ケース 設定例
    以下のログを[YYYY-MM-DD hh:mm:ss]から次の[YYYY-
    MM-DD hh:mm:ss]までを1つのログとして認識する
    [2020-12-03 13:59:59] ERROR in app: Exception on /error [PUT]
    Traceback (most recent call last):
    File "/home/pallets/.pyenv/versions/3.8.5/lib/python3.8/site-
    packages/flask/app.py", line 2447, in wsgi_app
    [2020-12-03 14:01:59] ERROR in app: Exception on /error [PUT]
    - multiline:
    firstline: '^¥[¥d{4}-¥d{2}-¥d{2} ¥d{1,2}:¥d{2}:¥d{2}¥]'

    View Slide

  16. promtailのpipeline
     matchステージ
     LogQLに合致するログのみdropまたはstageを適用
    ケース 設定例
    error_logというLabelを持つログのうち、
    errorまたはfatal文字列を含まないログをDrop
    - match:
    selector: ‘{job=“error_log”} !~ “(error|fatal)”’
    action: drop
    以下のようなjson形式のapi_logというLabelを持つ
    ログの一部のkey(app, level)をLoki(LogQL)でLabelとして使え
    るようにする
    {“app”: “ingester”, “level”: “warn”, “msg”: “something bad”}
    {“app”: “querier”, “level”: “info”, “msg”: “something good”}
    - match:
    selector: ‘{job=“api_log”}’
    stages:
    - json:
    expressions:
    app:
    level:

    View Slide

  17. promtailのpipeline
     metricsステージ
     特定の文字列を含む/LogQLに合致するログに対して
    Prometheus形式のメトリクスを生成し、promtailから開示する
     ここで生成されたメトリクス名は「promtail_custom_」から始まる
    ケース 設定例
    pod_name=loki-distributed-gatewayのLabelを持つ
    ログにstatus_code, method, uriのLabelを付与し、
    「promtail_custom_loki_gateway_total」という名前でメトリ
    クスを生成する
    → メトリクスにもstatus_code, method, uriのLabelが付
    与されるのでstatus_code, method, uriごとに
    メトリクスを集計することができる
    - match:
    selector: '{pod_name=~"multi-tenant-loki-distributed-gateway-.*"}'
    stages:
    - regex:
    expression: "^.* (?P[0-9]{3}) ¥"(?P[A-Z].*) (?P.*?) "
    - labels:
    status_code:
    method:
    uri:
    - metrics:
    loki_gateway_total:
    type: Counter
    config:
    match_all: true
    action: inc
    • Pipelines | Grafana Loki documentation
    • Stages | Grafana Loki documentation

    View Slide

  18. APIを使ったログ送信
    • Lokiは様々なHTTP API エンドポイントを公開しており、ログpush用
    エンドポイント「/loki/api/v1/push」に直接ログを送ることも可能
    • 実際の活用例
    1. CloudWatch LogsをLokiに連携
     Lokiから正式に提供されているものがあり、以下のデプロイ方法から選択
    1. Terraform
    2. CloudFormation
    3. ソースコードをダウンロードし、build/zip化して直接アップロード (推奨)
    HTTP API | Grafana Loki documentation
    Lambda promtail Loki
    CloudWatchLogs
    Lambda Promtail | Grafana Loki documentation
    promtailも/pushエンドポイントを公開していて、
    Lokiに直接送るかpromtailを経由するか選択可能

    View Slide

  19. APIを使ったログ送信
    2. S3上のALB/CloudfrontログをLokiに連携
    • 注意点として独自にリトライと連携失敗検知の仕組みを考える必要がある
     LambdaのタイムアウトをMAXの15分に設定
     CloudWatchLogsの場合はbuild前に以下コードのmaxRetriesを修正
     Pythonの場合は標準ライブラリ(e.g. urllib3)のリトライ機能を使用
    S3 Lambda
    SNS promtail Loki
    SNSを経由せずに直接Lambdaに送ることも可能
    ※S3イベントを複数のところに連携したい場合はSNSを経由
    Lambdaサンプルコード
    S3イベント通知
    S3イベント通知
    Lambda Promtailのリトライに関するコードの部分

    View Slide

  20. Lokiでできること
    1. ログ検索
     ログ検索は必ずLabelから入る(選択する)ので
    検索したいログに付与されているLabelを把握している必要がある
    2. ログからメトリクス生成
     LogQLはログからメトリクスを生成するMetric queriesにも対応している
    3. ログに対するアラート設定
     Rulerを使ってAlertManagerにアラートを連携する方式と
    Grafanaにて直接Lokiに対してアラートを設定する方式(推奨)がある
     アラートにはMetric queriesを使う必要がある
    ※特定文字列を含むログが何件以上というような
    数値に対してアラートを設定する必要があるため
    Metric queries | Grafana Loki documentation

    View Slide

  21. LogQL
    1.ログ検索
    {Label selector} filter operator | parser expression | line format expression
    1-1. filter operator
    operator 説明 LogQL例
    |= 特定の文字列を含むログに絞る (grep) {app=“loki”}|=“fatal”
    != 特定の文字列を含まないログに絞る (grep -v) {app=“loki”}!=“info”
    |~ 特定の文字列を含むログに絞る (正規表現) {app=“loki”}|~“error|warn|quer.+”
    !~ 特定の文字列を含まないログに絞る (正規表現) {app=“loki”}!~“info|debug”
    = 検索したいログのLabelを指定 {namespace=“monitoring”,app=“loki”}| env=“stg”
    =~ 検索したいログのLabelを指定 (正規表現) {namespace=“monitoring”,app=“loki”}| env=~“dev.*”
    > >= < <= 検索したいログのLabelの値が数値の場合、
    比較演算子を使うことが可能
    {pod_name=“nginx”}| bytes_sent > 500
    必須 任意

    View Slide

  22. LogQL
    1-2.Parser expression
    Parser 説明 ログ例 LogQL例
    json json形式ログの
    KeyをLabelに変換
    {
    "servers": “jenkins_slave",
    “method”: “PUT”,
    "request": {
    "time": "6.032",
    "size": "55"
    }
    }
    {host=“jenkins_slave”} | json | ¥
    method=“PUT” and request_time > 5
    logfmt “=“を区切り文字として
    Key=ValueのKeyを
    Labelに変換
    app=loki component=ruler env=stg
    app=loki component=distributor env=stg
    app=loki component=distributor env=prd
    {host=“loki-cluster”} | logfmt | ¥
    component=“distributor” and env=“prd”
    pattern パターンが決まって
    いるのログに対して
    特定部分をLabelに設定
    2023/02/21 15:17 204 POST /api/v1/push
    2023/02/21 16:12 404 GET /api/v1/read
    2023/02/21 16:15 500 PUT /api/v1/update
    {app=“nginx”} | ¥
    pattern “<_> <_> ”
    regex 正規表現を使って
    特定部分をLabelに設定
    GET /api/v1/read (200) 1.5s
    PUT /api/v1/update (404) 2.1s
    {app=“api”} | regexp “(?P¥¥w+) ¥
    (?P[¥¥w|/]+) ¥¥((?P¥¥d+?)¥¥) ¥
    (?P.*)”
    Syntax · google/re2 Wiki · GitHub

    View Slide

  23. LogQL
    1-3. Line format expression
    • Log queries | Grafana Loki documentation
    • Query examples | Grafana Loki documentation
    Line
    format
    説明 ログ例 LogQL例
    line_format Labelを使って
    出力するログの
    フォーマットを編集
    app=loki component=ruler env=stg
    app=loki component=distributor env=stg
    app=loki component=distributor env=prd
    {host=“loki-cluster”} | logfmt | line_format ¥
    “component is {{.component}} in {{.env}} env”

    View Slide

  24. LogQL
    2.ログからメトリクス生成
     Log queriesに追加で指定する形
    function 説明 LogQL例
    count_over_time() Time Range間で
    発生したログ件数
    count_over_time({job=“app_log”}|~”err|error|fatal”[5m])
    rate() Time Range間の
    1秒当たりの平均値
    rate({app=“nginx”} | ¥
    pattern “<_> <_> ”[1m])
    sum() 全体合計 sum(rate({app=“nginx”} | ¥
    pattern “<_> <_> ”[1m]))
    sum by() Labelごとの合計 sum(rate({app=“nginx”} | ¥
    pattern “<_> <_> ”[1m])) by (url)
    topk() / bottomk() 値の大きい/小さい
    順で上位n個までを
    表示
    topk(10, sum(rate({app=“nginx”} | ¥
    pattern “<_> <_> ”[1m])) by (url))
    • Metric queries | Grafana Loki documentation
    • Query examples | Grafana Loki documentation

    View Slide

  25. Lokiアンチパターン
    1. Metric Queriesを長いTime Rangeで実行する
     長いTime Range(e.g. 1週間)に対するMetric queriesは
    Querierに大きな負担がかかるため、避けるべき
    特に継続的にクエリーが発生するダッシュボード/アラートでの設定は要注意
     Best Practice
     RulerのRecording Rulesやpromtailのmetricsステージを使って
    Prometheus形式のメトリクスを生成し、Prometheusに連携する
     これによりLokiの保存期間ではなく、Prometheusの長期保存ツール
    (e.g. AMP)の保存期間分、ログに対するメトリクスを保存(確認)できる
    Grafana Loki top 5 query performance tips - YouTube

    View Slide

  26. Lokiアンチパターン
    2. 広い範囲のLabel/Time Rangeでログ検索を行う
     長いTime Range(e.g. 1週間)/広い範囲のLabelに対するログ検索は
    Querierのリソース逼迫によるQuerierの再起動、ひいては
    全体的な読み込みパフォーマンス低下につながる恐れがある
     Best Practice
     確認したいログの具体的なLabelを指定する
     Time Rangeはできるだけ確認したい時間帯に絞る
    Grafana Loki top 5 query performance tips - YouTube
    app=“loki”
    app=“loki”,
    component=“compactor”
    確認したいのがcompactorに関するログの場合、
    {app=“loki”}ではなく、
    {app=“loki”, component=“compactor”}のように
    Labelを具体的に指定する

    View Slide

  27. Lokiアンチパターン
    3. カーディナリティの高い値をLabelに設定する
     ラベルセットごとにindex/chunkが生成されるため、
    ラベルセットが多い → index/chunkが多くなる・chunkサイズが小さくなる
    → 検索パフォーマンスの低下につながる
     chunk数がLimitを超えstream_limitエラーでログが破棄される恐れもある
     例えば、HPAが設定されていて頻繁に作成/終了されるPodがあるとする
     Best Practice
     極力は固定/カーディナリティの低い値をLabelに設定する
     filter operatorで検索できるものはLabelに設定せずfilter operatorを使う
    Best practices | Grafana Loki documentation
    {cluster=“unk”, namespace=“monitoring”}
    {cluster=“unk”, namespace=“monitoring”, pod_name=“abcd”}
    {cluster=“unk”, namespace=“monitoring”, pod_name=“efgh”}
    {cluster=“unk”, namespace=“monitoring”, pod_name=“ijkl”}
    Pod名をLabelに設定した場合は
    Podの数の分index/chunkが生成され、
    1chunkのサイズも小さくなる。
    Pod名をLabelに設定してない場合は
    全体で1index/chunkになるので
    その分検索も早くなる

    View Slide

  28. Loki/promtailのオブザーバビリティ
    Component Metric Name Description
    promtail promtail_dropped_entries_total すべてのリトライが失敗しDropされたログ数
    promtail promtail_sent_entries_total promtailがLokiに送信したログ数
    distributor loki_discarded_samples_total Validation checkに引っ掛かり拒否されたログ数
    distributor loki_request_duration_seconds 受信したHTTPリクエスト(status_codeを確認)
    distributor loki_distributor_bytes_received_total distributorが受信したログbytes
    distributor loki_distributor_lines_received_total distributorが受信したログ数
    ingester loki_ingester_memory_streams ingesterメモリ内のstream数
    ingester loki_ingester_memory_chunks ingesterメモリ内のchunk数
    ingester cortex_ring_members{name="ingester",
    state="Unhealthy"}
    ring上のunhealthyなingester数
    ※一定の数を超えるとLokiへの書き込みができなくなる
    -
    (k8s)
    kubelet_volume_stats_available_bytes Helmでデプロイし、ingesterごとにPV(EBS)を
    払い出している場合、WALディスクの空き容量監視用
    • promtail/Lokiが開示しているメトリクスを活用してLokiクラスターを監視
    • 主要メトリクス
    Observability | Grafana Loki documentation

    View Slide

  29. まとめ
    • Lokiの特性(得意/不得意)を理解したうえで正しく使いましょう
    • すでにPrometheusでGrafanaを使っている場合は
    ログ管理ツールの有力な候補
    • 学習コストは高いが、使いこなせると大量のログを
    低コストで管理できる
    • 試してみたい方はまずはスモールスタート
    Single Binaryモードがあり、すべてのコンポーネントを
    1つのバイナリとして動かすこともできる
    ※Single Binaryモードでも約100GB/dayは捌ける(らしい)
    Deployment modes | Grafana Loki documentation

    View Slide