Ana içeriğe geç
Versiyon: 1.0.1

Deployment Guide

This guide covers deploying Milvaion to production environments using Docker, Kubernetes, and traditional VMs.

Deployment Checklist

Before going to production, ensure you have:

  • PostgreSQL with proper backup strategy
  • Redis with persistence enabled (RDB or AOF)
  • RabbitMQ with durable queues
  • Health checks configured
  • Resource limits defined

Docker Compose (Simple Production)

For single-server or small deployments:

docker-compose.prod.yml

version: '3.8'

services:
postgres:
image: postgres:16-alpine
container_name: milvaion-postgres
restart: always
environment:
POSTGRES_DB: MilvaionDb
POSTGRES_USER: milvaion
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backups:/backups
healthcheck:
test: ["CMD-SHELL", "pg_isready -U milvaion -d MilvaionDb"]
interval: 10s
timeout: 5s
retries: 5

redis:
image: redis:7-alpine
container_name: milvaion-redis
restart: always
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5

rabbitmq:
image: rabbitmq:3-management-alpine
container_name: milvaion-rabbitmq
restart: always
environment:
RABBITMQ_DEFAULT_USER: milvaion
RABBITMQ_DEFAULT_PASS_FILE: /run/secrets/rabbitmq_password
secrets:
- rabbitmq_password
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmqctl", "status"]
interval: 30s
timeout: 10s
retries: 5

milvaion-api:
image: milvasoft/milvaion-api:1.0.0
container_name: milvaion-api
restart: always
ports:
- "5000:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__DefaultConnectionString=Host=postgres;Port=5432;Database=MilvaionDb;Username=milvaion;Password=${DB_PASSWORD}
- MilvaionConfig__Redis__ConnectionString=redis:6379
- MilvaionConfig__Redis__Password=${REDIS_PASSWORD}
- MilvaionConfig__RabbitMQ__Host=rabbitmq
- MilvaionConfig__RabbitMQ__Username=milvaion
- MilvaionConfig__RabbitMQ__Password=${RABBITMQ_PASSWORD}
- MILVAION_ROOT_PASSWORD=admin
- MILVA_ENV=prod
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3

email-worker:
image: my-company/email-worker:1.0.0
restart: always
deploy:
replicas: 3
environment:
- Worker__WorkerId=email-worker
- Worker__MaxParallelJobs=20
- Worker__RabbitMQ__Host=rabbitmq
- Worker__RabbitMQ__Username=milvaion
- Worker__RabbitMQ__Password=${RABBITMQ_PASSWORD}
- Worker__Redis__ConnectionString=redis:6379,password=${REDIS_PASSWORD}
depends_on:
- milvaion-api

secrets:
db_password:
file: ./secrets/db_password.txt
rabbitmq_password:
file: ./secrets/rabbitmq_password.txt

volumes:
postgres_data:
redis_data:
rabbitmq_data:

Deploy Commands

# Create secrets
mkdir -p secrets
echo "your-secure-db-password" > secrets/db_password.txt
echo "your-secure-rabbitmq-password" > secrets/rabbitmq_password.txt

# Create .env file
cat > .env << EOF
DB_PASSWORD=your-secure-db-password
REDIS_PASSWORD=your-secure-redis-password
RABBITMQ_PASSWORD=your-secure-rabbitmq-password
EOF

# Deploy
docker compose -f docker-compose.prod.yml up -d

# Scale workers
docker compose -f docker-compose.prod.yml up -d --scale email-worker=5

Kubernetes Deployment

For larger, scalable deployments:

Namespace and Secrets

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: milvaion

---
# secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: milvaion-secrets
namespace: milvaion
type: Opaque
stringData:
db-password: "your-secure-db-password"
redis-password: "your-secure-redis-password"
rabbitmq-password: "your-secure-rabbitmq-password"

API Deployment

# api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: milvaion-api
namespace: milvaion
spec:
replicas: 2
selector:
matchLabels:
app: milvaion-api
template:
metadata:
labels:
app: milvaion-api
spec:
containers:
- name: api
image: milvasoft/milvaion-api:1.0.0
ports:
- containerPort: 8080
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
- name: ConnectionStrings__DefaultConnectionString
valueFrom:
secretKeyRef:
name: milvaion-secrets
key: db-connection-string
- name: MilvaionConfig__Redis__ConnectionString
value: "redis.milvaion.svc:6379"
- name: MilvaionConfig__Redis__Password
valueFrom:
secretKeyRef:
name: milvaion-secrets
key: redis-password
- name: MilvaionConfig__RabbitMQ__Host
value: "rabbitmq.milvaion.svc"
- name: MilvaionConfig__RabbitMQ__Password
valueFrom:
secretKeyRef:
name: milvaion-secrets
key: rabbitmq-password
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
name: milvaion-api
namespace: milvaion
spec:
selector:
app: milvaion-api
ports:
- port: 80
targetPort: 8080
type: ClusterIP

Worker Deployment

# worker-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: email-worker
namespace: milvaion
spec:
replicas: 3
selector:
matchLabels:
app: email-worker
template:
metadata:
labels:
app: email-worker
spec:
containers:
- name: worker
image: my-company/email-worker:1.0.0
env:
- name: Worker__WorkerId
value: "email-worker"
- name: Worker__MaxParallelJobs
value: "20"
- name: Worker__RabbitMQ__Host
value: "rabbitmq.milvaion.svc"
- name: Worker__RabbitMQ__Password
valueFrom:
secretKeyRef:
name: milvaion-secrets
key: rabbitmq-password
- name: Worker__Redis__ConnectionString
value: "redis.milvaion.svc:6379"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
exec:
command: ["cat", "/tmp/healthy"]
initialDelaySeconds: 30
periodSeconds: 10

Horizontal Pod Autoscaler

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: email-worker-hpa
namespace: milvaion
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: email-worker
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70

Health Checks

See the health check section for detailed information.


Resource Recommendations

API Server

WorkloadCPUMemoryReplicas
Small (<1K jobs/day)250m512Mi1
Medium (1K-10K jobs/day)500m2Gi1
Large (>10K jobs/day)1000m4Gi1

Workers

Job TypeCPUMemoryConcurrency
I/O-bound (email, API calls)100m128Mi50-100
CPU-bound (reports, processing)500m512Mi2-5
Memory-intensive (data analysis)250m2Gi1-2

Infrastructure

ComponentProduction Recommendation
PostgreSQL2 CPU, 4GB RAM, SSD storage
Redis1 CPU, 2GB RAM, persistence enabled
RabbitMQ2 CPU, 2GB RAM, durable queues

Logging in Workers

Structured Logging

Configure JSON logging for production:

{
"Serilog": {
"Using": ["Serilog.Sinks.Console"],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "Console",
"Args": {
"formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog"
}
}
],
"Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"]
}
}

Centralized Logging

Send logs to ELK, Seq, or cloud logging:

// Program.cs
builder.Host.UseSerilog((context, config) =>
{
config
.ReadFrom.Configuration(context.Configuration)
.WriteTo.Seq("http://seq.internal:5341")
.Enrich.WithProperty("Service", "email-worker")
.Enrich.WithProperty("Environment", context.HostingEnvironment.EnvironmentName);
});

Backup Strategy

PostgreSQL

# Daily backup script
#!/bin/bash
BACKUP_DIR="/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
pg_dump -h localhost -U milvaion MilvaionDb | gzip > $BACKUP_DIR/milvaion_$TIMESTAMP.sql.gz

# Keep last 7 days
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete

Redis

Enable persistence in redis.conf:

# RDB snapshots
save 900 1
save 300 10
save 60 10000

# AOF for durability
appendonly yes
appendfsync everysec

Security Checklist

  • All passwords are in secrets, not config files
  • TLS enabled for all external connections
  • Database accessible only from API server
  • Redis/RabbitMQ not exposed publicly
  • API behind load balancer/reverse proxy
  • Rate limiting on API endpoints
  • Audit logging enabled
  • Regular security updates

What's Next?