Skip to content
This repository was archived by the owner on Dec 12, 2025. It is now read-only.

Commit ddf31fe

Browse files
author
Rodrigo Valin
authored
Updates service and documents servicemonitor (#899)
1 parent 43111e1 commit ddf31fe

File tree

11 files changed

+440
-23
lines changed

11 files changed

+440
-23
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ run: install install-rbac
4747
eval $$(scripts/dev/get_e2e_env_vars.py $(cleanup)); \
4848
go run ./cmd/manager/main.go
4949

50+
debug: install install-rbac
51+
eval $$(scripts/dev/get_e2e_env_vars.py $(cleanup)); \
52+
dlv debug ./cmd/manager/main.go
53+
5054
# Install CRDs into a cluster
5155
install: manifests helm install-crd
5256

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ The MongoDB Community Kubernetes Operator supports the following features:
4545
- Secure client-to-server and server-to-server connections with TLS
4646
- Create users with [SCRAM](https://docs.mongodb.com/manual/core/security-scram/) authentication
4747
- Create custom roles
48+
- Enable a [metrics target that can be used with Prometheus](docs/prometheus/README.md)
4849

4950
### Planned Features
5051
- Server internal authentication via keyfile

api/v1/mongodbcommunity_types.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ const (
3434
type Phase string
3535

3636
const (
37-
Running Phase = "Running"
38-
Failed Phase = "Failed"
39-
Pending Phase = "Pending"
40-
)
37+
Running Phase = "Running"
38+
Failed Phase = "Failed"
39+
Pending Phase = "Pending"
40+
defaultPasswordKey = "password"
4141

42-
const (
43-
defaultPasswordKey = "password"
42+
// Keep in sync with controllers/prometheus.go
43+
defaultPrometheusPort = 9216
4444
)
4545

4646
// SCRAM-SHA-256 and SCRAM-SHA-1 are the supported auth modes.
@@ -161,6 +161,14 @@ func (p Prometheus) GetPasswordKey() string {
161161
return "password"
162162
}
163163

164+
func (p Prometheus) GetPort() int {
165+
if p.Port != 0 {
166+
return p.Port
167+
}
168+
169+
return defaultPrometheusPort
170+
}
171+
164172
// ConvertToAutomationConfigCustomRole converts between a custom role defined by the crd and a custom role
165173
// that can be used in the automation config.
166174
func (c CustomRole) ConvertToAutomationConfigCustomRole() automationconfig.CustomRole {

config/samples/mongodb.com_v1_mongodbcommunity_prometheus.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ spec:
2424
# Optional defaults to 9216
2525
# port: 9216
2626

27+
# Prometheus endpoint can be configured to use HTTPS
28+
# tlsSecretKeyRef:
29+
# name: "<kubernetes.io/tls secret name>"
30+
2731
security:
2832
authentication:
2933
modes: ["SCRAM"]

controllers/prometheus.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package controllers
33
import (
44
"fmt"
55

6+
corev1 "k8s.io/api/core/v1"
7+
68
mdbv1 "github.com/mongodb/mongodb-kubernetes-operator/api/v1"
79
"github.com/mongodb/mongodb-kubernetes-operator/pkg/automationconfig"
810
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/secret"
@@ -12,7 +14,9 @@ import (
1214
)
1315

1416
const (
15-
listenAddress = "0.0.0.0"
17+
// Keep in sync with api/v1/mongodbcommunity_types.go
18+
DefaultPrometheusPort = 9216
19+
ListenAddress = "0.0.0.0"
1620
)
1721

1822
// PrometheusModification adds Prometheus configuration to AutomationConfig.
@@ -50,7 +54,7 @@ func getPrometheusModification(getUpdateCreator secret.GetUpdateCreator, mdb mdb
5054
promConfig.Password = password
5155

5256
if mdb.Spec.Prometheus.Port > 0 {
53-
promConfig.ListenAddress = fmt.Sprintf("%s:%d", listenAddress, mdb.Spec.Prometheus.Port)
57+
promConfig.ListenAddress = fmt.Sprintf("%s:%d", ListenAddress, mdb.Spec.Prometheus.Port)
5458
}
5559

5660
if mdb.Spec.Prometheus.MetricsPath != "" {
@@ -60,3 +64,16 @@ func getPrometheusModification(getUpdateCreator secret.GetUpdateCreator, mdb mdb
6064
config.Prometheus = &promConfig
6165
}, nil
6266
}
67+
68+
// prometheusPort returns a `corev1.ServicePort` to be configured in the StatefulSet
69+
// for the Prometheus endpoint. This function will only return a new Port when
70+
// Prometheus has been configured, and nil otherwise.
71+
func prometheusPort(mdb mdbv1.MongoDBCommunity) *corev1.ServicePort {
72+
if mdb.Spec.Prometheus != nil {
73+
return &corev1.ServicePort{
74+
Port: int32(mdb.Spec.Prometheus.GetPort()),
75+
Name: "prometheus",
76+
}
77+
}
78+
return nil
79+
}

controllers/replica_set_controller.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,15 +543,25 @@ func buildService(mdb mdbv1.MongoDBCommunity, isArbiter bool) corev1.Service {
543543
SetName(name).
544544
SetNamespace(mdb.Namespace).
545545
SetSelector(label).
546+
SetLabels(label).
546547
SetServiceType(corev1.ServiceTypeClusterIP).
547548
SetClusterIP("None").
548-
SetPort(int32(mdb.GetMongodConfiguration().GetDBPort())).
549-
SetPortName("mongodb").
550549
SetPublishNotReadyAddresses(true).
551550
SetOwnerReferences(mdb.GetOwnerReferences()).
551+
AddPort(mongoDBPort(mdb)).
552+
AddPort(prometheusPort(mdb)).
552553
Build()
553554
}
554555

556+
// mongoDBPort returns a `corev1.ServicePort` to be configured in the StatefulSet
557+
// for this MongoDB resource.
558+
func mongoDBPort(mdb mdbv1.MongoDBCommunity) *corev1.ServicePort {
559+
return &corev1.ServicePort{
560+
Port: int32(mdb.GetMongodConfiguration().GetDBPort()),
561+
Name: "mongodb",
562+
}
563+
}
564+
555565
// validateSpec checks if the MongoDB resource Spec is valid.
556566
// If there has not yet been a successful configuration, the function runs the intial Spec validations. Otherwise
557567
// it checks that the attempted Spec is valid in relation to the Spec that resulted from that last successful configuration.

controllers/replicaset_controller_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,81 @@ func TestService_usesCustomMongodPortWhenSpecified(t *testing.T) {
296296
assertReconciliationSuccessful(t, res, err)
297297
}
298298

299+
func TestService_configuresPrometheusCustomPorts(t *testing.T) {
300+
mdb := newTestReplicaSet()
301+
mdb.Spec.Prometheus = &mdbv1.Prometheus{
302+
Username: "username",
303+
PasswordSecretRef: mdbv1.SecretKeyReference{
304+
Name: "secret",
305+
},
306+
Port: 4321,
307+
}
308+
309+
mongodConfig := objx.New(map[string]interface{}{})
310+
mongodConfig.Set("net.port", 1000.)
311+
mdb.Spec.AdditionalMongodConfig.Object = mongodConfig
312+
313+
mgr := client.NewManager(&mdb)
314+
err := secret.CreateOrUpdate(mgr.Client,
315+
secret.Builder().
316+
SetName("secret").
317+
SetNamespace(mdb.Namespace).
318+
SetField("password", "my-password").
319+
Build(),
320+
)
321+
322+
assert.NoError(t, err)
323+
r := NewReconciler(mgr)
324+
res, err := r.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: mdb.Namespace, Name: mdb.Name}})
325+
assertReconciliationSuccessful(t, res, err)
326+
327+
svc := corev1.Service{}
328+
err = mgr.GetClient().Get(context.TODO(), types.NamespacedName{Name: mdb.ServiceName(), Namespace: mdb.Namespace}, &svc)
329+
assert.NoError(t, err)
330+
assert.Equal(t, svc.Spec.Type, corev1.ServiceTypeClusterIP)
331+
assert.Equal(t, svc.Spec.Selector["app"], mdb.ServiceName())
332+
assert.Len(t, svc.Spec.Ports, 2)
333+
assert.Equal(t, svc.Spec.Ports[0], corev1.ServicePort{Port: 1000, Name: "mongodb"})
334+
assert.Equal(t, svc.Spec.Ports[1], corev1.ServicePort{Port: 4321, Name: "prometheus"})
335+
336+
assert.Equal(t, svc.Labels["app"], mdb.ServiceName())
337+
338+
res, err = r.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: mdb.Namespace, Name: mdb.Name}})
339+
assertReconciliationSuccessful(t, res, err)
340+
}
341+
342+
func TestService_configuresPrometheus(t *testing.T) {
343+
mdb := newTestReplicaSet()
344+
mdb.Spec.Prometheus = &mdbv1.Prometheus{
345+
Username: "username",
346+
PasswordSecretRef: mdbv1.SecretKeyReference{
347+
Name: "secret",
348+
},
349+
}
350+
351+
mgr := client.NewManager(&mdb)
352+
err := secret.CreateOrUpdate(mgr.Client,
353+
secret.Builder().
354+
SetName("secret").
355+
SetNamespace(mdb.Namespace).
356+
SetField("password", "my-password").
357+
Build(),
358+
)
359+
assert.NoError(t, err)
360+
361+
r := NewReconciler(mgr)
362+
res, err := r.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: mdb.Namespace, Name: mdb.Name}})
363+
assertReconciliationSuccessful(t, res, err)
364+
365+
svc := corev1.Service{}
366+
err = mgr.GetClient().Get(context.TODO(), types.NamespacedName{Name: mdb.ServiceName(), Namespace: mdb.Namespace}, &svc)
367+
assert.NoError(t, err)
368+
369+
assert.Len(t, svc.Spec.Ports, 2)
370+
assert.Equal(t, svc.Spec.Ports[0], corev1.ServicePort{Port: 27017, Name: "mongodb"})
371+
assert.Equal(t, svc.Spec.Ports[1], corev1.ServicePort{Port: 9216, Name: "prometheus"})
372+
}
373+
299374
func TestCustomNetPort_Configuration(t *testing.T) {
300375
svc, _ := performReconciliationAndGetService(t, "specify_net_port.yaml")
301376
assert.Equal(t, svc.Spec.Type, corev1.ServiceTypeClusterIP)

docs/prometheus/README.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Using Prometheus with your MongoDB Resource
2+
3+
We have added a sample yaml file that you could use to deploy a MongoDB resource
4+
in your Kubernetes cluster, with a
5+
[`ServiceMonitor`](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/getting-started.md#related-resources)
6+
to indicate Prometheus how to consume metrics data from it.
7+
8+
This is a simple MongoDB resource with one user, and with the `spec.Prometheus`
9+
attribute with basic HTTP Auth and no TLS, that will allow you to test
10+
Prometheus metrics coming from MongoDB.
11+
12+
## Quick Start
13+
14+
We have tested this setup with version 0.54 of the [Prometheus
15+
Operator](https://github.com/prometheus-operator/prometheus-operator).
16+
17+
### Installing Prometheus Operator
18+
19+
The Prometheus Operator can be installed using Helm. Find the installation
20+
instructions
21+
[here](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack#kube-prometheus-stack):
22+
23+
This can be done with:
24+
25+
``` shell
26+
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
27+
helm repo update
28+
helm install prometheus prometheus-community/kube-prometheus-stack --namespace prometheus-system --create-namespace
29+
```
30+
31+
### Installing MongoDB
32+
33+
*Change after release to a proper Helm install.*
34+
35+
* Create a Namespace to hold our MongoDB Operator and Resources
36+
37+
``` shell
38+
kubectl create namespace mongodb
39+
```
40+
41+
* Follow the [Installation Instructions](https://github.com/mongodb/mongodb-kubernetes-operator/blob/master/docs/install-upgrade.md#operator-in-same-namespace-as-resources)
42+
43+
## Creating a MongoDB Resource
44+
45+
We have created a sample yaml definition that you can use to create a MongoDB
46+
resource and a `ServiceMonitor` that will indicate Prometheus to start scraping
47+
its metrics information.
48+
49+
You can apply it directly with:
50+
51+
``` shell
52+
kubectl apply -f mongodb-prometheus-sample.yaml
53+
```
54+
55+
This will create 2 `Secrets` containing authentication for a new MongoDB user
56+
and Basic HTTP Auth for the Prometheus endpoint. All of this in the `mongodb`
57+
Namespace.
58+
59+
It will also create a `ServiceMonitor` that will configure Prometheus to consume
60+
this resurce's metrics. This will be created in the `prometheus-system`
61+
namespace.
62+
63+
64+
## Bonus: Enable TLS on the Prometheus Endpoint
65+
66+
### Installing Cert-Manager
67+
68+
We will install [Cert-Manager](https://cert-manager.io/) from using Helm.
69+
70+
``` shell
71+
helm repo add jetstack https://charts.jetstack.io
72+
helm repo update
73+
helm install \
74+
cert-manager jetstack/cert-manager \
75+
--namespace cert-manager \
76+
--create-namespace \
77+
--version v1.7.1 \
78+
--set installCRDs=true
79+
```
80+
81+
Now with Cert-Manager installed we we'll create a Cert-Manager `Issuer` and then
82+
a `Certificate`. We provide 2 files that can be used to create a new `Issuer`.
83+
84+
First we need to create a `Secret` holding a TLS certificate `tls.crt` and
85+
`tls.key` entries. We provide the certificate and key files that can be used to
86+
create a Cert-Manager `Certificate`, they are in the `testdata/tls` directory.
87+
88+
``` shell
89+
$ kubectl create secret tls issuer-secret --cert=../../testdata/tls/ca.crt --key=../../testdata/tls/ca.key \
90+
--namespace mongodb
91+
secret/issuer-secret created
92+
```
93+
94+
And now we are ready to create a new `Issuer` and `Certificate`, by running the
95+
following command:
96+
97+
``` shell
98+
$ kubectl apply -f issuer-and-cert.yaml --namespace mongodb
99+
issuer.cert-manager.io/ca-issuer created
100+
certificate.cert-manager.io/prometheus-target-cert created
101+
```
102+
103+
### Enabling TLS on the MongoDB CRD
104+
105+
<center>_Make sure this configuration is not used in Production environments! A Security
106+
expert should be advising you on how to configure TLS_</center>
107+
108+
We need to add a new entry to `spec.prometheus` section of the MongoDB
109+
`CustomResource`; we can do it executing the following
110+
[patch](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/)
111+
operation.
112+
113+
``` shell
114+
$ kubectl patch mdbc mongodb --type='json' \
115+
-p='[{"op": "add", "path": "/spec/prometheus/tlsSecretKeyRef", "value":{"name": "prometheus-target-cert"}}]' \
116+
--namespace mongodb
117+
118+
mongodbcommunity.mongodbcommunity.mongodb.com/mongodb patched
119+
```
120+
121+
After a few minutes, the MongoDB resource should be back in Running phase. Now
122+
we need to configure our Prometheus `ServiceMonitor` to point at the HTTPS
123+
endpoint.
124+
125+
### Update ServiceMonitor
126+
127+
To update our `ServiceMonitor` we will again patch the resource:
128+
129+
``` shell
130+
$ kubectl patch servicemonitors mongodb-sm --type='json' \
131+
-p='
132+
[
133+
{"op": "replace", "path": "/spec/endpoints/0/scheme", "value": "https"},
134+
{"op": "add", "path": "/spec/endpoints/0/tlsConfig", "value": {"insecureSkipVerify": true}}
135+
]
136+
' \
137+
--namespace mongodb
138+
139+
servicemonitor.monitoring.coreos.com/mongodb-sm patched
140+
```
141+
142+
With these changes, the new `ServiceMonitor` will be pointing at the HTTPS
143+
endpoint (defined in `/spec/endpoints/0/scheme`). We are also setting
144+
`spec/endpoints/0/tlsConfig/insecureSkipVerify` to `true`, which will make
145+
Prometheus to not verify TLS certificates on MongoDB's end.
146+
147+
Prometheus should now be able to scrape the MongoDB's target using HTTPS this
148+
time.

0 commit comments

Comments
 (0)