Skip to content

Config Platform - Deployment Blueprint

Overview

This document provides concrete deployment patterns and configurations for Config Platform services. Use these patterns to generate deployment manifests, infrastructure code, and CI/CD pipelines.

Container Deployment

Dockerfile Pattern

FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["ConfigRegistry/ConfigRegistry.csproj", "ConfigRegistry/"]
RUN dotnet restore "ConfigRegistry/ConfigRegistry.csproj"
COPY . .
WORKDIR "/src/ConfigRegistry"
RUN dotnet build "ConfigRegistry.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "ConfigRegistry.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ConfigRegistry.dll"]

Container Image Configuration

# Container registry and image
image: connectsoft.azurecr.io/config-registry:1.0.0
ports:
  - containerPort: 80
    protocol: TCP
  - containerPort: 443
    protocol: TCP
env:
  - name: ASPNETCORE_ENVIRONMENT
    value: "Production"
  - name: ASPNETCORE_URLS
    value: "http://+:80"
resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: 2000m
    memory: 2Gi

Kubernetes Deployment

Config Registry Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: config-registry
  namespace: config-platform
  labels:
    app: config-registry
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: config-registry
  template:
    metadata:
      labels:
        app: config-registry
        version: v1
    spec:
      containers:
      - name: config-registry
        image: connectsoft.azurecr.io/config-registry:1.0.0
        ports:
        - containerPort: 80
          name: http
        env:
        - name: ConnectionStrings__DefaultConnection
          valueFrom:
            secretKeyRef:
              name: config-registry-secrets
              key: database-connection
        - name: Redis__ConnectionString
          valueFrom:
            secretKeyRef:
              name: config-registry-secrets
              key: redis-connection
        - name: EventBus__ConnectionString
          valueFrom:
            secretKeyRef:
              name: config-registry-secrets
              key: eventbus-connection
        livenessProbe:
          httpGet:
            path: /health/live
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 5
        startupProbe:
          httpGet:
            path: /health/startup
            port: 80
          initialDelaySeconds: 0
          periodSeconds: 10
          failureThreshold: 30
        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: 2000m
            memory: 2Gi
---
apiVersion: v1
kind: Service
metadata:
  name: config-registry
  namespace: config-platform
spec:
  selector:
    app: config-registry
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
  type: ClusterIP

Horizontal Pod Autoscaler

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: config-registry-hpa
  namespace: config-platform
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: config-registry
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15
      - type: Pods
        value: 2
        periodSeconds: 60
      selectPolicy: Max

ConfigMap for Application Settings

apiVersion: v1
kind: ConfigMap
metadata:
  name: config-registry-config
  namespace: config-platform
data:
  appsettings.json: |
    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "ConfigPlatform": {
        "CacheTTL": "01:00:00",
        "MaxCacheSize": "1000"
      }
    }

Secrets Management

apiVersion: v1
kind: Secret
metadata:
  name: config-registry-secrets
  namespace: config-platform
type: Opaque
stringData:
  database-connection: "Server=cockroachdb;Database=config;..."
  redis-connection: "config-redis:6379"
  eventbus-connection: "Endpoint=sb://..."

Azure App Service Deployment

App Service Configuration

{
  "name": "config-registry",
  "type": "Microsoft.Web/sites",
  "location": "East US",
  "properties": {
    "serverFarmId": "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/serverfarms/config-platform-plan",
    "siteConfig": {
      "linuxFxVersion": "DOCKER|connectsoft.azurecr.io/config-registry:1.0.0",
      "alwaysOn": true,
      "http20Enabled": true,
      "minTlsVersion": "1.2",
      "ftpsState": "Disabled",
      "appSettings": [
        {
          "name": "ASPNETCORE_ENVIRONMENT",
          "value": "Production"
        },
        {
          "name": "ConnectionStrings__DefaultConnection",
          "value": "@Microsoft.KeyVault(SecretUri=https://vault.vault.azure.net/secrets/db-connection/)"
        }
      ]
    },
    "httpsOnly": true
  }
}

App Service Plan

{
  "name": "config-platform-plan",
  "type": "Microsoft.Web/serverfarms",
  "location": "East US",
  "sku": {
    "name": "P1v2",
    "tier": "PremiumV2",
    "capacity": 3
  },
  "properties": {
    "reserved": true
  }
}

Database Migration Patterns

NHibernate Schema Update

public class DatabaseMigrationService
{
    private readonly ISessionFactory _sessionFactory;

    public async Task MigrateAsync()
    {
        var configuration = new Configuration();
        configuration.Configure();
        configuration.AddAssembly(typeof(ConfigItem).Assembly);

        var schemaUpdate = new SchemaUpdate(configuration);
        await Task.Run(() => schemaUpdate.Execute(false, true));
    }
}

Flyway Migration Script

-- V1__Initial_schema.sql
CREATE TABLE config_items (
    id UUID PRIMARY KEY,
    key VARCHAR(500) NOT NULL,
    value TEXT NOT NULL,
    content_type VARCHAR(100),
    is_secret BOOLEAN DEFAULT FALSE,
    is_feature_toggle BOOLEAN DEFAULT FALSE,
    labels TEXT[],
    description VARCHAR(1000),
    version INTEGER NOT NULL,
    etag VARCHAR(64) NOT NULL,
    created_at TIMESTAMP NOT NULL,
    updated_at TIMESTAMP NOT NULL,
    created_by VARCHAR(200) NOT NULL,
    updated_by VARCHAR(200) NOT NULL,
    config_set_id UUID NOT NULL,
    tenant_id UUID NOT NULL,
    CONSTRAINT fk_config_set FOREIGN KEY (config_set_id) 
        REFERENCES config_bundles(id),
    CONSTRAINT uk_config_key UNIQUE (config_set_id, tenant_id, key)
);

CREATE INDEX idx_config_items_tenant ON config_items(tenant_id);
CREATE INDEX idx_config_items_config_set ON config_items(config_set_id);

Migration Job

apiVersion: batch/v1
kind: Job
metadata:
  name: config-registry-migration
  namespace: config-platform
spec:
  template:
    spec:
      containers:
      - name: migration
        image: connectsoft.azurecr.io/config-registry:1.0.0
        command: ["dotnet", "ConfigRegistry.dll", "migrate"]
        env:
        - name: ConnectionStrings__DefaultConnection
          valueFrom:
            secretKeyRef:
              name: config-registry-secrets
              key: database-connection
      restartPolicy: Never
  backoffLimit: 3

Environment Configuration

Development Environment

# k8s/dev/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: config-platform-dev
---
# k8s/dev/config-registry-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: config-registry
  namespace: config-platform-dev
spec:
  replicas: 1
  # ... (same as production but with dev-specific settings)

Production Environment

# k8s/prod/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: config-platform-prod
---
# k8s/prod/config-registry-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: config-registry
  namespace: config-platform-prod
spec:
  replicas: 5
  # ... (production settings with higher resources)

Scaling Patterns

Auto-Scaling Based on Metrics

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: config-registry-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: config-registry
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "100"
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Database Connection Pooling

services.AddNHibernate(options =>
{
    options.ConnectionString = configuration.GetConnectionString("DefaultConnection");
    options.ConnectionPoolSize = 20;
    options.MaxPoolSize = 100;
    options.MinPoolSize = 5;
});

High Availability Patterns

Multi-Region Deployment

# Primary region
apiVersion: apps/v1
kind: Deployment
metadata:
  name: config-registry-us-east
  namespace: config-platform
  labels:
    region: us-east
spec:
  replicas: 5
  # ...

# Secondary region
apiVersion: apps/v1
kind: Deployment
metadata:
  name: config-registry-us-west
  namespace: config-platform
  labels:
    region: us-west
spec:
  replicas: 3
  # ...

Redis Cluster Configuration

apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
data:
  redis.conf: |
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 5000
    appendonly yes

Health Checks

Liveness Probe

app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("live")
});

Readiness Probe

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready")
});

Startup Probe

app.MapHealthChecks("/health/startup", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("startup")
});

References