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:[![image-1740662017336.png](https://wiki.cvut.it/uploads/images/gallery/2025-02/scaled-1680-/wDGGlKNmbKmjce4f-image-1740662017336.png)](https://wiki.cvut.it/uploads/images/gallery/2025-02/wDGGlKNmbKmjce4f-image-1740662017336.png)
    - 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"
        }
}
SNMP

Konfigurace prometheus-snmp-exporter které používám. Pro základ je potřeba mít konfiguraci jako je tato:

auths:

  cisco_v2:
    community: ciskotrysko
    version: 2

  9px_v2:
    community: whooops
    version: 2

  epdu_v1:
    community: eatingison
    version: 1

modules:

  # Default
  system:
    walk:
      - sysObjectID
      - sysContact
      - sysLocation
      - sysDescr
      - sysServices
      - sysName
      - sysUpTime

  # Common interfaces MIB
  if_mib:
    walk:
      - "SNMPv2-MIB::system"
      - "IF-MIB::interfaces"
      - "IF-MIB::ifXTable"
    lookups:
      - source_indexes: [ifIndex]
        lookup: ifAlias
      - source_indexes: [ifIndex]
        # Uis OID to avoid conflict with PaloAlto PAN-COMMON-MIB.
        lookup: 1.3.6.1.2.1.2.2.1.2 # ifDescr
      - source_indexes: [ifIndex]
        # Use OID to avoid conflict with Netscaler NS-ROOT-MIB.
        lookup: 1.3.6.1.2.1.31.1.1.1.1 # ifName
    overrides:
      ifAlias:
        ignore: true # Lookup metric
      ifDescr:
        ignore: true # Lookup metric
      ifName:
        ignore: true # Lookup metric
      ifType:
        type: EnumAsInf

Zde si definujeme základní možnosti autentizace k SNMP endpointům a dva moduly:

  • system - výchozí modul který je v drtivé většině případů univerzální.
  • if_mib - univerzální modul načítající informace o interfaces zařízení.

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í:

WireGuard - jednoduchý Monitoring VPN pomocí prometheus exporteru github.com/mdlayher/wireguard_exporter s upraveným collector.go.

⚠️ Pro správnou funkčnost je potřeba před sestavením upravit collector.go před sestavením pomocí mdlayher_collector.go.patch a příkazu: patch -p0 mdlayher/wg-export/source/collector.go < mdlayher_collector.go.patch

    Download: WireGuard-1771239683104.json Screenshot: WireGuard - Dashboards - Grafana.png
    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 ;-)