From cdbf50ef0a860621edb6d18103c8468b65adfb28 Mon Sep 17 00:00:00 2001 From: Yonghwan SO Date: Sat, 30 Apr 2022 13:36:26 +0900 Subject: [PATCH] updated readme.md, also fixed multi-exporter bug --- README.md | 224 ++++++++++++++++++++++++++++-------- cmd/bogo/main.go | 4 +- exporters/exporters.go | 10 ++ exporters/exporters_test.go | 13 +++ exporters/stackdriver.go | 10 +- exporters/stdout.go | 8 +- version.go | 2 +- 7 files changed, 203 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index c4a32db..fb4b203 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Bogo, an event collector/reporter for Hyeoncheon. +# Bogo, a looking glass for Hyeoncheon [![Test](https://github.com/hyeoncheon/bogo/actions/workflows/test.yml/badge.svg)](https://github.com/hyeoncheon/bogo/actions/workflows/test.yml) [![DeepSource](https://deepsource.io/gh/hyeoncheon/bogo.svg/?label=active+issues&token=suXiU-8eOt2HTIniLbcCLbq2)](https://deepsource.io/gh/hyeoncheon/bogo/?ref=repository-badge) @@ -7,34 +7,41 @@ [![Maintainability](https://api.codeclimate.com/v1/badges/1a36b1292948783341d0/maintainability)](https://codeclimate.com/github/hyeoncheon/bogo/maintainability) [![Go Report Card](https://goreportcard.com/badge/github.com/hyeoncheon/bogo)](https://goreportcard.com/report/github.com/hyeoncheon/bogo) [![Go Reference](https://img.shields.io/badge/go-reference-blue)](https://pkg.go.dev/github.com/hyeoncheon/bogo) - [![GitHub license](https://img.shields.io/github/license/hyeoncheon/bogo)](https://github.com/hyeoncheon/bogo/blob/main/LICENSE.md) +Bogo, a part of the Hyeoncheon project, is a looking glass agent for +diagnosing network connectivity issue. -Bogo is an event collector and reporter for the Hyeoncheon project.cw -It will be a successor of Kyeong which was ruby based event collector. -(Basically it was designed for Hyeoncheon project but currently works as -standalone collector) +Bogo was originally designed as a general-purpose event collector and reporter +for the Hyeoncheon project, as a successor of Kyeong which was written in Ruby +programming language, but now the focusing area of Bogo is networking and also +it is written in Go programming language. -Currently, it only runs on a GCE(Google Compute Engine) VM when you want -to use integrated exporter for Google Cloud Monitoring. Also, only ping -checker is supported now. +Note: when it reports to Google Cloud Monitoring(formerly known as Stackdriver +Monitoring) using `stackdriver` exporter, the feature is only supported when +Bogo is running on a GCE instance for now. +## Features -## Feature +As a periodic connectivity data reporter: -* reports ping latency and packet loss from prober vm to specified targets. - (powered by https://github.com/go-ping/ping) -* exports to standard out -* exports to Google Cloud Monitoring (fka Stackdriver Monitoring) +* Reports ping latency and packet loss to targets periodically. +* Reports its own health status to check if the agent is running. +* Exports collected data to Google Cloud Monitoring. +* Exports collected data to the standard output (mostly for test purposes). +* Targets can be specified with GCE metadata. -Currently, the default (not fallback) exporter is the Stackdriver exporter. -For using this exporter, the prober VM should runs on the GCE vm to interact -with Google Cloud Monitoring automatically without any additional exporter -configuration. +The default exporter is the `stackdriver` exporter that reports to Google +Cloud Monitoring. For this, the agent must run on a GCE instance to get proper +information such as project, zone (which indicates the location of the prober) +it runs on, and the connection to Google Cloud Monitoring. +When it runs on a GCE instance, it will collect all necessary information from +the metadata server of the instance, including ping targets, and it makes the +configuration easier. -You can find the exported metrics on the project's monitoring explorer: +You can find the exported metrics on the project's monitoring explorer with +the following keys: * `custom.googleapis.com/bogo/ping/packet_loss` * `custom.googleapis.com/bogo/ping/rtt_average` @@ -42,13 +49,42 @@ You can find the exported metrics on the project's monitoring explorer: The best display mode is "Group by: mean". +As a looking glass server: + +* Handles `/mtr` request to allow users to see the traceroute from the agent + to the specified destination. + +The response for the MTR request is the raw output of the `mtr` command. To +use this feature, you need to install the `mtr` command and make sure it is +executable from the agent process's execution environment. + +Currently, Bogo does not support access control (to deny or to allow specific +sources of the requests) by itself so you need to configure firewall rules or +need to place the agent behind any reverse proxy which support access control. + ## Architecture and Components +Bogo consists of checkers, exporters, request handlers, and the webserver to +run the handlers. +Checkers are data collectors. it can be expanded to collect any type of data, +however, it does not support external checkers so all checkers are compiled as +a single binary. +Currently, `healthcheck` checker and `ping` checker are supported. -## Data Flow +Exporters are metric exporters. They are a kind of interface to the monitoring +systems such as Google Cloud Monitoring. The technical/internal mechanism of +them is the same as checkers. +For now, `stackdriver` and `stdout` are supported. +Request handlers are basically HTTP request handlers that serve requests from +the users. Checkers and exporters run periodically, but handlers run request +basis. +Supported paths are `/mtr` and `/echo`. + +The web server of Bogo is not a full-featured web server. It is just designed +to serve specific, pre-configured requests such as `/mtr` as described above. ## Install @@ -60,8 +96,7 @@ by cloning this repo manually and run `go build` command. ### Requirement -As of now, supported Go version is 1.16. (well tested, but not fully tested) -However, it works fine with go 1.17, and I guess 1.18 also fine. +Supported Go versions are 1.16 or above. ### Get and Build @@ -70,33 +105,114 @@ Just get it! ```console $ go install github.com/hyeoncheon/bogo/cmd/bogo@latest -go: downloading github.com/hyeoncheon/bogo v0.1.0 +go: downloading github.com/hyeoncheon/bogo v0.4.0 $ which bogo /home/sio4/go/bin/bogo $ bogo --version -bogo v0.1.0 +bogo v0.4.0 $ ``` - ## Setup and Run -Since Bogo runs on GCP VM, you need to configure a GCP project and VMs. +Desired running environment is GCE. This section assumes you are running Bogo +on a GCE environment. (This is not a limitation for whole feature of Bogo, but +`stackdriver` exporter, currently, only runs on GCE environment.) -### Configure Prober VM on GCP +### Runtime Options -To write the monitoring data from VM instances, your VMs should have the -following access scope. (This scope is included in the default scope so -no additional job required if you already use the default scope.) +You can see the available options by running it with `--help`. + +```console +$ bogo --help +bogo v0.4.0 +Usage: bogo [-dhv] [-a value] [-c value] [--copts value] [--eopts value] [-e value] [-l value] [parameters ...] + -a, --address=value + webserver's listen address [127.0.0.1:6090] + -c, --checkers=value + set checkers + --copts=value checker options + -d, --debug debugging mode + --eopts=value exporter options + -e, --exporter=value + set exporter [stackdriver] + -h, --help show help message + -l, --log=value log level. (debug, info, warn, or error) [info] + -v, --version show version +$ +``` + +Checkers and Exporters (`-c` and `-e`) can be configured with a comma-separated +list of checkers and exporters accordingly. By default, all checkers are +enabled if no `-c` (or `--checkers`) option is provided, and `stackdriver` +exporter will be enabled by default if no `-e` (or `--exporter`) option is +provided. + +Note: Bogo does not support multiple exporters at the same time for now even +thought the option accepts list of exporters. + + +### Plugin Options + +Additionally, Bogo plugins (checkers and exporters) support configurable +options depending on each but they are not shown in the help message. The +`--copts` and `--eopts` are used and the supported format is: + +``` +::;... +``` + +For example, the following command means: + +* Enables all checkers (no `-c` option is provided) +* Enables `stackdriver` and `stdout` exporters. (`-e`) +* Configures `ping` exporter with + * `www.example.com` and `master` as ping targets. + * 30 seconds of checking interval. + * 500 milliseconds of ping interval. + +```console +$ bogo --copts "ping:targets:www.example.com,master;ping:check_interval:30;ping:ping_interval:500" -e stackdriver,stdout +``` + +Available plugin options are: + +* `heartbeat` checker + * `interval`: heartbeat interval. (default: 60 seconds) +* `ping` checker + * `check_interval`: time interval in seconds for checks. + (default: 30 seconds) + * `ping_interval`: time interval in milliseconds for ping requests. + (default: 1000 milliseconds) + +Note: ping count is fixed to 10 times, so if you configured `ping_interval` as +1000 milliseconds, the check will spend 10 seconds for each which means you +should not configure `check_interval` less than 10 seconds. Otherwise, Bogo +will ping the target too much. + +Note: ping targets could be configured with plugin option but the easiest +way to configure the targets is using GCE metadata. `ping` checker will use +the value provided with the option but if no option is provided for target, +it will check the metadata and uses it. Instance metadata will be checked, +but if there is no instance metadata, then it will check project metadata. +When you runs many probers, using project metadata will be the best option. + + +### Configure Prober Instance on GCE + +To write the monitoring data from GCE instances, using `stackdriver` exporter, +your prober instances should have the following access scope. (This scope is +included in the default scope so no additional job is required if you already +use the default scope.) * `https://www.googleapis.com/auth/monitoring.write` -Bogo uses https://github.com/go-ping/ping and it requires running following -command on the prober VM. I would suggest you to add the following command -on your startup script. (See also bundled `startup` script. This script can -be used as startup script for prober vms.) +Bogo uses https://github.com/go-ping/ping internally and it requires running +the following command on the prober instance. I would suggest you add the +following command to your startup script. (See also bundled `startup` script. +This script can be used as a startup script for prober instances.) ```console sudo sysctl -w net.ipv4.ping_group_range="0 2147483647" @@ -108,31 +224,38 @@ sudo sysctl -w net.ipv4.ping_group_range="0 2147483647" ### Run -Currently, it does not support daemon mode. To run it in background, -please consider the other method such as `nohup` or run by `at` command. +Currently, it does not support daemon mode. To run it in background, please +consider the other method such as `nohup` or run by `at` command. (See also bundled `startup` script) ```console -$ ./bogo www.google.com www.youtube.com -starting bogo for [www.google.com www.youtube.com] -stackdriver exporter: initialize exporter... -stat: 74.125.70.104 10 10 0 1.004398ms 1.444576ms 3.124352ms 582.238µs -stat: 142.251.120.93 10 10 0 1.562428ms 2.002906ms 3.66068ms 566.932µs -stat: 142.250.152.106 10 10 0 598.31µs 782.081µs 959.154µs 91.505µs -stat: 142.250.1.190 10 10 0 613.655µs 692.17µs 766.469µs 45.128µs -^Csignal caught: interrupt -interrupted! -stackdriver: bye +$ ./bogo -e stdout -c ping --copts "ping:targets:www.google.com,www.youtube.com" +WARN[0000] hey, it seems like I am on a legacy server or unsupported cloud! +INFO[0000] starting checker ping... module=checker +INFO[0000] starting exporter stdout... module=exporter +INFO[0000] 1 checkers and 1 exporters started +INFO[0000] starting webserver on 127.0.0.1:6090... +INFO[0000] ping www.google.com every 30s checker=ping +INFO[0000] ping www.youtube.com every 30s checker=ping +⇨ http server started on 127.0.0.1:6090 +INFO[0039] ping: {www.youtube.com 172.217.175.14 10 0 33.175457ms 290.418808ms 109.783825ms 84.456195ms} exporter=stdout +INFO[0039] ping: {www.google.com 172.217.26.228 10 0 31.665286ms 294.040755ms 111.926156ms 85.734723ms} exporter=stdout +^CWARN[0040] signal caught: interrupt +INFO[0040] shutting down webserver... +INFO[0040] webserver closed +INFO[0040] ping checker for www.youtube.com exited checker=ping +INFO[0040] stdout exporter exited exporter=stdout +INFO[0040] ping checker for www.google.com exited checker=ping $ ``` ### Run in debugging mode -Bogo has `-d` flag but currently not working (since debuggins is enabled by -default :-) You can also use standard out exporter for testing purpose. -(`bogo -e stdout`) - +Bogo supports `-d` flag for debugging purposes. The flag enables debugging +output and you can check some more information about its internal flows. +Also, you can use `stdout` exporter to see which data is collected from the +console. ## Author @@ -140,7 +263,6 @@ default :-) You can also use standard out exporter for testing purpose. Yonghwan SO https://github.com/sio4, http://www.sauru.so - ## Copyright (GNU General Public License v3.0) Copyright 2020-2022 Yonghwan SO diff --git a/cmd/bogo/main.go b/cmd/bogo/main.go index 51af5f2..e844aca 100644 --- a/cmd/bogo/main.go +++ b/cmd/bogo/main.go @@ -31,7 +31,7 @@ func main() { getopt.FlagLong(&copts, "copts", 0, "checker options") getopt.FlagLong(&eopts, "eopts", 0, "exporter options") - getopt.FlagLong(&opts.Checkers, "checker", 'c', "set checker") + getopt.FlagLong(&opts.Checkers, "checkers", 'c', "set checkers") getopt.FlagLong(&opts.Exporters, "exporter", 'e', "set exporter") getopt.FlagLong(&opts.Address, "address", 'a', "webserver's listen address") getopt.FlagLong(&opts.LogLevel, "log", 'l', "log level. (debug, info, warn, or error)") @@ -129,7 +129,7 @@ func startWebRoutine(c common.Context, opts *common.Options) (meari.Server, erro go func() { // nolint defer c.WG().Done() - logger.Info("starting webserver on %s...", server.Address()) + logger.Infof("starting webserver on %s...", server.Address()) err := server.Serve() if errors.Is(err, http.ErrServerClosed) { diff --git a/exporters/exporters.go b/exporters/exporters.go index c37ee06..d5996de 100644 --- a/exporters/exporters.go +++ b/exporters/exporters.go @@ -32,6 +32,16 @@ func StartAll(c common.Context, opts *common.Options, ch chan interface{}) int { logger := c.Logger().WithField("module", "exporter") n := 0 + // TODO: oh! this is design issue. + // currently there is only one channel and exporters will pick the message in + // random order. limiting only one exporter should be run at the same time, but + // could be improved by chaining the interface if it is required. + if len(opts.Exporters) != 1 { + logger.Error("only one exporter is supported!") + + return n + } + for _, x := range common.Plugins(reflect.TypeOf(&Exporter{})) { if len(opts.Exporters) > 0 && !common.Contains(opts.Exporters, x.Name()) { logger.Debugf("%v is not on the exporter list. skipping...", x.Name()) diff --git a/exporters/exporters_test.go b/exporters/exporters_test.go index 0e439a3..54d7cc2 100644 --- a/exporters/exporters_test.go +++ b/exporters/exporters_test.go @@ -26,3 +26,16 @@ func TestStartAll(t *testing.T) { ctx.Cancel() } + +func TestStartAll_MoreThanOne(t *testing.T) { + r := require.New(t) + opts := common.DefaultOptions() + opts.Exporters = []string{"stdout", "stackdriver"} + opts.ExporterOptions = map[string]common.PluginOptions{} + ctx, _ := common.NewDefaultContext(&opts) + + n := StartAll(ctx, &opts, ctx.Channel()) + r.Equal(0, n) + + ctx.Cancel() +} diff --git a/exporters/stackdriver.go b/exporters/stackdriver.go index b14524d..cd496f6 100644 --- a/exporters/stackdriver.go +++ b/exporters/stackdriver.go @@ -15,10 +15,9 @@ import ( ) const ( - stackdriverMetricPrefix = "custom.googleapis.com/bogo" - stackdriverExporter = "stackdriver" - stackdriverExporterInterval = 1 * time.Minute - recordTimeout = 30 * time.Second + stackdriverMetricPrefix = "custom.googleapis.com/bogo" + stackdriverExporter = "stackdriver" + recordTimeout = 30 * time.Second ) // RegisterStackdriver returns a new Exporter and it is used by StartAll(). @@ -66,9 +65,6 @@ func stackdriverRunner(c common.Context, _ common.PluginOptions, in chan interfa go func() { //nolint defer c.WG().Done() - ticker := time.NewTicker(stackdriverExporterInterval) - defer ticker.Stop() - // defer for exporter defer exporter.Flush() defer exporter.StopMetricsExporter() diff --git a/exporters/stdout.go b/exporters/stdout.go index 12488a8..5ce2fb2 100644 --- a/exporters/stdout.go +++ b/exporters/stdout.go @@ -1,15 +1,12 @@ package exporters import ( - "time" - "github.com/hyeoncheon/bogo" "github.com/hyeoncheon/bogo/internal/common" ) const ( - stdoutExporter = "stdout" - stdoutExporterInterval = 1 * time.Minute + stdoutExporter = "stdout" ) // RegisterStdout returns a new Exporter and it is used by StartAll(). @@ -30,9 +27,6 @@ func stdoutRunner(c common.Context, _ common.PluginOptions, in chan interface{}) go func() { //nolint defer c.WG().Done() - ticker := time.NewTicker(stdoutExporterInterval) - defer ticker.Stop() - infinite: for { select { diff --git a/version.go b/version.go index f9ed2ee..a308001 100644 --- a/version.go +++ b/version.go @@ -4,5 +4,5 @@ package bogo // constants: name and version of the application. const ( Name = "bogo" - Version = "v0.4.0" + Version = "v0.4.1" )