Zabbix Agent 2 рдХреЗ рд▓рд┐рдП рдкреНрд▓рдЧрдЗрди рд╡рд┐рдХрд╛рд╕

рдЖрдЦрд┐рд░реА рдЬрд╝реИрдмрд┐рдХреНрд╕ рд╢рд┐рдЦрд░ рд╕рдореНрдореЗрд▓рди 2019 рдореЗрдВ, рдЬрд╝реИрдмрд┐рдХреНрд╕ 4.4 рдХреА рд░рд┐рд▓реАрдЬрд╝ рдХреЗ рд╕рд╛рде, рдирдП рдЬрд╝реИрдмрд┐рдХреНрд╕ рдПрдЬреЗрдВрдЯ 2 рдХреА рдШреЛрд╖рдгрд╛ рдХреА рдЧрдИ, рдЬрд┐рд╕рдХреА рдкреНрд░рдореБрдЦ рд╡рд┐рд╢реЗрд╖рддрд╛ рдЧреЛ рднрд╛рд╖рд╛ рдореЗрдВ рдЗрд╕рдХреЗ рд▓рд┐рдП рдкреНрд▓рдЧ-рдЗрди рд▓рд┐рдЦрдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рд╣реИред рдФрд░ рдХрдИ рд▓реЛрдЧ рддреБрд░рдВрдд рдкреВрдЫрдиреЗ рд▓рдЧреЗ: рд▓реЗрдХрд┐рди рдХреИрд╕реЗ, рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдпреЗ рдкреНрд▓рдЧрдЗрдиреНрд╕ рд▓рд┐рдЦрддреЗ рд╣реИрдВ, рд╡реЗ рдХреИрд╕реЗ рд╡реНрдпрд╡рд╕реНрдерд┐рдд рд╣реЛрддреЗ рд╣реИрдВ? рдкреНрд░рд▓реЗрдЦрди рдФрд░ рдЙрджрд╛рд╣рд░рдг рдХрд╣рд╛рдВ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ?


рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдореИрдВ рдЗрди рдФрд░ рдХреБрдЫ рдЕрдиреНрдп рд╕рд╡рд╛рд▓реЛрдВ рдХреЗ рдЬрд╡рд╛рдм рджреЗрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред рдХреНрд░рдо рдореЗрдВ рд╕рдм рдХреБрдЫ, рд▓реЗрдХрд┐рди рдЕрдЧрд░ рдЖрдк рдЙрди рд▓реЛрдЧреЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рд╣реИрдВ рдЬреЛ рддреБрд░рдВрдд рд▓рдбрд╝рд╛рдИ рдореЗрдВ рдЯреВрдЯ рдЬрд╛рддреЗ рд╣реИрдВ, рддреЛ рд╕реНрд╡рддрдВрддреНрд░ рд░реВрдк рд╕реЗ рдкрд░рд┐рдЪрдпрд╛рддреНрдордХ рднрд╛рдЧ рдХреЛ рдЫреЛрдбрд╝реЗрдВ рдФрд░ рдЕрднреНрдпрд╛рд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдЧреЗ рдмрдврд╝реЗрдВ if if


рдЗрд╕рд▓рд┐рдП...



рдХрд┐рд╕ рддрд░рд╣ рдХрд╛ рдирдпрд╛ рдПрдЬреЗрдВрдЯ, рдФрд░ рд╡рд╣ рдХреНрдпреЛрдВ рджрд┐рдЦрд╛рдИ рджрд┐рдпрд╛?


рдпрджрд┐ рдЖрдкрдиреЗ рдкрд╣рд▓реЗ рдЬрд╝реИрдмрд┐рдХреНрд╕ рдПрдЬреЗрдВрдЯ рдХреЗ рд▓рд┐рдП рдкреНрд▓рдЧрдЗрдиреНрд╕ рд▓рд┐рдЦрдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХреА, рдпрд╛ рдХрдо рд╕реЗ рдХрдо рдЗрд╕реЗ рдХрд░рдиреЗ рдХрд╛ рдЗрд░рд╛рджрд╛ рд╣реИ, рддреЛ рдЖрдкрдиреЗ рд╢рд╛рдпрдж рдзреНрдпрд╛рди рджрд┐рдпрд╛ рдХрд┐ рдЖрдкрдХреЗ рд╡рд┐рдХрд▓реНрдк рдмрд╣реБрдд рд╕реАрдорд┐рдд рд╣реИрдВред


рдкрд╣рд▓реЗ рдПрдЬреЗрдВрдЯ рдХреЗ рд▓рд┐рдП рдкреНрд▓рдЧрдЗрди рдХрдИ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ рдореЗрдВ рдЪрд▓ рд╕рдХрддрд╛ рд╣реИ, рдирд┐рд░реНрдорд╛рддрд╛ рдХреЛ рдЗрд╕реЗ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рдирд┐рдпрдВрддреНрд░рдг рдирд╣реАрдВ рджреЗрдирд╛, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд▓рдЧрд╛рддрд╛рд░ рдХрдиреЗрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛, рдЪреЗрдХ рдХреЗ рдмреАрдЪ рд╕реНрдерд┐рддрд┐ рдмрдирд╛рдП рд░рдЦрдирд╛, рдЬрд╛рд▓ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ - рдРрд╕реА рдЪреАрдЬреЗрдВ рдХрд░рдирд╛ рдпрд╛ рддреЛ рдореБрд╢реНрдХрд┐рд▓ рдпрд╛ рдЕрд╕рдВрднрд╡ рдерд╛ред


. Go ( Zabbix Agent), . Go- тАФ тАФ . , Go- Zabbix Agent , .


, Go-:


  • onfig ;
  • "out-of-the-box";
  • ;
  • Windows ;
  • .
    Zabbix 4.4 , Zabbix 5.0 "production-ready".


, , " ". , Zabbix 4.4. , Go- , , Zabbix 5.0 - .


тАФ ServerConnector, ServerListener Scheduler.


ServerConnector ( / ), items . .


ServerListener Scheduler. , .


Scheduler . Scheduler () , item'.


: ( Bulk Passive, ). , , .


. тАФ PlantUML ┬п\(уГД)/┬п




: ServerConnector ResultCache, .




Scheduler , ServerConnector' ServerListener item'. , ResultWriter, .



items Zabbix , ServerConnector updateRequest , . , . , , .



:


  • ;
  • .
    . - , ( ) , .

:


  1. configuratorTask
  2. starterTask
  3. collectorTask
  4. watcherTask
  5. exporterTask (directExporterTask)
  6. stopperTask
    (taskBase) , , .

exporterTask
ExporterTask ( bulk ). item, . Scheduler Export Exporter ResultWriter.


directExporterTask
directExporterTask ExporterTask , , ( ), , 1 . . тАФ directExporterTask .


watcherTask
WatcherTask () . Watch Watcher, .


collectorTask
Scheduler Collect Collector Period() .


starterTask
Start Runner, .


stopperTask
Stop Runner, .


configuratorTask
Configure Configurator, , .



5 : Exporter, Watcher, Collector, Runner Configurator.
Exporter Watcher : Exporter pull , Watcher тАФ push.


plugin.Exporter


type Exporter interface {
    Export(key string, params []string, context ContextProvider) (result interface{}, err error)
}

Exporter тАФ , , , . , . . , , . , , - .


, . . Go: , , , sync.Map . race-, .


Export() тАФ 100 . , plugin.Base.SetCapacity.


func (b *Base) SetCapacity(capacity int)

, capacity . :
Plugins.<PluginName>.Capacity=1


plugin.Watcher


type Watcher interface {
    Watch(requests []*Request, context ContextProvider)
}

Watcher , . , trapping, . use case тАФ , , , . , , , , .


plugin.Collector


type Collector interface {
    Collect() error
    Period() int
}

Collector , . , ExporterтАЩ.


use case Collector тАФ , , Zabbix .


CollectorтАЩ 2 :


  • Collect ;
  • Period .
    Collector, , .

plugin.Runner


type Runner interface {
    Start()
    Stop()
}

Runner , ( Start), , ( Stop).
, , , - , , ..


() . , (Zabbix Server Proxy), . , , . , . , , , 24 .


plugin.Configurator


type Configurator interface {
    Configure(globalOptions *GlobalOptions, privateOptions interface{})
    Validate(privateOptions interface{}) error
}

Configurator , .
2 :


  • Configure .
  • Validate config . , , .

, , . .


Go- Zabbix .



. , тАФ , , . internal/agent "plugin_" . , , UserParameters.


, , ( , , , ..) тАФ . . go/plugins, .


Hello, world!


тАФ Go, , . :


package packageName

import "zabbix.com/pkg/plugin"

type Plugin struct {
    plugin.Base
}

var impl Plugin

func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (res interface{}, err error) {
    // Write your code here
    return
}

func init() {
    plugin.RegisterMetrics(&impl, "PluginName", "key", "Description.")
}

, bash python, ? тОЭ^╧Й^тОа , - .


, , .


:
Zabbix.


$ git clone https://git.zabbix.com/scm/zbx/zabbix.git --depth 1 zabbix-agent2
$ cd zabbix-agent2
#     master ,            
$ git checkout -b feature/myplugin release/4.4

src/go/plugins/weather weather.go , .


, "zabbix.com/pkg/plugin".


package weather
import  "zabbix.com/pkg/plugin"

, Base plugin. .


type Plugin struct {
    plugin.Base
}
var impl Plugin

. , , :


  1. GET API (, wttr.in)
  2. .

Exporter.


func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) {
    if len(params) != 1 {
        return nil, errors.New("Wrong parameters.")
    }

    // https://github.com/chubin/wttr.in
    res, err := p.httpClient.Get(fmt.Sprintf("https://wttr.in/~%s?format=%%t", params[0]))
    if err != nil {
        return nil, err
    }

    temp, err := ioutil.ReadAll(res.Body)
    _ = res.Body.Close()
    if err != nil {
        return nil, err
    }

    return string(temp)[0 : len(temp)-4], nil
}

тАФ , .


plugin.RegisterMetrics.


// impl тАФ    
// name тАФ  
// params тАФ       (key1, descr1, key2, descr2, keyN, descrN...)
func RegisterMetrics(impl Accessor, name string, params ...string)

init ( ).


func init() {
  plugin.RegisterMetrics(&impl, "Weather", "weather.temp", "Returns Celsius temperature.")
}

, .


package plugins 
import (
  _ "zabbix.com/plugins/kernel"
  _ "zabbix.com/plugins/log"
// ...
  _ "zabbix.com/plugins/weather"
)

, 3 : linux, darwin windows. , , , .


: , src/go/plugins/plugins_<platform>.go.



Go, .


Go 1.13.


, --enable-agent2 make.


$ cd <zabbix-source>
$ ./bootstrap.sh; ./configure --enable-agent2 --enable-static; make

, . тАЬmoscowтАЭ .


$ <zabbix-source>/src/go/bin/zabbix_agent2 -t weather.temp[moscow]
+1

. , go run, .


$ go run <zabbix-source>/src/go/cmd/zabbix_agent2/zabbix_agent2.go


, zabbix.com/pkg/log: Tracef, Debugf, Warningf, Infof, Errf, Critf. Plugin, log. , [<PluginName>] .



Go- . Plugins. , -, , , . : Plugins.<PluginName>.<Parameter>=<Value>. :


  1. , ;
  2. ;
  3. ;
  4. ;
  5. .

, Configurator. Timeout, HTTP .


, , Timeout 1 30 , ( ) .


, .


type PluginOptions struct {
    // Timeout is the maximum time for waiting when a request has to be done. Default value equals the global timeout.
    Timeout int `conf:"optional,range=1:30"`
}

, conf. .


: [name=<name>,][optional,][range=<range>,][default=<default value>], :


  • <name> тАФ ( );
  • optional тАФ , ;
  • <range> тАФ <min>:<max>, <min> <max> ;
  • <default value> тАФ . .

Plugin , тАФ http.Client, .


type Plugin struct {
    plugin.Base
    options PluginOptions
    httpClient http.Client
}

Configurator. , 2 : Configure Validate.


func (p *Plugin) Configure(global *plugin.GlobalOptions, privateOptions interface{}) {
    if err := conf.Unmarshal(privateOptions, &p.options); err != nil {
        p.Errf("cannot unmarshal configuration options: %s", err)
    }

    // Set default value
    if p.options.Timeout == 0 {
        p.options.Timeout = global.Timeout
    }

    p.httpClient = http.Client{Timeout: time.Duration(p.options.Timeout) * time.Second}
}

func (p *Plugin) Validate(privateOptions interface{}) error {
    // Nothing to validate
    return nil
}

conf.Unmarshal .
http.Get p.httpClient.Get.


res, err := p.httpClient.Get(fmt.Sprintf("https://wttr.in/~%s?format=%%t", params[0]))
if err != nil {
    if err.(*url.Error).Timeout() {
        return nil, errors.New("Request timeout.")
    }
    return nil, err
}

, :
Plugins.Weather.Timeout=1


, .


, - ? тАФ . Timeout default, .. .


( , Validate Configure).


, . , , , , . Validate. , .


func (p *Plugin) Validate(privateOptions interface{}) error {
    var opts PluginOptions
    return conf.Unmarshal(privateOptions, &opts)
}

, , : "cannot create scheduling manager: invalid plugin Weather configuration: Cannot assign configuration: invalid parameter Plugins.Weather.Timeout at line 411: value out of range".


" ". runtime Validate Configure, . , - Configure тАФ , . , Start Stop ( Runner).


: https://github.com/VadimIpatov/zabbix-weather-plugin.



Exporter . . , Collector Runner.


! - . , HTTP .


. "net/http/httptrace" ( Go 1.7).


type timeSample struct {
    DnsLookup         float64 `json:"dnsLookup"`
    Connect           float64 `json:"connect"`
    TlsHandshake      float64 `json:"tlsHandshake"`
    FirstResponseByte float64 `json:"firstResponseByte"`
    Rtt               float64 `json:"rtt"`
}

func (p *Plugin) measureTime(url string) (timeSample, error) {
    var (
        sample                            timeSample
        start, connect, dns, tlsHandshake time.Time
    )

    req, _ := http.NewRequest("GET", url, nil)

    trace := &httptrace.ClientTrace{
        DNSStart: func(_ httptrace.DNSStartInfo) {
            dns = time.Now()
        },
        DNSDone: func(_ httptrace.DNSDoneInfo) {
            sample.DnsLookup = float64(time.Since(dns) / time.Millisecond)
        },

        ConnectStart: func(_, _ string) {
            connect = time.Now()
        },
        ConnectDone: func(net, addr string, err error) {
            if err != nil {
                p.Errf("unable to connect to host %s: %s", addr, err.Error())
            }
            sample.Connect = float64(time.Since(connect) / time.Millisecond)
        },

        GotFirstResponseByte: func() {
            sample.FirstResponseByte = float64(time.Since(start) / time.Millisecond)
        },

        TLSHandshakeStart: func() {
            tlsHandshake = time.Now()
        },
        TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
            sample.TlsHandshake = float64(time.Since(tlsHandshake) / time.Millisecond)
        },
    }

    ctx, cancel := context.WithTimeout(req.Context(), time.Duration(p.options.Timeout)*time.Second)
    defer cancel()
    req = req.WithContext(httptrace.WithClientTrace(ctx, trace))

    start = time.Now()
    if _, err := http.DefaultTransport.RoundTrip(req); err != nil {
        return timeSample{}, err
    }
    sample.Rtt = float64(time.Since(start) / time.Millisecond)

    return sample, nil
}

- . (Ring Buffer). , тАФ github.com/VadimIpatov/gcircularqueue. , . Open Source Go тАФ github.com/montanaflynn/stats. .


type Plugin struct {
    plugin.Base
    urls map[string]*urlUnit
    sync.Mutex
    options Options
}

type urlUnit struct {
    url      string
    history  *gcircularqueue.CircularQueue
    accessed time.Time // last access time
    modified time.Time // data collect time
}

Start Stop Runner.


func (p *Plugin) Start() {
    p.urls = make(map[string]*urlUnit)
}

func (p *Plugin) Stop() {
    p.urls = nil
}

Collector.


func (p *Plugin) Collect() (err error) {
    now := time.Now()
    p.Lock()
    for key, url := range p.urls {
        if now.Sub(url.accessed) > maxInactivityPeriod {
            p.Debugf("removed expired url %s", url.url)
            delete(p.urls, key)
            continue
        }
        res, err := p.measureTime(url.url)
        if err != nil {
            p.Errf(err.Error())
            continue
        }
        url.history.Push(res)
        if url.history.IsFull() {
            _ = url.history.Shift()
        }
        url.modified = now
    }
    p.Unlock()

    return
}

func (p *Plugin) Period() int {
    return p.options.Interval
}

URL ( ), p.measureTime(url.url) . , url.modified.


URL , .


, Collector . Exporter.


func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) {
    if len(params) != 1 {
        return nil, errors.New("Wrong parameters.")
    }

    url, err := parseURL(params[0])
    if err != nil {
        return nil, err
    }

    switch key {
    case keyHttpTraceStats:
        if _, ok := p.urls[url]; !ok {
            p.urls[url] = &urlUnit{
                url:     url,
                history: gcircularqueue.NewCircularQueue(maxHistory),
            }
        }
        p.Lock()
        defer p.Unlock()
        p.urls[url].accessed = time.Now()
        if p.urls[url].history.Len() < minStatRange {
            // no data gathered yet
            return
        }

        data := prepareData(p.urls[url].history.Elements())

        jsonRes, err := json.Marshal(stat{
            // Median: timeSample{...},
            // P75:    timeSample{...},
            // P95:    timeSample{...},
            P99: timeSample{
                DnsLookup:         percentile(data[metricDnsLookup], p99),
                Connect:           percentile(data[metricConnect], p99),
                TlsHandshake:      percentile(data[metricTlsHandshake], p99),
                FirstResponseByte: percentile(data[metricFirstResponseByte], p99),
                Rtt:               percentile(data[metricRtt], p99),
            },
        })
        if err != nil {
            p.Errf(err.Error())
            return nil, errors.New("Cannot marshal JSON.")
        }

        value := string(jsonRes)
        return plugin.Result{
            Value: &value,
            Ts:    p.urls[url].modified,
        }, nil

    default:
        return nil, plugin.UnsupportedMetricError
    }
}

, Collect Export, .. .
( ):


$ zabbix_get -s zabbix.local -k "httptrace.stats[yoursite.com]"
{
    "median": {
        "dnsLookup": 13,
        "connect": 28,
        "tlsHandshake": 56,
        "firstResponseByte": 126.5,
        "rtt": 126.5
    },
    "p75": {
        "dnsLookup": 20,
        "connect": 31,
        "tlsHandshake": 60,
        "firstResponseByte": 138.5,
        "rtt": 138.5
    },
    "p95": {
        "dnsLookup": 22.5,
        "connect": 35,
        "tlsHandshake": 78.5,
        "firstResponseByte": 159.5,
        "rtt": 159.5
    },
    "p99": {
        "dnsLookup": 50,
        "connect": 51.5,
        "tlsHandshake": 125.5,
        "firstResponseByte": 266.5,
        "rtt": 266.5
    }
}

: https://github.com/VadimIpatov/zabbix-httptrace-plugin.



runtime metrics, . .
:


$ zabbix_agent2 -R metrics
...
[Weather]
active: true
capacity: 0/100
tasks: 0
weather.temp: Returns Celsius temperature.
...

тАФ HTTP. StatusPort=, http://<ZabbixAgentHost>:<Port>/status.


?


. , :


  • ( ).
  • , .. .
  • Zabbix. , DockerтАЩ Mysql.

!


, , :
Templates guidelines тАФ .
An official guide to making and managing great templates тАФ Zabbix Summit .
Magic of the new zabbix agent тАФ Zabbix Agent 2.
Zabbix Agent 2.
Zabbix Agent 2 ( : git.zabbix.com). , .
Weather.
HttpTrace.
Writing watcher Zabbix Agent2 MQTT plugin in Go тАФ Watcher .
Go, " Go" , , A Tour of Go ╩ХтШЙ╤атШЙ╩Ф



Zabbix Agent 2 тАФ Zabbix . Go, , , C Loadable modules.


Stay tuned!


P.S. .


All Articles