Skip to main content

Monitoring pomocí Grafana + Loki + Prometheus

Instalace

Server (ten, který sbírá data)

Prometheus

V případě Debianu a jemu podobných stačí nainstalovat balíček prometheus:

apt -y update
apt -y install prometheus

Nastavení se nachází na dvou místech:

  • /etc/default/prometheus => Nastavení jak se má server spouštět. Já do ARGS doplnil --storage.tsdb.retention.time=90d, aby se mi uchovávalo "pouze" 90 dní dat.
  • /etc/prometheus/prometheus.yml => Nastavení collectoru, sběrače dat. Zde nastavujeme odkud chceme tahat metriky a jaké.

prometheus.yml

V základu používám node_exporter na Linux a Windows s touto konfigurací. Data sbírám každou minutu, monitoruji i sám sebe a to každou minutu. Každou minutu také zpracovávám pravidla (které tady žádné nemám), alertmanager vystavuji na portu 9093 a adresy serverů ukládám do labelu instance.

# Sample config for Prometheus.

global:
  scrape_interval:     1m # Set the scrape interval. Default is every 1 minute.
  evaluation_interval: 1m # Evaluate rules every x seconds. The default is every 1 minute.
  scrape_timeout: 30s  # scrape_timeout. Global default is (10s).

  # Attach these labels to any time series or alerts when communicating with
  # external systems (federation, remote storage, Alertmanager).
  external_labels:
      monitor: 'promgraf'

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets: ['localhost:9093']

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:

  - job_name: 'linux_nodes'
    metrics_path: /metrics
    static_configs:
      - targets:
          - localhost:9100
          - mujlinux.bali.cz
    relabel_configs:
      - source_labels: [__address__]
        regex: (.*):(9100)
        target_label: instance
        replacement: '${1}'

  - job_name: 'windows_nodes'
    metrics_path: /metrics
    static_configs:
      - targets:
          - mojewidle.bali.cz:9100
    relabel_configs:
      - source_labels: [__address__]
        regex: (.*):(9100)
        target_label: instance
        replacement: '${1}'

No.. a aby nám to celé fungovalo tak ještě potřebuji aby se Prometheus dostal ke klientům. nftables pravidlo vypadá takto:

#!/usr/sbin/nft -f
table inet filter {
        chain OUTPUT {
                tcp dport 9100 accept comment "Allow connection to prometheus exporter targets - OUT"
        }
}

Grafana + Loki

Grafanu a Lokiho instalujeme z repozitářů grafany dle našeho systému.

Pro Debian Linux potřebuji přidat repozitář s gpg klíčem a poté mohu instalovat:

echo 'deb https://apt.grafana.com stable main' | sudo tee /etc/apt/sources.list.d/grafana-oss.list
curl https://apt.grafana.com/gpg.key | gpg --dearmor > /etc/apt/trusted.gpg.d/grafana.gpg
apt -y update
apt -y install grafana loki

Grafana

Grafana má konfiguraci v /etc/grafana. Přistupujeme na <adresu serveru>:3100, takže např.: http://10.66.0.81:3100. Výchozí login je admin / admin.

Dashboard

Mnou vyrobené dashboardy ke stažení:

Provisioned datasources

Grafaně dokážeme přednastavit zdroje dat pomocí yaml konfiguračních souborů. V následujících ukázkových souborech nastavuji Prometheus a Loki jako zdroje dat běžící na stejném serveru jako Grafana (localhost).

Prometheus datasource
# Configuration file version
apiVersion: 1

# Localhost loki datasource
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://localhost:9090
    isDefault: true
    jsonData:
      timeInterval: 60s
      timeout: 60

Loki datasource
# Configuration file version
apiVersion: 1

# Localhost loki datasource
datasources:
  - name: Loki
    type: loki
    access: proxy
    url: http://localhost:3100
    jsonData:
      timeout: 60
      maxLines: 5000
Authentication
Entra ID

🗒️ Oficiální návod je k dispozici ZDE

Jak nastavíme přihlášení pomocí vlastní Entra ID ?

  • EntraID

    • Vytvořit App registration.
    • Authentication => [ + Add a platform ] => Web => Nastavit Redirect URI na https://grafana.example.com/login/azuread + https://<grafana.example.com
    • Certificates & secrets
      • [ + New client secret ] => < Pojmenovat si klíč a nastavit platnost (volil bych nejdelší - 24 měsíců) > => [Add]. Zkopírovat HODNOTU (Value), nikoliv Secret ID a uložit si jej někam (později jej budeme potřebovat)
      • [ + New federated credential ]:
        • Federated credential scenario: Other issuer
        • Issuer: https://login.microsoftonline.com/<TENANT-ID>/v2.0
        • Name: Nějak si to pojmenovat 🤷‍♂️
        • Description: třeba: "Grafana OAuth."
        • Audience: api://AzureADTokenExchange
    • API permissions
      • [ + Add a permission ] => Microsoft Graph => Delegated permissions => Nyní máme dvě možnosti jak nastavit oprávnění ke čtení uživatelského profilu. Buď pomocí vybrání jednotlivých oprávnění nebo obecného User.Read (Sign in and read user profile).
        • OpenId permissions

          🗒️ User.Read => Allows users to sign-in to the app, and allows the app to read the profile of signed-in users. It also allows the app to read basic company information of signed-in users.
          GroupMember.Read.All => Allows the app to list groups, read basic group properties and read membership of all groups the signed-in user has access to.

          • email (Může být použito User.Read)
          • openid (Může být použito User.Read)
          • profile (Může být použito User.Read)
        • => [ Add permissions ]
  • Grafana

    • Client Id => <Application (client) ID>
    • Client secret => Zde vyplnit Value z client secret vytvořeného v Entra Id
    • Scopes => openid, email, profile
    • FIC managed identity client Id => Zde vyplnit <Application (client) ID>
    • FIC audience => api://AzureADTokenExchange

Loki

Loki má konfiguraci v /etc/loki/config.yml. Má základní konfigurace:

  • Poslouchá na portu 3100
  • Ukládá data do /var/lib/loki
  • Odstraňuje data starší 90 dní
    auth_enabled: false
    
    server:
      http_listen_port: 3100
      grpc_listen_port: 9096
      log_level: warn
      grpc_server_max_concurrent_streams: 2000
    
    common:
      instance_addr: 127.0.0.1
      path_prefix: /var/lib/loki
      storage:
        filesystem:
          chunks_directory: /var/lib/loki/chunks
          rules_directory: /var/lib/loki/rules
      replication_factor: 1
      ring:
        kvstore:
          store: inmemory
    
    query_range:
      results_cache:
        cache:
          embedded_cache:
            enabled: true
            max_size_mb: 2048
    
    limits_config:
      metric_aggregation_enabled: true
      max_query_lookback: 2160h #90d
      retention_period: 2160h #90d
    
    compactor:
     working_directory: /tmp/loki/retention
     retention_enabled: true
     retention_delete_delay: 24h
     delete_request_store: filesystem
    
    schema_config:
      configs:
        - from: 2020-10-24
          store: tsdb
          object_store: filesystem
          schema: v13
          index:
            prefix: index_
            period: 24h
    
    pattern_ingester:
      enabled: true
      metric_aggregation:
        loki_address: localhost:3100
    
    ruler:
      alertmanager_url: http://localhost:9093
    
    frontend:
      encoding: protobuf
    

⚠️ Před Lokiho je potřeba přidat proxy s autentizací protože sám Loki tuto vrstvu nemá (viz Grafana docs). Základní zabezpečení pomocí nginx je popsáno zde. Mimo jiné je možné si jej alespoň v rámci interní sítě omezit pomocí firewallu:

#!/usr/sbin/nft -f
table inet filter {
       chain INPUT {
               ip saddr 10.66.0.0/24 tcp dport 3100 accept comment "Allow Loki target from my services subnet - IN"
       }
}

rsyslog

Tento nástroj používáme pro odchytávání logů pomocí syslogu ze zařízení jako jsou switche. rsyslog má konfiguraci v /etc/rsyslog.conf a /etc/rsyslog.d.

  • Nainstalovat:

    apt -yq install rsyslog
    
  • V /etc/rsyslog.d si vytvořit konfigurační soubor:

    • pro "nastartování vysílače" / serveru který bude zachytávat logy které do něj budeme posílat, 01-remote_server.conf:
      # Enable imudp, UDP Syslog Input Module
      # https://www.rsyslog.com/doc/configuration/modules/imudp.html
      module(load="imudp")
      input(type="imudp" port="514")
      
      # Enable imutcp, TCP Syslog Input Module
      # https://www.rsyslog.com/doc/configuration/modules/imtcp.html
      module(load="imtcp")
      input(type="imtcp" port="601")
      
      # Allow receiving from localhost and local network
      #$AllowedSender UDP, 127.0.0.1, 10.0.0.0/8
      #$AllowedSender TCP, 127.0.0.1, 10.0.0.0/8
      

      💡 Dle poznámky lze omezit z jakých adres/subnetů budeme přijímat komunikaci.

    • pro logování například Cisco switche si nastavím 10-cisco_switch.conf který bude ukládat logy do speciální podsložky a bude zde řadit klienty z určitých adres či logy obsahující určité zprávy:
      template(name="Cisco_Switch" type="string" string="/var/log/remote/cisco/%HOSTNAME%.log")
      
      :fromhost-ip, regex, "^10\.39\.\7.\.*" -?Cisco_Switch
      :msg, contains, " sw-a1269-hardcore.net.cvut.it:" -?Cisco_Switch
      
  • Povolit provoz na UDP a TCP porty v nftables (v mém případě do interních sítí):

    ip saddr 10.0.0.0/8 tcp dport 601 accept comment "Allow rsyslog on internal sites (TCP/601) - IN"
    ip saddr 10.0.0.0/8 udp dport 514 accept comment "Allow rsyslog on internal sites (UDP/514) - IN"
    

Klient (monitorovaný server)

Prometheus

Balíčky Promethea jsou u Debianu a z něj vycházejících distribucí většinou přímo v repozitářích. Základem je node_exporter který nám exportuje základní údaje o stavu operačního systému (packages.debian.org)

apt -y update
apt -y install prometheus-node-exporter

V případě používání nftables je potřeba vytvořit příchozí pravidlo pro port 9100 aby si mohl Prometheus vyzvedávat metriky.
Příklad, ve kterém je 10.66.0.81 náš Prometheus collector (server který sbírá data a jmenuje se promgraf dle komentáře pravidla).

#!/usr/sbin/nft -f
table inet filter {
        chain INPUT {
                ip saddr 10.66.0.81 tcp dport 9100 accept comment "Allow Prometheus metrics access from promgraf - IN"
        }

💡 Prometheus lze nastavit, aby sbíral textové soubory jako metriky, které ale musí být v kompatibilním formátu.
(např.: nfsd_cc{hostname="pc666-105.domena",ip="10.66.6.105",path="/srv/linux/debian/shared"} 1 jako soubor nfsd_cc.prom). Do tohoto souboru zapisujeme řádky pomocí sponge z balíčku moreutils.

/etc/default/prometheus:

# Set the command-line arguments to pass to the server.
# Due to shell escaping, to pass backslashes for regexes, you need to double
# them (\\d for \d). If running under systemd, you need to double them again
# (\\\\d to mean \d), and escape newlines too.
ARGS=""
# To collect text files in this directory, add the line to ARGS variable.
# There might be APT or some other metrics already.
# --collector.textfile.directory="/var/lib/prometheus/node-exporter"

Alloy

Linux

Konfiguraci Alloy bych doporučil rozložit do souborů a nastavit, aby služba brala konfiguraci ze složky místo výchozího /etc/default/config.alloy.
Nastavení se provede přidáním cesty do proměnné CUSTOM_ARGS jako posledního parametru (pokud zde mají být i jiné přepínače, cesta musí být poslední). Následně ostraním výchozí konfiguraci v /etc/alloy/config.alloy, protože zde budu dávat vlastní configy.

  • /etc/default/alloy:

    ## Path:
    ## Description: Grafana Alloy settings
    ## Type:        string
    ## Default:     ""
    ## ServiceRestart: alloy
    #
    # Command line options for Alloy.
    #
    # The configuration file holding the Alloy config.
    #CONFIG_FILE="/etc/alloy/config.alloy"
    
    # User-defined arguments to pass to the run command.
    # Specified <PATH_NAME> to include directory: https://grafana.com/docs/alloy/latest/reference/cli/run/
    CUSTOM_ARGS="/etc/alloy"
    
    # Restart on system upgrade. Defaults to true.
    RESTART_ON_UPGRADE=true
    
  • /etc/alloy

    • 00-config.alloy - výchozí obecná nastavení

      // For a full configuration reference, see https://grafana.com/docs/alloy
      // Log only warnings and more
      logging {
        level = "warn"
      }
      
    • 01-endpoint.alloy - nastavení endpointu => Loki serveru na který se budou posílat data. více informací
      Zde se vyplňuje adresa ve tvaru: <http(s)>/<FQDN>/<Ingest endpoint>. Jako příklad uvedu svou konfiguraci:

      // Data will be sent to this loki instance
      loki.write "endpoint" {
        endpoint {
          url = "http://promgraf.in.cvut.it:3100/loki/api/v1/push"
          // basic_auth {
          //  username = "admin"
          //  password = "admin"
          // }
        }
      }
      
    • 09-journal-relabel.alloy - Základní nastavení journal collectoru (sbírání logů z systemd systémových služeb).

      // Setup journal relabel rules
      loki.relabel "journal" {
        forward_to = []
              rule {
                      target_label  = "instance"
                      replacement = constants.hostname
              }
              rule {
                      source_labels = ["__journal__systemd_unit"]
                      target_label  = "unit"
              }
      }
      
    • 10-varlog-collector.alloy - Nastavení sběru všech logů z /var/log s příponou souboru (suffixem) .log

      // Setup varlogs job with labels
      discovery.relabel "varlogs" {
      
              // Read .log files from /var/log, recursively
              targets = [{
                      __address__  = "localhost",
                      __path__     = "/var/log/**/*.log",
                      service_name = "varlogs",
              }]
              rule {
                      target_label  = "instance"
                      replacement = constants.hostname
              }
      }
      
      // Match files defined in relabel target
      local.file_match "varlogs" {
              path_targets = discovery.relabel.varlogs.output
      }
      
      // Send files to loki endpoint
      loki.source.file "varlogs" {
              targets               = local.file_match.varlogs.targets
              forward_to            = [loki.write.endpoint.receiver]
      }
      
    • 50-journal-ssh.alloy - Odesílání logů systemd jednotky (unit) ssh.service.

      loki.source.journal "ssh" {
        matches        = "_SYSTEMD_UNIT=ssh.service _SYSTEMD_UNIT=sshguard.service"
        format_as_json = true
        relabel_rules  = loki.relabel.journal.rules
        forward_to     = [loki.write.endpoint.receiver]
      }
      
    • 51-journal-monitoring.alloy - Odesílání logů systemd jednotek (units) alloy.service a prometheus-node-exporter.service. Sledujeme tímto stav služeb které nám mají za úkol poskytovat metriky a logy.

      loki.source.journal "alloy" {
        matches        = "_SYSTEMD_UNIT=alloy.service"
        format_as_json = true
        relabel_rules  = loki.relabel.journal.rules
        forward_to     = [loki.write.endpoint.receiver]
      }
      
      loki.source.journal "prometheus_node_exporter" {
        matches        = "_SYSTEMD_UNIT=prometheus-node-exporter.service"
        format_as_json = true
        relabel_rules  = loki.relabel.journal.rules
        forward_to     = [loki.write.endpoint.receiver]
      }
      
    • /etc/alloy/52-journal-journal.alloy/ - Odesílání logů systemd jednotky (unit) systemd-journald.service.

      loki.source.journal "systemd_journald" {
        matches        = "_SYSTEMD_UNIT=systemd-journald.service"
        format_as_json = true
        relabel_rules  = loki.relabel.journal.rules
        forward_to     = [loki.write.endpoint.receiver]
      }
      

Windows

TBD ;-)