diff --git a/BUILD.md b/BUILD.md index f802ef9560c2c..aef018d32c72d 100644 --- a/BUILD.md +++ b/BUILD.md @@ -144,6 +144,10 @@ bin/conduit stat deployments # view a live pipeline of requests bin/conduit tap deploy emojivoto/voting + +# view grafana dashboard +kubectl -n conduit port-forward $(kubectl --namespace=conduit get po --selector=app=grafana -o jsonpath='{.items[*].metadata.name}') 3000:3000 +open http://localhost:3000 ``` ## Go diff --git a/cli/cmd/install.go b/cli/cmd/install.go index 3759ec473dfb4..54f7f5ca57d3f 100644 --- a/cli/cmd/install.go +++ b/cli/cmd/install.go @@ -20,6 +20,9 @@ type installConfig struct { ControllerImage string WebImage string PrometheusImage string + GrafanaImage string + VizDashboard string + HealthDashboard string ControllerReplicas uint WebReplicas uint PrometheusReplicas uint @@ -63,6 +66,9 @@ func validateAndBuildConfig() (*installConfig, error) { ControllerImage: fmt.Sprintf("%s/controller:%s", dockerRegistry, conduitVersion), WebImage: fmt.Sprintf("%s/web:%s", dockerRegistry, conduitVersion), PrometheusImage: "prom/prometheus:v2.1.0", + GrafanaImage: "grafana/grafana:5.0.0-beta4", + VizDashboard: install.Viz, + HealthDashboard: install.Health, ControllerReplicas: controllerReplicas, WebReplicas: webReplicas, PrometheusReplicas: prometheusReplicas, diff --git a/cli/cmd/install_test.go b/cli/cmd/install_test.go index 6eb978df1c0d8..4da21ca72de0b 100644 --- a/cli/cmd/install_test.go +++ b/cli/cmd/install_test.go @@ -25,6 +25,9 @@ func TestRender(t *testing.T) { ControllerImage: "ControllerImage", WebImage: "WebImage", PrometheusImage: "PrometheusImage", + GrafanaImage: "GrafanaImage", + VizDashboard: "VizDashboard", + HealthDashboard: "HealthDashboard", ControllerReplicas: 1, WebReplicas: 2, PrometheusReplicas: 3, diff --git a/cli/cmd/test_helper.go b/cli/cmd/test_helper.go index 837302c749efe..53d91dcfbef30 100644 --- a/cli/cmd/test_helper.go +++ b/cli/cmd/test_helper.go @@ -13,7 +13,12 @@ func diffCompare(t *testing.T, actual string, expected string) { if actual != expected { dmp := diffmatchpatch.New() diffs := dmp.DiffMain(expected, actual, true) - patches := dmp.PatchMake(expected, diffs) + + // colorized output for local testing + // t.Fatalf("Unexpected output:\n%+v", dmp.DiffPrettyText(diffs)) + + diffs = dmp.DiffCleanupSemantic(diffs) + patches := dmp.PatchMake(diffs) patchText := dmp.PatchToText(patches) t.Fatalf("Unexpected output:\n%+v", patchText) } diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden index 05f7c08a2f9ef..11e1e22b9badf 100755 --- a/cli/cmd/testdata/install_default.golden +++ b/cli/cmd/testdata/install_default.golden @@ -546,4 +546,273 @@ data: - source_labels: [__meta_kubernetes_pod_container_name] action: replace target_label: job + +### Grafana ### +--- +kind: Service +apiVersion: v1 +metadata: + name: grafana + namespace: conduit + labels: + conduit.io/control-plane-component: grafana + annotations: + conduit.io/created-by: conduit/cli undefined +spec: + type: ClusterIP + selector: + conduit.io/control-plane-component: grafana + ports: + - name: http + port: 3000 + targetPort: 3000 + +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + annotations: + conduit.io/created-by: conduit/cli undefined + creationTimestamp: null + labels: + conduit.io/control-plane-component: grafana + name: grafana + namespace: conduit +spec: + replicas: 1 + strategy: {} + template: + metadata: + annotations: + conduit.io/created-by: conduit/cli undefined + conduit.io/proxy-version: undefined + creationTimestamp: null + labels: + conduit.io/control-plane-component: grafana + conduit.io/control-plane-ns: conduit + spec: + containers: + - image: grafana/grafana:5.0.0-beta4 + imagePullPolicy: IfNotPresent + name: grafana + ports: + - containerPort: 3000 + name: http + resources: {} + volumeMounts: + - mountPath: /etc/grafana + name: grafana-config + readOnly: true + - mountPath: /var/lib/grafana/dashboards + name: grafana-dashboards + - mountPath: /usr/share/grafana/public/dashboards + name: grafana-dashboard-home + readOnly: true + - env: + - name: CONDUIT_PROXY_LOG + value: warn,conduit_proxy=info + - name: CONDUIT_PROXY_CONTROL_URL + value: tcp://proxy-api.conduit.svc.cluster.local:8086 + - name: CONDUIT_PROXY_CONTROL_LISTENER + value: tcp://0.0.0.0:4190 + - name: CONDUIT_PROXY_PRIVATE_LISTENER + value: tcp://127.0.0.1:4140 + - name: CONDUIT_PROXY_PUBLIC_LISTENER + value: tcp://0.0.0.0:4143 + - name: CONDUIT_PROXY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: CONDUIT_PROXY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: CONDUIT_PROXY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONDUIT_PROXY_DESTINATIONS_AUTOCOMPLETE_FQDN + value: Kubernetes + image: gcr.io/runconduit/proxy:undefined + imagePullPolicy: IfNotPresent + name: conduit-proxy + ports: + - containerPort: 4143 + name: conduit-proxy + resources: {} + securityContext: + runAsUser: 2102 + initContainers: + - args: + - --incoming-proxy-port + - "4143" + - --outgoing-proxy-port + - "4140" + - --proxy-uid + - "2102" + - --inbound-ports-to-ignore + - "4190" + image: gcr.io/runconduit/proxy-init:undefined + imagePullPolicy: IfNotPresent + name: conduit-init + resources: {} + securityContext: + capabilities: + add: + - NET_ADMIN + privileged: false + volumes: + - configMap: + items: + - key: grafana.ini + path: grafana.ini + - key: datasources.yaml + path: provisioning/datasources/datasources.yaml + - key: dashboards.yaml + path: provisioning/dashboards/dashboards.yaml + name: grafana-config + name: grafana-config + - configMap: + name: grafana-dashboards + name: grafana-dashboards + - configMap: + items: + - key: conduit-viz.json + path: home.json + name: grafana-dashboards + name: grafana-dashboard-home +status: {} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: grafana-config + namespace: conduit + labels: + conduit.io/control-plane-component: grafana + annotations: + conduit.io/created-by: conduit/cli undefined +data: + grafana.ini: |- + instance_name = conduit-grafana + + [auth] + disable_login_form = true + + [auth.anonymous] + enabled = true + org_role = Editor + + [auth.basic] + enabled = false + + datasources.yaml: |- + apiVersion: 1 + datasources: + - name: prometheus + type: prometheus + access: proxy + orgId: 1 + url: http://prometheus.conduit.svc.cluster.local:9090 + isDefault: true + jsonData: + timeInterval: "5s" + version: 1 + editable: true + + dashboards.yaml: |- + apiVersion: 1 + providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: true + editable: true + options: + path: /var/lib/grafana/dashboards + homeDashboardId: conduit-viz + +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: grafana-dashboards + namespace: conduit + labels: + conduit.io/control-plane-component: grafana + annotations: + conduit.io/created-by: conduit/cli undefined +data: + conduit-viz.json: |- + { + "rows": [ + { + "collapse": false, + "height": "50px", + "panels": [ + { + "content": "
\n
\n \n
\n
\n Conduit is a next-generation ultralight service mesh for Kubernetes.\n
\n Need help? Visit conduit.io.\n
\n
", + "height": "1px", + "id": 14, + "links": [], + "mode": "html", + "span": 12, + "title": "", + "transparent": true, + "type": "text" + } + ], + "showTitle": false + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "legend": { + }, + "lines": true, + "targets": [ + { + "expr": "sum(irate(responses_total[20s])) by (target_deployment)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{target_deployment}}", + "refId": "A" + } + ], + "title": "Request Volume", + "type": "graph", + "xaxis": { + "show": true + }, + "yaxes": [ + { + "format": "rps", + "show": true + }, + { + "show": true + } + ] + } + ], + "showTitle": false + } + ], + "refresh": "5s", + "time": { + "from": "now-5m", + "to": "now" + }, + "title": "conduit-viz" + } + + + conduit-health.json: |- + { + "title": "conduit-health" + } + --- diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden index a532c7253c3ef..6524732f48382 100644 --- a/cli/cmd/testdata/install_output.golden +++ b/cli/cmd/testdata/install_output.golden @@ -549,4 +549,208 @@ data: - source_labels: [__meta_kubernetes_pod_container_name] action: replace target_label: job + +### Grafana ### +--- +kind: Service +apiVersion: v1 +metadata: + name: grafana + namespace: Namespace + labels: + ControllerComponentLabel: grafana + annotations: + CreatedByAnnotation: CliVersion +spec: + type: ClusterIP + selector: + ControllerComponentLabel: grafana + ports: + - name: http + port: 3000 + targetPort: 3000 + +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + annotations: + CreatedByAnnotation: CliVersion + creationTimestamp: null + labels: + ControllerComponentLabel: grafana + name: grafana + namespace: Namespace +spec: + replicas: 1 + strategy: {} + template: + metadata: + annotations: + CreatedByAnnotation: CliVersion + conduit.io/created-by: conduit/cli undefined + conduit.io/proxy-version: undefined + creationTimestamp: null + labels: + ControllerComponentLabel: grafana + conduit.io/control-plane-ns: Namespace + spec: + containers: + - image: GrafanaImage + imagePullPolicy: ImagePullPolicy + name: grafana + ports: + - containerPort: 3000 + name: http + resources: {} + volumeMounts: + - mountPath: /etc/grafana + name: grafana-config + readOnly: true + - mountPath: /var/lib/grafana/dashboards + name: grafana-dashboards + - mountPath: /usr/share/grafana/public/dashboards + name: grafana-dashboard-home + readOnly: true + - env: + - name: CONDUIT_PROXY_LOG + value: warn,conduit_proxy=info + - name: CONDUIT_PROXY_CONTROL_URL + value: tcp://proxy-api.Namespace.svc.cluster.local:8086 + - name: CONDUIT_PROXY_CONTROL_LISTENER + value: tcp://0.0.0.0:4190 + - name: CONDUIT_PROXY_PRIVATE_LISTENER + value: tcp://127.0.0.1:4140 + - name: CONDUIT_PROXY_PUBLIC_LISTENER + value: tcp://0.0.0.0:4143 + - name: CONDUIT_PROXY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: CONDUIT_PROXY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: CONDUIT_PROXY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONDUIT_PROXY_DESTINATIONS_AUTOCOMPLETE_FQDN + value: Kubernetes + image: gcr.io/runconduit/proxy:undefined + imagePullPolicy: IfNotPresent + name: conduit-proxy + ports: + - containerPort: 4143 + name: conduit-proxy + resources: {} + securityContext: + runAsUser: 2102 + initContainers: + - args: + - --incoming-proxy-port + - "4143" + - --outgoing-proxy-port + - "4140" + - --proxy-uid + - "2102" + - --inbound-ports-to-ignore + - "4190" + image: gcr.io/runconduit/proxy-init:undefined + imagePullPolicy: IfNotPresent + name: conduit-init + resources: {} + securityContext: + capabilities: + add: + - NET_ADMIN + privileged: false + volumes: + - configMap: + items: + - key: grafana.ini + path: grafana.ini + - key: datasources.yaml + path: provisioning/datasources/datasources.yaml + - key: dashboards.yaml + path: provisioning/dashboards/dashboards.yaml + name: grafana-config + name: grafana-config + - configMap: + name: grafana-dashboards + name: grafana-dashboards + - configMap: + items: + - key: conduit-viz.json + path: home.json + name: grafana-dashboards + name: grafana-dashboard-home +status: {} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: grafana-config + namespace: Namespace + labels: + ControllerComponentLabel: grafana + annotations: + CreatedByAnnotation: CliVersion +data: + grafana.ini: |- + instance_name = conduit-grafana + + [auth] + disable_login_form = true + + [auth.anonymous] + enabled = true + org_role = Editor + + [auth.basic] + enabled = false + + datasources.yaml: |- + apiVersion: 1 + datasources: + - name: prometheus + type: prometheus + access: proxy + orgId: 1 + url: http://prometheus.Namespace.svc.cluster.local:9090 + isDefault: true + jsonData: + timeInterval: "5s" + version: 1 + editable: true + + dashboards.yaml: |- + apiVersion: 1 + providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: true + editable: true + options: + path: /var/lib/grafana/dashboards + homeDashboardId: conduit-viz + +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: grafana-dashboards + namespace: Namespace + labels: + ControllerComponentLabel: grafana + annotations: + CreatedByAnnotation: CliVersion +data: + conduit-viz.json: |- + VizDashboard + + conduit-health.json: |- + HealthDashboard --- diff --git a/cli/install/health.go b/cli/install/health.go new file mode 100644 index 0000000000000..2804495004ff9 --- /dev/null +++ b/cli/install/health.go @@ -0,0 +1,7 @@ +package install + +// Health defines the Conduit Health Grafana dashboard, installed via the `conduit install` command. +const Health = `{ + "title": "conduit-health" + } +` diff --git a/cli/install/template.go b/cli/install/template.go index 47eb8ff66ef30..539e4c9d521f1 100644 --- a/cli/install/template.go +++ b/cli/install/template.go @@ -368,4 +368,149 @@ data: - source_labels: [__meta_kubernetes_pod_container_name] action: replace target_label: job + +### Grafana ### +--- +kind: Service +apiVersion: v1 +metadata: + name: grafana + namespace: {{.Namespace}} + labels: + {{.ControllerComponentLabel}}: grafana + annotations: + {{.CreatedByAnnotation}}: {{.CliVersion}} +spec: + type: ClusterIP + selector: + {{.ControllerComponentLabel}}: grafana + ports: + - name: http + port: 3000 + targetPort: 3000 + +--- +kind: Deployment +apiVersion: extensions/v1beta1 +metadata: + name: grafana + namespace: {{.Namespace}} + labels: + {{.ControllerComponentLabel}}: grafana + annotations: + {{.CreatedByAnnotation}}: {{.CliVersion}} +spec: + replicas: 1 + template: + metadata: + labels: + {{.ControllerComponentLabel}}: grafana + annotations: + {{.CreatedByAnnotation}}: {{.CliVersion}} + spec: + volumes: + - name: grafana-config + configMap: + name: grafana-config + items: + - key: grafana.ini + path: grafana.ini + - key: datasources.yaml + path: provisioning/datasources/datasources.yaml + - key: dashboards.yaml + path: provisioning/dashboards/dashboards.yaml + - name: grafana-dashboards + configMap: + name: grafana-dashboards + - name: grafana-dashboard-home + configMap: + name: grafana-dashboards + items: + - key: conduit-viz.json + path: home.json + containers: + - name: grafana + ports: + - name: http + containerPort: 3000 + volumeMounts: + - name: grafana-config + mountPath: /etc/grafana + readOnly: true + - name: grafana-dashboards + mountPath: /var/lib/grafana/dashboards + readOnly: false + - name: grafana-dashboard-home + mountPath: /usr/share/grafana/public/dashboards + readOnly: true + image: {{.GrafanaImage}} + imagePullPolicy: {{.ImagePullPolicy}} + +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: grafana-config + namespace: {{.Namespace}} + labels: + {{.ControllerComponentLabel}}: grafana + annotations: + {{.CreatedByAnnotation}}: {{.CliVersion}} +data: + grafana.ini: |- + instance_name = conduit-grafana + + [auth] + disable_login_form = true + + [auth.anonymous] + enabled = true + org_role = Editor + + [auth.basic] + enabled = false + + datasources.yaml: |- + apiVersion: 1 + datasources: + - name: prometheus + type: prometheus + access: proxy + orgId: 1 + url: http://prometheus.{{.Namespace}}.svc.cluster.local:9090 + isDefault: true + jsonData: + timeInterval: "5s" + version: 1 + editable: true + + dashboards.yaml: |- + apiVersion: 1 + providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: true + editable: true + options: + path: /var/lib/grafana/dashboards + homeDashboardId: conduit-viz + +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: grafana-dashboards + namespace: {{.Namespace}} + labels: + {{.ControllerComponentLabel}}: grafana + annotations: + {{.CreatedByAnnotation}}: {{.CliVersion}} +data: + conduit-viz.json: |- + {{.VizDashboard}} + + conduit-health.json: |- + {{.HealthDashboard}} ` diff --git a/cli/install/viz.go b/cli/install/viz.go new file mode 100644 index 0000000000000..705358b49d66b --- /dev/null +++ b/cli/install/viz.go @@ -0,0 +1,67 @@ +package install + +// Viz defines the primary Conduit Grafana dashboard, installed via the `conduit install` command. +const Viz = `{ + "rows": [ + { + "collapse": false, + "height": "50px", + "panels": [ + { + "content": "
\n
\n \n
\n
\n Conduit is a next-generation ultralight service mesh for Kubernetes.\n
\n Need help? Visit conduit.io.\n
\n
", + "height": "1px", + "id": 14, + "links": [], + "mode": "html", + "span": 12, + "title": "", + "transparent": true, + "type": "text" + } + ], + "showTitle": false + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "legend": { + }, + "lines": true, + "targets": [ + { + "expr": "sum(irate(responses_total[20s])) by (target_deployment)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{target_deployment}}", + "refId": "A" + } + ], + "title": "Request Volume", + "type": "graph", + "xaxis": { + "show": true + }, + "yaxes": [ + { + "format": "rps", + "show": true + }, + { + "show": true + } + ] + } + ], + "showTitle": false + } + ], + "refresh": "5s", + "time": { + "from": "now-5m", + "to": "now" + }, + "title": "conduit-viz" + } +`