Telemetry to cover failure modes which are not covered by container logs and metrics for finding resource constraints. ## Motivation and Context When there is any issue with registry container we should be notified. ## How Has This Been Tested? - Local seup ## Breaking Changes - No ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] 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 <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [ ] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [ ] My code follows the repository's style guidelines - [ ] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have added or updated documentation as needed ## Additional context - No additional exporter is used, taken advantage of opentelemetry collector - It covers metrics related to resource constraints, currently only limited to default namespace. - Takes cares of kubernetes events as logs which are the source of figuring out any problem with service, covers all such scenarios where pod is not able to start yet and get missed because there are no container logs for such cases. Limited to default namespace. - Taken care of daemonset deployment i.e. deploying otel collector as agent by using correct filtering. - Cardinality contributing factors are only pod ids (but have to observe more), node ids will not increase cardinality as scale up will lead to limited nodes. - Shipping of metrics for resources happens every 60s and list of metrics that will be emitted [https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/kubeletstatsreceiver/metadata.yaml](url) - Container errors <img width="1440" height="816" alt="Screenshot 2025-10-10 at 1 21 14 AM" src="https://github.com/user-attachments/assets/ba90a217-2a49-4522-aa44-a98c02adf95b" /> - Resource metrics <img width="1440" height="816" alt="Screenshot 2025-10-10 at 1 23 51 AM" src="https://github.com/user-attachments/assets/3467be96-db3c-4930-afa2-3cbf5f0ced8b" />
684 lines
21 KiB
Go
684 lines
21 KiB
Go
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("128Mi"),
|
|
"cpu": pulumi.String("50m"),
|
|
},
|
|
"limits": pulumi.Map{
|
|
"memory": pulumi.String("256Mi"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, 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("64Mi"),
|
|
"cpu": pulumi.String("25m"),
|
|
},
|
|
"limits": pulumi.Map{
|
|
"memory": pulumi.String("128Mi"),
|
|
},
|
|
},
|
|
},
|
|
}, pulumi.Provider(cluster.Provider))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Deploy VictoriaLogs for log storage
|
|
err = deployVictoriaLogs(ctx, cluster, ns, environment)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Deploy OpenTelemetry Collector DaemonSet
|
|
err = deployOtelCollectorDaemonSet(ctx, cluster, ns, environment)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Deploy Grafana
|
|
return deployGrafana(ctx, cluster, ns, environment, ingressNginx)
|
|
}
|
|
|
|
// deployVictoriaLogs deploys VictoriaLogs for log storage
|
|
func deployVictoriaLogs(ctx *pulumi.Context, cluster *providers.ProviderInfo, ns *corev1.Namespace, environment string) error {
|
|
// Deploy VictoriaLogs using Helm chart
|
|
_, err := helm.NewChart(ctx, "victoria-logs", helm.ChartArgs{
|
|
Chart: pulumi.String("victoria-logs-single"),
|
|
Version: pulumi.String("0.11.8"),
|
|
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("15d"),
|
|
"resources": pulumi.Map{
|
|
"requests": pulumi.Map{
|
|
"memory": pulumi.String("256Mi"),
|
|
"cpu": pulumi.String("100m"),
|
|
},
|
|
"limits": pulumi.Map{
|
|
"memory": pulumi.String("2Gi"),
|
|
"cpu": pulumi.String("1000m"),
|
|
},
|
|
},
|
|
"persistence": pulumi.Map{
|
|
"enabled": pulumi.Bool(true),
|
|
"size": pulumi.String("20Gi"),
|
|
},
|
|
},
|
|
},
|
|
}, pulumi.Provider(cluster.Provider))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// deployOtelCollectorDaemonSet deploys OpenTelemetry Collector using Helm chart
|
|
func deployOtelCollectorDaemonSet(ctx *pulumi.Context, cluster *providers.ProviderInfo, ns *corev1.Namespace, environment string) error {
|
|
// Deploy OpenTelemetry Collector using Helm chart
|
|
_, err := helm.NewChart(ctx, "opentelemetry-collector", helm.ChartArgs{
|
|
Chart: pulumi.String("opentelemetry-collector"),
|
|
Version: pulumi.String("0.133.0"),
|
|
Namespace: ns.Metadata.Name().Elem(),
|
|
FetchArgs: helm.FetchArgs{
|
|
Repo: pulumi.String("https://open-telemetry.github.io/opentelemetry-helm-charts"),
|
|
},
|
|
Values: pulumi.Map{
|
|
"mode": pulumi.String("daemonset"),
|
|
"image": pulumi.Map{
|
|
"repository": pulumi.String("otel/opentelemetry-collector-contrib"),
|
|
"tag": pulumi.String("0.133.0"),
|
|
},
|
|
"hostNetwork": pulumi.Bool(true),
|
|
"dnsPolicy": pulumi.String("ClusterFirstWithHostNet"),
|
|
"clusterRole": pulumi.Map{
|
|
"create": pulumi.Bool(true),
|
|
"rules": pulumi.Array{
|
|
pulumi.Map{
|
|
"apiGroups": pulumi.StringArray{pulumi.String("")},
|
|
"resources": pulumi.StringArray{
|
|
pulumi.String("pods"),
|
|
pulumi.String("pods/log"),
|
|
pulumi.String("nodes"),
|
|
pulumi.String("nodes/stats"),
|
|
pulumi.String("nodes/proxy"),
|
|
pulumi.String("namespaces"),
|
|
pulumi.String("events"),
|
|
},
|
|
"verbs": pulumi.StringArray{
|
|
pulumi.String("get"),
|
|
pulumi.String("list"),
|
|
pulumi.String("watch"),
|
|
},
|
|
},
|
|
pulumi.Map{
|
|
"apiGroups": pulumi.StringArray{pulumi.String("apps")},
|
|
"resources": pulumi.StringArray{
|
|
pulumi.String("replicasets"),
|
|
pulumi.String("deployments"),
|
|
pulumi.String("daemonsets"),
|
|
},
|
|
"verbs": pulumi.StringArray{
|
|
pulumi.String("get"),
|
|
pulumi.String("list"),
|
|
pulumi.String("watch"),
|
|
},
|
|
},
|
|
pulumi.Map{
|
|
"nonResourceURLs": pulumi.StringArray{
|
|
pulumi.String("/stats/*"),
|
|
pulumi.String("/metrics"),
|
|
},
|
|
"verbs": pulumi.StringArray{
|
|
pulumi.String("get"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"config": pulumi.Map{
|
|
"receivers": pulumi.Map{
|
|
"filelog": pulumi.Map{
|
|
"include": pulumi.StringArray{pulumi.String("/var/log/pods/default_mcp-registry*/*/*.log")},
|
|
"exclude": pulumi.StringArray{pulumi.String("/var/log/pods/*/*-collector-*/*.log")},
|
|
"start_at": pulumi.String("end"),
|
|
"include_file_path": pulumi.Bool(true),
|
|
"include_file_name": pulumi.Bool(false),
|
|
"operators": pulumi.Array{
|
|
pulumi.Map{
|
|
"type": pulumi.String("regex_parser"),
|
|
"id": pulumi.String("extract_metadata_from_filepath"),
|
|
"regex": pulumi.String(`^.*\/[^_]+_[^_]+_(?P<uid>[a-f0-9\-]{36})\/[^\._]+\/(?P<restart_count>\d+)\.log`),
|
|
"parse_from": pulumi.String("attributes[\"log.file.path\"]"),
|
|
"cache": pulumi.Map{
|
|
"size": pulumi.Int(128),
|
|
},
|
|
},
|
|
pulumi.Map{
|
|
"type": pulumi.String("move"),
|
|
"from": pulumi.String("attributes.restart_count"),
|
|
"to": pulumi.String("resource[\"k8s.container.restart_count\"]"),
|
|
},
|
|
pulumi.Map{
|
|
"type": pulumi.String("move"),
|
|
"from": pulumi.String("attributes.uid"),
|
|
"to": pulumi.String("resource[\"k8s.pod.uid\"]"),
|
|
},
|
|
},
|
|
},
|
|
"kubeletstats": pulumi.Map{
|
|
"collection_interval": pulumi.String("60s"),
|
|
"auth_type": pulumi.String("serviceAccount"),
|
|
"endpoint": pulumi.String("https://${env:KUBERNETES_NODE_NAME}:10250"),
|
|
"insecure_skip_verify": pulumi.Bool(true),
|
|
},
|
|
"k8s_events": pulumi.Map{
|
|
"auth_type": pulumi.String("serviceAccount"),
|
|
"namespaces": pulumi.StringArray{
|
|
pulumi.String("default"),
|
|
},
|
|
},
|
|
},
|
|
"processors": pulumi.Map{
|
|
"batch": pulumi.Map{},
|
|
"filter/kubeletstats_filter": pulumi.Map{
|
|
"metrics": pulumi.Map{
|
|
"datapoint": pulumi.StringArray{
|
|
pulumi.String(`resource.attributes["k8s.namespace.name"] != "default"`),
|
|
},
|
|
},
|
|
},
|
|
"k8sattributes": pulumi.Map{
|
|
"auth_type": pulumi.String("serviceAccount"),
|
|
"passthrough": pulumi.Bool(false),
|
|
"filter": pulumi.Map{
|
|
"node_from_env_var": pulumi.String("KUBERNETES_NODE_NAME"),
|
|
},
|
|
"extract": pulumi.Map{
|
|
"metadata": pulumi.StringArray{
|
|
pulumi.String("k8s.pod.name"),
|
|
pulumi.String("k8s.pod.uid"),
|
|
pulumi.String("k8s.deployment.name"),
|
|
pulumi.String("k8s.namespace.name"),
|
|
pulumi.String("k8s.node.name"),
|
|
pulumi.String("container.image.name"),
|
|
pulumi.String("container.image.tag"),
|
|
},
|
|
},
|
|
"pod_association": pulumi.Array{
|
|
pulumi.Map{
|
|
"sources": pulumi.Array{
|
|
pulumi.Map{
|
|
"from": pulumi.String("resource_attribute"),
|
|
"name": pulumi.String("k8s.pod.ip"),
|
|
},
|
|
},
|
|
},
|
|
pulumi.Map{
|
|
"sources": pulumi.Array{
|
|
pulumi.Map{
|
|
"from": pulumi.String("resource_attribute"),
|
|
"name": pulumi.String("k8s.pod.uid"),
|
|
},
|
|
},
|
|
},
|
|
pulumi.Map{
|
|
"sources": pulumi.Array{
|
|
pulumi.Map{
|
|
"from": pulumi.String("connection"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"exporters": pulumi.Map{
|
|
"otlphttp/victorialogs": pulumi.Map{
|
|
"logs_endpoint": pulumi.String("http://victoria-logs-victoria-logs-single-server:9428/insert/opentelemetry/v1/logs"),
|
|
"headers": pulumi.Map{
|
|
"VL-Msg-Field": pulumi.String("body"),
|
|
"VL-Time-Field": pulumi.String("timestamp"),
|
|
"VL-Stream-Fields": pulumi.String("k8s.namespace.name,k8s.pod.name,k8s.container.name,log.iostream"),
|
|
},
|
|
"timeout": pulumi.String("10s"),
|
|
"retry_on_failure": pulumi.Map{
|
|
"enabled": pulumi.Bool(true),
|
|
"initial_interval": pulumi.String("5s"),
|
|
"max_interval": pulumi.String("30s"),
|
|
"max_elapsed_time": pulumi.String("300s"),
|
|
},
|
|
"sending_queue": pulumi.Map{
|
|
"enabled": pulumi.Bool(true),
|
|
"num_consumers": pulumi.Int(10),
|
|
"queue_size": pulumi.Int(50),
|
|
},
|
|
},
|
|
"otlphttp/victoriametrics": pulumi.Map{
|
|
"metrics_endpoint": pulumi.String("http://victoria-metrics-victoria-metrics-single-server:8428/opentelemetry/v1/metrics"),
|
|
"timeout": pulumi.String("10s"),
|
|
"retry_on_failure": pulumi.Map{
|
|
"enabled": pulumi.Bool(true),
|
|
"initial_interval": pulumi.String("5s"),
|
|
"max_interval": pulumi.String("30s"),
|
|
"max_elapsed_time": pulumi.String("300s"),
|
|
},
|
|
},
|
|
},
|
|
"service": pulumi.Map{
|
|
"pipelines": pulumi.Map{
|
|
"logs": pulumi.Map{
|
|
"receivers": pulumi.StringArray{pulumi.String("filelog"), pulumi.String("k8s_events")},
|
|
"processors": pulumi.StringArray{pulumi.String("k8sattributes"), pulumi.String("batch")},
|
|
"exporters": pulumi.StringArray{pulumi.String("otlphttp/victorialogs")},
|
|
},
|
|
"metrics": pulumi.Map{
|
|
"receivers": pulumi.StringArray{pulumi.String("kubeletstats")},
|
|
"processors": pulumi.StringArray{pulumi.String("k8sattributes"), pulumi.String("filter/kubeletstats_filter"), pulumi.String("batch")},
|
|
"exporters": pulumi.StringArray{pulumi.String("otlphttp/victoriametrics")},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"extraVolumes": pulumi.Array{
|
|
pulumi.Map{
|
|
"name": pulumi.String("varlogpods"),
|
|
"hostPath": pulumi.Map{
|
|
"path": pulumi.String("/var/log/pods"),
|
|
},
|
|
},
|
|
pulumi.Map{
|
|
"name": pulumi.String("varlibdockercontainers"),
|
|
"hostPath": pulumi.Map{
|
|
"path": pulumi.String("/var/lib/docker/containers"),
|
|
},
|
|
},
|
|
pulumi.Map{
|
|
"name": pulumi.String("proc"),
|
|
"hostPath": pulumi.Map{
|
|
"path": pulumi.String("/proc"),
|
|
},
|
|
},
|
|
pulumi.Map{
|
|
"name": pulumi.String("sys"),
|
|
"hostPath": pulumi.Map{
|
|
"path": pulumi.String("/sys"),
|
|
},
|
|
},
|
|
},
|
|
"extraVolumeMounts": pulumi.Array{
|
|
pulumi.Map{
|
|
"name": pulumi.String("varlogpods"),
|
|
"mountPath": pulumi.String("/var/log/pods"),
|
|
"readOnly": pulumi.Bool(true),
|
|
},
|
|
pulumi.Map{
|
|
"name": pulumi.String("varlibdockercontainers"),
|
|
"mountPath": pulumi.String("/var/lib/docker/containers"),
|
|
"readOnly": pulumi.Bool(true),
|
|
},
|
|
pulumi.Map{
|
|
"name": pulumi.String("proc"),
|
|
"mountPath": pulumi.String("/host/proc"),
|
|
"readOnly": pulumi.Bool(true),
|
|
},
|
|
pulumi.Map{
|
|
"name": pulumi.String("sys"),
|
|
"mountPath": pulumi.String("/host/sys"),
|
|
"readOnly": pulumi.Bool(true),
|
|
},
|
|
},
|
|
"extraEnvs": pulumi.Array{
|
|
pulumi.Map{
|
|
"name": pulumi.String("KUBERNETES_NODE_NAME"),
|
|
"valueFrom": pulumi.Map{
|
|
"fieldRef": pulumi.Map{
|
|
"fieldPath": pulumi.String("spec.nodeName"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"resources": pulumi.Map{
|
|
"requests": pulumi.Map{
|
|
"memory": pulumi.String("200Mi"),
|
|
"cpu": pulumi.String("100m"),
|
|
},
|
|
"limits": pulumi.Map{
|
|
"memory": pulumi.String("400Mi"),
|
|
"cpu": pulumi.String("200m"),
|
|
},
|
|
},
|
|
"tolerations": pulumi.Array{
|
|
pulumi.Map{
|
|
"key": pulumi.String("node-role.kubernetes.io/master"),
|
|
"operator": pulumi.String("Exists"),
|
|
"effect": pulumi.String("NoSchedule"),
|
|
},
|
|
pulumi.Map{
|
|
"key": pulumi.String("node-role.kubernetes.io/control-plane"),
|
|
"operator": pulumi.String("Exists"),
|
|
"effect": pulumi.String("NoSchedule"),
|
|
},
|
|
},
|
|
},
|
|
}, pulumi.Provider(cluster.Provider))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
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,
|
|
"enablePDB": false,
|
|
"storage": map[string]any{
|
|
"size": "10Gi",
|
|
},
|
|
},
|
|
},
|
|
}, pulumi.Provider(cluster.Provider))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create VictoriaMetrics and VictoriaLogs datasources
|
|
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,
|
|
},
|
|
{
|
|
"name": "VictoriaLogs",
|
|
"type": "victoriametrics-logs-datasource",
|
|
"url": "http://victoria-logs-victoria-logs-single-server:9428",
|
|
"access": "proxy",
|
|
"jsonData": map[string]interface{}{
|
|
"maxLines": 1000,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
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
|
|
grafanaHost := "grafana." + environment + ".registry.modelcontextprotocol.io"
|
|
_, 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{
|
|
"plugins": pulumi.Array{
|
|
pulumi.String("victoriametrics-logs-datasource"),
|
|
},
|
|
"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{
|
|
"server": pulumi.Map{
|
|
"root_url": pulumi.String("https://" + grafanaHost),
|
|
},
|
|
"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("128Mi"),
|
|
"cpu": pulumi.String("50m"),
|
|
},
|
|
"limits": pulumi.Map{
|
|
"memory": pulumi.String("256Mi"),
|
|
},
|
|
},
|
|
},
|
|
}, pulumi.Provider(cluster.Provider))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create ingress for external access
|
|
_, 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
|
|
}
|