Skip to content

Commit

Permalink
internal instrumentation for k8s cache (#1365)
Browse files Browse the repository at this point in the history
* internal instrumentation for k8s cache

* Helm chart

* Fixed lints and addressed code comments

* Fixed formatting
  • Loading branch information
mariomac authored Nov 18, 2024
1 parent e766187 commit d52d50e
Show file tree
Hide file tree
Showing 15 changed files with 334 additions and 25 deletions.
2 changes: 1 addition & 1 deletion charts/beyla/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
apiVersion: v2
name: beyla
version: 1.4.11
version: 1.4.12
appVersion: 1.8.6
description: eBPF-based autoinstrumentation HTTP, HTTP2 and gRPC services, as well as network metrics.
home: https://grafana.com/oss/beyla-ebpf/
Expand Down
9 changes: 6 additions & 3 deletions charts/beyla/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# beyla

![Version: 1.4.11](https://img.shields.io/badge/Version-1.4.11-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.8.6](https://img.shields.io/badge/AppVersion-1.8.6-informational?style=flat-square)
![Version: 1.4.12](https://img.shields.io/badge/Version-1.4.12-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.8.6](https://img.shields.io/badge/AppVersion-1.8.6-informational?style=flat-square)

eBPF-based autoinstrumentation HTTP, HTTP2 and gRPC services, as well as network metrics.

Expand Down Expand Up @@ -41,7 +41,8 @@ eBPF-based autoinstrumentation HTTP, HTTP2 and gRPC services, as well as network
| image.registry | string | `"docker.io"` | Beyla image registry (defaults to docker.io) |
| image.repository | string | `"grafana/beyla"` | Beyla image repository. |
| image.tag | string | `nil` | Beyla image tag. When empty, the Chart's appVersion is used. |
| k8sCache | object | `{"env":{},"envValueFrom":{},"image":{"digest":null,"pullPolicy":"IfNotPresent","pullSecrets":[],"registry":"docker.io","repository":"grafana/beyla-k8s-cache","tag":null},"profile_port":0,"replicas":0,"resources":{},"service":{"annotations":{},"labels":{},"name":"beyla-k8s-cache","port":50055}}` | Options to deploy the Kubernetes metadata cache as a separate service |
| k8sCache | object | `{"annotations":{},"env":{},"envValueFrom":{},"image":{"digest":null,"pullPolicy":"IfNotPresent","pullSecrets":[],"registry":"docker.io","repository":"grafana/beyla-k8s-cache","tag":null},"internalMetrics":{"path":"/metrics","port":0,"portName":"metrics"},"podAnnotations":{},"podLabels":{},"profilePort":0,"replicas":0,"resources":{},"service":{"annotations":{},"labels":{},"name":"beyla-k8s-cache","port":50055}}` | Options to deploy the Kubernetes metadata cache as a separate service |
| k8sCache.annotations | object | `{}` | Deployment annotations. |
| k8sCache.env | object | `{}` | extra environment variables |
| k8sCache.envValueFrom | object | `{}` | extra environment variables to be set from resources such as k8s configMaps/secrets |
| k8sCache.image.digest | string | `nil` | K8s Cache image's SHA256 digest (either in format "sha256:XYZ" or "XYZ"). When set, will override `image.tag`. |
Expand All @@ -50,7 +51,9 @@ eBPF-based autoinstrumentation HTTP, HTTP2 and gRPC services, as well as network
| k8sCache.image.registry | string | `"docker.io"` | K8s Cache image registry (defaults to docker.io) |
| k8sCache.image.repository | string | `"grafana/beyla-k8s-cache"` | K8s Cache image repository. |
| k8sCache.image.tag | string | `nil` | K8s Cache image tag. When empty, the Chart's appVersion is used. |
| k8sCache.profile_port | int | `0` | Enables the profile port for the Beyla cache |
| k8sCache.podAnnotations | object | `{}` | Adds custom annotations to the Beyla Kube Cache Pods. |
| k8sCache.podLabels | object | `{}` | Adds custom labels to the Beyla Kube Cache Pods. |
| k8sCache.profilePort | int | `0` | Enables the profile port for the Beyla cache |
| k8sCache.replicas | int | `0` | Number of replicas for the Kubernetes metadata chache service. 0 disables the service. |
| k8sCache.service.annotations | object | `{}` | Service annotations. |
| k8sCache.service.labels | object | `{}` | Service labels. |
Expand Down
24 changes: 24 additions & 0 deletions charts/beyla/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,27 @@ Calculate name of image ID to use for "beyla-cache".
{{- printf ":%s" .Chart.AppVersion }}
{{- end }}
{{- end }}

{{/*
Common kube cache labels
*/}}
{{- define "beyla.cache.labels" -}}
helm.sh/chart: {{ include "beyla.chart" . }}
{{ include "beyla.cache.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/part-of: beyla
{{- end }}

{{/*
Selector (pod) labels
*/}}
{{- define "beyla.cache.selectorLabels" -}}
app.kubernetes.io/name: {{ .Values.k8sCache.service.name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- with .Values.k8sCache.podLabels }}
{{ toYaml . }}
{{- end }}
{{- end }}
24 changes: 17 additions & 7 deletions charts/beyla/templates/cache-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ metadata:
name: {{ .Values.k8sCache.service.name }}
namespace: {{ include "beyla.namespace" .}}
labels:
{{- include "beyla.labels" . | nindent 4 }}
{{- include "beyla.cache.labels" . | nindent 4 }}
app.kubernetes.io/component: workload
{{- with .Values.k8sCache.service.annotations }}
{{- with .Values.k8sCache.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
Expand All @@ -19,11 +19,12 @@ spec:
app.kubernetes.io/name: {{ .Values.k8sCache.service.name }}
template:
metadata:
{{- with .Values.podAnnotations }}
{{- with .Values.k8sCache.podAnnotations }}
annotations:
{{- tpl (toYaml . | nindent 8) $root }}
{{- end }}
labels:
{{- include "beyla.cache.labels" . | nindent 8 }}
app.kubernetes.io/name: {{ .Values.k8sCache.service.name }}
spec:
{{- if .Values.serviceAccount.create }}
Expand All @@ -45,9 +46,14 @@ spec:
- containerPort: {{ .Values.k8sCache.service.port }}
protocol: TCP
name: grpc
{{- if .Values.k8sCache.profile_port }}
{{- if .Values.k8sCache.profilePort }}
- name: profile
containerPort: {{ .Values.k8sCache.profile_port }}
containerPort: {{ .Values.k8sCache.profilePort }}
protocol: TCP
{{- end }}
{{- if .Values.k8sCache.internalMetrics.port }}
- name: {{ .Values.k8sCache.internalMetrics.portName }}
containerPort: {{ .Values.k8sCache.internalMetrics.port }}
protocol: TCP
{{- end }}
{{- with .Values.k8sCache.resources }}
Expand All @@ -57,9 +63,13 @@ spec:
env:
- name: BEYLA_K8S_CACHE_PORT
value: "{{ .Values.k8sCache.service.port }}"
{{- if .Values.k8sCache.profile_port }}
{{- if .Values.k8sCache.profilePort }}
- name: BEYLA_K8S_CACHE_PROFILE_PORT
value: "{{ .Values.k8sCache.profile_port }}"
value: "{{ .Values.k8sCache.profilePort }}"
{{- end }}
{{- if .Values.k8sCache.internalMetrics.port }}
- name: BEYLA_K8S_CACHE_INTERNAL_METRICS_PROMETHEUS_PORT
value: "{{ .Values.k8sCache.internalMetrics.port }}"
{{- end }}
{{- range $key, $value := .Values.k8sCache.env }}
- name: {{ $key }}
Expand Down
7 changes: 7 additions & 0 deletions charts/beyla/templates/cache-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ spec:
- port: {{ .Values.k8sCache.service.port }}
protocol: TCP
targetPort: grpc
name: grpc
{{ if .Values.k8sCache.internalMetrics.port }}
- port: {{ .Values.k8sCache.internalMetrics.port }}
targetPort: {{ .Values.k8sCache.internalMetrics.portName }}
name: {{ .Values.k8sCache.internalMetrics.portName }}
protocol: TCP
{{- end }}
selector:
app.kubernetes.io/name: {{ .Values.k8sCache.service.name }}
{{- end }}
16 changes: 15 additions & 1 deletion charts/beyla/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ k8sCache:
# -- Number of replicas for the Kubernetes metadata chache service. 0 disables the service.
replicas: 0
# -- Enables the profile port for the Beyla cache
profile_port: 0
profilePort: 0
## Env variables that will override configmap values
## For example:
## BEYLA_K8S_CACHE_LOG_LEVEL: "debug"
Expand Down Expand Up @@ -325,3 +325,17 @@ k8sCache:
annotations: {}
# -- Service labels.
labels: {}
internalMetrics:
# 0: disabled by default
port: 0
path: /metrics
portName: metrics
# prometheus:
# port: 6060
# path: /metrics
# -- Deployment annotations.
annotations: {}
# -- Adds custom annotations to the Beyla Kube Cache Pods.
podAnnotations: {}
# -- Adds custom labels to the Beyla Kube Cache Pods.
podLabels: {}
4 changes: 4 additions & 0 deletions cmd/k8s-cache/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/grafana/beyla/pkg/buildinfo"
"github.com/grafana/beyla/pkg/kubecache"
"github.com/grafana/beyla/pkg/kubecache/instrument"
"github.com/grafana/beyla/pkg/kubecache/meta"
"github.com/grafana/beyla/pkg/kubecache/service"
)
Expand Down Expand Up @@ -55,6 +56,9 @@ func main() {
// Adding shutdown hook for graceful stop.
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

// add the internal metrics to the context
ctx = instrument.Start(ctx, &config.InternalMetrics)

if err := ic.Run(ctx,
meta.WithResyncPeriod(config.InformerResyncPeriod)); err != nil {
slog.Error("starting informers' cache service", "error", err)
Expand Down
4 changes: 4 additions & 0 deletions pkg/kubecache/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (

"github.com/caarlos0/env/v9"
"gopkg.in/yaml.v3"

"github.com/grafana/beyla/pkg/kubecache/instrument"
)

// Config options of the Kubernetes Cache service. Check the "DefaultConfig" variable for a view of the default values.
Expand All @@ -21,6 +23,8 @@ type Config struct {
ProfilePort int `yaml:"profile_port" env:"BEYLA_K8S_CACHE_PROFILE_PORT"`
// InformerResyncPeriod is the time interval between complete resyncs of the informers
InformerResyncPeriod time.Duration `yaml:"informer_resync_period" env:"BEYLA_K8S_CACHE_INFORMER_RESYNC_PERIOD"`

InternalMetrics instrument.InternalMetricsConfig `yaml:"internal_metrics"`
}

var DefaultConfig = Config{
Expand Down
145 changes: 145 additions & 0 deletions pkg/kubecache/instrument/imetrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package instrument

import (
"runtime"

"github.com/prometheus/client_golang/prometheus"

"github.com/grafana/beyla/pkg/buildinfo"
"github.com/grafana/beyla/pkg/internal/connector"
)

// InternalMetrics accounts diverse events of the Beyla Cache service
type InternalMetrics interface {
InformerNew()
InformerUpdate()
InformerDelete()

ClientConnect()
ClientDisconnect()

MessageSubmit()
MessageSucceed()
MessageTimeout()
MessageError()
}

type noopMetrics struct{}

func (n noopMetrics) InformerNew() {}
func (n noopMetrics) InformerUpdate() {}
func (n noopMetrics) InformerDelete() {}
func (n noopMetrics) ClientConnect() {}
func (n noopMetrics) ClientDisconnect() {}
func (n noopMetrics) MessageSubmit() {}
func (n noopMetrics) MessageSucceed() {}
func (n noopMetrics) MessageTimeout() {}
func (n noopMetrics) MessageError() {}

type promInternalMetrics struct {
connector *connector.PrometheusManager
informerNew prometheus.Counter
informerUpdate prometheus.Counter
informerDelete prometheus.Counter
connectedClients prometheus.Gauge
messageSubmit prometheus.Counter
messageSucceed prometheus.Counter
messageError prometheus.Counter
messageTimeout prometheus.Counter
beylaCacheInfo prometheus.Gauge
}

func prometheusInternalMetrics(
cfg *InternalMetricsConfig,
manager *connector.PrometheusManager,
) *promInternalMetrics {
pr := &promInternalMetrics{
connector: manager,
informerNew: prometheus.NewCounter(prometheus.CounterOpts{
Name: "beyla_kube_cache_informer_new_total",
Help: "How many 'new' metadata events has the informer received",
}),
informerUpdate: prometheus.NewCounter(prometheus.CounterOpts{
Name: "beyla_kube_cache_informer_update_total",
Help: "How many 'update' metadata events has the informer received",
}),
informerDelete: prometheus.NewCounter(prometheus.CounterOpts{
Name: "beyla_kube_cache_informer_delete_total",
Help: "How many 'delete' metadata events has the informer received",
}),
connectedClients: prometheus.NewGauge(prometheus.GaugeOpts{
Name: "beyla_kube_cache_connected_clients",
Help: "How many concurrent Beyla instances are connected to this cache service",
}),
messageSubmit: prometheus.NewCounter(prometheus.CounterOpts{
Name: "beyla_kube_cache_client_message_submits_total",
Help: "How many notifications have been started to be submitted to" +
" the subscriber client. This includes messages not yet received or failed",
}),
messageSucceed: prometheus.NewCounter(prometheus.CounterOpts{
Name: "beyla_kube_cache_client_message_successes_total",
Help: "How many notifications have been successfully submitted to the subscriber client",
}),
messageError: prometheus.NewCounter(prometheus.CounterOpts{
Name: "beyla_kube_cache_client_message_errors_total",
Help: "How many notifications couldn't be submitted to the subscriber client due to an error",
}),
messageTimeout: prometheus.NewCounter(prometheus.CounterOpts{
Name: "beyla_kube_cache_client_message_timeouts_total",
Help: "How many notifications timed out before finish its submission to the subscriber client",
}),
beylaCacheInfo: prometheus.NewGauge(prometheus.GaugeOpts{
Name: "beyla_kube_cache_internal_build_info",
Help: "A metric with a constant '1' value labeled by version, revision, branch, " +
"goversion from which Beyla was built, the goos and goarch for the build.",
ConstLabels: map[string]string{
"goarch": runtime.GOARCH,
"goos": runtime.GOOS,
"goversion": runtime.Version(),
"version": buildinfo.Version,
"revision": buildinfo.Revision,
},
}),
}
pr.beylaCacheInfo.Set(1)
manager.Register(cfg.Port, cfg.Path,
pr.informerNew,
pr.informerUpdate,
pr.informerDelete,
pr.connectedClients,
pr.messageSubmit,
pr.messageSucceed,
pr.messageError,
pr.messageTimeout,
pr.beylaCacheInfo)

return pr
}

func (n *promInternalMetrics) InformerNew() {
n.informerNew.Inc()
}
func (n *promInternalMetrics) InformerUpdate() {
n.informerUpdate.Inc()
}
func (n *promInternalMetrics) InformerDelete() {
n.informerDelete.Inc()
}
func (n *promInternalMetrics) ClientConnect() {
n.connectedClients.Inc()
}
func (n *promInternalMetrics) ClientDisconnect() {
n.connectedClients.Dec()
}
func (n *promInternalMetrics) MessageSubmit() {
n.messageSubmit.Inc()
}
func (n *promInternalMetrics) MessageSucceed() {
n.messageSucceed.Inc()
}
func (n *promInternalMetrics) MessageTimeout() {
n.messageTimeout.Inc()
}
func (n *promInternalMetrics) MessageError() {
n.messageError.Inc()
}
41 changes: 41 additions & 0 deletions pkg/kubecache/instrument/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package instrument

import (
"context"

"github.com/grafana/beyla/pkg/internal/connector"
)

const defaultMetricsPath = "/metrics"

type InternalMetricsConfig struct {
Port int `yaml:"port,omitempty" env:"BEYLA_K8S_CACHE_INTERNAL_METRICS_PROMETHEUS_PORT"`
Path string `yaml:"path,omitempty" env:"BEYLA_K8S_CACHE_INTERNAL_METRICS_PROMETHEUS_PATH"`
}

type contextKey struct{}

// Start in background an internal metrics handler and return a context containing it.
// The metrics handler can be later retrieved via FromContext function.
func Start(ctx context.Context, cfg *InternalMetricsConfig) context.Context {
if cfg == nil || cfg.Port == 0 {
return ctx
}
if cfg.Path == "" {
cfg.Path = defaultMetricsPath
}
promMgr := connector.PrometheusManager{}
metrics := prometheusInternalMetrics(cfg, &promMgr)
promMgr.StartHTTP(ctx)
return context.WithValue(ctx, contextKey{}, InternalMetrics(metrics))
}

// FromContext returns the internal metrics handler from the context. If any.
// If no metrics handler has been defined, it will return a NOOP metrics handler.
func FromContext(ctx context.Context) InternalMetrics {
metrics := ctx.Value(contextKey{})
if metrics == nil {
return &noopMetrics{}
}
return metrics.(InternalMetrics)
}
Loading

0 comments on commit d52d50e

Please sign in to comment.