registry/deploy/pkg/providers/local/provider.go
pree-dew a9ff705d80
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
<!-- 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. -->
- [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
<img width="1422" height="666" alt="Screenshot 2025-08-31 at 4 35 28 AM"
src="https://github.com/user-attachments/assets/e54ce846-653c-4fa3-aa7a-fe277f0810e6"
/>

---------

Co-authored-by: Adam Jones <adamj@anthropic.com>
Co-authored-by: adam jones <adamj+git@anthropic.com>
2025-09-02 12:40:43 +00:00

273 lines
8.7 KiB
Go

package local
import (
"fmt"
"os/exec"
"strings"
"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes"
appsv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apps/v1"
batchv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/batch/v1"
corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/modelcontextprotocol/registry/deploy/infra/pkg/providers"
)
// Provider implements the ClusterProvider interface for local Kubernetes clusters
type Provider struct{}
// getClusterName attempts to get the current Kubernetes cluster name from kubectl config
func getClusterName() string {
cmd := exec.Command("kubectl", "config", "current-context")
output, err := cmd.Output()
if err != nil {
// Fallback to default if we can't get the context
return "local"
}
return strings.TrimSpace(string(output))
}
// CreateCluster configures access to a local Kubernetes cluster via kubeconfig
func (p *Provider) CreateCluster(ctx *pulumi.Context, environment string) (*providers.ProviderInfo, error) {
// Create Kubernetes provider for local cluster
k8sProvider, err := kubernetes.NewProvider(ctx, "k8s-provider", &kubernetes.ProviderArgs{})
if err != nil {
return nil, err
}
// Get the actual cluster name from kubectl config
clusterName := getClusterName()
return &providers.ProviderInfo{
Name: pulumi.String(clusterName).ToStringOutput(),
Provider: k8sProvider,
}, nil
}
// CreateBackupStorage creates MinIO for S3-compatible backup storage
func (p *Provider) CreateBackupStorage(ctx *pulumi.Context, cluster *providers.ProviderInfo, environment string) (*providers.BackupStorageInfo, error) {
// Create MinIO namespace
minioNamespace, err := corev1.NewNamespace(ctx, "minio", &corev1.NamespaceArgs{
Metadata: &metav1.ObjectMetaArgs{
Name: pulumi.String("minio"),
},
}, pulumi.Provider(cluster.Provider))
if err != nil {
return nil, fmt.Errorf("failed to create MinIO namespace: %w", err)
}
// Create MinIO credentials secret
minioSecret, err := corev1.NewSecret(ctx, "minio-credentials", &corev1.SecretArgs{
Metadata: &metav1.ObjectMetaArgs{
Name: pulumi.String("minio-credentials"),
Namespace: minioNamespace.Metadata.Name(),
},
Type: pulumi.String("Opaque"),
StringData: pulumi.StringMap{
"accesskey": pulumi.String("minioadmin"),
"secretkey": pulumi.String("minioadmin"),
},
}, pulumi.Provider(cluster.Provider))
if err != nil {
return nil, fmt.Errorf("failed to create MinIO credentials: %w", err)
}
// Create MinIO PVC for data storage
_, err = corev1.NewPersistentVolumeClaim(ctx, "minio-pvc", &corev1.PersistentVolumeClaimArgs{
Metadata: &metav1.ObjectMetaArgs{
Name: pulumi.String("minio-pvc"),
Namespace: minioNamespace.Metadata.Name(),
},
Spec: &corev1.PersistentVolumeClaimSpecArgs{
AccessModes: pulumi.StringArray{
pulumi.String("ReadWriteOnce"),
},
Resources: &corev1.VolumeResourceRequirementsArgs{
Requests: pulumi.StringMap{
"storage": pulumi.String("10Gi"),
},
},
},
}, pulumi.Provider(cluster.Provider))
if err != nil {
return nil, fmt.Errorf("failed to create MinIO PVC: %w", err)
}
// Deploy MinIO
minioDeployment, err := appsv1.NewDeployment(ctx, "minio", &appsv1.DeploymentArgs{
Metadata: &metav1.ObjectMetaArgs{
Name: pulumi.String("minio"),
Namespace: minioNamespace.Metadata.Name(),
},
Spec: &appsv1.DeploymentSpecArgs{
Replicas: pulumi.Int(1),
Selector: &metav1.LabelSelectorArgs{
MatchLabels: pulumi.StringMap{
"app": pulumi.String("minio"),
},
},
Template: &corev1.PodTemplateSpecArgs{
Metadata: &metav1.ObjectMetaArgs{
Labels: pulumi.StringMap{
"app": pulumi.String("minio"),
},
},
Spec: &corev1.PodSpecArgs{
Containers: corev1.ContainerArray{
&corev1.ContainerArgs{
Name: pulumi.String("minio"),
Image: pulumi.String("minio/minio:RELEASE.2025-07-23T15-54-02Z"),
Args: pulumi.StringArray{
pulumi.String("server"),
pulumi.String("/data"),
pulumi.String("--console-address"),
pulumi.String(":9001"),
},
Env: corev1.EnvVarArray{
&corev1.EnvVarArgs{
Name: pulumi.String("MINIO_ROOT_USER"),
ValueFrom: &corev1.EnvVarSourceArgs{
SecretKeyRef: &corev1.SecretKeySelectorArgs{
Name: minioSecret.Metadata.Name(),
Key: pulumi.String("accesskey"),
},
},
},
&corev1.EnvVarArgs{
Name: pulumi.String("MINIO_ROOT_PASSWORD"),
ValueFrom: &corev1.EnvVarSourceArgs{
SecretKeyRef: &corev1.SecretKeySelectorArgs{
Name: minioSecret.Metadata.Name(),
Key: pulumi.String("secretkey"),
},
},
},
},
Ports: corev1.ContainerPortArray{
&corev1.ContainerPortArgs{
ContainerPort: pulumi.Int(9000),
Name: pulumi.String("api"),
},
&corev1.ContainerPortArgs{
ContainerPort: pulumi.Int(9001),
Name: pulumi.String("console"),
},
},
VolumeMounts: corev1.VolumeMountArray{
&corev1.VolumeMountArgs{
Name: pulumi.String("data"),
MountPath: pulumi.String("/data"),
},
},
},
},
Volumes: corev1.VolumeArray{
&corev1.VolumeArgs{
Name: pulumi.String("data"),
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSourceArgs{
ClaimName: pulumi.String("minio-pvc"),
},
},
},
},
},
},
}, pulumi.Provider(cluster.Provider))
if err != nil {
return nil, fmt.Errorf("failed to create MinIO deployment: %w", err)
}
// Create MinIO service
minioService, err := corev1.NewService(ctx, "minio-service", &corev1.ServiceArgs{
Metadata: &metav1.ObjectMetaArgs{
Name: pulumi.String("minio"),
Namespace: minioNamespace.Metadata.Name(),
},
Spec: &corev1.ServiceSpecArgs{
Selector: pulumi.StringMap{
"app": pulumi.String("minio"),
},
Ports: corev1.ServicePortArray{
&corev1.ServicePortArgs{
Name: pulumi.String("api"),
Port: pulumi.Int(9000),
TargetPort: pulumi.Int(9000),
},
&corev1.ServicePortArgs{
Name: pulumi.String("console"),
Port: pulumi.Int(9001),
TargetPort: pulumi.Int(9001),
},
},
},
}, pulumi.Provider(cluster.Provider))
if err != nil {
return nil, fmt.Errorf("failed to create MinIO service: %w", err)
}
// Create a Job to initialize the MinIO bucket
_, err = batchv1.NewJob(ctx, "minio-bucket-init", &batchv1.JobArgs{
Metadata: &metav1.ObjectMetaArgs{
Name: pulumi.String("minio-bucket-init"),
Namespace: minioNamespace.Metadata.Name(),
},
Spec: &batchv1.JobSpecArgs{
Template: &corev1.PodTemplateSpecArgs{
Spec: &corev1.PodSpecArgs{
RestartPolicy: pulumi.String("OnFailure"),
Containers: corev1.ContainerArray{
&corev1.ContainerArgs{
Name: pulumi.String("mc"),
Image: pulumi.String("minio/mc:latest@sha256:fb8f773eac8ef9d6da0486d5dec2f42f219358bcb8de579d1623d518c9ebd4cc"),
Command: pulumi.StringArray{
pulumi.String("/bin/sh"),
pulumi.String("-c"),
},
Args: pulumi.StringArray{
pulumi.String(`
until mc alias set myminio http://minio:9000 minioadmin minioadmin; do
echo 'Waiting for MinIO to be ready...'
sleep 5
done
mc mb -p myminio/k8up-backups
echo 'Bucket created successfully'
`),
},
},
},
},
},
},
}, pulumi.Provider(cluster.Provider), pulumi.DependsOn([]pulumi.Resource{minioService}))
if err != nil {
return nil, fmt.Errorf("failed to create bucket initialization job: %w", err)
}
// Create backup credentials secret for S3 access
backupSecret, err := corev1.NewSecret(ctx, "k8up-backup-credentials", &corev1.SecretArgs{
Metadata: &metav1.ObjectMetaArgs{
Name: pulumi.String("k8up-backup-credentials"),
Namespace: pulumi.String("default"),
Labels: pulumi.StringMap{
"k8up.io/backup": pulumi.String("true"),
"environment": pulumi.String(environment),
},
},
Type: pulumi.String("Opaque"),
StringData: pulumi.StringMap{
"AWS_ACCESS_KEY_ID": pulumi.String("minioadmin"),
"AWS_SECRET_ACCESS_KEY": pulumi.String("minioadmin"),
},
}, pulumi.Provider(cluster.Provider), pulumi.DependsOn([]pulumi.Resource{minioDeployment}))
if err != nil {
return nil, fmt.Errorf("failed to create backup credentials secret: %w", err)
}
return &providers.BackupStorageInfo{
Endpoint: "http://minio.minio:9000",
BucketName: "k8up-backups",
Credentials: backupSecret,
}, nil
}