You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by pi...@apache.org on 2022/08/23 04:21:03 UTC

[submarine] branch master updated: SUBMARINE-1286. Install grafana

This is an automated email from the ASF dual-hosted git repository.

pingsutw pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git


The following commit(s) were added to refs/heads/master by this push:
     new 3fc12adc SUBMARINE-1286. Install grafana
3fc12adc is described below

commit 3fc12adc9da1768c60e8c273a41d08210f8c82a7
Author: jeff-901 <b0...@ntu.edu.tw>
AuthorDate: Mon Jul 4 22:32:12 2022 +0800

    SUBMARINE-1286. Install grafana
    
    ### What is this PR for?
    Install grafana by using grafana original image
    
    ### What type of PR is it?
    Feature
    
    ### Todos
    
    ### What is the Jira issue?
    https://issues.apache.org/jira/browse/SUBMARINE-1286
    
    ### How should this be tested?
    go to http://{istio ingress ip}/grafana
    
    ### Screenshots (if appropriate)
    ![image](https://user-images.githubusercontent.com/54139205/174744181-7b38c5b1-7309-4be9-a61a-15286dc3a927.png)
    
    ### Questions:
    * Do the license files need updating? No
    * Are there breaking changes for older versions? No
    * Does this need new documentation? No
    
    Author: jeff-901 <b0...@ntu.edu.tw>
    
    Signed-off-by: Kevin <pi...@apache.org>
    
    Closes #970 from jeff-901/SUBMARINE-1286 and squashes the following commits:
    
    a7d4661c [jeff-901] set database
    c99c29f4 [jeff-901] reset file permission
    41b60684 [jeff-901] install grafana
    fed79026 [jeff-901] grafana ingress
    122abe3c [jeff-901] install grafana
---
 dev-support/docker-images/database/startup.sh      |   3 +
 .../artifacts/submarine/submarine-grafana.yaml     | 118 +++++++++++++
 .../artifacts/submarine/submarine-ingress.yaml     |   8 +
 submarine-cloud-v2/main.go                         |   1 +
 submarine-cloud-v2/pkg/controller/controller.go    |  11 ++
 .../pkg/controller/controller_builder.go           |   2 +
 .../pkg/controller/controller_builder_config.go    |   8 +
 .../pkg/controller/controller_event_handlers.go    |  17 ++
 submarine-cloud-v2/pkg/controller/parser.go        |  11 ++
 .../pkg/controller/submarine_grafana.go            | 182 +++++++++++++++++++++
 10 files changed, 361 insertions(+)

diff --git a/dev-support/docker-images/database/startup.sh b/dev-support/docker-images/database/startup.sh
index c52fc936..af917c98 100755
--- a/dev-support/docker-images/database/startup.sh
+++ b/dev-support/docker-images/database/startup.sh
@@ -36,4 +36,7 @@ use metastore; source /tmp/database/metastore.sql;
 CREATE DATABASE mlflowdb;
 CREATE USER 'mlflow'@'%' IDENTIFIED BY 'password';
 GRANT ALL PRIVILEGES ON * . * TO 'mlflow'@'%';
+CREATE DATABASE grafana;
+CREATE USER 'grafana'@'%' IDENTIFIED BY 'password';
+GRANT ALL PRIVILEGES ON * . * TO 'grafana'@'%';
 EOF
diff --git a/submarine-cloud-v2/artifacts/submarine/submarine-grafana.yaml b/submarine-cloud-v2/artifacts/submarine/submarine-grafana.yaml
new file mode 100644
index 00000000..3a42735c
--- /dev/null
+++ b/submarine-cloud-v2/artifacts/submarine/submarine-grafana.yaml
@@ -0,0 +1,118 @@
+---
+# Source: submarine/templates/submarine-mlflow.yaml
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: submarine-grafana-config
+data:
+  grafana.ini: |
+    [server]
+    root_url = http://submarine-grafana-service/grafana/
+    serve_from_sub_path = true
+    [database]
+    type = mysql
+    name = grafana
+    host = submarine-database:3306
+    user = grafana
+    password = password
+    url = mysql://grafana:password@submarine-database:3306/grafana
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  name: submarine-grafana-pvc
+spec:
+  accessModes:
+    - ReadWriteOnce
+  resources:
+    requests:
+      storage: 1Gi
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  labels:
+    app: submarine-grafana
+  name: submarine-grafana
+spec:
+  selector:
+    matchLabels:
+      app: submarine-grafana
+  template:
+    metadata:
+      labels:
+        app: submarine-grafana
+    spec:
+      securityContext:
+        fsGroup: 472
+        supplementalGroups:
+          - 0
+      containers:
+        - name: submarine-grafana
+          image: grafana/grafana:8.3.1
+          imagePullPolicy: IfNotPresent
+          ports:
+            - containerPort: 3000
+              name: http-grafana
+              protocol: TCP
+          readinessProbe:
+            failureThreshold: 3
+            httpGet:
+              path: /robots.txt
+              port: 3000
+              scheme: HTTP
+            initialDelaySeconds: 10
+            periodSeconds: 30
+            successThreshold: 1
+            timeoutSeconds: 2
+          livenessProbe:
+            failureThreshold: 3
+            initialDelaySeconds: 30
+            periodSeconds: 10
+            successThreshold: 1
+            tcpSocket:
+              port: 3000
+            timeoutSeconds: 1
+          volumeMounts:
+            - mountPath: /var/lib/grafana
+              name: submarine-grafana-pv
+            - mountPath: /etc/grafana
+              name: submarine-grafana-config
+      volumes:
+        - name: submarine-grafana-pv
+          persistentVolumeClaim:
+            claimName: submarine-grafana-pvc
+        - name: submarine-grafana-config
+          configMap:
+            name: submarine-grafana-config
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: submarine-grafana-service
+  labels:
+    app: submarine-grafana
+spec:
+  ports:
+    - port: 3000
+      targetPort: 3000
+      protocol: TCP
+      targetPort: http-grafana
+  selector:
+    app: submarine-grafana
diff --git a/submarine-cloud-v2/artifacts/submarine/submarine-ingress.yaml b/submarine-cloud-v2/artifacts/submarine/submarine-ingress.yaml
index 73f4c842..f60f7989 100644
--- a/submarine-cloud-v2/artifacts/submarine/submarine-ingress.yaml
+++ b/submarine-cloud-v2/artifacts/submarine/submarine-ingress.yaml
@@ -49,6 +49,14 @@ spec:
             host: submarine-minio-service
             port:
               number: 9000
+    - match:
+        - uri:
+            prefix: /grafana
+      route:
+        - destination:
+            host: submarine-grafana-service
+            port:
+              number: 3000
     - route:
         - destination:
             host: submarine-server
diff --git a/submarine-cloud-v2/main.go b/submarine-cloud-v2/main.go
index f977ae93..48a90357 100644
--- a/submarine-cloud-v2/main.go
+++ b/submarine-cloud-v2/main.go
@@ -155,6 +155,7 @@ func NewSubmarineController(
 		WithServiceInformer(kubeInformerFactory.Core().V1().Services()).
 		WithServiceAccountInformer(kubeInformerFactory.Core().V1().ServiceAccounts()).
 		WithPersistentVolumeClaimInformer(kubeInformerFactory.Core().V1().PersistentVolumeClaims()).
+		WithConfigMapClaimInformer(kubeInformerFactory.Core().V1().ConfigMaps()).
 		WithIngressInformer(kubeInformerFactory.Extensions().V1beta1().Ingresses()).
 		// WithIngressRouteInformer(traefikInformerFactory.Traefik().V1alpha1().IngressRoutes()).
 		WithVirtualServiceInformer(istioInformerFactory.Networking().V1alpha3().VirtualServices()).
diff --git a/submarine-cloud-v2/pkg/controller/controller.go b/submarine-cloud-v2/pkg/controller/controller.go
index 10117c99..0e31156b 100644
--- a/submarine-cloud-v2/pkg/controller/controller.go
+++ b/submarine-cloud-v2/pkg/controller/controller.go
@@ -79,10 +79,15 @@ const (
 	minioPvcName                = minioName + "-pvc"
 	minioServiceName            = minioName + "-service"
 	minioIngressRouteName       = minioName + "-ingressroute"
+	grafanaName                 = "submarine-grafana"
+	grafanaPvcName              = grafanaName + "-pvc"
+	grafanaServiceName          = grafanaName + "-service"
+	grafanaConfigMapName        = grafanaName + "-config"
 	artifactPath                = "./artifacts/submarine/"
 	databaseYamlPath            = artifactPath + "submarine-database.yaml"
 	ingressYamlPath             = artifactPath + "submarine-ingress.yaml"
 	minioYamlPath               = artifactPath + "submarine-minio.yaml"
+	grafanaYamlPath             = artifactPath + "submarine-grafana.yaml"
 	mlflowYamlPath              = artifactPath + "submarine-mlflow.yaml"
 	serverYamlPath              = artifactPath + "submarine-server.yaml"
 	tensorboardYamlPath         = artifactPath + "submarine-tensorboard.yaml"
@@ -142,6 +147,7 @@ type Controller struct {
 	serviceaccountLister        corelisters.ServiceAccountLister
 	serviceLister               corelisters.ServiceLister
 	persistentvolumeclaimLister corelisters.PersistentVolumeClaimLister
+	configMapLister             corelisters.ConfigMapLister
 	ingressLister               extlisters.IngressLister
 	// ingressrouteLister          traefiklisters.IngressRouteLister
 	virtualServiceLister istioListers.VirtualServiceLister
@@ -512,6 +518,11 @@ func (c *Controller) createSubmarine(submarine *v1alpha1.Submarine) error {
 		return err
 	}
 
+	err = c.createSubmarineGrafana(submarine)
+	if err != nil && !errors.IsAlreadyExists(err) {
+		return err
+	}
+
 	return nil
 }
 
diff --git a/submarine-cloud-v2/pkg/controller/controller_builder.go b/submarine-cloud-v2/pkg/controller/controller_builder.go
index 192fea4e..6c34f8b9 100644
--- a/submarine-cloud-v2/pkg/controller/controller_builder.go
+++ b/submarine-cloud-v2/pkg/controller/controller_builder.go
@@ -90,6 +90,7 @@ func (cb *ControllerBuilder) addListers() *ControllerBuilder {
 	cb.controller.serviceLister = cb.config.serviceInformer.Lister()
 	cb.controller.serviceaccountLister = cb.config.serviceaccountInformer.Lister()
 	cb.controller.persistentvolumeclaimLister = cb.config.persistentvolumeclaimInformer.Lister()
+	cb.controller.configMapLister = cb.config.configMapInformer.Lister()
 	cb.controller.ingressLister = cb.config.ingressInformer.Lister()
 	// cb.controller.ingressrouteLister = cb.config.ingressrouteInformer.Lister()
 	cb.controller.virtualServiceLister = cb.config.virtualServiceInformer.Lister()
@@ -111,6 +112,7 @@ func (cb *ControllerBuilder) addEventHandlers() *ControllerBuilder {
 	cb.addServiceEventHandlers()
 	cb.addServiceAccountEventHandlers()
 	cb.addPersistentVolumeClaimEventHandlers()
+	cb.addConfigMapEventHandlers()
 	cb.addIngressEventHandlers()
 	// cb.addIngressRouteEventHandlers()
 	cb.addRoleEventHandlers()
diff --git a/submarine-cloud-v2/pkg/controller/controller_builder_config.go b/submarine-cloud-v2/pkg/controller/controller_builder_config.go
index b8a2dcc9..91b8100b 100644
--- a/submarine-cloud-v2/pkg/controller/controller_builder_config.go
+++ b/submarine-cloud-v2/pkg/controller/controller_builder_config.go
@@ -46,6 +46,7 @@ type BuilderConfig struct {
 	serviceInformer               coreinformers.ServiceInformer
 	serviceaccountInformer        coreinformers.ServiceAccountInformer
 	persistentvolumeclaimInformer coreinformers.PersistentVolumeClaimInformer
+	configMapInformer             coreinformers.ConfigMapInformer
 	ingressInformer               extinformers.IngressInformer
 	// ingressrouteInformer          traefikinformers.IngressRouteInformer
 	virtualServiceInformer istioInformers.VirtualServiceInformer
@@ -158,6 +159,13 @@ func (bc *BuilderConfig) WithPersistentVolumeClaimInformer(
 	return bc
 }
 
+func (bc *BuilderConfig) WithConfigMapClaimInformer(
+	configMapInformer coreinformers.ConfigMapInformer,
+) *BuilderConfig {
+	bc.configMapInformer = configMapInformer
+	return bc
+}
+
 func (bc *BuilderConfig) WithIngressInformer(
 	ingressInformer extinformers.IngressInformer,
 ) *BuilderConfig {
diff --git a/submarine-cloud-v2/pkg/controller/controller_event_handlers.go b/submarine-cloud-v2/pkg/controller/controller_event_handlers.go
index 1e0036c5..20efdd81 100644
--- a/submarine-cloud-v2/pkg/controller/controller_event_handlers.go
+++ b/submarine-cloud-v2/pkg/controller/controller_event_handlers.go
@@ -122,6 +122,23 @@ func (cb *ControllerBuilder) addPersistentVolumeClaimEventHandlers() *Controller
 	return cb
 }
 
+func (cb *ControllerBuilder) addConfigMapEventHandlers() *ControllerBuilder {
+	cb.config.configMapInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
+		AddFunc: cb.controller.handleObject,
+		UpdateFunc: func(old, new interface{}) {
+			newConfigMap := new.(*corev1.ConfigMap)
+			oldConfigMap := old.(*corev1.ConfigMap)
+			if newConfigMap.ResourceVersion == oldConfigMap.ResourceVersion {
+				return
+			}
+			cb.controller.handleObject(new)
+		},
+		DeleteFunc: cb.controller.handleObject,
+	})
+
+	return cb
+}
+
 func (cb *ControllerBuilder) addIngressEventHandlers() *ControllerBuilder {
 	cb.config.ingressInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
 		AddFunc: cb.controller.handleObject,
diff --git a/submarine-cloud-v2/pkg/controller/parser.go b/submarine-cloud-v2/pkg/controller/parser.go
index e844f3e8..040e64eb 100644
--- a/submarine-cloud-v2/pkg/controller/parser.go
+++ b/submarine-cloud-v2/pkg/controller/parser.go
@@ -169,6 +169,17 @@ func ParsePersistentVolumeClaimYaml(relativePath string) (*v1.PersistentVolumeCl
 	return &pvc, nil
 }
 
+// ParseConfigMap parse ConfigMap from yaml file.
+func ParseConfigMapYaml(relativePath string) (*v1.ConfigMap, error) {
+	var configMap v1.ConfigMap
+	marshaled, err := parseYaml(relativePath, "ConfigMap")
+	if err != nil {
+		return nil, err
+	}
+	json.Unmarshal(marshaled, &configMap)
+	return &configMap, nil
+}
+
 // ParseIngressRouteYaml parse IngressRoute from yaml file.
 func ParseIngressRouteYaml(relativePath string) (*traefikv1alpha1.IngressRoute, error) {
 	var ingressRoute traefikv1alpha1.IngressRoute
diff --git a/submarine-cloud-v2/pkg/controller/submarine_grafana.go b/submarine-cloud-v2/pkg/controller/submarine_grafana.go
new file mode 100644
index 00000000..90c8f2b2
--- /dev/null
+++ b/submarine-cloud-v2/pkg/controller/submarine_grafana.go
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 controller
+
+import (
+	"context"
+	"fmt"
+
+	v1alpha1 "github.com/apache/submarine/submarine-cloud-v2/pkg/apis/submarine/v1alpha1"
+
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/klog/v2"
+)
+
+func newSubmarineGrafanaConfigMap(submarine *v1alpha1.Submarine) *corev1.ConfigMap {
+	configMap, err := ParseConfigMapYaml(grafanaYamlPath)
+	if err != nil {
+		klog.Info("[Error] ParseConfigMap", err)
+	}
+	configMap.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
+		*metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
+	}
+
+	return configMap
+}
+
+func newSubmarineGrafanaPersistentVolumeClaim(submarine *v1alpha1.Submarine) *corev1.PersistentVolumeClaim {
+	pvc, err := ParsePersistentVolumeClaimYaml(grafanaYamlPath)
+	if err != nil {
+		klog.Info("[Error] ParsePersistentVolumeClaim", err)
+	}
+	pvc.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
+		*metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
+	}
+
+	return pvc
+}
+
+func newSubmarineGrafanaDeployment(submarine *v1alpha1.Submarine) *appsv1.Deployment {
+	deployment, err := ParseDeploymentYaml(grafanaYamlPath)
+	if err != nil {
+		klog.Info("[Error] ParseDeploymentYaml", err)
+	}
+	deployment.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
+		*metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
+	}
+
+	return deployment
+}
+
+func newSubmarineGrafanaService(submarine *v1alpha1.Submarine) *corev1.Service {
+	service, err := ParseServiceYaml(grafanaYamlPath)
+	if err != nil {
+		klog.Info("[Error] ParseServiceYaml", err)
+	}
+	service.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
+		*metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
+	}
+	return service
+}
+
+// createSubmarineGrafana is a function to create submarine-grafana
+func (c *Controller) createSubmarineGrafana(submarine *v1alpha1.Submarine) error {
+	klog.Info("[createSubmarineGrafana]")
+
+	// Step 1: Create ConfigMap
+	configMap, err := c.configMapLister.ConfigMaps(submarine.Namespace).Get(grafanaConfigMapName)
+	klog.Info("	Create ConfigMap: ", configMap)
+	// If the resource doesn't exist, we'll create it
+	if errors.IsNotFound(err) {
+		klog.Info("Into not found")
+		configMap, err = c.kubeclientset.CoreV1().ConfigMaps(submarine.Namespace).Create(context.TODO(),
+			newSubmarineGrafanaConfigMap(submarine),
+			metav1.CreateOptions{})
+		if err != nil {
+			klog.Info(err)
+		}
+		klog.Info("	Create ConfigMap: ", configMap.Name)
+	}
+	// If an error occurs during Get/Create, we'll requeue the item so we can
+	// attempt processing again later. This could have been caused by a
+	// temporary network failure, or any other transient reason.
+	if err != nil {
+		return err
+	}
+
+	if !metav1.IsControlledBy(configMap, submarine) {
+		msg := fmt.Sprintf(MessageResourceExists, configMap.Name)
+		c.recorder.Event(submarine, corev1.EventTypeWarning, ErrResourceExists, msg)
+		return fmt.Errorf(msg)
+	}
+
+	// Step 2: Create PersistentVolumeClaim
+	pvc, err := c.persistentvolumeclaimLister.PersistentVolumeClaims(submarine.Namespace).Get(grafanaPvcName)
+	// If the resource doesn't exist, we'll create it
+	if errors.IsNotFound(err) {
+		pvc, err = c.kubeclientset.CoreV1().PersistentVolumeClaims(submarine.Namespace).Create(context.TODO(),
+			newSubmarineGrafanaPersistentVolumeClaim(submarine),
+			metav1.CreateOptions{})
+		if err != nil {
+			klog.Info(err)
+		}
+		klog.Info("	Create PersistentVolumeClaim: ", pvc.Name)
+	}
+	// If an error occurs during Get/Create, we'll requeue the item so we can
+	// attempt processing again later. This could have been caused by a
+	// temporary network failure, or any other transient reason.
+	if err != nil {
+		return err
+	}
+
+	if !metav1.IsControlledBy(pvc, submarine) {
+		msg := fmt.Sprintf(MessageResourceExists, pvc.Name)
+		c.recorder.Event(submarine, corev1.EventTypeWarning, ErrResourceExists, msg)
+		return fmt.Errorf(msg)
+	}
+
+	// Step 3: Create Deployment
+	deployment, err := c.deploymentLister.Deployments(submarine.Namespace).Get(grafanaName)
+	if errors.IsNotFound(err) {
+		deployment, err = c.kubeclientset.AppsV1().Deployments(submarine.Namespace).Create(context.TODO(), newSubmarineGrafanaDeployment(submarine), metav1.CreateOptions{})
+		if err != nil {
+			klog.Info(err)
+		}
+		klog.Info("	Create Deployment: ", deployment.Name)
+	}
+	// If an error occurs during Get/Create, we'll requeue the item so we can
+	// attempt processing again later. This could have been caused by a
+	// temporary network failure, or any other transient reason.
+	if err != nil {
+		return err
+	}
+
+	if !metav1.IsControlledBy(deployment, submarine) {
+		msg := fmt.Sprintf(MessageResourceExists, deployment.Name)
+		c.recorder.Event(submarine, corev1.EventTypeWarning, ErrResourceExists, msg)
+		return fmt.Errorf(msg)
+	}
+
+	// Step 4: Create Service
+	service, err := c.serviceLister.Services(submarine.Namespace).Get(grafanaServiceName)
+	// If the resource doesn't exist, we'll create it
+	if errors.IsNotFound(err) {
+		service, err = c.kubeclientset.CoreV1().Services(submarine.Namespace).Create(context.TODO(), newSubmarineGrafanaService(submarine), metav1.CreateOptions{})
+		if err != nil {
+			klog.Info(err)
+		}
+		klog.Info(" Create Service: ", service.Name)
+	}
+	// If an error occurs during Get/Create, we'll requeue the item so we can
+	// attempt processing again later. This could have been caused by a
+	// temporary network failure, or any other transient reason.
+	if err != nil {
+		return err
+	}
+
+	if !metav1.IsControlledBy(service, submarine) {
+		msg := fmt.Sprintf(MessageResourceExists, service.Name)
+		c.recorder.Event(submarine, corev1.EventTypeWarning, ErrResourceExists, msg)
+		return fmt.Errorf(msg)
+	}
+
+	return nil
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org