toantd1202 / Prometheus_new

0 stars 0 forks source link

Issue3 #13

Open toantd1202 opened 4 years ago

toantd1202 commented 4 years ago

Monitoring Network

  1. Xác định các tiêu chí giám sát network cho host.
  2. Tìm hiểu các metrics tương ứng với các tiêu chí ấy ( trong phạm vị node_exporter, các em có thể tìm thêm trong một số exporter khác )
  3. Viết alert rule dựa trên các tiêu chí đã định nghĩa ở 1. Cần giải thích được các operator, func PromQL sử dụng trong expr ấy.
  4. Bonus: Trực quan các metric về network lên Grafana
toantd1202 commented 4 years ago

1. Các tiêu chí giám sát network cho host

2. Các metrics tương ứng

Băng thông

Đối với bandwidth, các metrics ứng với tiền tố như node_network_receivenode_network_transmit. Các metrics này trả về kiểu dữ liệu là counter. node_network_receive trả về các giá trị ứng với luồng xuống(nhận) về host tại các mốc thời gian tương ứng, ngược lại các metrics node_network_transmit trả về các giá trị, thông số của luồn up(truyền), trong cùng một thời điểm, cả hai luồng đều có thể hoạt động, vì vậy, độ rộng của bandwidth là tổng của cả luồng lên và luồng xuống.

Các đơn vị thường dùng cho các luồng như:

Metrics description types
node_network_receive_bytes_total Network device statistic receive_bytes counter
node_network_receive_compressed_total Network device statistic receive_compressed counter
node_network_receive_drop_total Network device statistic receive_drop counter
node_network_receive_errs_total Network device statistic receive_errs counter
node_network_receive_fifo_total Network device statistic receive_fifo counter
node_network_receive_frame_total Network device statistic receive_frame counter
node_network_receive_multicast_total Network device statistic receive_multicast counter
node_network_receive_packets_total Network device statistic receive_packets counter
node_network_speed_bytes speed_bytes value of /sys/class/net/ gauge
node_network_transmit_bytes_total Network device statistic transmit_bytes counter
node_network_transmit_carrier_total Network device statistic transmit_carrier counter
node_network_transmit_colls_total Network device statistic transmit_colls counter
node_network_transmit_compressed_total Network device statistic transmit_compressed counter
node_network_transmit_drop_total Network device statistic transmit_drop counter
node_network_transmit_errs_total Network device statistic transmit_errs counter
node_network_transmit_fifo_total Network device statistic transmit_fifo counter
node_network_transmit_packets_total Network device statistic transmit_packets counter
node_network_transmit_queue_length transmit_queue_length value of /sys/class/net/ gauge

ví dụ

Tổng băng thông được sử dụng:

node_network_receive_bytes_total + node_network_transmit_bytes_total

Để tính tỷ lệ lỗi truyền trong 5 phút ta có thể sử dụng:

  rate(node_network_transmit_errs_total[5m]) / rate(node_network_transmit_packets_total[5m])

Ta cũng có thể xác định đơn vị truyền cực đại thông qua metrics: node_network_mtu_bytes, metrics trả về kiểu dữ liệu là gauge.

Đối với các container, để giám sát network, ta sử dụng cAdvisor, một node exporter hỗ trợ việc giám sát các container. Tương tự như node exporter, cAdvisor cũng có các metrics giám sát mạng như:

Metrics description types
container_network_receive_bytes_total Cumulative count of bytes received counter
container_network_receive_errors_total Cumulative count of errors encountered while receiving counter
container_network_receive_packets_dropped_total Cumulative count of packets dropped while receiving counter
container_network_receive_packets_total Cumulative count of packets received counter
container_network_transmit_bytes_total Cumulative count of bytes transmitted counter
container_network_transmit_errors_total Cumulative count of errors encountered while transmitting counter
container_network_transmit_packets_dropped_total Cumulative count of packets dropped while transmitting counter
container_network_transmit_packets_total Cumulative count of packets transmitted counter

Thông tin trên các interface

Với việc sử dụng metrics node_network_info, các thông tin của interface được trả về với với các giá trị như ip address, device name, tình trạng hoạt động.

ví dụ:

node_network_info{address="00:00:00:00:00:00",broadcast="00:00:00:00:00:00",device="lo",duplex="",ifalias="",operstate="unknown"} 1
node_network_info{address="02:42:ac:17:00:03",broadcast="ff:ff:ff:ff:ff:ff",device="eth0",duplex="full",ifalias="",operstate="up"} 1

Hay với node_network_address_assign_type cho biết địa chỉ MAC đến từ đâu.

3. Viết alert rule

vtdat commented 4 years ago

OK, đọc thêm làm thế nào để exporter collect được các metrics như vậy nhé. Dẫn chứng đến code luôn.

toantd1202 commented 4 years ago

Prometheus cung cấp các collectors tùy chỉnh. Có thể viết các exporters trong các client libraries. Trong exporter, có gaugecounter metrics. Sử dụng prometheus_client để nhập các thư viện. Như này đúng không anh!

import time
from prometheus_client.core import GaugeMetricFamily, REGISTRY, CounterMetricFamily
from prometheus_client import start_http_server

class CustomCollector(object):
    def __init__(self):
        pass

    def collect(self):
        g = GaugeMetricFamily("MemoryUsage", 'Help text', labels=['instance'])
        g.add_metric(["instance01.us.west.local"], 20)
        yield g

        c = CounterMetricFamily("HttpRequests", 'Help text', labels=['app'])
        c.add_metric(["example"], 2000)
        yield c

if __name__ == '__main__':
    start_http_server(8000)
    REGISTRY.register(CustomCollector())
    while True:
        time.sleep(1)
vtdat commented 4 years ago

Ý anh là từng metrics nó catch chỗ nào. Rồi ở đấy nó xử lí cái gì để ra được cái metrics ý em.

g.add_metric(["instance01.us.west.local"], 20)

VD: như này là fix cứng giá trị. Còn các metrics phải tính toán thì sao

toantd1202 commented 4 years ago

@vtdat anh!!! Prometheus exporter cho các metrics phần cứng và hệ điều hành được hiển thị bởi các *NIX kernels, được viết bằng Go với các metrics collectors có thể cài cắm được để lấy dữ liệu.

Exporter thu thập các metrics của nút máy chủ đang hoạt động (một số cũng có thể thu thập từ xa) và hiển thị chúng qua HTTP, cấu hình mặc định của nút này được đặt tại /metrics. Điều này cho phép Prometheus scrape target thông qua HTTP GET.

Collectors

Collectors được kích hoạt bằng cách sử dụng cờ --collector.<name>. Collectors được bật theo mặc định và có thể tắt bằng cách sử dụng cờ --no-collector.<name>.

Đối với các metrics về network ta có: name description os
netclass Exposes network interface info from /sys/class/net/ Linux
netdev Exposes network interface statistics such as bytes transferred Darwin, Dragonfly, FreeBSD, Linux, OpenBSD
netstat Exposes network statistics from /proc/net/netstat. This is the same information as netstat -s Linux

ví dụ: các metrics về network có tiền tố là node_network, bằng cách sử dụng các thư viện hỗ trợ cho việc lấy các thông tin về network interface (ifaddrs.h, net/if.h), sẽ được collectors như sau:

package collector

import (
    "errors"
    "regexp"
    "strconv"

    "github.com/go-kit/kit/log"
    "github.com/go-kit/kit/log/level"
)

#cgo CFLAGS: -D_IFI_OQDROPS
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/if.h>

func getNetDevStats(ignore regexp.Regexp, accept regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) { netDev := map[string]map[string]string{}

var ifap, ifa *C.struct_ifaddrs
if C.getifaddrs(&ifap) == -1 {
    return nil, errors.New("getifaddrs() failed")
}
defer C.freeifaddrs(ifap)

for ifa = ifap; ifa != nil; ifa = ifa.ifa_next {
    if ifa.ifa_addr.sa_family == C.AF_LINK {
        dev := C.GoString(ifa.ifa_name)
        if ignore != nil && ignore.MatchString(dev) {
            level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
            continue
        }
        if accept != nil && !accept.MatchString(dev) {
            level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
            continue
        }

        devStats := map[string]string{}
        data := (*C.struct_if_data)(ifa.ifa_data)

        devStats["receive_packets"] = convertFreeBSDCPUTime(uint64(data.ifi_ipackets))
        devStats["transmit_packets"] = convertFreeBSDCPUTime(uint64(data.ifi_opackets))
        devStats["receive_errs"] = convertFreeBSDCPUTime(uint64(data.ifi_ierrors))
        devStats["transmit_errs"] = convertFreeBSDCPUTime(uint64(data.ifi_oerrors))
        devStats["receive_bytes"] = convertFreeBSDCPUTime(uint64(data.ifi_ibytes))
        devStats["transmit_bytes"] = convertFreeBSDCPUTime(uint64(data.ifi_obytes))
        devStats["receive_multicast"] = convertFreeBSDCPUTime(uint64(data.ifi_imcasts))
        devStats["transmit_multicast"] = convertFreeBSDCPUTime(uint64(data.ifi_omcasts))
        devStats["receive_drop"] = convertFreeBSDCPUTime(uint64(data.ifi_iqdrops))
        devStats["transmit_drop"] = convertFreeBSDCPUTime(uint64(data.ifi_oqdrops))
        netDev[dev] = devStats
    }
}

return netDev, nil

}

func convertFreeBSDCPUTime(counter uint64) string { return strconv.FormatUint(counter, 10) }



Em nghĩ là với các metrics phải tính toán thì sẽ sử dụng rules để tính dựa trên các số liệu từ các metrics có liên quan.
toantd1202 commented 4 years ago

@vtdat anh!!! Dẫn chứng code mới: netstat

import

const (
    netStatsSubsystem = "netstat"
)

khai báo hằng số netStatsSubsystem nhận giá trị là chuỗi netstat

var (
    netStatFields = kingpin.Flag("collector.netstat.fields", "Regexp of fields to return for netstat collector.").Default("^(.*_(InErrors|InErrs)|Ip_Forwarding|Ip(6|Ext)_(InOctets|OutOctets)|Icmp6?_(InMsgs|OutMsgs)|TcpExt_(Listen.*|Syncookies.*|TCPSynRetrans)|Tcp_(ActiveOpens|InSegs|OutSegs|PassiveOpens|RetransSegs|CurrEstab)|Udp6?_(InDatagrams|OutDatagrams|NoPorts|RcvbufErrors|SndbufErrors))$").String()
)

kingpin: Kingpin là một trình phân tích cú pháp dòng lệnh an toàn. Nó hỗ trợ cờ, lệnh lồng nhau và đối số vị trí.

func registerCollector(collector string, isDefaultEnabled bool, factory func(logger log.Logger) (Collector, error)) {
    var helpDefaultState string
    if isDefaultEnabled {
        helpDefaultState = "enabled"
    } else {
        helpDefaultState = "disabled"
    }

    flagName := fmt.Sprintf("collector.%s", collector)
    flagHelp := fmt.Sprintf("Enable the %s collector (default: %s).", collector, helpDefaultState)
    defaultValue := fmt.Sprintf("%v", isDefaultEnabled)

    flag := kingpin.Flag(flagName, flagHelp).Default(defaultValue).Action(collectorFlagAction(collector)).Bool()
    collectorState[collector] = flag

    factories[collector] = factory
}

NewNetStatCollector lấy và trả về một Collector mới hiển thị các số liệu thống kê mạng.

func NewNetStatCollector(logger log.Logger) (Collector, error) {
    pattern := regexp.MustCompile(*netStatFields)
    return &netStatCollector{
        fieldPattern: pattern,
        logger:       logger,
    }, nil
}

NewNetStatCollector(logger log.Logger) (Collector, error) pattern := regexp.MustCompile(*netStatFields) Tạo các biến toàn cục với các biểu thức dựa trên các giá trị của cờ netStatFields. Trả về các giá trị cho struct netStatCollector

return &netStatCollector{
        fieldPattern: pattern,
        logger:       logger,
    }

Tạo ra các metrics

func (c *netStatCollector) Update(ch chan<- prometheus.Metric) error {
    netStats, err := getNetStats(procFilePath("net/netstat"))
    if err != nil {
        return fmt.Errorf("couldn't get netstats: %s", err)
    }
    snmpStats, err := getNetStats(procFilePath("net/snmp"))
    if err != nil {
        return fmt.Errorf("couldn't get SNMP stats: %s", err)
    }
    snmp6Stats, err := getSNMP6Stats(procFilePath("net/snmp6"))
    if err != nil {
        return fmt.Errorf("couldn't get SNMP6 stats: %s", err)
    }
    // Hợp nhất kết quả của snmpStats vào netStats (có thể xung đột, nhưng các khóa luôn là duy nhất cho trường hợp sử dụng đã cho).
    for k, v := range snmpStats {
        netStats[k] = v
    }
    for k, v := range snmp6Stats {
        netStats[k] = v
    }
    for protocol, protocolStats := range netStats {
        for name, value := range protocolStats {
            key := protocol + "_" + name
            v, err := strconv.ParseFloat(value, 64)
            if err != nil {
                return fmt.Errorf("invalid value %s in netstats: %s", value, err)
            }
            if !c.fieldPattern.MatchString(key) {
                continue
            }
            ch <- prometheus.MustNewConstMetric(
                prometheus.NewDesc(
                    prometheus.BuildFQName(namespace, netStatsSubsystem, key),
                    fmt.Sprintf("Statistic %s.", protocol+name),
                    nil, nil,
                ),
                prometheus.UntypedValue, v,
            )
        }
    }
    return nil
}

Các file lấy dữ liệu như sau:

func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) Metric {
    m, err := NewConstMetric(desc, valueType, value, labelValues...)
    if err != nil {
        panic(err)
    }
    return m
}

Lấy ra dữ liệu từ file netstats:

func getNetStats(fileName string) (map[string]map[string]string, error) {
    file, err := os.Open(fileName)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    return parseNetStats(file, fileName)
}

Phân tích cú pháp, dữ liệu NetStats

func parseNetStats(r io.Reader, fileName string) (map[string]map[string]string, error) {
    var (
        netStats = map[string]map[string]string{}
        scanner  = bufio.NewScanner(r)
    )

    for scanner.Scan() {
        nameParts := strings.Split(scanner.Text(), " ")
        scanner.Scan()
        valueParts := strings.Split(scanner.Text(), " ")
        // Remove trailing :.
        protocol := nameParts[0][:len(nameParts[0])-1]
        netStats[protocol] = map[string]string{}
        if len(nameParts) != len(valueParts) {
            return nil, fmt.Errorf("mismatch field count mismatch in %s: %s",
                fileName, protocol)
        }
        for i := 1; i < len(nameParts); i++ {
            netStats[protocol][nameParts[i]] = valueParts[i]
        }
    }

    return netStats, scanner.Err()
}

định nghĩa 1 số biến:

    var (
        netStats = map[string]map[string]string{}
        scanner  = bufio.NewScanner(r)
    )

Lấy dữ liệu SNMP6Stats

func getSNMP6Stats(fileName string) (map[string]map[string]string, error) {
    file, err := os.Open(fileName)
    if err != nil {
        // On systems with IPv6 disabled, this file won't exist.
        // Do nothing.
        if os.IsNotExist(err) {
            return nil, nil
        }

        return nil, err
    }
    defer file.Close()

    return parseSNMP6Stats(file)
}

Phân tích cú pháp trong SNMPStats:

func parseSNMP6Stats(r io.Reader) (map[string]map[string]string, error) {
    var (
        netStats = map[string]map[string]string{}
        scanner  = bufio.NewScanner(r)
    )

    for scanner.Scan() {
        stat := strings.Fields(scanner.Text())
        if len(stat) < 2 {
            continue
        }
        // Expect to have "6" in metric name, skip line otherwise
        if sixIndex := strings.Index(stat[0], "6"); sixIndex != -1 {
            protocol := stat[0][:sixIndex+1]
            name := stat[0][sixIndex+1:]
            if _, present := netStats[protocol]; !present {
                netStats[protocol] = map[string]string{}
            }
            netStats[protocol][name] = stat[1]
        }
    }

    return netStats, scanner.Err()
}
vtdat commented 4 years ago

tao MR đi em