Skip to content

Commit b916a9c

Browse files
authored
Implement Prometheus Collector pattern (#607)
* Implement HTTP Prometheus metrics server in IO The legacy registry is no longer used. * Use kube-rbac-proxy for TLS * Remove WithServer call from operator start * Register gather time metric using new registry * Use Prometheus module instead of metrics * Cleanup new metrics code * Remove example metric * Vendor the Prometheus collectors module * Register Go runtime and process metrics * Auto-start server from metrics init function * Update Prometheus client module to latest * Run metrics server from operator.go in loop * Fix last gather time metric name * Add info-level log message on metrics server start * Fix vendoring issues after major rebase
1 parent 3eada68 commit b916a9c

File tree

15 files changed

+475
-47
lines changed

15 files changed

+475
-47
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/openshift/client-go v0.0.0-20220525160904-9e1acff93e4a
1515
github.com/openshift/installer v0.9.0-master.0.20191219195746-103098955ced
1616
github.com/openshift/library-go v0.0.0-20220525173854-9b950a41acdc
17-
github.com/prometheus/common v0.32.1
17+
github.com/prometheus/client_golang v1.12.1
1818
github.com/spf13/cobra v1.4.0
1919
github.com/spf13/pflag v1.0.5
2020
github.com/stretchr/testify v1.7.0

manifests/06-deployment.yaml

+23-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ spec:
7878
- mountPath: /var/run/secrets/serving-cert
7979
name: serving-cert
8080
ports:
81-
- containerPort: 8443
82-
name: https
81+
- containerPort: 8080
82+
name: metrics
8383
resources:
8484
requests:
8585
cpu: 10m
@@ -99,3 +99,24 @@ spec:
9999
- start
100100
- -v=4
101101
- --config=/etc/insights-operator/server.yaml
102+
- name: kube-rbac-proxy
103+
image: quay.io/openshift/origin-kube-rbac-proxy:latest
104+
args:
105+
- --logtostderr
106+
- --secure-listen-address=:8443
107+
- --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
108+
- --upstream=http://127.0.0.1:8080/
109+
- --tls-cert-file=/etc/tls/private/tls.crt
110+
- --tls-private-key-file=/etc/tls/private/tls.key
111+
ports:
112+
- containerPort: 8443
113+
name: https
114+
resources:
115+
requests:
116+
cpu: 1m
117+
memory: 20Mi
118+
terminationMessagePolicy: FallbackToLogsOnError
119+
volumeMounts:
120+
- mountPath: /etc/tls/private
121+
name: serving-cert
122+
readOnly: false

pkg/cmd/start/start.go

-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ func runOperator(operator *controller.Operator, cfg *controllercmd.ControllerCom
168168
builder := controllercmd.NewController("openshift-insights-operator", operator.Run).
169169
WithKubeConfigFile(cmd.Flags().Lookup("kubeconfig").Value.String(), nil).
170170
WithLeaderElection(operatorConfig.LeaderElection, "", "openshift-insights-operator-lock").
171-
WithServer(operatorConfig.ServingInfo, operatorConfig.Authentication, operatorConfig.Authorization).
172171
WithRestartOnChange(exitOnChangeReactorCh, startingFileContent, observedFiles...)
173172
if err := builder.Run(ctx2, unstructured); err != nil {
174173
klog.Error(err)

pkg/controller/operator.go

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/openshift/insights-operator/pkg/controller/periodic"
2525
"github.com/openshift/insights-operator/pkg/controller/status"
2626
"github.com/openshift/insights-operator/pkg/gather"
27+
"github.com/openshift/insights-operator/pkg/insights"
2728
"github.com/openshift/insights-operator/pkg/insights/insightsclient"
2829
"github.com/openshift/insights-operator/pkg/insights/insightsreport"
2930
"github.com/openshift/insights-operator/pkg/insights/insightsuploader"
@@ -53,6 +54,9 @@ func (s *Operator) Run(ctx context.Context, controller *controllercmd.Controller
5354
}
5455
s.Controller = cont
5556

57+
// Start the Prometheus metrics server.
58+
go insights.RunMetricsServer()
59+
5660
// these are operator clients
5761
kubeClient, err := kubernetes.NewForConfig(controller.ProtoKubeConfig)
5862
if err != nil {

pkg/insights/insightsclient/insightsclient.go

+5-18
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ import (
2020
"k8s.io/client-go/pkg/version"
2121
"k8s.io/client-go/rest"
2222
"k8s.io/client-go/transport"
23-
"k8s.io/component-base/metrics"
24-
"k8s.io/component-base/metrics/legacyregistry"
2523

2624
"k8s.io/klog/v2"
2725

2826
configv1 "github.com/openshift/api/config/v1"
2927
configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
28+
"github.com/openshift/insights-operator/pkg/insights"
29+
"github.com/prometheus/client_golang/prometheus"
3030
apierrors "k8s.io/apimachinery/pkg/api/errors"
3131
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3232
apimachineryversion "k8s.io/apimachinery/pkg/version"
@@ -265,29 +265,16 @@ func (c *Client) createAndWriteMIMEHeader(source *Source, mw *multipart.Writer,
265265
}
266266

267267
var (
268-
counterRequestSend = metrics.NewCounterVec(&metrics.CounterOpts{
268+
counterRequestSend = prometheus.NewCounterVec(prometheus.CounterOpts{
269269
Name: "insightsclient_request_send_total",
270270
Help: "Tracks the number of metrics sends",
271271
}, []string{"client", "status_code"})
272-
counterRequestRecvReport = metrics.NewCounterVec(&metrics.CounterOpts{
272+
counterRequestRecvReport = prometheus.NewCounterVec(prometheus.CounterOpts{
273273
Name: "insightsclient_request_recvreport_total",
274274
Help: "Tracks the number of reports requested",
275275
}, []string{"client", "status_code"})
276276
)
277277

278278
func init() {
279-
err := legacyregistry.Register(
280-
counterRequestSend,
281-
)
282-
if err != nil {
283-
fmt.Println(err)
284-
}
285-
286-
err = legacyregistry.Register(
287-
counterRequestRecvReport,
288-
)
289-
if err != nil {
290-
fmt.Println(err)
291-
}
292-
279+
insights.MustRegisterMetricCollectors(counterRequestSend, counterRequestRecvReport)
293280
}

pkg/insights/insightsreport/insightsreport.go

+20-24
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import (
88
"time"
99

1010
"k8s.io/apimachinery/pkg/util/wait"
11-
"k8s.io/component-base/metrics"
12-
"k8s.io/component-base/metrics/legacyregistry"
1311
"k8s.io/klog/v2"
1412

1513
"github.com/openshift/insights-operator/pkg/authorizer"
1614
"github.com/openshift/insights-operator/pkg/config/configobserver"
1715
"github.com/openshift/insights-operator/pkg/controllerstatus"
16+
"github.com/openshift/insights-operator/pkg/insights"
1817
"github.com/openshift/insights-operator/pkg/insights/insightsclient"
18+
"github.com/prometheus/client_golang/prometheus"
1919
)
2020

2121
// Controller gathers the report from Smart Proxy
@@ -38,10 +38,13 @@ type InsightsReporter interface {
3838
ArchiveUploaded() <-chan struct{}
3939
}
4040

41-
var (
41+
const (
42+
insightsLastGatherTimeName = "insightsclient_last_gather_time"
43+
)
4244

45+
var (
4346
// insightsStatus contains a metric with the latest report information
44-
insightsStatus = metrics.NewGaugeVec(&metrics.GaugeOpts{
47+
insightsStatus = prometheus.NewGaugeVec(prometheus.GaugeOpts{
4548
Namespace: "health",
4649
Subsystem: "statuses",
4750
Name: "insights",
@@ -51,28 +54,11 @@ var (
5154
retryThreshold = 2
5255

5356
// insightsLastGatherTime contains time of the last Insights data gathering
54-
insightsLastGatherTime = metrics.NewGauge(&metrics.GaugeOpts{
55-
Name: "insightsclient_last_gather_time",
57+
insightsLastGatherTime = prometheus.NewGauge(prometheus.GaugeOpts{
58+
Name: insightsLastGatherTimeName,
5659
})
5760
)
5861

59-
func init() {
60-
err := legacyregistry.Register(insightsStatus)
61-
if err != nil {
62-
fmt.Println(err)
63-
}
64-
err = legacyregistry.Register(insightsLastGatherTime)
65-
if err != nil {
66-
fmt.Println(err)
67-
}
68-
69-
insightsStatus.WithLabelValues("low").Set(float64(-1))
70-
insightsStatus.WithLabelValues("moderate").Set(float64(-1))
71-
insightsStatus.WithLabelValues("important").Set(float64(-1))
72-
insightsStatus.WithLabelValues("critical").Set(float64(-1))
73-
insightsStatus.WithLabelValues("total").Set(float64(-1))
74-
}
75-
7662
// New initializes and returns a Gatherer
7763
func New(client *insightsclient.Client, configurator configobserver.Configurator, reporter InsightsReporter) *Controller {
7864
return &Controller{
@@ -286,8 +272,18 @@ func updateInsightsMetrics(report SmartProxyReport) {
286272

287273
t, err := time.Parse(time.RFC3339, string(report.Meta.GatheredAt))
288274
if err != nil {
289-
klog.Errorf("Metric %s not updated. Failed to parse time: %v", insightsLastGatherTime.Name, err)
275+
klog.Errorf("Metric %s not updated. Failed to parse time: %v", insightsLastGatherTimeName, err)
290276
return
291277
}
292278
insightsLastGatherTime.Set(float64(t.Unix()))
293279
}
280+
281+
func init() {
282+
insights.MustRegisterMetricCollectors(insightsStatus, insightsLastGatherTime)
283+
284+
insightsStatus.WithLabelValues("low").Set(float64(-1))
285+
insightsStatus.WithLabelValues("moderate").Set(float64(-1))
286+
insightsStatus.WithLabelValues("important").Set(float64(-1))
287+
insightsStatus.WithLabelValues("critical").Set(float64(-1))
288+
insightsStatus.WithLabelValues("total").Set(float64(-1))
289+
}

pkg/insights/metrics.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package insights
2+
3+
import (
4+
"net/http"
5+
"time"
6+
7+
"github.com/prometheus/client_golang/prometheus"
8+
"github.com/prometheus/client_golang/prometheus/collectors"
9+
"github.com/prometheus/client_golang/prometheus/promhttp"
10+
"k8s.io/klog/v2"
11+
)
12+
13+
var (
14+
insightsMetricsRegistry *prometheus.Registry
15+
)
16+
17+
func init() {
18+
insightsMetricsRegistry = prometheus.NewRegistry()
19+
MustRegisterMetricCollectors(
20+
collectors.NewGoCollector(),
21+
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
22+
)
23+
}
24+
25+
// RunMetricsServer starts an HTTP server for the Insights metrics registry.
26+
// The server will run synchronously in an infinite loop. In case of an error,
27+
// it will be logged, and the server will be restarted after a short sleep
28+
// (to avoid spamming the log with the same error).
29+
func RunMetricsServer() {
30+
mux := http.NewServeMux()
31+
mux.Handle("/metrics", promhttp.HandlerFor(insightsMetricsRegistry, promhttp.HandlerOpts{}))
32+
for {
33+
klog.Info("Starting the Prometheus metrics server")
34+
klog.Errorf("Unable to serve metrics: %v", http.ListenAndServe(":8080", mux))
35+
time.Sleep(time.Minute)
36+
}
37+
}
38+
39+
// RegisterMetricCollector registers a new metric collector or a new metric in
40+
// the Insights metrics registry. This function should be called from init()
41+
// functions only, because it uses the MustRegister method, and therefore panics
42+
// in case of an error.
43+
func MustRegisterMetricCollectors(collectors ...prometheus.Collector) {
44+
insightsMetricsRegistry.MustRegister(collectors...)
45+
}

vendor/github.com/prometheus/client_golang/prometheus/collectors/collectors.go

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/prometheus/client_golang/prometheus/collectors/dbstats_collector.go

+119
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)