diff --git a/charts/mysql-operator/templates/_helpers.tpl b/charts/mysql-operator/templates/_helpers.tpl index 1486c0ced..67a3aa055 100644 --- a/charts/mysql-operator/templates/_helpers.tpl +++ b/charts/mysql-operator/templates/_helpers.tpl @@ -40,3 +40,16 @@ Create the name of the service account to use {{ default "default" .Values.serviceAccount.name }} {{- end -}} {{- end -}} + +{{/* +Common labels +*/}} +{{- define "mysql-operator.labels" -}} +app.kubernetes.io/name: {{ include "mysql-operator.name" . }} +helm.sh/chart: {{ include "mysql-operator.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} \ No newline at end of file diff --git a/charts/mysql-operator/templates/servicemonitor.yaml b/charts/mysql-operator/templates/servicemonitor.yaml new file mode 100644 index 000000000..e23301b7f --- /dev/null +++ b/charts/mysql-operator/templates/servicemonitor.yaml @@ -0,0 +1,28 @@ +{{ if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" -}}{{ if .Values.serviceMonitor.enabled -}} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "mysql-operator.fullname" . }} + labels: + {{- include "mysql-operator.labels" . | nindent 4 }} + {{- if .Values.serviceMonitor.additionalLabels }} + {{ toYaml .Values.serviceMonitor.additionalLabels }} + {{- end }} +spec: + {{- if .Values.serviceMonitor.jobLabel }} + jobLabel: {{ .Values.serviceMonitor.jobLabel }} + {{- end }} + {{- if .Values.serviceMonitor.targetLabels }} + targetLabels: {{ .Values.serviceMonitor.targetLabels }} + {{- end }} + {{- if .Values.serviceMonitor.podTargetLabels }} + podTargetLabels: {{ .Values.serivceMonitor.podTargetLabels }} + {{- end }} + endpoints: + - interval: {{ .Values.serviceMonitor.interval }} + scrapeTimeout: {{ .Values.serviceMonitor.scrapeTimeout }} + port: metrics + path: /metrics + namespaceSelector: {{ toYaml .Values.serviceMonitor.namespaceSelector | nindent 4 }} + selector: {{ toYaml .Values.serviceMonitor.selector | nindent 4 }} +{{ end -}}{{ end -}} diff --git a/charts/mysql-operator/values.yaml b/charts/mysql-operator/values.yaml index 4c75ad47c..b23a33adb 100644 --- a/charts/mysql-operator/values.yaml +++ b/charts/mysql-operator/values.yaml @@ -43,3 +43,20 @@ rbacProxy: leaderElection: create: true + +serviceMonitor: + enabled: true + ## Additional labels for the serviceMonitor. Useful if you have multiple prometheus operators running to select only specific ServiceMonitors + # additionalLabels: + # prometheus: prom-internal + interval: 10s + scrapeTimeout: 3s + # jobLabel: + # targetLabels: + # podTargetLabels: + namespaceSelector: + any: true + selector: + matchLabels: + app.kubernetes.io/managed-by: mysql.radondb.com + app.kubernetes.io/name: mysql diff --git a/cluster/cluster.go b/cluster/cluster.go index d10d0f3c2..30a4927dc 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -260,6 +260,8 @@ func (c *Cluster) GetNameForResource(name utils.ResourceName) string { return fmt.Sprintf("%s-leader", c.Name) case utils.FollowerService: return fmt.Sprintf("%s-follower", c.Name) + case utils.MetricsService: + return fmt.Sprintf("%s-metrics", c.Name) case utils.Secret: return fmt.Sprintf("%s-secret", c.Name) default: diff --git a/cluster/syncer/headless_service.go b/cluster/syncer/headless_service.go index f70f83907..1cc146c52 100644 --- a/cluster/syncer/headless_service.go +++ b/cluster/syncer/headless_service.go @@ -56,21 +56,14 @@ func NewHeadlessSVCSyncer(cli client.Client, c *cluster.Cluster) syncer.Interfac // Use `publishNotReadyAddresses` to be able to access pods even if the pod is not ready. service.Spec.PublishNotReadyAddresses = true - service.Spec.Ports = []corev1.ServicePort{ - { - Name: utils.MysqlPortName, - Port: utils.MysqlPort, - TargetPort: intstr.FromInt(utils.MysqlPort), - }, + if len(service.Spec.Ports) != 1 { + service.Spec.Ports = make([]corev1.ServicePort, 1) } - if c.Spec.MetricsOpts.Enabled { - service.Spec.Ports = append(service.Spec.Ports, corev1.ServicePort{ - Name: utils.MetricsPortName, - Port: utils.MetricsPort, - TargetPort: intstr.FromInt(utils.MetricsPort), - }) - } + service.Spec.Ports[0].Name = utils.MysqlPortName + service.Spec.Ports[0].Port = utils.MysqlPort + service.Spec.Ports[0].TargetPort = intstr.FromInt(utils.MysqlPort) + return nil }) } diff --git a/cluster/syncer/metrics_service.go b/cluster/syncer/metrics_service.go new file mode 100644 index 000000000..4535f9a0a --- /dev/null +++ b/cluster/syncer/metrics_service.go @@ -0,0 +1,57 @@ +/* +Copyright 2021 RadonDB. + +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. +*/ + +package syncer + +import ( + "github.com/presslabs/controller-util/syncer" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/radondb/radondb-mysql-kubernetes/cluster" + "github.com/radondb/radondb-mysql-kubernetes/utils" +) + +// NewLeaderSVCSyncer returns leader service syncer. +func NewMetricsSVCSyncer(cli client.Client, c *cluster.Cluster) syncer.Interface { + service := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: c.GetNameForResource(utils.MetricsService), + Namespace: c.Namespace, + Labels: c.GetLabels(), + }, + } + return syncer.NewObjectSyncer("MetricsSVC", c.Unwrap(), service, cli, func() error { + service.Spec.Type = "ClusterIP" + service.Spec.Selector = c.GetSelectorLabels() + + if len(service.Spec.Ports) != 1 { + service.Spec.Ports = make([]corev1.ServicePort, 1) + } + + service.Spec.Ports[0].Name = utils.MetricsPortName + service.Spec.Ports[0].Port = utils.MetricsPort + service.Spec.Ports[0].TargetPort = intstr.FromInt(utils.MetricsPort) + + return nil + }) +} diff --git a/controllers/cluster_controller.go b/controllers/cluster_controller.go index ed3782009..fba466acf 100644 --- a/controllers/cluster_controller.go +++ b/controllers/cluster_controller.go @@ -118,6 +118,10 @@ func (r *ClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct clustersyncer.NewPDBSyncer(r.Client, instance), } + if instance.Spec.MetricsOpts.Enabled { + syncers = append(syncers, clustersyncer.NewMetricsSVCSyncer(r.Client, instance)) + } + // run the syncers for _, sync := range syncers { if err = syncer.Sync(ctx, sync, r.Recorder); err != nil { diff --git a/utils/constants.go b/utils/constants.go index 30c5e51ff..3227d2e85 100644 --- a/utils/constants.go +++ b/utils/constants.go @@ -102,6 +102,8 @@ const ( LeaderService ResourceName = "leader-service" // FollowerService is the name of a service that points healthy followers (excludes leader). FollowerService ResourceName = "follower-service" + // MetricsService is the name of the service that points to all nodes. + MetricsService ResourceName = "metrics-service" // Secret is the name of the secret that contains operator related credentials. Secret ResourceName = "secret" // Role is the alias of the role resource.