Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion examples/psmdb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ examples/psmdb/

3. Install the PSMDB operator:
```bash
kubectl apply --server-side -f https://raw.githubusercontent.com/percona/percona-server-mongodb-operator/v1.21.1/deploy/bundle.yaml
kubectl apply --server-side -f https://raw.githubusercontent.com/percona/percona-server-mongodb-operator/v1.21.1/deploy/cw-bundle.yaml

# For testing, give the operator admin permissions
kubectl create clusterrolebinding psmdb-operator-admin --clusterrole=cluster-admin --serviceaccount=default:percona-server-mongodb-operator
```

**Note:** This is a PoC requirement. In production, the underlying database operator (PSMDB in this case) should be packaged within the provider's Helm chart to ensure it installs automatically with the provider.
Expand Down Expand Up @@ -77,6 +80,16 @@ kubectl get psmdb
kubectl get datastore
```

### Monitor a datastore with Prometheus

```bash
kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.89.0/bundle.yaml
```

```bash
kubectl apply -f datastore-exporter.yaml
```

## 📖 Understanding the Code

### Business Logic (`internal/provider.go`)
Expand Down
10 changes: 9 additions & 1 deletion examples/psmdb/cmd/generate-manifest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,21 +70,27 @@ func psmdbMetadata() *sdk.ProviderMetadata {
},
"backup": {
Versions: []sdk.ComponentVersionMeta{
{Version: "2.9.1", Image: "percona/percona-server-mongodb-backup:2.9.1", Default: true},
{Version: "2.9.1", Image: "percona/percona-backup-mongodb:2.9.1", Default: true},
},
},
"pmm": {
Versions: []sdk.ComponentVersionMeta{
{Version: "2.44.1", Image: "percona/pmm-server:2.44.1", Default: true},
},
},
"exporter": {
Versions: []sdk.ComponentVersionMeta{
{Version: "0.47.2", Image: "percona/mongodb_exporter:0.47.2", Default: true},
},
},
},
Components: map[string]sdk.ComponentMeta{
"engine": {Type: "mongod"},
"configServer": {Type: "mongod"},
"proxy": {Type: "mongod"},
"backupAgent": {Type: "backup"},
"monitoring": {Type: "pmm"},
"metrics": {Type: "exporter"},
},
Topologies: map[string]sdk.TopologyMeta{
"standard": {
Expand All @@ -95,6 +101,7 @@ func psmdbMetadata() *sdk.ProviderMetadata {
},
"backupAgent": {Optional: true},
"monitoring": {Optional: true},
"metrics": {Optional: true},
},
},
"sharded": {
Expand All @@ -107,6 +114,7 @@ func psmdbMetadata() *sdk.ProviderMetadata {
"configServer": {Optional: false},
"backupAgent": {Optional: true},
"monitoring": {Optional: true},
"metrics": {Optional: true},
},
},
},
Expand Down
122 changes: 122 additions & 0 deletions examples/psmdb/datastore-exporter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
apiVersion: everest.percona.com/v2alpha1
kind: DataStore
metadata:
name: psmdb-exporter
namespace: default
spec:
# Provider identifier
provider: psmdb

# Topology is optional - will use provider's default (typically "replicaset")
# If omitted, the provider determines the default topology

# Components with smart defaults
components:
# Engine is the only required component for default topology
engine:
type: mongod
# Version is optional - will use default from provider metadata
# version: 8.0.8-3
replicas: 3
storage:
size: 10Gi

# Optional: backup agent
backupAgent:
type: backup
# version: 2.9.1
replicas: 1

# Optional: metrics exporter
metrics:
type: exporter
customSpec:
enabled: true
# version: 0.47.2
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus
rules:
- apiGroups: [""]
resources:
- nodes
- nodes/metrics
- services
- endpoints
- pods
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources:
- configmaps
verbs: ["get"]
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs: ["get", "list", "watch"]
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: default
---
apiVersion: v1
kind: Secret
metadata:
name: psmdb-exporter-scrape-config
namespace: default
type: Opaque
stringData:
scrape-config.yaml: |
- job_name: 'psmdb-exporter'
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- default
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_managed_by]
action: keep
regex: percona-server-mongodb-operator
- source_labels: [__address__]
action: replace
regex: ([^:]+)(?::\d+)?
replacement: ${1}:9216
target_label: __address__
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: pod
metrics_path: /metrics
---
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: prometheus
spec:
serviceAccountName: prometheus
additionalScrapeConfigs:
name: psmdb-exporter-scrape-config
key: scrape-config.yaml
---
124 changes: 115 additions & 9 deletions examples/psmdb/internal/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ const (
ComponentProxy = "proxy"
ComponentBackupAgent = "backupAgent"
ComponentMonitoring = "monitoring"
ComponentMetrics = "metrics"

ComponentTypeMongod = "mongod"
ComponentTypeBackup = "backup"
ComponentTypePMM = "pmm"
ComponentTypeMongod = "mongod"
ComponentTypeBackup = "backup"
ComponentTypePMM = "pmm"
ComponentTypeExporter = "exporter"
)

const (
Expand Down Expand Up @@ -265,6 +267,80 @@ func configureBackup(c *sdk.Context) psmdbv1.BackupSpec {
}
}

// configureExporter creates a sidecar container configuration for MongoDB Exporter.
// It exposes metrics on localhost:9216/metrics.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kubectl -n everest exec psmdb-exporter-rs0-0 -- curl localhost:9216/metrics
...
# HELP go_sched_gomaxprocs_threads The current runtime.GOMAXPROCS setting, or the number of operating system threads that can execute user-level Go code simultaneously. Sourced from /sched/gomaxprocs:threads.
# TYPE go_sched_gomaxprocs_threads gauge
go_sched_gomaxprocs_threads 14
# HELP go_threads Number of OS threads created.
# TYPE go_threads gauge
go_threads 11
# HELP mongodb_fcv_feature_compatibility_version Feature compatibility version
# TYPE mongodb_fcv_feature_compatibility_version gauge
mongodb_fcv_feature_compatibility_version{version="8.0"} 8
# HELP mongodb_up Whether MongoDB is up.
# TYPE mongodb_up gauge
mongodb_up{cluster_role="mongod"} 1
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 0.03
# HELP process_max_fds Maximum number of open file descriptors.
# TYPE process_max_fds gauge
process_max_fds 1.048576e+06
# HELP process_network_receive_bytes_total Number of bytes received by the process over the network.
# TYPE process_network_receive_bytes_total counter
process_network_receive_bytes_total 3.523682e+06
# HELP process_network_transmit_bytes_total Number of bytes sent by the process over the network.
# TYPE process_network_transmit_bytes_total counter
process_network_transmit_bytes_total 3.690337e+06
# HELP process_open_fds Number of open file descriptors.
# TYPE process_open_fds gauge
process_open_fds 13
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 3.3107968e+07
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.77018535928e+09
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 1.292095488e+09
# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes.
# TYPE process_virtual_memory_max_bytes gauge
process_virtual_memory_max_bytes 1.8446744073709552e+19

// Returns nil if metrics component is not enabled or if customSpec.enabled is false.
func configureExporter(c *sdk.Context, secretName string) (*corev1.Container, error) {
metricsComponent, ok := c.DB().Spec.Components[ComponentMetrics]
if !ok {
return nil, nil
}

// Check if the metrics component has a CustomSpec with Enabled field
if metricsComponent.CustomSpec != nil && metricsComponent.CustomSpec.Raw != nil {
var spec types.ExporterSpec
if err := c.DecodeComponentCustomSpec(metricsComponent, &spec); err != nil {
return nil, fmt.Errorf("failed to decode custom spec for metrics component: %w", err)
}

// TODO handle disabled
if !spec.Enabled {
return nil, nil
}
}

// Use the image from the component spec if provided, otherwise use default
var exporterImage string
if metadata := c.Metadata(); metadata != nil {
exporterImage = metadata.GetDefaultImage(ComponentTypeExporter)
} else {
exporterImage = PSMDBMetadata().GetDefaultImage(ComponentTypeExporter)
}

return &corev1.Container{
Name: c.Name() + "-metrics-exporter",
Image: exporterImage,
Args: []string{"--discovering-mode", "--compatible-mode", "--collect-all", "--mongodb.uri=$(MONGODB_URI)"},
Env: []corev1.EnvVar{
{
Name: "MONGODB_USER",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: secretName,
},
Key: "MONGODB_CLUSTER_MONITOR_USER",
},
},
},
{
Name: "MONGODB_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: secretName,
},
Key: "MONGODB_CLUSTER_MONITOR_PASSWORD",
},
},
},
{
Name: "POD_NAME",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.name",
},
},
},
{
Name: "MONGODB_URI",
Value: "mongodb://$(MONGODB_USER):$(MONGODB_PASSWORD)@$(POD_NAME)",
},
},
}, nil
}

// SyncPSMDB ensures all PSMDB resources exist and are configured correctly.
func SyncPSMDB(c *sdk.Context) error {
fmt.Println("Syncing PSMDB cluster:", c.Name())
Expand Down Expand Up @@ -306,6 +382,27 @@ func SyncPSMDB(c *sdk.Context) error {
SSLInternal: c.Name() + "-ssl-internal",
}

// attaches the MongoDB exporter sidecar to replsets, if enabled.
sidecar, err := configureExporter(c, psmdb.Spec.Secrets.Users)
if err != nil {
fmt.Printf("Error configuring exporter: %v\n", err)
}

if sidecar != nil {
for _, replset := range psmdb.Spec.Replsets {
if replset == nil {
continue
}

replset.MultiAZ.Sidecars, _ = replset.MultiAZ.WithSidecars(*sidecar)
}

// Also add exporter to config server replset in sharded topology
if psmdb.Spec.Sharding.Enabled && psmdb.Spec.Sharding.ConfigsvrReplSet != nil {
psmdb.Spec.Sharding.ConfigsvrReplSet.MultiAZ.Sidecars, _ = psmdb.Spec.Sharding.ConfigsvrReplSet.MultiAZ.WithSidecars(*sidecar)
}
}

if err := c.Apply(psmdb); err != nil {
return err
}
Expand Down Expand Up @@ -362,6 +459,7 @@ func PSMDBTopologyDefinitions() map[string]sdk.TopologyDefinition {
ComponentEngine: {Optional: false, Defaults: map[string]interface{}{"replicas": 3}},
ComponentBackupAgent: {Optional: true},
ComponentMonitoring: {Optional: true},
ComponentMetrics: {Optional: true},
},
},
string(types.TopologyTypeSharded): {
Expand All @@ -372,6 +470,7 @@ func PSMDBTopologyDefinitions() map[string]sdk.TopologyDefinition {
ComponentConfigServer: {Optional: false},
ComponentBackupAgent: {Optional: true},
ComponentMonitoring: {Optional: true},
ComponentMetrics: {Optional: true},
},
},
}
Expand Down Expand Up @@ -406,7 +505,7 @@ func PSMDBMetadata() *sdk.ProviderMetadata {
// backup is the backup agent component
ComponentTypeBackup: {
Versions: []sdk.ComponentVersionMeta{
{Version: "2.9.1", Image: "percona/percona-server-mongodb-backup:2.9.1", Default: true},
{Version: "2.9.1", Image: "percona/percona-backup-mongodb:2.9.1", Default: true},
},
},
// pmm is the Percona Monitoring and Management component
Expand All @@ -415,16 +514,22 @@ func PSMDBMetadata() *sdk.ProviderMetadata {
{Version: "2.44.1", Image: "percona/pmm-server:2.44.1", Default: true},
},
},
ComponentTypeExporter: {
Versions: []sdk.ComponentVersionMeta{
{Version: "0.47.2", Image: "percona/mongodb_exporter:0.47.2", Default: true},
},
},
},

// Components defines the logical components that use the component types.
// Multiple components can reference the same component type (e.g., engine and configServer both use mongod).
Components: map[string]sdk.ComponentMeta{
ComponentEngine: {Type: ComponentTypeMongod}, // Main database engine
ComponentConfigServer: {Type: ComponentTypeMongod}, // Config server for sharded clusters
ComponentProxy: {Type: ComponentTypeMongod}, // Proxy/mongos for sharded clusters
ComponentBackupAgent: {Type: ComponentTypeBackup}, // Backup agent
ComponentMonitoring: {Type: ComponentTypePMM}, // Monitoring agent
ComponentEngine: {Type: ComponentTypeMongod}, // Main database engine
ComponentConfigServer: {Type: ComponentTypeMongod}, // Config server for sharded clusters
ComponentProxy: {Type: ComponentTypeMongod}, // Proxy/mongos for sharded clusters
ComponentBackupAgent: {Type: ComponentTypeBackup}, // Backup agent
ComponentMonitoring: {Type: ComponentTypePMM}, // Monitoring agent
ComponentMetrics: {Type: ComponentTypeExporter}, // Metrics exporter
},
}

Expand Down Expand Up @@ -487,6 +592,7 @@ func (p *PSMDBProvider) ComponentSchemas() map[string]interface{} {
ComponentProxy: &types.MongosCustomSpec{},
ComponentBackupAgent: &types.BackupCustomSpec{},
ComponentMonitoring: &types.PMMCustomSpec{},
ComponentMetrics: &types.ExporterSpec{},
}
}

Expand Down
Loading