Skip to content

Commit

Permalink
Added example how to use plugin API for telemetry. (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwhittaker authored Nov 6, 2023
1 parent e6d3dc9 commit 291bb9c
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 4 deletions.
1 change: 1 addition & 0 deletions examples/telemetry/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
telemetry
12 changes: 12 additions & 0 deletions examples/telemetry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Telemetry

This directory contains an example of how to use the `weaver kube` plugin API to
configure how metrics and traces are exported. In `main.go`, we register a
plugin to export traces to Jaeger and a plugin to export metrics to Prometheus.
Compile the `telemetry` binary and use it as you would `weaver kube`. Use
`prometheus.yaml` and `jaeger.yaml` to deploy Prometheus and Jaeger to a
Kubernetes cluster.

```console
$ kubectl apply -f jaeger.yaml -f prometheus.yaml -f $(telemetry deploy kube_deploy.yaml)
```
51 changes: 51 additions & 0 deletions examples/telemetry/jaeger.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Jaegar Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: jaeger
spec:
selector:
matchLabels:
app: jaeger
template:
metadata:
labels:
app: jaeger
spec:
containers:
- name: jaeger
image: jaegertracing/all-in-one:latest

---

# Jaegar Service
apiVersion: v1
kind: Service
metadata:
name: jaeger
spec:
ports:
- name: ui-port
port: 16686
targetPort: 16686
protocol: TCP
- name: collector-port
port: 14268
targetPort: 14268
protocol: TCP
selector:
app: jaeger
105 changes: 105 additions & 0 deletions examples/telemetry/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This file contains an example of how to use the plugin API to configure how
// metrics and traces are exported. We register a plugin to export traces to
// Jaeger and a plugin to export metrics to Prometheus. Compile the telemetry
// binary and use it as you would "weaver kube". Use "prometheus.yaml" and
// "jaeger.yaml" to deploy Prometheus and Jaeger to a Kubernetes cluster.
//
// $ kubectl apply \
// -f jaeger.yaml \
// -f prometheus.yaml \
// -f $(telemetry deploy kube_deploy.yaml)

package main

import (
"bytes"
"context"
"fmt"
"net"
"net/http"
"sync"

"github.com/ServiceWeaver/weaver-kube/tool"
"github.com/ServiceWeaver/weaver/runtime/metrics"
"github.com/ServiceWeaver/weaver/runtime/prometheus"
"go.opentelemetry.io/otel/exporters/jaeger" //lint:ignore SA1019 TODO: Update
"go.opentelemetry.io/otel/sdk/trace"
)

const (
// The Jaeger and Prometheus ports. These values should be the same as the
// ones in jaeger.yaml and prometheus.yaml.
jaegerPort = 14268
prometheusPort = 9090
)

// prometheusExporter exports metrics via HTTP in Prometheus text format.
type prometheusExporter struct {
mu sync.Mutex
metrics []*metrics.MetricSnapshot
}

// serve runs an HTTP server that exports metrics in Prometheus text format.
func (p *prometheusExporter) serve(lis net.Listener) error {
mux := http.NewServeMux()
mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
p.mu.Lock()
defer p.mu.Unlock()
var b bytes.Buffer
prometheus.TranslateMetricsToPrometheusTextFormat(&b, p.metrics, r.Host, "/metrics")
w.Write(b.Bytes()) //nolint:errcheck // response write error
})
return http.Serve(lis, mux)
}

// handleMetrics implements tool.Plugins.HandleMetrics.
func (p *prometheusExporter) handleMetrics(_ context.Context, metrics []*metrics.MetricSnapshot) error {
p.mu.Lock()
defer p.mu.Unlock()
p.metrics = metrics
return nil
}

func main() {
// Export traces to Jaegar.
jaegerURL := fmt.Sprintf("http://jaeger:%d/api/traces", jaegerPort)
endpoint := jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(jaegerURL))
traceExporter, err := jaeger.New(endpoint)
if err != nil {
panic(err)
}
handleTraceSpans := func(ctx context.Context, spans []trace.ReadOnlySpan) error {
return traceExporter.ExportSpans(ctx, spans)
}

// Export metrics to Prometheus.
p := &prometheusExporter{}
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", prometheusPort))
if err != nil {
panic(err)
}
go func() {
if err := p.serve(lis); err != nil {
panic(err)
}
}()

tool.Run("deploy", tool.Plugins{
HandleTraceSpans: handleTraceSpans,
HandleMetrics: p.handleMetrics,
})
}
112 changes: 112 additions & 0 deletions examples/telemetry/prometheus.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Give Prometheus permissions to list pods.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: pods-getter
namespace: default
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
creationTimestamp: null
name: default-pods-getter
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: pods-getter
subjects:
- kind: ServiceAccount
name: default
namespace: default

---

# Store prometheus.yaml in a ConfigMap.
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
data:
prometheus.yaml: |
global:
scrape_interval: 15s
scrape_configs:
- job_name: "serviceweaver"
kubernetes_sd_configs:
- role: pod
---

# Prometheus Service.
apiVersion: v1
kind: Service
metadata:
name: prometheus
spec:
ports:
- port: 80
protocol: TCP
targetPort: 9090
selector:
app: prometheus

---

# Prometheus Deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
containers:
- name: prometheus
image: prom/prometheus:v2.30.3
args:
- --config.file=/etc/prometheus.yaml
- --storage.tsdb.path=/prometheus
ports:
- containerPort: 9090
volumeMounts:
- mountPath: /etc/prometheus.yaml
name: prometheus-config
subPath: prometheus.yaml
volumes:
- name: prometheus-config
configMap:
name: prometheus-config
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ require (
github.com/ServiceWeaver/weaver v0.22.1-0.20231019162801-c2294d1ae0e8
github.com/google/uuid v1.3.1
go.opentelemetry.io/otel v1.19.0
go.opentelemetry.io/otel/sdk v1.16.0
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
go.opentelemetry.io/otel/sdk v1.17.0
go.opentelemetry.io/otel/trace v1.19.0
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
golang.org/x/sync v0.4.0
Expand Down
7 changes: 5 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
Expand All @@ -230,12 +231,14 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZ
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q=
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 h1:+XWJd3jf75RXJq29mxbuXhCXFDG3S3R4vBUeSI2P7tE=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0/go.mod h1:hqgzBPTf4yONMFgdZvL/bK42R/iinTyVQtiWihs3SZc=
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE=
go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down
2 changes: 1 addition & 1 deletion internal/impl/babysitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func (b *babysitter) Serve() error {
if b.opts.HandleMetrics != nil {
// Periodically call b.opts.HandleMetrics with the set of metrics.
group.Go(func() error {
ticker := time.NewTimer(time.Second)
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
Expand Down

0 comments on commit 291bb9c

Please sign in to comment.