registry/deploy/README.md
adam jones 667e2840b8
feat(infra): Add Pulumi-based Kubernetes deployment infrastructure (#237)
Original PR: #227

- Add Pulumi-based infrastructure as code for deploying MCP Registry to
Kubernetes
- Support for both local development (minikube) and Azure Kubernetes
Service (AKS)
- Complete deployment orchestration including:
- cluster setup: e.g. you point this at an Azure account, and it can set
up and manage the cluster for you. e.g. K8s version, number of nodes,
type of nodes, ...
  - cloud agnostic K8s services: cert-manager, nginx-ingress
- app services: MongoDB, and registry application (currently using nginx
as a placeholder, blocked on #225 (as is #190). but should be a 1 line
change)

## How is this different to #190

- Supports cluster setup and management. This enables:
- Non-hosting maintainers managing many devops workflows (e.g. scaling
up the cluster, or bumping K8s versions). Without this, we'd need to
bug/page the organisation hosting the registry when we need these things
changed.
- Makes it easy to spin up things like staging/temporary clusters, as
well as enables contributors to replicate the stack exactly on their own
Azure accounts.
- Sets up cloud-agnostic services. For example, rather than using the
Azure-managed ingresses and CA, we install nginx-ingress and
cert-manager. This enables:
- Running the entire infra stack can also run locally (e.g. in minikube,
k3s, orbstack, colima) - making it much easier for contributors to test
changes to infra stuff.
- Moving between cloud providers much more easily, e.g. we could shift
from Azure to GCP/AWS/other with minimal hassle.
- Everything stays written in Go, rather than Helm templates. This means
we get things like type-checking etc. for free (which from my experience
makes AI tools wayyy better at editing K8s stuff), and contributors
don't need to learn a new language if they're already using Go.

## Testing

I've got this running well:
- locally in minikube
- on cloud in Azure (my personal Azure account)

<details><summary>Claude written architecture and security
review</summary>
<p>

## Deployment Review & Assessment

### Current Architecture Strengths
**Pulumi IaC Approach**
- Well-structured infrastructure as code using Pulumi
- Multi-provider support (AKS, local) with clean abstraction
- Good separation of concerns in `pkg/` directory

**Security Fundamentals**
- Non-root container execution (`appuser` with UID 10001)
- Secrets properly managed via Kubernetes secrets
- TLS/SSL certificate management with cert-manager and Let's Encrypt

### Critical Issues & High-Priority Improvements

**1. Production Deployment Not Ready** 🚨
The registry deployment uses `nginx:alpine` placeholder image instead of
the actual MCP registry:
- `deploy/pkg/k8s/registry.go:67` - TODO comments indicate incomplete
setup
- Health probes are commented out
- Port mapping doesn't match actual application (80 vs 8080)

**Fix:** Build and publish actual registry container image to GHCR,
update deployment

**2. Database Security Considerations** 🔒
- MongoDB deployed without authentication
- No backup/disaster recovery strategy
- Database credentials hardcoded

*Note: MongoDB is not exposed externally (ClusterIP service), so this is
not a critical security risk but should be addressed for production.*

**3. Monitoring & Observability Gaps** 📊
- No Prometheus/Grafana monitoring stack
- No log aggregation (ELK/Loki)
- No application metrics/health dashboards
- No alerting configured

**4. High Availability & Reliability** ⚠️
- Single MongoDB instance (no replication)
- No persistent volume backup strategy
- Fixed 10Gi storage without growth planning
- Only 2 replicas for registry service
- No pod disruption budgets
- No horizontal pod autoscaling

### Recommended Improvements

**Immediate (High Priority)**
1. Complete Registry Deployment - Build proper container image pipeline,
enable health checks
2. Secure MongoDB - Add authentication credentials, implement backup
strategy

**Medium Priority**
3. Add Monitoring Stack - Prometheus, Grafana deployment
4. Security Hardening (Nice to Have) - RBAC policies, Network Policies,
Pod Security Standards
5. CI/CD Pipeline Enhancement - Container image building/publishing,
automated deployment

**Lower Priority**
6. High Availability - MongoDB replica set, HPA for registry pods
7. Operational Excellence - Kubernetes dashboard, cost optimization

### Configuration Issues
- Production config has test credentials: `deploy/Pulumi.prod.yaml:4-5`
- Missing environment-specific resource sizing
- Hardcoded domain names (`example.com`)

The deployment setup shows good architectural foundations but needs
significant work before production readiness. The most critical issue is
the placeholder nginx container - priority should be completing the
actual registry application deployment before addressing the other
improvements. Security measures like RBAC and Network Policies are nice
to have but not strictly necessary given that MongoDB is not exposed
externally.

🤖 Generated with [Claude Code](https://claude.ai/code)

</p>
</details> 


## Metadata

Working towards #91

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-08-11 19:43:49 +01:00

5.5 KiB

MCP Registry Kubernetes Deployment

This directory contains Pulumi infrastructure code to deploy the MCP Registry service to a Kubernetes cluster. It supports multiple Kubernetes providers: Azure Kubernetes Service (AKS) and local (using existing kubeconfig).

Quick Start

Local Development

Pre-requisites:

  1. Set Pulumi's backend to local: pulumi login --local
  2. Init the local stack: pulumi stack init local (fine to leave password blank)
  3. Set your config:
    # General environment
    pulumi config set mcp-registry:environment local
    
    # To use your local kubeconfig (default)
    pulumi config set mcp-registry:provider local
    # Alternative: To use AKS
    # pulumi config set mcp-registry:provider aks
    
    # GitHub OAuth
    pulumi config set mcp-registry:githubClientId <your-github-client-id>
    pulumi config set --secret mcp-registry:githubClientSecret <your-github-client-secret>
    
  4. Deploy: go build && PULUMI_CONFIG_PASSPHRASE="" pulumi up --yes
  5. Access the repository via the ingress load balancer. You can find its external IP with kubectl get svc nginx-ingress-ingress-nginx-controller -n ingress-nginx (with minikube, if it's 'pending' you might need minikube tunnel). Then run curl -H "Host: mcp-registry-local.example.com" -k https://<EXTERNAL-IP>/v0/ping to check that the service is up.

Production Deployment (AKS)

Note: This is how the production deployment will be set up once. But then the plan will be future updates are effectively a login + pulumi up from GitHub Actions.

Pre-requisites:

  1. Login to Azure: az login
  2. Create a resource group: az group create --name official-mcp-registry-prod --location eastus
  3. Add the storage resource provider: az provider register --namespace Microsoft.Storage
  4. Create a storage account: az storage account create --name officialmcpregistryprod --resource-group official-mcp-registry-prod --location eastus --sku Standard_LRS
  5. Add the 'Storage Blob Data Contributor' role assignment for yourself on the storage account: az role assignment create --assignee $(az ad signed-in-user show --query id -o tsv) --role "Storage Blob Data Contributor" --scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/official-mcp-registry-prod"
  6. Create a container: az storage container create --name pulumi-state --account-name officialmcpregistryprod
  7. Set Pulumi's backend to Azure: pulumi login 'azblob://pulumi-state?storage_account=officialmcpregistryprod'
  8. Init the production stack: pulumi stack init prod
  • TODO: This has a password that maybe needs to be shared with select contributors?
  1. Deploy: go build && PULUMI_CONFIG_PASSPHRASE="" pulumi up --yes
  2. Access the repository via the ingress load balancer. You can find its external IP with kubectl get svc nginx-ingress-ingress-nginx-controller -n ingress-nginx or view it in the Pulumi outputs. Then run curl -H "Host: mcp-registry-prod.example.com" -k https://<EXTERNAL-IP>/v0/ping to check that the service is up.

Structure

├── main.go              # Pulumi program entry point
├── Pulumi.yaml          # Project configuration
├── Pulumi.local.yaml    # Local stack configuration
├── Pulumi.prod.yaml     # Production stack configuration
├── Makefile             # Build and deployment targets
├── go.mod               # Go module dependencies
├── go.sum               # Go module checksums
└── pkg/                 # Infrastructure packages
    ├── k8s/             # Kubernetes deployment components
    │   ├── cert_manager.go    # SSL certificate management
    │   ├── deploy.go          # Deployment orchestration
    │   ├── ingress.go         # Ingress controller setup
    │   ├── mongodb.go         # MongoDB deployment
    │   └── registry.go        # MCP Registry deployment
    └── providers/       # Kubernetes cluster providers
        ├── types.go           # Provider interface definitions
        ├── aks/               # Azure Kubernetes Service provider
        └── local/             # Local kubeconfig provider

Architecture Overview

Deployment Flow

  1. Pulumi program starts in main.go
  2. Configuration is loaded from Pulumi config files
  3. Provider factory creates appropriate cluster provider (AKS or local)
  4. Cluster provider sets up Kubernetes access
  5. k8s.DeployAll() orchestrates complete deployment:
    • Certificate manager for SSL/TLS
    • Ingress controller for external access
    • MongoDB for data persistence
    • MCP Registry application

Configuration

Parameter Description Required
environment Deployment environment (dev/prod) Yes
provider Kubernetes provider (local/aks) No (default: local)
githubClientId GitHub OAuth Client ID Yes
githubClientSecret GitHub OAuth Client Secret Yes

Troubleshooting

Check Status

kubectl get pods
kubectl get deployment
kubectl get svc
kubectl get ingress
kubectl get svc -n ingress-nginx

View Logs

kubectl logs -l app=mcp-registry
kubectl logs -l app=mongodb