From a9ff705d80d37ce4fc4c1831515c4e592ad629d3 Mon Sep 17 00:00:00 2001 From: pree-dew Date: Tue, 2 Sep 2025 18:10:43 +0530 Subject: [PATCH] Add monitoring infrastructure: victoriametrics and grafana (#328) This includes infra setup for metrics collection and visualisation ## Motivation and Context It is important to have basic observability setup, so that common issues can be identified quickly ## How Has This Been Tested? - Tested on local - Tested on one of the production setup ## Breaking Changes No ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [X] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update ## Checklist - [X] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [X] My code follows the repository's style guidelines - [X] New and existing tests pass locally - [X] I have added appropriate error handling - [ ] I have added or updated documentation as needed ## Additional context - This includes set up of following components: - Victoriametrics single node cluster for storing metrics data with persistent storage configuration for local and production env. - Vmagent with target discovery for mcp-registry pods with scrape interval of 30s. - Grafana for visualising metrics and setting alerts. - Datasource of victoriametrics is pre-configured as config map - Persistent volume for basic configuration - Sqlite for local and Postgres for production setup for storing dashboard, alerts etc details ## Metrics in action Screenshot 2025-08-31 at 4 35 28 AM --------- Co-authored-by: Adam Jones Co-authored-by: adam jones --- deploy/Pulumi.aksProd.yaml | 6 - deploy/Pulumi.gcpProd.yaml | 2 + deploy/Pulumi.gcpStaging.yaml | 2 + deploy/Pulumi.local.yaml | 2 + deploy/go.mod | 1 + deploy/pkg/k8s/deploy.go | 6 + deploy/pkg/k8s/monitoring.go | 329 +++++++++++++++++++++++++ deploy/pkg/k8s/postgres.go | 4 +- deploy/pkg/providers/local/provider.go | 2 +- 9 files changed, 345 insertions(+), 9 deletions(-) delete mode 100644 deploy/Pulumi.aksProd.yaml create mode 100644 deploy/pkg/k8s/monitoring.go diff --git a/deploy/Pulumi.aksProd.yaml b/deploy/Pulumi.aksProd.yaml deleted file mode 100644 index 4fdf247..0000000 --- a/deploy/Pulumi.aksProd.yaml +++ /dev/null @@ -1,6 +0,0 @@ -config: - mcp-registry:environment: prod - mcp-registry:provider: aks - mcp-registry:githubClientId: test-client-id - mcp-registry:githubClientSecret: some-secret-here -encryptionsalt: v1:oDjcEdLMFwM=:v1:FdgMU4r7kVWUbaUt:1aTSUxrNy+JNW51KRXzoMACuAXruUg== diff --git a/deploy/Pulumi.gcpProd.yaml b/deploy/Pulumi.gcpProd.yaml index af198a5..f3a0c88 100644 --- a/deploy/Pulumi.gcpProd.yaml +++ b/deploy/Pulumi.gcpProd.yaml @@ -9,4 +9,6 @@ config: secure: v1:hyZWlpeMTFDnMcz2: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 mcp-registry:jwtPrivateKey: secure: v1:0QwJl5e504ECQfjb:j8b4v1KxxqS8g9E8JTL0Wq5EEDbk/xPUglyg4/hyuL8go+pH2EE6skjf+7D5aAsld1SDRGlhXQBOZIVnjLBffsVN7cBhVlKy2qwTCC6QH5Q= + mcp-registry:googleOauthClientSecret: + secure: v1:6AUQwlFpA6TjYPTa:MUqOWbXufz2tr2s+OvapEsAWFl3VbDE0VxDRTcJ1qbAQM/phmDj/BVCutozvlolmurHD encryptionsalt: v1:0funtAX4m9k=:v1:yMCnBXyBO+q4+/yy:AqWJTzwwIWXmUK0JGCzqbeg0RUr8Jw== diff --git a/deploy/Pulumi.gcpStaging.yaml b/deploy/Pulumi.gcpStaging.yaml index 52c3492..a71472c 100644 --- a/deploy/Pulumi.gcpStaging.yaml +++ b/deploy/Pulumi.gcpStaging.yaml @@ -9,4 +9,6 @@ config: secure: v1:RaHpGsBp37XO/EhJ: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 mcp-registry:jwtPrivateKey: secure: v1:0NSeI0qWrdcHeVfh:P56IEP/700/e839TSbF7Ns2j2orZnD5cRNXohjxCPOKyIDYn+0bmiTuk8pyFnFk8WibS/w7M2FFgv3/BL0Djo0XMVe9tq+7HjLN4tPEJnCU= + mcp-registry:googleOauthClientSecret: + secure: v1:MfA48Ri4VssQwUi7:TUF9sBGIgTnBUhePfEF7L+h1VVn5e6j2uGLbOwbGbjyBO9Z72SKC+z/21xHmJtqUSr2t encryptionsalt: v1:EKBwmTmss1c=:v1:JLd4a7cM0X8Jroh+:z9/q6RSCEFTDzMV6X6h5Tpbw0tnpkA== diff --git a/deploy/Pulumi.local.yaml b/deploy/Pulumi.local.yaml index 908a863..a1a92dc 100644 --- a/deploy/Pulumi.local.yaml +++ b/deploy/Pulumi.local.yaml @@ -4,4 +4,6 @@ config: mcp-registry:githubClientId: Iv23licy3GSiM9Km5jtd mcp-registry:githubClientSecret: 0e8db54879b02c29adef51795586f3c510a9341d mcp-registry:jwtPrivateKey: bb2c6b424005acd5df47a9e2c87f446def86dd740c888ea3efb825b23f7ef47c + mcp-registry:googleOauthClientSecret: + secure: v1:DUSCqfRvRSVmSihj:JIYznEU+T1vbmVF8MkdSUtqUM50498sxBWsfwNaS1r5jkF/+gtltWfJoKWb1OLgcX8lb encryptionsalt: v1:ijIHaqhbXVA=:v1:7voX1Kv+Bunz33iN:fyVHMOhlGIymzJ+ILgUBy3ExTwUUnA== diff --git a/deploy/go.mod b/deploy/go.mod index 1e24cb4..08bb2e6 100644 --- a/deploy/go.mod +++ b/deploy/go.mod @@ -8,6 +8,7 @@ require ( github.com/pulumi/pulumi-gcp/sdk/v8 v8.39.0 github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.18.2 github.com/pulumi/pulumi/sdk/v3 v3.175.0 + gopkg.in/yaml.v2 v2.4.0 ) require ( diff --git a/deploy/pkg/k8s/deploy.go b/deploy/pkg/k8s/deploy.go index f8b1142..2511835 100644 --- a/deploy/pkg/k8s/deploy.go +++ b/deploy/pkg/k8s/deploy.go @@ -39,5 +39,11 @@ func DeployAll(ctx *pulumi.Context, cluster *providers.ProviderInfo, backupStora return nil, err } + // Deploy monitoring stack + err = DeployMonitoringStack(ctx, cluster, environment, ingressNginx) + if err != nil { + return nil, err + } + return service, nil } diff --git a/deploy/pkg/k8s/monitoring.go b/deploy/pkg/k8s/monitoring.go new file mode 100644 index 0000000..37348e9 --- /dev/null +++ b/deploy/pkg/k8s/monitoring.go @@ -0,0 +1,329 @@ +package k8s + +import ( + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apiextensions" + corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1" + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3" + metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1" + networkingv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/networking/v1" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config" + "gopkg.in/yaml.v2" + + "github.com/modelcontextprotocol/registry/deploy/infra/pkg/providers" +) + +func DeployMonitoringStack(ctx *pulumi.Context, cluster *providers.ProviderInfo, environment string, ingressNginx *helm.Chart) error { + // Create namespace + ns, err := corev1.NewNamespace(ctx, "monitoring", &corev1.NamespaceArgs{ + Metadata: &metav1.ObjectMetaArgs{ + Name: pulumi.String("monitoring"), + }, + }, pulumi.Provider(cluster.Provider)) + if err != nil { + return err + } + + // Deploy VictoriaMetrics + _, err = helm.NewChart(ctx, "victoria-metrics", helm.ChartArgs{ + Chart: pulumi.String("victoria-metrics-single"), + Version: pulumi.String("0.24.4"), + Namespace: ns.Metadata.Name().Elem(), + FetchArgs: helm.FetchArgs{ + Repo: pulumi.String("https://victoriametrics.github.io/helm-charts/"), + }, + Values: pulumi.Map{ + "server": pulumi.Map{ + "retentionPeriod": pulumi.String("14d"), + "resources": pulumi.Map{ + "requests": pulumi.Map{ + "memory": pulumi.String("2Gi"), + "cpu": pulumi.String("500m"), + }, + "limits": pulumi.Map{ + "memory": pulumi.String("4Gi"), + "cpu": pulumi.String("1"), + }, + }, + }, + }, + }, pulumi.Provider(cluster.Provider)) + if err != nil { + return err + } + + // Deploy VMAgent + _, err = helm.NewChart(ctx, "victoria-metrics-agent", helm.ChartArgs{ + Chart: pulumi.String("victoria-metrics-agent"), + Version: pulumi.String("0.25.3"), + Namespace: ns.Metadata.Name().Elem(), + FetchArgs: helm.FetchArgs{ + Repo: pulumi.String("https://victoriametrics.github.io/helm-charts/"), + }, + Values: pulumi.Map{ + "remoteWrite": pulumi.Array{ + pulumi.Map{ + "url": pulumi.String("http://victoria-metrics-victoria-metrics-single-server:8428/api/v1/write"), + }, + }, + "config": pulumi.Map{ + "global": pulumi.Map{ + "scrape_interval": pulumi.String("60s"), + }, + "scrape_configs": pulumi.Array{ + pulumi.Map{ + "job_name": pulumi.String("mcp-registry"), + "kubernetes_sd_configs": pulumi.Array{ + pulumi.Map{ + "role": pulumi.String("pod"), + "namespaces": pulumi.Map{ + "names": pulumi.Array{pulumi.String("default")}, + }, + }, + }, + "relabel_configs": pulumi.Array{ + pulumi.Map{ + "source_labels": pulumi.Array{pulumi.String("__meta_kubernetes_pod_label_app")}, + "regex": pulumi.String("mcp-registry.*"), + "action": pulumi.String("keep"), + }, + }, + }, + }, + }, + "resources": pulumi.Map{ + "requests": pulumi.Map{ + "memory": pulumi.String("256Mi"), + "cpu": pulumi.String("100m"), + }, + "limits": pulumi.Map{ + "memory": pulumi.String("512Mi"), + "cpu": pulumi.String("200m"), + }, + }, + }, + }, pulumi.Provider(cluster.Provider)) + if err != nil { + return err + } + + // Deploy Grafana + return deployGrafana(ctx, cluster, ns, environment, ingressNginx) +} + +func deployGrafana(ctx *pulumi.Context, cluster *providers.ProviderInfo, ns *corev1.Namespace, environment string, ingressNginx *helm.Chart) error { + conf := config.New(ctx, "mcp-registry") + grafanaSecret, err := corev1.NewSecret(ctx, "grafana-secrets", &corev1.SecretArgs{ + Metadata: &metav1.ObjectMetaArgs{ + Name: pulumi.String("grafana-secrets"), + Namespace: ns.Metadata.Name(), + }, + StringData: pulumi.StringMap{ + "GF_AUTH_GOOGLE_CLIENT_SECRET": conf.RequireSecret("googleOauthClientSecret"), + }, + Type: pulumi.String("Opaque"), + }, pulumi.Provider(cluster.Provider)) + if err != nil { + return err + } + + grafanaPgCluster, err := apiextensions.NewCustomResource(ctx, "grafana-pg", &apiextensions.CustomResourceArgs{ + ApiVersion: pulumi.String("postgresql.cnpg.io/v1"), + Kind: pulumi.String("Cluster"), + Metadata: &metav1.ObjectMetaArgs{ + Name: pulumi.String("grafana-pg"), + Namespace: ns.Metadata.Name(), + Labels: pulumi.StringMap{ + "app": pulumi.String("grafana-pg"), + "environment": pulumi.String(environment), + }, + }, + OtherFields: map[string]any{ + "spec": map[string]any{ + "instances": 1, + "storage": map[string]any{ + "size": "10Gi", + }, + }, + }, + }, pulumi.Provider(cluster.Provider)) + if err != nil { + return err + } + + // Create VictoriaMetrics datasource + datasourcesConfig := map[string]interface{}{ + "apiVersion": 1, + "datasources": []map[string]interface{}{ + { + "name": "VictoriaMetrics", + "type": "prometheus", + "url": "http://victoria-metrics-victoria-metrics-single-server:8428", + "access": "proxy", + "isDefault": true, + }, + }, + } + + datasourcesConfigYAML, _ := yaml.Marshal(datasourcesConfig) + grafanaDataSourcesConfigMap, err := corev1.NewConfigMap(ctx, "grafana-datasources", &corev1.ConfigMapArgs{ + Metadata: &metav1.ObjectMetaArgs{ + Name: pulumi.String("grafana-datasources"), + Namespace: ns.Metadata.Name(), + }, + Data: pulumi.StringMap{ + "datasources.yaml": pulumi.String(string(datasourcesConfigYAML)), + }, + }, pulumi.Provider(cluster.Provider)) + if err != nil { + return err + } + + // Deploy Grafana + _, err = helm.NewChart(ctx, "grafana", helm.ChartArgs{ + Chart: pulumi.String("grafana"), + Version: pulumi.String("9.4.4"), + FetchArgs: &helm.FetchArgs{ + Repo: pulumi.String("https://grafana.github.io/helm-charts"), + }, + Namespace: ns.Metadata.Name().Elem(), + Values: pulumi.Map{ + "extraConfigmapMounts": pulumi.Array{ + pulumi.Map{ + "name": pulumi.String("grafana-datasources"), + "mountPath": pulumi.String("/etc/grafana/provisioning/datasources"), + "configMap": grafanaDataSourcesConfigMap.Metadata.Name(), + "readOnly": pulumi.Bool(true), + }, + }, + "grafana.ini": pulumi.Map{ + "auth": pulumi.Map{ + "disable_login_form": pulumi.Bool(true), + }, + "auth.basic": pulumi.Map{ + "enabled": pulumi.Bool(false), + }, + "security": pulumi.Map{ + "disable_initial_admin_creation": pulumi.Bool(true), + }, + "users": pulumi.Map{ + "auto_assign_org_role": pulumi.String("Admin"), + }, + "auth.google": pulumi.Map{ + "enabled": pulumi.Bool(true), + "client_id": pulumi.String("606636202366-tpjm7d5vpp4lp9helg5ld2vrcafnrgh7.apps.googleusercontent.com"), + "hosted_domain": pulumi.String("modelcontextprotocol.io"), + "allowed_domains": pulumi.String("modelcontextprotocol.io"), + "skip_org_role_sync": pulumi.Bool(true), + }, + "database": pulumi.Map{ + "type": pulumi.String("postgres"), + "host": pulumi.String("grafana-pg-rw:5432"), + }, + }, + "envValueFrom": pulumi.Map{ + "GF_AUTH_GOOGLE_CLIENT_SECRET": pulumi.Map{ + "secretKeyRef": pulumi.Map{ + "name": grafanaSecret.Metadata.Name(), + "key": pulumi.String("GF_AUTH_GOOGLE_CLIENT_SECRET"), + }, + }, + "GF_DATABASE_USER": pulumi.Map{ + "secretKeyRef": pulumi.Map{ + "name": grafanaPgCluster.Metadata.Name().ApplyT(func(name *string) string { + if name == nil { + return "grafana-pg-app" + } + return *name + "-app" + }).(pulumi.StringOutput), + "key": pulumi.String("username"), + }, + }, + "GF_DATABASE_PASSWORD": pulumi.Map{ + "secretKeyRef": pulumi.Map{ + "name": grafanaPgCluster.Metadata.Name().ApplyT(func(name *string) string { + if name == nil { + return "grafana-pg-app" + } + return *name + "-app" + }).(pulumi.StringOutput), + "key": pulumi.String("password"), + }, + }, + "GF_DATABASE_NAME": pulumi.Map{ + "secretKeyRef": pulumi.Map{ + "name": grafanaPgCluster.Metadata.Name().ApplyT(func(name *string) string { + if name == nil { + return "grafana-pg-app" + } + return *name + "-app" + }).(pulumi.StringOutput), + "key": pulumi.String("dbname"), + }, + }, + }, + "resources": pulumi.Map{ + "requests": pulumi.Map{ + "memory": pulumi.String("512Mi"), + "cpu": pulumi.String("200m"), + }, + "limits": pulumi.Map{ + "memory": pulumi.String("1Gi"), + "cpu": pulumi.String("500m"), + }, + }, + }, + }, pulumi.Provider(cluster.Provider)) + if err != nil { + return err + } + + // Create ingress for external access + grafanaHost := "grafana.registry." + environment + ".modelcontextprotocol.io" + + _, err = networkingv1.NewIngress(ctx, "grafana-ingress", &networkingv1.IngressArgs{ + Metadata: &metav1.ObjectMetaArgs{ + Name: pulumi.String("grafana-ingress"), + Namespace: ns.Metadata.Name(), + Annotations: pulumi.StringMap{ + "cert-manager.io/cluster-issuer": pulumi.String("letsencrypt-prod"), + "kubernetes.io/ingress.class": pulumi.String("nginx"), + }, + }, + Spec: &networkingv1.IngressSpecArgs{ + Tls: networkingv1.IngressTLSArray{ + &networkingv1.IngressTLSArgs{ + Hosts: pulumi.StringArray{pulumi.String(grafanaHost)}, + SecretName: pulumi.String("grafana-tls"), + }, + }, + Rules: networkingv1.IngressRuleArray{ + &networkingv1.IngressRuleArgs{ + Host: pulumi.String(grafanaHost), + Http: &networkingv1.HTTPIngressRuleValueArgs{ + Paths: networkingv1.HTTPIngressPathArray{ + &networkingv1.HTTPIngressPathArgs{ + Path: pulumi.String("/"), + PathType: pulumi.String("Prefix"), + Backend: &networkingv1.IngressBackendArgs{ + Service: &networkingv1.IngressServiceBackendArgs{ + Name: pulumi.String("grafana"), + Port: &networkingv1.ServiceBackendPortArgs{ + Number: pulumi.Int(80), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, pulumi.Provider(cluster.Provider), pulumi.DependsOnInputs(ingressNginx.Ready)) + if err != nil { + return err + } + + ctx.Export("grafanaUrl", pulumi.Sprintf("https://%s", grafanaHost)) + return nil +} diff --git a/deploy/pkg/k8s/postgres.go b/deploy/pkg/k8s/postgres.go index 664c32f..ec8e48e 100644 --- a/deploy/pkg/k8s/postgres.go +++ b/deploy/pkg/k8s/postgres.go @@ -60,10 +60,10 @@ func DeployPostgresDatabases(ctx *pulumi.Context, cluster *providers.ProviderInf }, }, }, - }, pulumi.Provider(cluster.Provider), pulumi.DependsOnInputs(cloudNativePG.Ready)) + }, pulumi.Provider(cluster.Provider), pulumi.DependsOnInputs(cloudNativePG.Ready), pulumi.RetainOnDelete(true)) if err != nil { return nil, err } return pgCluster, nil -} \ No newline at end of file +} diff --git a/deploy/pkg/providers/local/provider.go b/deploy/pkg/providers/local/provider.go index 1244a93..c06434b 100644 --- a/deploy/pkg/providers/local/provider.go +++ b/deploy/pkg/providers/local/provider.go @@ -220,7 +220,7 @@ func (p *Provider) CreateBackupStorage(ctx *pulumi.Context, cluster *providers.P Containers: corev1.ContainerArray{ &corev1.ContainerArgs{ Name: pulumi.String("mc"), - Image: pulumi.String("minio/mc:RELEASE.2025-07-19T03-52-40Z"), + Image: pulumi.String("minio/mc:latest@sha256:fb8f773eac8ef9d6da0486d5dec2f42f219358bcb8de579d1623d518c9ebd4cc"), Command: pulumi.StringArray{ pulumi.String("/bin/sh"), pulumi.String("-c"),