# MiniMediaMetadataAPI - Deployment Analysis ## Containerization ### Dockerfile **Location:** `Dockerfile` (project root) **Strategy:** Multi-stage build **Full Dockerfile:** ```dockerfile # Stage 1: Base runtime image FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 8080 EXPOSE 8081 # Stage 2: Build environment FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src # Copy project files COPY ["MiniMediaMetadataAPI/MiniMediaMetadataAPI.csproj", "MiniMediaMetadataAPI/"] COPY ["MiniMediaMetadataAPI.Application/MiniMediaMetadataAPI.Application.csproj", "MiniMediaMetadataAPI.Application/"] # Restore dependencies RUN dotnet restore "MiniMediaMetadataAPI/MiniMediaMetadataAPI.csproj" # Copy source code COPY . . # Build project WORKDIR "/src/MiniMediaMetadataAPI" RUN dotnet build "MiniMediaMetadataAPI.csproj" -c $BUILD_CONFIGURATION -o /app/build # Stage 3: Publish FROM build AS publish ARG BUILD_CONFIGURATION=Release RUN dotnet publish "MiniMediaMetadataAPI.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false # Stage 4: Final runtime image FROM base AS final WORKDIR /app COPY --from=publish /app/publish . # Run as non-root user USER $APP_UID ENTRYPOINT ["dotnet", "MiniMediaMetadataAPI.dll"] ``` **Image Layers:** 1. **base:** ASP.NET Core 8.0 runtime (minimal) 2. **build:** .NET SDK 8.0 (includes build tools) 3. **publish:** Compiled application artifacts 4. **final:** Runtime + published app (smallest) **Port Exposure:** - **8080:** HTTP endpoint - **8081:** HTTPS endpoint (unused, HTTPS disabled) **Security Features:** - Non-root user (`$APP_UID` from base image) - Multi-stage build (no SDK in final image) - Minimal attack surface **Image Size:** - Base image: ~200 MB (aspnet:8.0) - Application layer: ~20 MB - **Total:** ~220 MB **Build Time:** - Restore: 10-30 seconds - Build: 20-40 seconds - Publish: 5-10 seconds - **Total:** ~1 minute ### Docker Compose (Development) **Location:** `compose.yaml` (project root) **Minimal Configuration:** ```yaml services: minimediametadataapi: image: minimediametadataapi build: context: . dockerfile: Dockerfile ``` **Features:** - Build only (no runtime configuration) - No port mapping - No environment variables - No volume mounts - No network configuration - No health checks **Purpose:** Development build testing only **Not Suitable For:** Running the application ### Docker Compose (Production) **Location:** Not in repository (documented in README) **Production Configuration:** ```yaml version: '3.8' services: minimediametadataapi: image: musicmovearr/minimediametadataapi:latest container_name: minimediametadataapi ports: - "56232:8080" volumes: - ./appsettings.json:/app/appsettings.json:ro environment: - ASPNETCORE_ENVIRONMENT=Production deploy: resources: limits: memory: 256M restart: unless-stopped depends_on: - postgres networks: - media-network postgres: image: postgres:16 container_name: minimediametadata-db environment: - POSTGRES_DB=minimediametadata - POSTGRES_USER=postgres - POSTGRES_PASSWORD=${DB_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data networks: - media-network volumes: postgres-data: networks: media-network: driver: bridge ``` **Key Configuration:** | Setting | Value | Purpose | |---------|-------|---------| | Port Mapping | 56232:8080 | External:Internal HTTP | | Memory Limit | 256M | Resource constraint | | Restart Policy | unless-stopped | Auto-restart on failure | | Volume Mount | appsettings.json | Configuration override | | Environment | Production | ASP.NET Core environment | | Network | media-network | Isolated network | **Dependencies:** - PostgreSQL 16 (separate container) - Shared network for database connectivity **Missing:** - Health checks - Logging configuration - Prometheus metrics port - HTTPS configuration - Resource CPU limits ## CI/CD Pipeline ### GitHub Actions **Location:** `.github/workflows/docker-image.yml` **Workflow:** ```yaml name: Docker Image CI on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile push: true tags: | musicmovearr/minimediametadataapi:latest musicmovearr/minimediametadataapi:${{ github.sha }} cache-from: type=registry,ref=musicmovearr/minimediametadataapi:buildcache cache-to: type=registry,ref=musicmovearr/minimediametadataapi:buildcache,mode=max ``` **Triggers:** - Push to `main` branch - Pull request to `main` branch **Steps:** 1. Checkout code 2. Set up Docker Buildx (multi-platform builds) 3. Log in to Docker Hub 4. Build Docker image 5. Push to Docker Hub with tags **Tags:** - `latest` - Most recent build - `` - Specific commit (e.g., `abc123def456`) **Caching:** - Registry cache for faster builds - Reuses layers from previous builds **Secrets Required:** - `DOCKER_USERNAME` - Docker Hub username - `DOCKER_PASSWORD` - Docker Hub password/token **Build Time:** 2-5 minutes (with cache) **Missing Steps:** - No test execution - No code quality checks - No security scanning - No deployment automation - No rollback mechanism ### Docker Hub **Repository:** `musicmovearr/minimediametadataapi` **Visibility:** Public **Tags:** - `latest` - Latest main branch build - `` - Specific commit builds **Image Pulls:** Unknown (public repository) **Automated Builds:** Via GitHub Actions (not Docker Hub auto-build) ## Configuration Management ### appsettings.json **Location:** `MiniMediaMetadataAPI/appsettings.json` **Default Configuration:** ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "DatabaseConfiguration": { "ConnectionString": "Host=localhost;Database=minimediametadata;Username=postgres;Password=postgres;MinPoolSize=5;MaxPoolSize=100" }, "Prometheus": { "MetricsUrl": "/metrics" } } ``` **Environment-Specific Overrides:** **appsettings.Development.json:** ```json { "Logging": { "LogLevel": { "Default": "Debug", "Microsoft.AspNetCore": "Information" } } } ``` **appsettings.Production.json:** Not included (use volume mount) **Configuration Hierarchy:** 1. `appsettings.json` (base) 2. `appsettings.{Environment}.json` (override) 3. Environment variables (override) 4. Command-line arguments (override) **Sensitive Data:** - Database password in plain text (NOT SECURE) - No secrets management - No encryption **Recommended Approach:** ```json { "DatabaseConfiguration": { "ConnectionString": "Host=${DB_HOST};Database=${DB_NAME};Username=${DB_USER};Password=${DB_PASSWORD};MinPoolSize=5;MaxPoolSize=100" } } ``` **Environment Variables:** ```bash export DB_HOST=postgres export DB_NAME=minimediametadata export DB_USER=postgres export DB_PASSWORD=secure_password_here ``` ### Volume Mounts **Production Pattern:** ```yaml volumes: - ./appsettings.json:/app/appsettings.json:ro ``` **Benefits:** - Configuration changes without rebuild - Environment-specific settings - Secrets outside image **Limitations:** - Requires file on host - Manual synchronization - No version control for production config **Alternative: Environment Variables** ```yaml environment: - DatabaseConfiguration__ConnectionString=Host=postgres;Database=minimediametadata;Username=postgres;Password=${DB_PASSWORD} - Prometheus__MetricsUrl=/metrics ``` **ASP.NET Core Syntax:** Double underscore (`__`) for nested properties. ## Deployment Environments ### Development **Setup:** ```bash # Clone repository git clone https://github.com/MusicMoveArr/MiniMediaMetadataAPI.git cd MiniMediaMetadataAPI # Run with .NET CLI dotnet run --project MiniMediaMetadataAPI # Or with Docker docker build -t minimediametadataapi . docker run -p 8080:8080 minimediametadataapi ``` **Database:** Local PostgreSQL or Docker container **Configuration:** `appsettings.Development.json` **Logging:** Debug level ### Staging **Not Documented:** No staging environment configuration **Recommended Setup:** - Separate Docker Compose file - Staging database (copy of production schema) - Production-like resource limits - Monitoring and logging ### Production **Deployment Method:** Docker Compose **Steps:** ```bash # Pull latest image docker pull musicmovearr/minimediametadataapi:latest # Create appsettings.json with production values cat > appsettings.json < { configuration .ReadFrom.Configuration(context.Configuration) .Enrich.FromLogContext() .Enrich.WithProperty("Application", "MiniMediaMetadataAPI") .WriteTo.Console(new JsonFormatter()) .WriteTo.File( new JsonFormatter(), "/app/logs/log-.json", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7); }); ``` ## Monitoring ### Prometheus Metrics **Endpoint:** `/metrics` **Exposed Metrics:** - `minimediametadataapi_request_total` (counter) **Prometheus Configuration:** ```yaml scrape_configs: - job_name: 'minimediametadataapi' static_configs: - targets: ['minimediametadataapi:8080'] metrics_path: '/metrics' scrape_interval: 15s ``` **Grafana Dashboard:** Not provided **Recommended Metrics:** - Request duration histogram - Database query duration - Error rate by provider - Active requests gauge - Connection pool usage ### Application Performance Monitoring **Status:** Not configured **Recommended: Application Insights** ```csharp builder.Services.AddApplicationInsightsTelemetry(options => { options.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"]; }); ``` **Alternatives:** - New Relic - Datadog APM - Elastic APM - Jaeger (distributed tracing) ## Scaling ### Horizontal Scaling **Docker Compose:** ```yaml services: minimediametadataapi: image: musicmovearr/minimediametadataapi:latest deploy: replicas: 3 resources: limits: memory: 256M ``` **Kubernetes:** ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: minimediametadataapi spec: replicas: 3 selector: matchLabels: app: minimediametadataapi template: metadata: labels: app: minimediametadataapi spec: containers: - name: api image: musicmovearr/minimediametadataapi:latest resources: limits: memory: 256M cpu: 1 ``` **Load Balancer:** ```yaml apiVersion: v1 kind: Service metadata: name: minimediametadataapi spec: type: LoadBalancer selector: app: minimediametadataapi ports: - port: 80 targetPort: 8080 ``` **Considerations:** - Stateless design (scales easily) - Database connection pool per instance - No session affinity needed - No distributed cache (yet) ### Vertical Scaling **Current:** 256 MB memory, no CPU limit **Scaling Up:** ```yaml deploy: resources: limits: memory: 512M cpu: 2 ``` **Diminishing Returns:** Beyond 512 MB, horizontal scaling more effective. ## Backup and Recovery ### Application **Backup:** Not needed (stateless) **Recovery:** Redeploy from Docker Hub **Rollback:** ```bash # Deploy specific version docker pull musicmovearr/minimediametadataapi: docker-compose up -d ``` ### Database **Responsibility:** Database administrator (not API) **Backup Strategy:** ```bash # Backup docker exec minimediametadata-db pg_dump -U postgres minimediametadata > backup.sql # Restore docker exec -i minimediametadata-db psql -U postgres minimediametadata < backup.sql ``` **Automated Backups:** ```yaml services: postgres-backup: image: prodrigestivill/postgres-backup-local environment: - POSTGRES_HOST=postgres - POSTGRES_DB=minimediametadata - POSTGRES_USER=postgres - POSTGRES_PASSWORD=${DB_PASSWORD} - SCHEDULE=@daily - BACKUP_KEEP_DAYS=7 volumes: - ./backups:/backups ``` ## Security ### Image Security **Base Image:** `mcr.microsoft.com/dotnet/aspnet:8.0` **Vulnerabilities:** Check with `docker scan` **Updates:** Monthly .NET patch releases **Non-Root User:** Yes (`$APP_UID`) **Secrets in Image:** No (configuration via volume mount) ### Network Security **HTTPS:** Disabled (expects reverse proxy) **Firewall:** Host-level (not container-level) **Network Isolation:** Docker bridge network **Recommendations:** - Enable HTTPS in production - Use secrets management (Docker secrets, Kubernetes secrets) - Implement network policies (Kubernetes) - Regular security scanning ## Deployment Checklist **Pre-Deployment:** - [ ] Database schema exists (via MiniMediaScanner) - [ ] PostgreSQL accessible from API container - [ ] `appsettings.json` configured with production values - [ ] Secrets stored securely (not in image) - [ ] Docker Hub credentials configured (CI/CD) **Deployment:** - [ ] Pull latest image - [ ] Update `docker-compose.yml` if needed - [ ] Start containers: `docker-compose up -d` - [ ] Verify API responds: `curl http://localhost:56232/swagger` - [ ] Check metrics: `curl http://localhost:56232/metrics` - [ ] Review logs: `docker logs minimediametadataapi` **Post-Deployment:** - [ ] Configure Prometheus scraping - [ ] Set up log aggregation - [ ] Configure alerts (uptime, errors) - [ ] Document deployment in runbook - [ ] Test rollback procedure ## Deployment Evaluation **Strengths:** - Multi-stage Docker build (small image) - Non-root user (security) - CI/CD automation (GitHub Actions) - Resource limits (memory) - Restart policy (resilience) **Weaknesses:** - No health checks - No staging environment - No automated tests in CI/CD - No security scanning - No deployment automation (manual docker-compose) - Secrets in plain text - No HTTPS configuration - No log aggregation - No APM integration **Production Readiness:** 6/10 **Recommendations:** 1. Implement health endpoints 2. Add health checks to Dockerfile 3. Configure HTTPS (reverse proxy or in-app) 4. Use secrets management 5. Add automated tests to CI/CD 6. Implement log aggregation 7. Set up APM monitoring 8. Create staging environment 9. Automate deployment (Kubernetes, Terraform) 10. Regular security scanning