Add support for tracing via OpenCencus
This adds a few flags for configuring the tracer. Includes support for jaeger tracing (built into OC).
This commit is contained in:
36
cencus_jaeger.go
Normal file
36
cencus_jaeger.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// +build !no_jaeger_exporter
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"go.opencensus.io/exporter/jaeger"
|
||||||
|
"go.opencensus.io/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterTracingExporter("jaeger", NewJaegerExporter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJaegerExporter(opts TracingExporterOptions) (trace.Exporter, error) {
|
||||||
|
jOpts := jaeger.Options{
|
||||||
|
Endpoint: os.Getenv("JAEGER_ENDPOINT"),
|
||||||
|
AgentEndpoint: os.Getenv("JAEGER_AGENT_ENDPOINT"),
|
||||||
|
Username: os.Getenv("JAEGER_USER"),
|
||||||
|
Password: os.Getenv("JAEGER_PASSWORD"),
|
||||||
|
Process: jaeger.Process{
|
||||||
|
ServiceName: opts.ServiceName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if jOpts.Endpoint == "" && jOpts.AgentEndpoint == "" {
|
||||||
|
return nil, errors.New("Must specify either JAEGER_ENDPOINT or JAEGER_AGENT_ENDPOINT")
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range opts.Tags {
|
||||||
|
jOpts.Process.Tags = append(jOpts.Process.Tags, jaeger.StringTag(k, v))
|
||||||
|
}
|
||||||
|
return jaeger.NewExporter(jOpts)
|
||||||
|
}
|
||||||
51
census.go
Normal file
51
census.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"go.opencensus.io/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
tracingExporters = make(map[string]TracingExporterInitFunc)
|
||||||
|
|
||||||
|
reservedTagNames = map[string]bool{
|
||||||
|
"operatingSystem": true,
|
||||||
|
"provider": true,
|
||||||
|
"nodeName": true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// TracingExporterOptions is used to pass options to the configured tracer
|
||||||
|
type TracingExporterOptions struct {
|
||||||
|
Tags map[string]string
|
||||||
|
ServiceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TracingExporterInitFunc is the function that is called to initialize an exporter.
|
||||||
|
// This is used when registering an exporter and called when a user specifed they want to use the exporter.
|
||||||
|
type TracingExporterInitFunc func(TracingExporterOptions) (trace.Exporter, error)
|
||||||
|
|
||||||
|
// RegisterTracingExporter registers a tracing exporter.
|
||||||
|
// For a user to select an exporter, it must be registered here.
|
||||||
|
func RegisterTracingExporter(name string, f TracingExporterInitFunc) {
|
||||||
|
tracingExporters[name] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTracingExporter gets the specified tracing exporter passing in the options to the exporter init function.
|
||||||
|
// For an exporter to be availbale here it must be registered with `RegisterTracingExporter`.
|
||||||
|
func GetTracingExporter(name string, opts TracingExporterOptions) (trace.Exporter, error) {
|
||||||
|
f, ok := tracingExporters[name]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("tracing exporter %q not found", name)
|
||||||
|
}
|
||||||
|
return f(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AvailableTraceExporters gets the list of registered exporters
|
||||||
|
func AvailableTraceExporters() []string {
|
||||||
|
out := make([]string, 0, len(tracingExporters))
|
||||||
|
for k := range tracingExporters {
|
||||||
|
out = append(out, k)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
85
root.go
85
root.go
@@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/virtual-kubelet/virtual-kubelet/providers"
|
"github.com/virtual-kubelet/virtual-kubelet/providers"
|
||||||
"github.com/virtual-kubelet/virtual-kubelet/providers/register"
|
"github.com/virtual-kubelet/virtual-kubelet/providers/register"
|
||||||
vkubelet "github.com/virtual-kubelet/virtual-kubelet/vkubelet"
|
vkubelet "github.com/virtual-kubelet/virtual-kubelet/vkubelet"
|
||||||
|
"go.opencensus.io/trace"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
@@ -58,6 +59,10 @@ var p providers.Provider
|
|||||||
var rm *manager.ResourceManager
|
var rm *manager.ResourceManager
|
||||||
var apiConfig vkubelet.APIConfig
|
var apiConfig vkubelet.APIConfig
|
||||||
|
|
||||||
|
var userTraceExporters []string
|
||||||
|
var userTraceConfig = TracingExporterOptions{Tags: make(map[string]string)}
|
||||||
|
var traceSampler string
|
||||||
|
|
||||||
// RootCmd represents the base command when called without any subcommands
|
// RootCmd represents the base command when called without any subcommands
|
||||||
var RootCmd = &cobra.Command{
|
var RootCmd = &cobra.Command{
|
||||||
Use: "virtual-kubelet",
|
Use: "virtual-kubelet",
|
||||||
@@ -94,6 +99,38 @@ func Execute() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mapVar map[string]string
|
||||||
|
|
||||||
|
func (mv mapVar) String() string {
|
||||||
|
var s string
|
||||||
|
for k, v := range mv {
|
||||||
|
if s == "" {
|
||||||
|
s = fmt.Sprintf("%s=%v", k, v)
|
||||||
|
} else {
|
||||||
|
s += fmt.Sprintf(", %s=%v", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv mapVar) Set(s string) error {
|
||||||
|
split := strings.SplitN(s, "=", 2)
|
||||||
|
if len(split) != 2 {
|
||||||
|
return errors.Errorf("invalid format, must be `key=value`: %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := mv[split[0]]
|
||||||
|
if ok {
|
||||||
|
return errors.Errorf("duplicate key: %s", split[0])
|
||||||
|
}
|
||||||
|
mv[split[0]] = split[1]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mv mapVar) Type() string {
|
||||||
|
return "map"
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cobra.OnInitialize(initConfig)
|
cobra.OnInitialize(initConfig)
|
||||||
|
|
||||||
@@ -120,6 +157,11 @@ func init() {
|
|||||||
RootCmd.PersistentFlags().MarkDeprecated("taint", "Taint key should now be configured using the VK_TAINT_KEY environment variable")
|
RootCmd.PersistentFlags().MarkDeprecated("taint", "Taint key should now be configured using the VK_TAINT_KEY environment variable")
|
||||||
RootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", `set the log level, e.g. "trace", debug", "info", "warn", "error"`)
|
RootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", `set the log level, e.g. "trace", debug", "info", "warn", "error"`)
|
||||||
|
|
||||||
|
RootCmd.PersistentFlags().StringSliceVar(&userTraceExporters, "trace-exporter", nil, fmt.Sprintf("sets the tracing exporter to use, available exporters: %s", AvailableTraceExporters()))
|
||||||
|
RootCmd.PersistentFlags().StringVar(&userTraceConfig.ServiceName, "trace-service-name", "virtual-kubelet", "sets the name of the service used to register with the trace exporter")
|
||||||
|
RootCmd.PersistentFlags().Var(mapVar(userTraceConfig.Tags), "trace-tag", "add tags to include with traces in key=value form")
|
||||||
|
RootCmd.PersistentFlags().StringVar(&traceSampler, "trace-sample-rate", "", "set probability of tracing samples")
|
||||||
|
|
||||||
// Cobra also supports local flags, which will only run
|
// Cobra also supports local flags, which will only run
|
||||||
// when this action is called directly.
|
// when this action is called directly.
|
||||||
// RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
// RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
@@ -223,6 +265,49 @@ func initConfig() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.WithError(err).Fatal("Error reading API config")
|
logger.WithError(err).Fatal("Error reading API config")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for k := range userTraceConfig.Tags {
|
||||||
|
if reservedTagNames[k] {
|
||||||
|
logger.WithField("tag", k).Fatal("must not use a reserved tag key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userTraceConfig.Tags["operatingSystem"] = operatingSystem
|
||||||
|
userTraceConfig.Tags["provider"] = provider
|
||||||
|
userTraceConfig.Tags["nodeName"] = nodeName
|
||||||
|
for _, e := range userTraceExporters {
|
||||||
|
exporter, err := GetTracingExporter(e, userTraceConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.L.WithError(err).WithField("exporter", e).Fatal("Cannot initialize exporter")
|
||||||
|
}
|
||||||
|
trace.RegisterExporter(exporter)
|
||||||
|
}
|
||||||
|
if len(userTraceExporters) > 0 {
|
||||||
|
var s trace.Sampler
|
||||||
|
switch strings.ToLower(traceSampler) {
|
||||||
|
case "":
|
||||||
|
case "always":
|
||||||
|
s = trace.AlwaysSample()
|
||||||
|
case "never":
|
||||||
|
s = trace.NeverSample()
|
||||||
|
default:
|
||||||
|
rate, err := strconv.Atoi(traceSampler)
|
||||||
|
if err != nil {
|
||||||
|
logger.WithError(err).WithField("rate", traceSampler).Fatal("unsupported trace sample rate, supported values: always, never, or number 0-100")
|
||||||
|
}
|
||||||
|
if rate < 0 || rate > 100 {
|
||||||
|
logger.WithField("rate", traceSampler).Fatal("trace sample rate must not be less than zero or greater than 100")
|
||||||
|
}
|
||||||
|
s = trace.ProbabilitySampler(float64(rate) / 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s != nil {
|
||||||
|
trace.ApplyConfig(
|
||||||
|
trace.Config{
|
||||||
|
DefaultSampler: s,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAPIConfig() (vkubelet.APIConfig, error) {
|
func getAPIConfig() (vkubelet.APIConfig, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user