Added influxdb as a metrics reporter/backend

This commit is contained in:
Geoff Bourne 2020-01-04 14:22:01 -06:00
parent 7f50c512f5
commit a12bbb88a3
7 changed files with 171 additions and 28 deletions

View File

@ -9,13 +9,42 @@ Routes Minecraft client connections to backend servers based upon the requested
# Usage # Usage
```text ```text
Flags: -api-binding host:port
--help Show context-sensitive help (also try --help-long The host:port bound for servicing API requests (env API_BINDING)
and --help-man). -connection-rate-limit int
--port=25565 The port bound to listen for Minecraft client Max number of connections to allow per second (env CONNECTION_RATE_LIMIT) (default 1)
connections -cpu-profile string
--api-binding=API-BINDING The host:port bound for servicing API requests Enables CPU profiling and writes to given path (env CPU_PROFILE)
--mapping=MAPPING,MAPPING Where MAPPING is externalHostname=host:port -debug
Enable debug logs (env DEBUG)
-in-kube-cluster
Use in-cluster kubernetes config (env IN_KUBE_CLUSTER)
-kube-config string
The path to a kubernetes configuration file (env KUBE_CONFIG)
-kube-discovery
Enables discovery of annotated kubernetes services (env KUBE_DISCOVERY)
-mapping string
Comma-separated mappings of externalHostname=host:port (env MAPPING)
-metrics-backend string
Backend to use for metrics exposure/publishing: discard,expvar,influxdb (env METRICS_BACKEND) (default "discard")
-metrics-backend-config-influxdb-addr string
(env METRICS_BACKEND_CONFIG_INFLUXDB_ADDR)
-metrics-backend-config-influxdb-database string
(env METRICS_BACKEND_CONFIG_INFLUXDB_DATABASE)
-metrics-backend-config-influxdb-interval duration
(env METRICS_BACKEND_CONFIG_INFLUXDB_INTERVAL) (default 1m0s)
-metrics-backend-config-influxdb-password string
(env METRICS_BACKEND_CONFIG_INFLUXDB_PASSWORD)
-metrics-backend-config-influxdb-retention-policy string
(env METRICS_BACKEND_CONFIG_INFLUXDB_RETENTION_POLICY)
-metrics-backend-config-influxdb-tags value
any extra tags to be included with all reported metrics (env METRICS_BACKEND_CONFIG_INFLUXDB_TAGS)
-metrics-backend-config-influxdb-username string
(env METRICS_BACKEND_CONFIG_INFLUXDB_USERNAME)
-port port
The port bound to listen for Minecraft client connections (env PORT) (default 25565)
-version
Output version and exit (env VERSION)
``` ```
# REST API # REST API

View File

@ -13,19 +13,34 @@ import (
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
"time"
) )
type MetricsBackendConfig struct {
Influxdb struct {
Interval time.Duration `default:"1m"`
Tags map[string]string `usage:"any extra tags to be included with all reported metrics"`
Addr string
Username string
Password string
Database string
RetentionPolicy string
}
}
type Config struct { type Config struct {
Port int `default:"25565" usage:"The [port] bound to listen for Minecraft client connections"` Port int `default:"25565" usage:"The [port] bound to listen for Minecraft client connections"`
Mapping string `usage:"Comma-separated mappings of externalHostname=host:port"` Mapping string `usage:"Comma-separated mappings of externalHostname=host:port"`
ApiBinding string `usage:"The [host:port] bound for servicing API requests"` ApiBinding string `usage:"The [host:port] bound for servicing API requests"`
Version bool `usage:"Output version and exit"` Version bool `usage:"Output version and exit"`
CpuProfile string `usage:"Enables CPU profiling and writes to given path"` CpuProfile string `usage:"Enables CPU profiling and writes to given path"`
Debug bool `usage:"Enable debug logs"` Debug bool `usage:"Enable debug logs"`
ConnectionRateLimit int `default:"1" usage:"Max number of connections to allow per second"` ConnectionRateLimit int `default:"1" usage:"Max number of connections to allow per second"`
InKubeCluster bool `usage:"Use in-cluster kubernetes config"` KubeDiscovery bool `usage:"Enables discovery of annotated kubernetes services"`
KubeConfig string `usage:"The path to a kubernetes configuration file"` InKubeCluster bool `usage:"Use in-cluster kubernetes config"`
MetricsBackend string `default:"discard" usage:"Backend to use for metrics exposure/publishing: discard,expvar"` KubeConfig string `usage:"The path to a kubernetes configuration file"`
MetricsBackend string `default:"discard" usage:"Backend to use for metrics exposure/publishing: discard,expvar,influxdb"`
MetricsBackendConfig MetricsBackendConfig
} }
var ( var (
@ -71,8 +86,9 @@ func main() {
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel()
metricsBuilder := NewMetricsBuilder(config.MetricsBackend) metricsBuilder := NewMetricsBuilder(config.MetricsBackend, &config.MetricsBackendConfig)
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
@ -98,22 +114,27 @@ func main() {
if config.InKubeCluster { if config.InKubeCluster {
err = server.K8sWatcher.StartInCluster() err = server.K8sWatcher.StartInCluster()
if err != nil { if err != nil {
logrus.WithError(err).Warn("Unable to start k8s integration") logrus.WithError(err).Fatal("Unable to start k8s integration")
} else { } else {
defer server.K8sWatcher.Stop() defer server.K8sWatcher.Stop()
} }
} else if config.KubeConfig != "" { } else if config.KubeConfig != "" {
err := server.K8sWatcher.StartWithConfig(config.KubeConfig) err := server.K8sWatcher.StartWithConfig(config.KubeConfig)
if err != nil { if err != nil {
logrus.WithError(err).Warn("Unable to start k8s integration") logrus.WithError(err).Fatal("Unable to start k8s integration")
} else { } else {
defer server.K8sWatcher.Stop() defer server.K8sWatcher.Stop()
} }
} }
err = metricsBuilder.Start(ctx)
if err != nil {
logrus.WithError(err).Fatal("Unable to start metrics reporter")
}
// wait for process-stop signal
<-c <-c
logrus.Info("Stopping") logrus.Info("Stopping")
cancel()
} }
func parseMappings(val string) map[string]string { func parseMappings(val string) map[string]string {

View File

@ -1,31 +1,44 @@
package main package main
import ( import (
"context"
"errors"
"fmt"
kitlogrus "github.com/go-kit/kit/log/logrus"
discardMetrics "github.com/go-kit/kit/metrics/discard" discardMetrics "github.com/go-kit/kit/metrics/discard"
expvarMetrics "github.com/go-kit/kit/metrics/expvar" expvarMetrics "github.com/go-kit/kit/metrics/expvar"
kitinflux "github.com/go-kit/kit/metrics/influx"
influx "github.com/influxdata/influxdb1-client/v2"
"github.com/itzg/mc-router/server" "github.com/itzg/mc-router/server"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"strings"
"time"
) )
type MetricsBuilder interface { type MetricsBuilder interface {
BuildConnectorMetrics() *server.ConnectorMetrics BuildConnectorMetrics() *server.ConnectorMetrics
Start(ctx context.Context) error
} }
func NewMetricsBuilder(backend string) MetricsBuilder { func NewMetricsBuilder(backend string, config *MetricsBackendConfig) MetricsBuilder {
switch backend { switch strings.ToLower(backend) {
case "discard":
return &discardMetricsBuilder{}
case "expvar": case "expvar":
return &expvarMetricsBuilder{} return &expvarMetricsBuilder{}
case "influxdb":
return &influxMetricsBuilder{config: config}
default: default:
logrus.Fatalf("Unsupported metrics backend: %s", backend) return &discardMetricsBuilder{}
return nil
} }
} }
type expvarMetricsBuilder struct { type expvarMetricsBuilder struct {
} }
func (b expvarMetricsBuilder) Start(ctx context.Context) error {
// nothing needed
return nil
}
func (b expvarMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics { func (b expvarMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
return &server.ConnectorMetrics{ return &server.ConnectorMetrics{
Errors: expvarMetrics.NewCounter("errors").With("subsystem", "connector"), Errors: expvarMetrics.NewCounter("errors").With("subsystem", "connector"),
@ -38,6 +51,11 @@ func (b expvarMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
type discardMetricsBuilder struct { type discardMetricsBuilder struct {
} }
func (b discardMetricsBuilder) Start(ctx context.Context) error {
// nothing needed
return nil
}
func (b discardMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics { func (b discardMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
return &server.ConnectorMetrics{ return &server.ConnectorMetrics{
Errors: discardMetrics.NewCounter(), Errors: discardMetrics.NewCounter(),
@ -46,3 +64,50 @@ func (b discardMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics
ActiveConnections: discardMetrics.NewGauge(), ActiveConnections: discardMetrics.NewGauge(),
} }
} }
type influxMetricsBuilder struct {
config *MetricsBackendConfig
metrics *kitinflux.Influx
}
func (b *influxMetricsBuilder) Start(ctx context.Context) error {
influxConfig := &b.config.Influxdb
if influxConfig.Addr == "" {
return errors.New("influx addr is required")
}
ticker := time.NewTicker(influxConfig.Interval)
client, err := influx.NewHTTPClient(influx.HTTPConfig{
Addr: influxConfig.Addr,
Username: influxConfig.Username,
Password: influxConfig.Password,
})
if err != nil {
return fmt.Errorf("failed to create influx http client: %w", err)
}
go b.metrics.WriteLoop(ctx, ticker.C, client)
logrus.WithField("addr", influxConfig.Addr).
Debug("reporting metrics to influxdb")
return nil
}
func (b *influxMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
influxConfig := &b.config.Influxdb
metrics := kitinflux.New(influxConfig.Tags, influx.BatchPointsConfig{
Database: influxConfig.Database,
RetentionPolicy: influxConfig.RetentionPolicy,
}, kitlogrus.NewLogrusLogger(logrus.StandardLogger()))
b.metrics = metrics
return &server.ConnectorMetrics{
Errors: metrics.NewCounter("errors"),
BytesTransmitted: metrics.NewCounter("transmitted_bytes"),
Connections: metrics.NewCounter("connections"),
ActiveConnections: metrics.NewGauge("connections_active"),
}
}

5
go.mod
View File

@ -5,6 +5,8 @@ go 1.12
require ( require (
github.com/VividCortex/gohistogram v1.0.0 // indirect github.com/VividCortex/gohistogram v1.0.0 // indirect
github.com/go-kit/kit v0.9.0 github.com/go-kit/kit v0.9.0
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/gogo/protobuf v1.2.1 // indirect github.com/gogo/protobuf v1.2.1 // indirect
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
github.com/golang/protobuf v1.3.1 // indirect github.com/golang/protobuf v1.3.1 // indirect
@ -13,7 +15,8 @@ require (
github.com/gorilla/mux v1.7.1 github.com/gorilla/mux v1.7.1
github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/imdario/mergo v0.3.7 // indirect github.com/imdario/mergo v0.3.7 // indirect
github.com/itzg/go-flagsfiller v1.4.1 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
github.com/itzg/go-flagsfiller v1.4.2
github.com/json-iterator/go v1.1.6 // indirect github.com/json-iterator/go v1.1.6 // indirect
github.com/juju/ratelimit v1.0.1 github.com/juju/ratelimit v1.0.1
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect

8
go.sum
View File

@ -6,6 +6,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
@ -25,8 +29,12 @@ github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFE
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/itzg/go-flagsfiller v1.4.1 h1:h/t5g+WkvsOR449bz1ngU8UGosKNm4Sr3iMNNgOqHfo= github.com/itzg/go-flagsfiller v1.4.1 h1:h/t5g+WkvsOR449bz1ngU8UGosKNm4Sr3iMNNgOqHfo=
github.com/itzg/go-flagsfiller v1.4.1/go.mod h1:mfQgTahSs4OHn8PYev2Wwi1LJXUiYiGuZVCpBLxzbYs= github.com/itzg/go-flagsfiller v1.4.1/go.mod h1:mfQgTahSs4OHn8PYev2Wwi1LJXUiYiGuZVCpBLxzbYs=
github.com/itzg/go-flagsfiller v1.4.2 h1:aQRyQ9/ps111FZYGhzxgKYiOewE7zbAks7rxF49Qlm8=
github.com/itzg/go-flagsfiller v1.4.2/go.mod h1:mfQgTahSs4OHn8PYev2Wwi1LJXUiYiGuZVCpBLxzbYs=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY= github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY=

View File

@ -0,0 +1,9 @@
version: '3'
services:
telegraf:
image: telegraf:1.13
ports:
- 8186:8186
volumes:
- ./telegraf.conf:/etc/telegraf/telegraf.conf:ro

View File

@ -0,0 +1,8 @@
[agent]
interval = "10s"
[[inputs.influxdb_listener]]
service_address = ":8186"
[[outputs.file]]
files = ["stdout"]