Files
metadata-agregator/docs/research/bedrock-api/analysis/DEPLOYMENT.md
T
Alexander a1f6701bac feat: initial implementation of metadata aggregator
- gRPC service with MusicBrainz provider
- PostgreSQL schema with migrations
- Service layer with database-first caching
- Repository pattern for data access
- YAML configuration support
- Research documentation for 17 music metadata projects
2026-04-28 16:28:53 +02:00

1040 lines
23 KiB
Markdown

# Bedrock-API Deployment
## Containerization
### Dockerfile
**File**: `Dockerfile`
**Strategy**: Multi-stage build (builder + runtime)
```dockerfile
# Builder stage
FROM golang:1.23-alpine AS builder
WORKDIR /app
# Install git (required for submodules)
RUN apk add --no-cache git
# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Initialize submodules
RUN git submodule update --init --recursive
# Build binary
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o bedrock-server ./bedrock_server
# Runtime stage
FROM alpine:latest
# Install ca-certificates (required for HTTPS requests to provider APIs)
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# Copy binary from builder
COPY --from=builder /app/bedrock-server .
# Copy migrations (if needed)
COPY --from=builder /app/db/migrations ./db/migrations
# Expose ports
EXPOSE 50052 8080
# Run server
CMD ["./bedrock-server"]
```
**Build Stages**:
1. **Builder** (`golang:1.23-alpine`):
- Installs git for submodule support
- Downloads Go dependencies
- Initializes spotapi-go submodule
- Compiles binary with optimizations (`-ldflags="-w -s"`)
- CGO disabled for static binary
2. **Runtime** (`alpine:latest`):
- Minimal image (~5 MB base)
- Installs ca-certificates for HTTPS
- Copies binary from builder
- Exposes gRPC (50052) and HTTP (8080) ports
**Image Size**: ~20 MB (builder stage discarded)
**Version Mismatch**: Dockerfile uses Go 1.23, but `go.mod` specifies 1.25
**Fix**:
```dockerfile
FROM golang:1.25-alpine AS builder
```
### Docker Build
**Build Command**:
```bash
docker build -t bedrock-api:latest .
```
**Build Arguments** (not implemented):
```dockerfile
ARG GO_VERSION=1.25
FROM golang:${GO_VERSION}-alpine AS builder
```
**Build Time**: ~2-3 minutes (first build), ~30 seconds (cached)
### Docker Run
**Run Command**:
```bash
docker run -d \
--name bedrock-api \
-p 50052:50052 \
-p 8080:8080 \
-e DATABASE_URL=postgresql://user:pass@host:5432/bedrock \
-e JWT_SECRET=your-secret \
-e SPOTIFY_CLIENT_ID=your-id \
-e SPOTIFY_CLIENT_SECRET=your-secret \
-e SOUNDCLOUD_CLIENT_IDS=id1,id2,id3 \
-e GENIUS_ACCESS_TOKEN=your-token \
bedrock-api:latest
```
**Environment Variables**: Passed via `-e` flags (no `.env` file in container)
**Port Mapping**:
- `50052:50052` - gRPC server
- `8080:8080` - HTTP proxy
**No Volume Mounts**: Binary is stateless (no local file storage)
## Docker Compose
### Compose File
**File**: `docker-compose.yml`
```yaml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: bedrock-postgres
environment:
POSTGRES_USER: bedrock
POSTGRES_PASSWORD: bedrock
POSTGRES_DB: bedrock
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U bedrock"]
interval: 10s
timeout: 5s
retries: 5
networks:
- bedrock-network
volumes:
postgres_data:
driver: local
networks:
bedrock-network:
driver: bridge
```
**Services**: PostgreSQL only (application not included)
**Missing Services**:
- No application service (must be added or run separately)
- No Redis (planned for caching)
- No reverse proxy (nginx, Caddy)
- No monitoring (Prometheus, Grafana)
### Complete Compose File (Recommended)
```yaml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: bedrock-postgres
environment:
POSTGRES_USER: bedrock
POSTGRES_PASSWORD: bedrock
POSTGRES_DB: bedrock
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./db/migrations:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U bedrock"]
interval: 10s
timeout: 5s
retries: 5
networks:
- bedrock-network
bedrock-api:
build: .
container_name: bedrock-api
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: postgresql://bedrock:bedrock@postgres:5432/bedrock?sslmode=disable
JWT_SECRET: ${JWT_SECRET}
SPOTIFY_CLIENT_ID: ${SPOTIFY_CLIENT_ID}
SPOTIFY_CLIENT_SECRET: ${SPOTIFY_CLIENT_SECRET}
SOUNDCLOUD_CLIENT_IDS: ${SOUNDCLOUD_CLIENT_IDS}
GENIUS_ACCESS_TOKEN: ${GENIUS_ACCESS_TOKEN}
YOUTUBE_COOKIES: ${YOUTUBE_COOKIES}
ports:
- "50052:50052"
- "8080:8080"
networks:
- bedrock-network
restart: unless-stopped
volumes:
postgres_data:
networks:
bedrock-network:
```
**Improvements**:
- Application service added
- Health check dependency (waits for PostgreSQL)
- Environment variables from `.env` file
- Automatic restart policy
- Migration initialization via volume mount
### Compose Commands
**Start Services**:
```bash
docker-compose up -d
```
**View Logs**:
```bash
docker-compose logs -f bedrock-api
```
**Stop Services**:
```bash
docker-compose down
```
**Rebuild**:
```bash
docker-compose up -d --build
```
**Clean Volumes**:
```bash
docker-compose down -v
```
## Local Development
### Prerequisites
- Go 1.25+
- PostgreSQL 15+
- Git (for submodules)
### Setup Steps
**1. Clone Repository**:
```bash
git clone https://github.com/feralbureau/bedrock-api
cd bedrock-api
```
**2. Initialize Submodules**:
```bash
git submodule update --init --recursive
```
**3. Install Dependencies**:
```bash
go mod download
```
**4. Setup Database**:
```bash
# Start PostgreSQL (Docker)
docker run -d \
--name bedrock-postgres \
-e POSTGRES_USER=bedrock \
-e POSTGRES_PASSWORD=bedrock \
-e POSTGRES_DB=bedrock \
-p 5432:5432 \
postgres:15-alpine
# Run migrations
psql postgresql://bedrock:bedrock@localhost:5432/bedrock -f db/migrations/001_create_users_table.up.sql
```
**5. Configure Environment**:
```bash
cp .env.example .env
# Edit .env with your credentials
```
**Example `.env`**:
```
DATABASE_URL=postgresql://bedrock:bedrock@localhost:5432/bedrock?sslmode=disable
JWT_SECRET=your-secret-key-change-this-in-production
SPOTIFY_CLIENT_ID=your_spotify_client_id
SPOTIFY_CLIENT_SECRET=your_spotify_client_secret
SOUNDCLOUD_CLIENT_IDS=client_id_1,client_id_2,client_id_3
DEEZER_APP_ID=your_deezer_app_id
YOUTUBE_COOKIES=your_youtube_cookies
GENIUS_ACCESS_TOKEN=your_genius_access_token
```
**6. Run Server**:
```bash
go run ./bedrock_server
```
**7. Verify**:
```bash
# gRPC health check (requires grpcurl)
grpcurl -plaintext localhost:50052 bedrock.BedrockService/GetServiceStatus
# HTTP proxy check
curl http://localhost:8080/stream/soundcloud/1234567890
```
### Development Workflow
**Hot Reload** (not configured):
```bash
# Install air
go install github.com/cosmtrek/air@latest
# Run with hot reload
air
```
**Example `.air.toml`**:
```toml
root = "."
tmp_dir = "tmp"
[build]
cmd = "go build -o ./tmp/main ./bedrock_server"
bin = "tmp/main"
include_ext = ["go", "proto"]
exclude_dir = ["tmp", "vendor"]
delay = 1000
```
### Testing
**Run Tests**:
```bash
go test ./...
```
**Integration Tests** (requires provider credentials):
```bash
export SPOTIFY_CLIENT_ID=your_id
export SPOTIFY_CLIENT_SECRET=your_secret
export SOUNDCLOUD_CLIENT_IDS=your_ids
export GENIUS_ACCESS_TOKEN=your_token
export BEDROCK_TEST_ADDR=localhost:50052
go test -v ./tests/
```
**Test Coverage**:
```bash
go test -cover ./...
```
## CI/CD Pipeline
### GitHub Actions
**Workflows**:
- `test.yml` - Integration tests
- `lint.yml` - Code linting
### Test Workflow
**File**: `.github/workflows/test.yml`
```yaml
name: Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_USER: bedrock
POSTGRES_PASSWORD: bedrock
POSTGRES_DB: bedrock
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.24'
- name: Download dependencies
run: go mod download
- name: Run migrations
run: |
psql postgresql://bedrock:bedrock@localhost:5432/bedrock -f db/migrations/001_create_users_table.up.sql
- name: Run tests
env:
DATABASE_URL: postgresql://bedrock:bedrock@localhost:5432/bedrock?sslmode=disable
JWT_SECRET: test-secret
SPOTIFY_CLIENT_ID: ${{ secrets.SPOTIFY_CLIENT_ID }}
SPOTIFY_CLIENT_SECRET: ${{ secrets.SPOTIFY_CLIENT_SECRET }}
SOUNDCLOUD_CLIENT_IDS: ${{ secrets.SOUNDCLOUD_CLIENT_IDS }}
GENIUS_ACCESS_TOKEN: ${{ secrets.GENIUS_ACCESS_TOKEN }}
YOUTUBE_COOKIES: ${{ secrets.YOUTUBE_COOKIES }}
run: go test -v -timeout 120s ./tests/
```
**Features**:
- PostgreSQL service container
- Submodule initialization
- Go 1.24 (should be 1.25 to match go.mod)
- Migration execution
- Integration tests with provider secrets
- 120 second timeout
**Required Secrets**:
- `SPOTIFY_CLIENT_ID`
- `SPOTIFY_CLIENT_SECRET`
- `SOUNDCLOUD_CLIENT_IDS`
- `GENIUS_ACCESS_TOKEN`
- `YOUTUBE_COOKIES`
### Lint Workflow
**File**: `.github/workflows/lint.yml`
```yaml
name: Lint
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
golangci-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.24'
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
comment-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Check for decorative comments
run: |
# Fail if decorative comments found (e.g., // ========)
if grep -r "^[[:space:]]*//[[:space:]]*[=\-*#]\{3,\}" --include="*.go" .; then
echo "Decorative comments found"
exit 1
fi
- name: Check for uppercase-leading comments
run: |
# Fail if comments start with uppercase (except TODO, FIXME, NOTE)
if grep -r "^[[:space:]]*//[[:space:]]*[A-Z]" --include="*.go" . | grep -v "TODO\|FIXME\|NOTE"; then
echo "Uppercase-leading comments found"
exit 1
fi
```
**Linters**:
- `golangci-lint` - Standard Go linting (gofmt, govet, staticcheck, etc.)
- Custom comment linter - Enforces comment style (no decorative comments, no uppercase-leading)
**Comment Rules**:
- No decorative comments (`// ========`, `// --------`, etc.)
- No uppercase-leading comments (except `TODO`, `FIXME`, `NOTE`)
## Production Deployment
### Reverse Proxy (TLS Termination)
**No Built-in TLS**: Application must be deployed behind reverse proxy
**Nginx Example**:
```nginx
upstream bedrock_grpc {
server localhost:50052;
}
upstream bedrock_http {
server localhost:8080;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
# gRPC endpoint
location /bedrock.BedrockService/ {
grpc_pass grpc://bedrock_grpc;
grpc_set_header X-Real-IP $remote_addr;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# HTTP proxy endpoints
location /stream/ {
proxy_pass http://bedrock_http;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffering off;
}
location /cover/ {
proxy_pass http://bedrock_http;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
**Caddy Example** (simpler):
```
api.example.com {
reverse_proxy /bedrock.BedrockService/* h2c://localhost:50052
reverse_proxy /stream/* localhost:8080
reverse_proxy /cover/* localhost:8080
}
```
### Systemd Service
**File**: `/etc/systemd/system/bedrock-api.service`
```ini
[Unit]
Description=Bedrock API Server
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=simple
User=bedrock
Group=bedrock
WorkingDirectory=/opt/bedrock-api
EnvironmentFile=/opt/bedrock-api/.env
ExecStart=/opt/bedrock-api/bedrock-server
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=bedrock-api
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/bedrock-api/logs
[Install]
WantedBy=multi-user.target
```
**Commands**:
```bash
# Enable service
sudo systemctl enable bedrock-api
# Start service
sudo systemctl start bedrock-api
# Check status
sudo systemctl status bedrock-api
# View logs
sudo journalctl -u bedrock-api -f
```
### Environment Variables (Production)
**Secure Storage**: Use secrets management (not `.env` file)
**AWS Secrets Manager**:
```bash
aws secretsmanager get-secret-value --secret-id bedrock-api/production --query SecretString --output text > /tmp/secrets.env
source /tmp/secrets.env
rm /tmp/secrets.env
```
**HashiCorp Vault**:
```bash
vault kv get -format=json secret/bedrock-api/production | jq -r '.data.data | to_entries[] | "\(.key)=\(.value)"' > /tmp/secrets.env
source /tmp/secrets.env
rm /tmp/secrets.env
```
**Kubernetes Secrets**:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: bedrock-api-secrets
type: Opaque
stringData:
DATABASE_URL: postgresql://user:pass@postgres:5432/bedrock
JWT_SECRET: your-secret
SPOTIFY_CLIENT_ID: your-id
SPOTIFY_CLIENT_SECRET: your-secret
SOUNDCLOUD_CLIENT_IDS: id1,id2,id3
GENIUS_ACCESS_TOKEN: your-token
```
### Database Migrations (Production)
**Manual Execution** (current):
```bash
psql $DATABASE_URL -f db/migrations/001_create_users_table.up.sql
```
**Automated with golang-migrate** (recommended):
```bash
# Install migrate
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.16.2/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate /usr/local/bin/
# Run migrations
migrate -path db/migrations -database $DATABASE_URL up
# Rollback
migrate -path db/migrations -database $DATABASE_URL down 1
```
**Migration Tracking**:
```sql
-- golang-migrate creates this table automatically
SELECT * FROM schema_migrations;
```
### Monitoring (Not Implemented)
**Recommended Stack**:
- Prometheus (metrics collection)
- Grafana (visualization)
- Loki (log aggregation)
- Jaeger (distributed tracing)
**Prometheus Metrics** (to implement):
```go
import "github.com/prometheus/client_golang/prometheus"
var (
requestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "bedrock_requests_total",
Help: "Total number of requests",
},
[]string{"method", "status"},
)
requestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "bedrock_request_duration_seconds",
Help: "Request duration in seconds",
},
[]string{"method"},
)
providerErrors = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "bedrock_provider_errors_total",
Help: "Total provider errors",
},
[]string{"provider"},
)
)
```
**Grafana Dashboard** (example queries):
```promql
# Request rate
rate(bedrock_requests_total[5m])
# Error rate
rate(bedrock_requests_total{status="error"}[5m]) / rate(bedrock_requests_total[5m])
# P95 latency
histogram_quantile(0.95, rate(bedrock_request_duration_seconds_bucket[5m]))
# Provider error rate
rate(bedrock_provider_errors_total[5m])
```
### Logging (Production)
**Structured Logging** (to implement):
```go
import "go.uber.org/zap"
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("search request",
zap.String("query", query),
zap.Int32("limit", limit),
zap.String("user_id", userID),
)
logger.Error("provider failed",
zap.String("provider", "spotify"),
zap.Error(err),
)
```
**Log Aggregation** (Loki):
```yaml
# promtail config
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: bedrock-api
static_configs:
- targets:
- localhost
labels:
job: bedrock-api
__path__: /var/log/bedrock-api/*.log
```
### Backup Strategy
**PostgreSQL Backups**:
```bash
# Daily backup script
#!/bin/bash
BACKUP_DIR=/backups/bedrock-api
DATE=$(date +%Y%m%d_%H%M%S)
pg_dump $DATABASE_URL | gzip > $BACKUP_DIR/bedrock_$DATE.sql.gz
# Keep last 30 days
find $BACKUP_DIR -name "bedrock_*.sql.gz" -mtime +30 -delete
# Upload to S3
aws s3 cp $BACKUP_DIR/bedrock_$DATE.sql.gz s3://backups/bedrock-api/
```
**Cron Schedule**:
```cron
0 2 * * * /opt/bedrock-api/scripts/backup.sh
```
**Point-in-Time Recovery** (WAL archiving):
```sql
-- Enable WAL archiving in postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'aws s3 cp %p s3://backups/bedrock-api/wal/%f'
```
### Scaling Strategies
**Vertical Scaling**:
- Increase CPU/RAM for single instance
- Increase PostgreSQL resources
- Increase connection pool size
**Horizontal Scaling**:
- Run multiple application instances behind load balancer
- Use read replicas for PostgreSQL (if read-heavy)
- Add Redis for caching (reduce provider API calls)
**Load Balancer** (nginx):
```nginx
upstream bedrock_grpc {
server bedrock-api-1:50052;
server bedrock-api-2:50052;
server bedrock-api-3:50052;
}
server {
listen 443 ssl http2;
location /bedrock.BedrockService/ {
grpc_pass grpc://bedrock_grpc;
}
}
```
**Kubernetes Deployment**:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: bedrock-api
spec:
replicas: 3
selector:
matchLabels:
app: bedrock-api
template:
metadata:
labels:
app: bedrock-api
spec:
containers:
- name: bedrock-api
image: bedrock-api:latest
ports:
- containerPort: 50052
name: grpc
- containerPort: 8080
name: http
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: bedrock-api-secrets
key: DATABASE_URL
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
exec:
command:
- grpc_health_probe
- -addr=:50052
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
exec:
command:
- grpc_health_probe
- -addr=:50052
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: bedrock-api
spec:
selector:
app: bedrock-api
ports:
- name: grpc
port: 50052
targetPort: 50052
- name: http
port: 8080
targetPort: 8080
type: LoadBalancer
```
## Deployment Checklist
### Pre-Deployment
- [ ] Update Go version in Dockerfile to match go.mod (1.25)
- [ ] Configure environment variables (secrets management)
- [ ] Run database migrations
- [ ] Test provider credentials
- [ ] Configure reverse proxy (TLS)
- [ ] Set up monitoring (Prometheus, Grafana)
- [ ] Set up logging (structured logs, aggregation)
- [ ] Configure backups (PostgreSQL, WAL archiving)
- [ ] Load test (ensure performance under load)
- [ ] Security audit (JWT secret, database credentials, etc.)
### Post-Deployment
- [ ] Verify gRPC endpoint (grpcurl)
- [ ] Verify HTTP proxy endpoints (curl)
- [ ] Check logs for errors
- [ ] Monitor metrics (request rate, error rate, latency)
- [ ] Test authentication (register, login, refresh)
- [ ] Test search (all providers)
- [ ] Test streaming (SoundCloud, YouTube Music)
- [ ] Test lyrics (LrcLib, Genius)
- [ ] Verify database connection
- [ ] Test backup restoration
### Ongoing Maintenance
- [ ] Monitor provider API changes
- [ ] Rotate JWT secret periodically
- [ ] Update dependencies (go mod tidy)
- [ ] Review logs for errors
- [ ] Monitor disk usage (PostgreSQL, logs)
- [ ] Test backup restoration monthly
- [ ] Update TLS certificates (Let's Encrypt auto-renewal)
- [ ] Review security advisories (Go, dependencies)
## Deployment Environments
### Development
**Infrastructure**: Local machine or Docker Compose
**Database**: PostgreSQL in Docker
**Secrets**: `.env` file
**TLS**: No (HTTP only)
**Monitoring**: No
**Backups**: No
### Staging
**Infrastructure**: Single VM or Kubernetes cluster
**Database**: Managed PostgreSQL (AWS RDS, Google Cloud SQL)
**Secrets**: Secrets manager (AWS Secrets Manager, Vault)
**TLS**: Yes (Let's Encrypt)
**Monitoring**: Prometheus + Grafana
**Backups**: Daily automated backups
### Production
**Infrastructure**: Kubernetes cluster (multi-region)
**Database**: Managed PostgreSQL with read replicas
**Secrets**: Secrets manager with rotation
**TLS**: Yes (Let's Encrypt or commercial cert)
**Monitoring**: Full observability stack (Prometheus, Grafana, Loki, Jaeger)
**Backups**: Hourly backups + WAL archiving + point-in-time recovery
**Scaling**: Horizontal pod autoscaling (HPA)
**High Availability**: Multi-zone deployment, load balancing
## Cost Estimation (AWS)
### Small Deployment (1000 requests/day)
| Resource | Specification | Monthly Cost |
|----------|---------------|--------------|
| EC2 Instance | t3.small (2 vCPU, 2 GB RAM) | $15 |
| RDS PostgreSQL | db.t3.micro (1 vCPU, 1 GB RAM) | $15 |
| Load Balancer | Application Load Balancer | $20 |
| Data Transfer | 100 GB/month | $9 |
| **Total** | | **$59/month** |
### Medium Deployment (100k requests/day)
| Resource | Specification | Monthly Cost |
|----------|---------------|--------------|
| EC2 Instances | 3x t3.medium (2 vCPU, 4 GB RAM) | $90 |
| RDS PostgreSQL | db.t3.small (2 vCPU, 2 GB RAM) | $30 |
| ElastiCache Redis | cache.t3.micro (1 vCPU, 0.5 GB RAM) | $12 |
| Load Balancer | Application Load Balancer | $20 |
| Data Transfer | 1 TB/month | $90 |
| **Total** | | **$242/month** |
### Large Deployment (1M requests/day)
| Resource | Specification | Monthly Cost |
|----------|---------------|--------------|
| EKS Cluster | Control plane | $73 |
| EC2 Instances | 10x t3.large (2 vCPU, 8 GB RAM) | $600 |
| RDS PostgreSQL | db.r5.large (2 vCPU, 16 GB RAM) + read replica | $300 |
| ElastiCache Redis | cache.r5.large (2 vCPU, 13 GB RAM) | $150 |
| Load Balancer | Application Load Balancer | $20 |
| Data Transfer | 10 TB/month | $900 |
| **Total** | | **$2,043/month** |
**Note**: Costs exclude provider API fees (Spotify, Genius, etc.)
## Deployment Recommendations for Metadata Aggregator
### Adopt
- Multi-stage Docker build (minimal runtime image)
- Docker Compose for local development
- GitHub Actions for CI/CD
- Reverse proxy for TLS termination
- Systemd service for production
### Avoid
- Manual migrations (use golang-migrate)
- No monitoring (implement Prometheus)
- No structured logging (use zap or zerolog)
- Go version mismatch (keep Dockerfile and go.mod in sync)
### Enhance
- Add health check endpoint (implement GetServiceStatus properly)
- Add graceful shutdown (handle SIGTERM)
- Add readiness probe (check database connection)
- Add metrics endpoint (/metrics for Prometheus)
- Add Redis for caching
- Add backup automation
- Add deployment documentation